如果你对接过两家以上不同的大模型服务,应该很熟悉这种烦躁感:A 家的 SDK 有自己的鉴权方式,参数命名跟你习惯的完全不同;B 家的流式返回格式又跟 A 家差了一截;C 家甚至没有现成 SDK,只能自己拼 HTTP 请求。我在给内部某知识库助手做多模型接入的时候,就被这种碎片化折磨了一段时间。后来在代码层和各个模型之间加了一个 litellm 统一网关,才把这些问题真正压下去。这篇就聊聊它是什么、能解决什么、怎么落地,以及我在实际使用中踩过的坑。
litellm 本质上是一个大模型调用代理层,屏蔽掉不同模型服务商之间的 API 差异,对外提供一套统一的 OpenAI 格式接口。你只需要按 OpenAI SDK 的写法去调用它,由它负责把请求转发到背后真实的模型服务商,再把返回结果转换成统一格式交给你。适合对接多个模型、需要统一管控成本和密钥、或者想给团队提供一个稳定模型接入点的场景。如果你只是本地调一个模型玩,那不需要它;但一旦你或你的团队要同时面对三四个模型供应商,它就是很实用的基础组件。
1. 为什么要在大模型和业务之间加一层统一网关
1.1 先看痛点:直连各家 SDK 有多烦
先说一个很直观的场景。你有一个内部问答助手,最初只接了一家模型服务商,代码里直接用对方 SDK 写业务逻辑,一切都很顺畅。过了一阵子你想对比一下另一家的效果,于是引入第二个供应商。麻烦开始出现:两家的temperature参数取值范围不一样,max_tokens命名不同,返回内容里 token 用量的字段位置也不同,而流式输出的数据结构完全是两套东西。
我实际遇到的情况是,同一个业务函数里为了兼容两家供应商,到处写if provider == "A"这种分支。刚开始只有两三家还能忍,后来还接了本地部署的模型服务,不同服务的输入输出差异越来越大,业务代码开始变成一堆补丁叠补丁,改一个需求要同时改两三个分支。更头疼的是密钥管理:每个服务商的 API key 散落在不同环境变量里,新同事入职要交接半天,图形化的成本统计更是一个都没有。其实最底层的问题不是哪家 SDK 不好,而是你让业务代码同时耦合了太多上游协议。这时候就需要一层东西把这些差异挡在外面。
1.2 litellm 的思路:把多样性挡在网关后面
litellm 的做法很直接:它自己作为代理服务跑起来,业务代码永远只跟它对话。它对外暴露的接口是 OpenAI 格式,即/chat/completions、/embeddings这类标准端点,入参结构也是大家最熟悉的messages列表加模型名。在网关内部,它负责把这份统一格式的请求翻译成目标服务商能理解的协议,再发送出去。
这种设计有一个特别实用的点:业务侧接入成本极低。团队里任何一个人只要会写 OpenAI 客户端,就天然会调用 litellm 网关。如果明天要把模型 A 换成模型 B,业务代码几乎不用动,只需要在网关配置里改一下模型映射关系,把原本指向 A 的模型别名指到 B 上就行。我跟同事开玩笑说,它就像是给各个大模型 API 加了一个万能转换头,你只要认准一个插口,后面插什么电器都行。
另一个好处是审计和管控的集中化。所有请求都会经过网关,所以谁在什么时候用了什么模型、消耗了多少 token、花了多少钱,都能在网关这一层统一拿到。这比让每个业务线各自去服务商后台查账单要清晰得多。
1.3 什么情况下才值得上 litellm
说实话,并不是所有项目都适合引入 litellm。如果你只是一个个人小项目,就调某一个固定模型,那直接用官方 SDK 反而更轻,多一层网关就是多一个需要部署和维护的组件,没必要。
但当出现以下任一情况时,就值得认真考虑了:团队内多人需要共用模型资源;你需要在多个供应商之间做效果对比和切换;你想按项目或按部门控制模型使用预算;你有多个后端服务都要接大模型,不希望每个服务都重复实现一套鉴权和重试逻辑。我自己是从个人脚本接入开始用的,后来因为内部项目要开放给多个业务方调用,才正式把它当独立服务部署起来,相当于给团队提供一个统一的模型入口。
2. litellm 的核心功能,拆开讲
2.1 统一请求格式与参数映射
litellm 最基础的能力就是把不同服务的请求参数映射成统一的 OpenAI 风格。比如你调chat/completions,传一个messages列表,里面有role和content,这是大家都熟悉的写法。网关收到后,会根据你配置的目标模型,将temperature、top_p、停止词等参数转换成对应供应商的字段。
需要注意的一点是,不同模型服务商对参数的实现程度不一样。有的模型支持frequency_penalty,有的不支持;有的对temperature的合法范围定义不同。litellm 的处理方式通常是把不支持的参数忽略或降级,而不是报错。这既是优点也是坑:如果你特别依赖某个模型独有参数,最好先去查一下 litellm 的文档确认映射规则,不然可能出现“网关显示调用成功,但实际效果跟预期不同”的情况。
日常开发中,我在业务侧维护一个模型别名常量表,代码里只引别名。比如fast-chat指代某个速度快的模型,big-reasoning指代能力更强的模型。哪天要升级模型能力,我就只改网关配置,业务服务不需要重新发布。这个看着很不起眼的操作,真到跨部门协作的时候,能省掉很多沟通成本。
2.2 多密钥、预算与租户隔离
密钥管理是 litellm 很强的一块。网关本身有一个master key,所有管理端请求都靠它。在这个主密钥之下,可以创建很多个虚拟 key,每个虚拟 key 可以绑定不同的预算上限、限流速率和使用权限。比如 A 组同学拿一把只能访问某个模型的 key,每月最多花 200 块;B 组同学拿另一把 key,可以访问另外几个模型。
这种设计解决了团队协作里一个很现实的管控问题:过去大家共用一个服务商的 API key,出了异常消耗很难定位是谁的锅。换成虚拟 key 之后,每把 key 都有独立 ID,litellm 后台能看到每个虚拟 key 的调用量和累计成本。出了突发情况,可以立刻查到是哪个 key 在大量调用,甚至可以随时吊销某个 key 而不影响其他人。
预算控制也不是一刀切的“超了就停”。litellm 支持设置预算告警阈值,到了 80% 就发通知,到 100% 再限制或停用。我们内部某个业务线就是这样配的,模型异常消耗再也没出现过月底爆账单的情况。如果你准备给多业务方提供服务,这组功能基本是刚需。
2.3 负载均衡、重试与故障转移
除了统一接口,litellm 在稳定性方面也做了不少事情。它支持同一个模型配置多个 API key,网关会在这些 key 之间做轮转或按负载分发,变相突破单个 key 的速率限制。比如某个厂商的 API 允许每分钟 600 次请求,但你的应用高峰期会瞬时打很高,就可以在配置里多放几个 key,litellm 自动分摊流量。
更让我觉得实用的是故障转移机制。你可以配置一个主模型和几个 fallback 模型,当主模型返回错误、超时或触发限流时,网关会自动把请求转到备用模型上。这一点在生产环境里价值很大:就算某个供应商临时故障,你的业务用户也不会直接感知到服务不可用,最多是响应稍微慢一点。
我实际测试过 fallback 的效果,主模型连续报错时,网关会在几百毫秒内切到备用模型,整体请求失败率显著下降。当然 fallback 也不是万能的,比如主模型和备用模型的响应风格差异可能很大,对内容质量要求高的场景,最好不要让 fallback 模型处理得那么“无感”,宁可提示用户稍后重试。
2.4 成本追踪与用量观测
litellm 的成本追踪是我愿意长期使用它的重要原因之一。它会根据请求的输入输出 token 数和模型费率,自动统计出每一次调用的估算成本。在它的管理页面上,可以按模型、按虚拟 key、按时间段去看成本曲线。这些数据也能通过回调集成到企业内部监控系统。
它内部维护了一张模型价格表,你接入的模型如果不在表里,也可以手动在配置里指定按 Token 计费的价格。对用自部署模型或特殊模型的团队来说,这个功能很实用,成本数据依然能汇总到同一个体系里。我在做月度成本复盘时,基本就是从 litellm 后台导出一份用量明细,按项目归类汇总,比对着各家服务商账单去梳理省力太多了。
3. 从零落地:部署、配置与第一个真实请求
3.1 快速把一个代理服务跑起来
litellm 提供两种使用方式:一种是直接在 Python 脚本里 import,当作一个 SDK 调用统一的completion()方法;另一种是启动一个独立代理服务,在本地或服务器上常驻运行。如果只是自己写脚本用,第一种就够了;如果想让团队共用,建议用第二种。我的实践是直接用 Docker 部署,它包好了运行环境,省掉很多折腾。
核心就一个命令的思路,我用 docker-compose 管理。一个最小化的配置大概是这样的:
services: litellm: image: ghcr.io/berriai/litellm:main-latest ports: - "4000:4000" volumes: - ./config.yaml:/app/config.yaml command: ["--config", "/app/config.yaml", "--port", "4000"]启动前只需要准备一个config.yaml配置文件。启动后访问/health/liveliness和/health/readiness两个端点,就可以确认服务是否健康。我第一次启动时连健康检查都没看,直接就去调接口,结果等了一会儿才发现服务没起来,白费了不少排查时间。所以如果你们也准备上这个,建议先跑通健康检查,再继续往下走。
3.2 配置一个真实的模型接入
config 文件是 litellm 的配置中心。一个最简的模型接入配置长这样:
model_list: - model_name: gpt-4o litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY - model_name: claude-flash litellm_params: model: anthropic/claude-3-5-sonnet api_key: os.environ/ANTHROPIC_API_KEY这里有一个很关键的细节:model_name是业务侧看到的名字,litellm_params里的model才是网关真正路由的标识,格式通常是“供应商名/实际模型名”。我第一次用的时候把model_name和model都写成了同一个名字,结果网关在路由时判断错了供应商,报了一堆莫名其妙的错误。后来才明白这两个字段是有分工的。
启动服务后,用 curl 简单测一下:
curl http://localhost:4000/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer sk-1234" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "你好"}] }'如果返回正常的choices结构,说明网关已经打通。这里返回的 JSON 结构和官方 API 几乎一样,业务方直接按熟悉的格式解析即可。
3.3 用 OpenAI SDK 调用网关
网关跑通之后,接入方拿到手的体验就是“它就是一个兼容接口”,所以很多现成工具也能直接复用到 litellm 上。比如一段原本调用官方 SDK 的代码,只需改三个东西:base_url、api_key、model,其余逻辑原封不动。我用 Python 示意一下:
from openai import OpenAI client = OpenAI( base_url="http://your-server:4000/v1", api_key="sk-your-virtual-key", ) resp = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "介绍一下桂林"}], ) print(resp.choices[0].message.content)这里配置文件中模型名是gpt-4o,在业务侧它不一定真的指代官方那个同名模型。它可能被映射到另一个厂商的模型,或是在多个供应商之间做了负载均衡。这个透明的映射关系,就是网关的核心价值之一。
还有一个非常实际的好处:由于外部接口格式统一,你之前写好的提示词模板、工具调用逻辑、后处理代码,在切换模型时基本不需要改动。换模型从“改代码发版”变成了“改网关配置然后告诉同事新别名”,这个体验的差别,经历过的人都懂。
3.4 负载均衡和 fallback 的配置细节
负载均衡和故障转移的配置都在同一个 config 文件里。同模型多 key 轮转可以这样写:
model_list: - model_name: gpt-4o litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY_1 model_info: supports_parallel_function_calling: true - model_name: gpt-4o litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY_2网关会在两个 key 之间轮换。如果同时配置了多个不同的model_name,可以通过路由规则把请求分配。故障转移则是在router_settings里配置fallbacks,比如:
router_settings: fallbacks: - gpt-4o: - claude-flash意思是当gpt-4o这个模型不可用时,自动尝试claude-flash。需要提醒的是,fallback 模型和主模型的能力差距可能不小,生产环境建议只在主模型临时不可用或限流时做兜底,而不是日常把流量导过去。另外 fallback 会带来一次额外的调用尝试,超时时间的配置要留足,不然业务平台已经等不及给你断了,网关还在尝试备用模型,这个体验反而更差。
4. 生产环境会遇到的坑和排查思路
4.1 常见报错速查表
用久了我发现,litellm 的报错大多集中在配置、鉴权和流式处理三类。整理一个速查表,方便遇到问题时先对号入座。
| 现象 | 通常原因 | 处理建议 |
|---|---|---|
| 404 模型不存在 | 请求里的 model 名在配置中没写,或model_name与路由标识混淆 | 检查 config 里的model_name和实际请求值是否一致 |
| 401 鉴权失败 | 虚拟 key 未创建或已禁用 | 通过管理接口生成新 key,确认Authorization头正确 |
| 429 限流或预算超限 | 虚拟 key 的 RPM/TPM 到顶,或预算用尽 | 提高限流配置,或检查预算报表定位消耗来源 |
| 502/504 上游请求失败 | 上游模型服务超时或不可用 | 检查上游供应商状态,同时看 fallback 配了没有 |
| 流式请求中 SQL 报错 | 使用某些异步客户端时连接处理不当 | 确保上游客户端关闭了 keep-alive 或正确释放连接 |
有一个报错我印象比较深:业务侧用异步 HTTP 客户端调用网关,请求量一上来就出现大量连接错误。排查了很久才发现是客户端连接池设置太小,压力全部压在 TCP 连接建立上。这跟 litellm 本身关系不大,但提醒我一个经验:网关稳定不等于链路稳定,客户端侧的连接池、超时、重试策略都要一起调。
4.2 从上游和下游两个方向排查
litellm 最常见的排查路径其实是分两侧的:一是看“网关拿到请求后有没有正确发到上游”,二是看“下游业务是否真的收到了预期的响应”。我在排查一个问题时,一般先看日志里有没有完整的请求转发记录,确认网关收到了请求、目标模型是哪个、响应状态码是多少。
litellm 的日志会打印出每次请求的模型标识和耗时,但没有业务上下文的话,你还需要在业务侧记录一个 request id,两端对起来查。这个看起来很简单,但遇到线上问题时能省下一个小时。后来我习惯在业务侧把request_id透传到日志里,再结合网关日志去定位,定位速度明显快很多。
还有个很隐蔽的问题:不同厂商流式返回的内容格式差异。流式响应里通常会带一堆delta,但有些供应商的delta字段在不同阶段会缺字段;有些供应商会把工具调用放在delta的特殊子字段里。litellm 一般会做适配,但如果你在业务侧直接处理原始流式对象,还是要按统一的 OpenAI 结构去写,不要依赖某个供应商的特殊字段。
4.3 容易被忽略的性能与成本细节
有几个细节我建议每个准备把 litellm 接进生产环境的人都注意一下。
第一个是流式请求的 token 统计。如果你用stream: true,默认情况下响应里可能没有完整的 usage 信息,导致成本统计出现缺口。解决办法是在发起请求时同时传stream_options: {"include_usage": true},这样流式响应的最后一段会带上用量。这个字段最初我经常忘,直到月底对账发现成本数据少了一块,才翻日志找到原因。
第二个是 embedding 模型的维度一致性。如果你同时接入了多个供应商的向量模型,它们的输出维度大概率不一样。业务库里如果已经有一部分向量是按旧的维度生成的,切换模型后新旧向量无法直接比较,这个问题在检索场景里很容易被忽视,好在 litellm 能帮你统一入口,但它不负责帮你统一向量维度,这需要业务侧自己做兼容。
第三个是超时和重试的配合。litellm 网关层有重试配置,业务侧不要同时做太多层重试,不然一个上游慢请求会被放大很多倍,拖垮整个链路。最初我们为了“稳定”做了三层重试,结果上游一抖动,网关下面的数据库和下游服务全被打爆。后来把重试收敛到网关一层,并把超时时间调整为分段模式,链路才算稳定下来。
4.4 一个小技巧:把模型调用日志接入内部监控
litellm 支持通过回调把日志推送到你已有的监控系统。对这个能力我想多强调一下它的价值,因为它不只是“方便查看日志”。我们内部实际做的是把每次调用的模型名、虚拟 key、耗时、token 用量、估算成本,统一推到内部的可观测平台,再按业务线做实时看板。这样成本趋势、异常调用、限流情况都能及时看到,我印象中第一次把看板做出来的时候,主管直接拿它来做每周汇报数据源,比从各家后台手动导出数据靠谱得多。
配置方式不复杂,核心就是启动代理服务时指定--detailed_log,或者通过配置自定义回调函数,把litellm_params和响应数据组织成 JSON 推送到内部 HTTP 端点。如果你还没有监控系统,也可以先让 litellm 把日志写到 JSON 文件,再用日志采集器去收。关键是数据的结构化要一开始就做好,至少把model、response_time、prompt_tokens、completion_tokens这几个核心字段留全。
我个人在这个项目上摸索下来,最大的体会是:litellm 解决的远不止是代码兼容问题,它把模型接入这件事从“各自为政”变成了“统一治理”。有了它之后,团队新增一个模型供应商的成本已经低到几乎可以忽略,而这在以前根本不敢想。如果你也刚好被多模型接入、成本失控或者密钥管理搞得头大,不妨花一个下午把代理跑起来,先用一个模型打通链路,再慢慢把其他服务接进来。这个投入产出比,我觉得是会让你觉得值的。最后再分享一个小经验:配置文件的变更不要太随意,建议像管理代码一样走版本管理,每次改动都留记录,因为模型切换导致线上行为变化的案例,我见过太多次了。