
最近在折腾一个内部 AI 应用需要同时对接好几家大模型服务商结果一开始就被各家的 SDK 格式差异折腾得够呛。后来我把目光投到了litellm这个开源项目上。它本身专注解决的事情很简单用一套代码风格调用几乎所有主流大模型同时承担路由、重试、费用统计这些杂活。这篇文章我会从实际使用角度拆解它适合那些正在做 LLM 应用集成、想把多家模型统一管理起来的开发者。内容会比较长但都是实测下来的真实经验。1. 从三套 SDK 到一套接口litellm 解决的真实问题1.1 多供应商接入时最让人崩溃的是什么先说一个场景。早期我负责的一个内部知识库问答系统最初只接了供应商A-example的一个闭源对话模型代码写得很顺。后来业务提了新需求希望支持另一家开源模型托管服务理由是某些场景下成本更低而且数据隐私要求也允许把请求发到私有化部署的实例上。问题马上就来了。两家服务的请求结构不一样一家用messages数组角色字段叫user、assistant另一家要求把对话历史拼成特定字符串模型名还要带版本尾巴。返回结构差异更大第一家会返回choices[0].message.content第二家则是data.outputs[0].text。仅仅是为了对齐这两家的输出我就得在业务代码里写一堆if provider xxx的分支判断那种代码越长越臭。这还只是两个供应商。当你接到第三家、第四家甚至还要兼容本地部署的开源模型时这种适配代码的维护成本会呈指数上升。我见过最典型的情况是上游服务商悄悄改了某个字段的命名规范下游所有调用这个供应商的旧逻辑全部报错降级手段只能是紧急上线补丁。说白了多供应商接入最费精力的不是模型效果调优而是翻译各家协议差异的过程。1.2 手写适配层的痛点以及 litellm 的不同在哪里很多人第一反应是自己写一个适配层我也这么干过。在一两个供应商、调用量不大的时候手写适配层确实能顶住。但你很快会发现适配层的复杂度往往不是来源模型本身的差异而是各家持续迭代的 API 版本。今天供应商 A 出了新接口明天 B 更新了鉴权方式后天 C 的 SDK 要求你升级 Python 版本你的适配层就得跟着动。一旦团队里没有人持续关注这些变化适配层就变成了一个「能用但没人敢动」的黑盒。litellm 的思路和我预想的不太一样它没有追求把每家 API 都抽象成一个最小公倍数而是采用「统一调用格式 供应商前缀标识」的方式。你仍然在使用 OpenAI 风格的messages、model、temperature这些参数但在指定模型名时加上一个前缀告诉 litellm 这个请求应该被翻译成哪家供应商的协议。真正发出去的请求格式、鉴权方式、返回解析全部由 litellm 内部处理。也就是说适配的任务被集中到了这个项目里而不是散落在你的业务代码中。这个设计有个明显的好处即使某个供应商的协议三个月大改一次你通常只需要升级 litellm 版本自己的业务代码几乎不需要变动。对我来说这一点就足够有吸引力了。1.3 项目适合谁不适合谁聊完设计思路再说说边界。litellm 适合的场景很清晰你的业务要接入多家大模型、需要快速切换模型、需要统一追踪调用成本和失败率、或者你想在团队里搭建一个统一的大模型访问入口。它非常适合 LLM 应用开发者、算法工程师、后端工程师尤其是那种「一个人需要同时照顾好几种不同模型」的独立开发者。不太适合的场景也有。如果你只需要固定调用一家供应商且业务极度依赖对方独有的高级参数用 litellm 反而多了一层间接性如果你的请求延迟必须压到极限比如高频实时语音交互那么本地直连供应商通常比经过一层代理转发更稳。我后面第六节会详细展开选型边界这里先提个醒工具再好也要看场景是否匹配。2. 核心 API 实战completion 调用、供应商前缀与流式输出2.1 最常用的 completion 方法一个函数走天下litellm 暴露的 API 风格非常接近我现在已经习惯的对话补全格式。我最常用的就是litellm.completion()它的入参和返回结构都保持高度一致。看一个最简单的例子import litellm response litellm.completion( modelprovider-a/text-chat-001, messages[ {role: system, content: 你是一个擅长总结文档的助手。}, {role: user, content: 请帮我总结这段项目日志的要点。}, ], temperature0.3, max_tokens400 ) answer response.choices[0].message.content print(answer)这里最需要注意的就是model参数。provider-a/是供应商前缀text-chat-001是模型名。实际请求发出后litellm 会把内部格式翻译成provider-a真正需要的字段结构然后等响应回来再统一封装成response.choices[0].message.content这种格式。这意味着你的业务代码不需要关心底层到底调的是谁。如果你之前只写过某一家供应商的官方 SDK你可能会觉得这是「SDK 套 SDK」多此一举。但结合前面说的场景当你有三个供应商在同时调用、而且还要做故障切换时这个统一函数的价值就会立刻体现出来。切换模型往往只需要改一个字符串前缀比改一坨适配逻辑省事太多。2.2 供应商命名规则和 api_base 的自定义映射model参数里的前缀是 litellm 判断供应商的关键。比如provider-b/、provider-c/、huggingface/这类标识各自代表不同的协议转换规则。你会遇到两种常见情况第一种是供应商有固定的公开接口你直接用litellm.completion(modelprovider-b/xxx-model-name)就行。第二种是供应商模型跑在某个私有化部署的地址上或者公司内部有网关统一转发外部请求这时候只靠前缀可能不够你需要显式指定api_base。我常用的写法是response litellm.completion( modelprovider-b/chat-model-latest, messagesmessages, api_basehttps://gateway.example-internal.com/v1, api_keysk-内部网关密钥 )这里有个经验如果你所在公司有统一的 API 网关强烈建议把api_base指向网关而不是直连各家厂商的原始域名。这样密钥统一管理、出问题也好排查。还有一个容易被忽略的细节是当api_base指向的不是厂商官方地址而是公司网关时模型名是否带前缀、返回结构是否被网关改过建议先拿一个最小请求验证一遍再接入业务代码。再提一个我比较喜欢的点litellm 支持自定义模型映射。你可以把内部的模型别名映射到实际供应商的模型名上类似「逻辑模型名」和「物理模型名」分离的思路。这样后续供应商更换模型版本时业务代码不用挨个改只改映射配置即可。2.3 流式输出和工具调用的处理方式对话应用如果不做流式输出用户等待体验会比较差尤其是模型生成较长内容的时候。litellm 对流式输出的支持方式和 OpenAI 风格非常接近只传streamTrue即可response litellm.completion( modelprovider-a/text-chat-001, messagesmessages, streamTrue, ) for chunk in response: delta chunk.choices[0].delta if delta and delta.content: print(delta.content, end, flushTrue)这里有一个坑要提醒不同供应商对流式返回的处理细节不太一样有些中间会返回空的choices数组有些会把思过程放到delta.reasoning_content里如果你的代码只盯着delta.content可能想当然地以为模型卡住了。所以优雅的处理方式是先判断chunk.choices是否为空再判断delta是否存在再去取内容字段。我在早期接入时就被某个供应商的空数组坑过一次加了个空判断就稳定了。工具调用方面litellm 也基本照搬了主流风格你传tools参数模型返回的tool_calls内容会被保留在响应结构里。我实际测试中感觉它的工具调用兼容性做得还可以但不同模型对tool_calls的触发时机会有差异建议在接入新模型时单独跑几轮工具调用测试不要直接上生产。毕竟工具调用出错比普通对话出错更难排查。3. 路由与故障自愈重试、熔断和降级策略的落地3.1 使用 Router 管理多模型策略如果只是手动切换模型那 litellm 的作用还停留在「统一接口」层面。真正让它变成一个「网关层」的是自带的Router类。有了 Router你可以预先定义好一组模型并给它们分配权重、设定负载均衡策略然后在业务代码里只需调用同一个 router由它决定这次请求到底发给谁。最基础的使用方法大概是这样from litellm import Router model_list [ { model_name: chat-main, litellm_params: { model: provider-a/text-chat-001, api_key: sk-xxx, }, model_info: {rpm: 180, cooldown_time: 60}, }, { model_name: chat-fallback, litellm_params: { model: provider-b/chat-model-latest, api_key: sk-yyy, }, model_info: {rpm: 60, cooldown_time: 120}, }, ] router Router(model_listmodel_list) response router.completion( modelchat-main, messages[{role: user, content: 你好}], )这里model_name叫做逻辑模型名业务调用的都是逻辑名litellm_params里的model才是真正对应的物理模型。这样设计的好处是业务方不需要知道模型部署在哪里只需要知道「我要一个能聊天的模型」。当主模型挂了或者限流litellm 会根据你配置的 fallback 策略自动把请求转到备用模型业务方基本无感。3.2 重试次数、熔断时间和失败比例怎么配Router 的默认配置能满足简单场景但生产环境必须调几个关键参数否则你会遇到「重试等于没有重试」的尴尬情况。控制故障自愈的最核心参数是这几个max_retries单个请求的最大重试次数。默认值在某些场景偏小我通常设为3。retry_strategy重试哪些异常类型。默认会重试 429、超时和连接错误很合理但注意如果供应商返回 400 这种业务错误重试是没有意义的白费流量。cooldown_time一个模型连续失败多少次后进入冷却期单位秒默认60左右。allowed_fails在触发冷却之前允许连续失败的次数。一般设置在2到5之间太高会导致故障期间请求继续撞墙。我跟别人交流时发现最多人踩的坑是设了max_retries但没调cooldown导致故障模型在冷却前一直被打满重试最后把供应商限流接口打到雪上加霜。正确的姿势是「重试 熔断」一起配小故障靠重试消化连续故障靠冷却切断流量。配上这两个机制之后曾经某次上游服务抖动持续了两分钟业务侧只有极少量请求受到轻微延迟影响基本没有报错。还有一个值得用的配置是fallbacks。你可以在初始化 Router 时加一个参数列表指定某些模型失败后转给哪些备用模型。比如主模型是闭源付费模型备用模型是开源模型当付费模型连续失败时自动降级到开源模型至少先保住业务可用性。降级会带来一定的效果差异但「效果略差」通常比「接口直接报错」好得多。3.3 配额与限流防止一个业务线把月度预算打穿多模型时代最容易出现的问题就是成本失控。单看每个请求费用不高但线上一天跑几百万次请求月底账单会很惊人。litellm 的 Router 支持设置预算管控和限流。你可以给某个逻辑模型设置每分钟最大请求数超出之后直接丢弃或者排队避免被供应商限流乃至封禁。我个人最推荐的做法是在model_info里先配置一个保守的rpm每分钟请求数再配合总预算控制。比如知识库问答这种业务rpm设为 100如果测试期流量已经到顶不要再硬调高先看看是哪里在刷请求。很多情况下预算打穿不是因为模型贵而是因为某个上游任务在死循环重试。记住一个教训限流配置不只是保护供应商资源也是保护你自己的钱包。4. 成本与观测让每笔调用的 token 费用清晰可见4.1 费用计算输入和输出分开算别只看总价做 LLM 应用成本统计这一块经常被忽略。凭感觉估预算往往月中就会出问题。litellm 内置了一个completion_cost方法可以根据请求和响应的 token 数直接算出费用支持按输入输出分开统计价格。用法大致是from litellm import completion, completion_cost response completion( modelprovider-a/text-chat-001, messagesmessages, max_tokens200, ) cost completion_cost(completion_responseresponse) print(f本次调用成本{cost:.6f} 元)这个计算基于该供应商官方定价表。你要是接的私有化模型价格和公开的不一样就得自己在配置里维护一份单价表。这里有个很容易踩的坑只看每次调用的总价格其实不够。不同模型输入和输出单价差距很大输入便宜、输出贵如果你的业务场景是「长文档总结」输出 token 占比高总成本就会跟你的预期差不少。建议每次统计都记录prompt_tokens、completion_tokens、总 tokens三项分析成本构成时才有据可依。4.2 日志结构谁在什么时间调了多少 token工具提供费用计算只是基础真正有价值的是把每次调用的明细落成日志。我通常会在 litellm 的callbacks里挂一个自定义函数把每次请求的关键字段记录下来写入本地的结构化日志服务。记录的信息至少包括这几个维度调用时间精确到毫秒调用方标识哪个业务线、哪个用户、哪个会话模型名逻辑名和物理名都要存输入输出 token 数拆开记录费用估算按当前配置的价格计算错误码如果请求失败带出失败原因延迟数据首 token 延迟和总耗时把这些字段记全之后优势很明显。某次线上反馈说响应变慢我直接查日志发现某一路由模型的 p95 延迟从 300ms 飙到 3s再对比该时段的 token 用量马上定位到是某个特定来源的请求量突增。如果没有这套日志遇到问题就只能靠「感觉」去猜效率非常低。4.3 对接监控面板和告警日志之外litellm 还支持把指标导出到常见的监控后端比如把费用、延迟、失败率这类指标推送到时序数据库再配合你自己的监控面板做可视化。它的核心思路是「模型网关作为数据源统一暴露指标」。由于每个请求都经过 litellm它能天然统计各供应商的成功率、平均延迟、限流次数这些黄金指标完全不需要业务代码埋点。我的实际操作习惯是对每个逻辑模型单独设置告警规则。成功率低于 95% 持续 5 分钟就触发报警平均延迟超过 2 秒持续 3 分钟触发预警。报警不需要太频繁但一旦触发就要能顺着日志链路查到问题。这比每天人工去看面板高效得多。有一说一litellm 的指标导出虽然方便但初次配置还是要花点时间。建议先把本地 JSON 日志跑通之后再折腾监控面板对接。一步到位的配置往往出现问题后更难排查。5. 网关化部署给团队一个统一的大模型访问入口5.1 用一条命令把 litellm 变成 API 服务litellm 不止是 Python 库它还自带一个代理服务模式。所谓的代理服务就是把它跑成一个独立的 API 服务团队里的其他应用只需要往这个服务发 OpenAI 风格的请求不需要各自安装 SDK、不需要各自管理供应商密钥。我实际部署验证过启动方式很简单基本上一条命令就能跑起来litellm --config /path/to/config.yaml --port 8080配置文件里放好模型列表、密钥、预算、限流规则。服务启动后它会暴露一个兼容 OpenAI 风格的/v1/chat/completions接口。客户端只需要把base_url指向这个服务地址代码里连 SDK 都不用额外装直接用标准的 HTTP 调用。从团队协作来看这个模式的收益是明显的各部门只需要拿一个内部 token 就能调用不用接触到各家供应商的真实密钥。如果有人离职、密钥轮换只需要在网关这边改配置所有下游应用不受影响。这种「统一入口」的模式和以前微服务架构里的网关思路是一致的。5.2 虚拟密钥与用户粒度限流网关模式下密钥管理是一个不可回避的问题。你当然可以直接把供应商密钥配置在网关服务端但更优雅的做法是利用 litellm 的虚拟密钥机制在网关上为每个团队或每个用户生成一个虚拟密钥虚拟密钥对应不同的模型访问范围和配额。我在团队里就是这么做的算法组一个 key只允许调用对话类模型运营组一个 key只允许调用轻量级模型某个外部合作方也发一个 key并设置月度预算上限。这样即使某个 key 泄露了影响面也被限制在对应范围内不至于整个云资源预算被打穿。设置用户粒度限流后某次一个数据回流程序写了个死循环半小时内发了上千次请求直接撞上配额上限被网关拦截事后排查才发现问题至少没有造成严重的费用损失。5.3 网关部署的架构注意点如果你打算把 litellm 以网关形态部署到生产环境有几件事需要提前考虑。第一是高可用。网关是单点的话挂了所有模型调用都受影响。至少要有两个实例和前置负载均衡配置要同步否则每个实例的模型列表不一致请求会被分发到不知道什么模型上。第二是超时设置。网关通常不适合继续沿用默认的 600 秒等待时间。生产环境我建议把连接超时控制在 10 秒左右读取超时根据业务需要设定。超时时间太长会造成连接占用过多太短长文本生成的请求会被误杀。第三是日志与审计。网关是流量必经之地日志必须开全否则出了问题很难追责。线上一次偶然的「模型答非所问」事件就是靠网关日志发现是某个请求在模型名前多打了一个空格被路由到了完全不同的模型。上面这些点我在初次部署时都吃过亏。尤其是配置同步这件事如果你有多个实例建议用统一的配置下发机制不要直接在每台机器上手动改配置文件。手改导致的「配置漂移」到后期会消耗大量排查时间。6. 实测避坑配置细节与选型局限6.1 五个容易忽略但影响很大的配置细节用 litellm 的过程中我总结了一些文档里不太显眼但实际影响很大的细节。整理成一个表格方便对照配置项表现建议超时时间长文本生成提前断连短文本请求占用连接过久区分连接超时和读取超时按业务耗时分布设定空 choices 处理流式输出或工具调用时解析报错先判空再取内容兼容不同供应商返回差异冷却时间失败模型反复被请求限流雪上加霜设置cooldown_time为 60-120 秒并配allowed_fails输入输出 token 分别计费成本分析失真长输出场景严重低估日志里分开记录 prompt 和 completion 的 token 数逻辑模型名映射供应商模型改名或版本升级时业务侧大量改动使用自定义映射将内部别名与物理模型名解耦尤其是冷启动阶段空 choices 和超时这两个问题最容易出现。我在接某一个开源模型时流式返回里频繁出现choices为空的 chunk一开始以为是代码写错后来发现是供应商的流式协议本身就带心跳包。如果不是打了解析日志去看原始返回这个问题可能很难定位。6.2 什么时候不该选 litellm我虽然推荐这个工具但也要说清楚边界它并不是银弹。如果你的需求是以下三种可能重新考虑才是对的第一种是「只需要单一供应商且不准备换」。这时候多一层封装和依赖没有意义直接用官方 SDK 反而简单接入新特性也最快。第二种是「延迟极度敏感」。litellm 作为一个统一层内部会有协议转换和额外处理虽然有优化但理论上不可能比直连官方 API 更快。高频实时语音、端到端低延迟交互这类场景建议直连或者走更轻量的协议。第三种是「已经有一个成熟的模型管理平台」。如果你公司内部已经自建了模型网关并且有专门的团队在维护那新起一个 litellm 网关大概率是重复造轮子。除非你们的目标是快速验证多个模型效果用 litellm 做临时的评估入口倒还挺方便。选择工具的核心逻辑永远是它是否匹配你当前的团队规模、业务复杂度和长期规划而不是看它功能多不多。litellm 的优势在「把复杂变得可控」但如果你的场景本身不复杂引入它反而会增加学习成本和运维负担。6.3 我的实际体会从我个人的实测经验来看litellm 最有价值的地方并不是「一行代码切换模型」这种宣传点而是它把多模型接入过程中那些琐碎、重复、容易出错的适配工作集中处理了。它有不足比如文档结构有点散有些高级配置需要翻源码才能搞清楚但总体而言它确实帮我把接模型这件事从「每个供应商写一套适配代码」变成了「配置一份模型列表」节省了相当多的时间。如果你也正在做多模型接入我的建议非常直白先用最小的 Demo 把completion、Router、日志输出这三件事跑通别一上来就部署网关。把基础能力验证好再逐步加路由策略、预算管控和监控告警。尤其是成本统计这一块务必从第一天就做好日志记录不要等到月底账单出来再后悔。说到底litellm 只是一个工具真正决定一个 AI 应用稳定性的还是你对模型路由、成本构成和故障处理的理解。工具帮我们省掉了重复劳动但要把它用好还是得自己把边界摸清楚。