1. 多模型接入的混乱,是从哪一根线开始的
先讲个我见过的典型场景。某团队做内部AI应用,一开始只接了一家模型厂商的接口,代码里封装了一个llm_client.py,里面写死模型名、API Key、超时时间,调用方传个 prompt 拿到字符串完事,看起来岁月静好。但随着业务跑起来,需求开始变味:要对比不同模型的效果、要做降本切换、要某个任务走专用模型、要在大模型服务商临时抖动时自动换路。于是llm_client.py里开始堆满 if-else,每个厂商一套 SDK、一套鉴权、一套错误码,改一个字段要翻三个配置文件,新同学入职第一周全在啃这个文件。
这个时候大多数人会想到一个办法:自己写一个统一封装层,用 OpenAI 格式作为标准,把各家接口映射过去。听起来工作量不大,但真正动手后就会发现,光处理鉴权方式、超时语义、流式协议差异、usage 统计、token 计算这些"脏活",就把人拖住了。而且封装层写好之后,还要考虑多租户、预算控制、并发限流、日志审计,这些已经不再是"客户端封装"的范畴,而是一个正经的服务端组件。
litellm 解决的就是这个问题。它本质是一个轻量的开源 LLM 网关,对外暴露 OpenAI 兼容的/chat/completions接口,对内适配市面上主流的闭源和开源模型服务。你的业务代码只需要认准 OpenAI 格式,改一个 base_url 就能把流量从直连模型换成经过 litellm,而具体请求落到哪家模型、用什么鉴权、有没有超预算、要不要重试,全部由网关层决定。这篇文章从头到尾梳理它的工作原理、部署方式、企业级配置和我在实际接入中踩过的坑,给正在纠结"要不要上模型网关"的团队一个可落地的参考。
2. litellm 的请求链路:一个代理到底做了什么
2.1 它不是一个普通 SDK,而是一个"入口"
很多开源项目都叫 SDK,但 litellm 的定位完全不同。SDK 是嵌在你的进程里,帮你把请求发出去;litellm 则是一个独立部署的服务,业务代码根本不和它跑在同一个进程里。这意味着它可以成为整个公司的模型统一入口,所有应用都经过它访问模型,而不是每个应用各自维护一套模型接入代码。
从部署上看,litellm 启动后就是一个 HTTP 服务,默认端口是 4000。你只需要把它当成一个"地址变了、密钥变了、其他都不变"的 OpenAI 服务来用。换句话说,你的现有代码如果已经用了 OpenAI 官方 SDK,那迁移成本极低:把base_url指向 litellm 的地址,把api_key换成 litellm 分配给你的 key,剩下的调用逻辑完全不需要动。
2.2 一次请求从进入到返回,中间发生了什么
我习惯把 litellm 的请求链路拆成四段,这样排查问题时会特别有条理。
第一段是协议接收。客户端发过来的是一个标准的 OpenAI 格式请求,比如你熟悉的model、messages、temperature、stream这些字段,litellm 的 Proxy 服务先把这个请求解析成内部统一的ChatCompletionRequest结构。
第二段是路由与治理。litellm 会根据model_list配置,把请求里的model参数映射到真实的后端模型。这段会先做身份认证(检查 API Key 是否有效)、预算检查(这笔请求会不会超出配额)、并发限制(当前并发是否超限),任何一个环节不通过,直接返回 429 或 401,根本不会把请求发到模型厂商那里。
第三段是协议转换。litellm 拿着统一的请求结构,根据目标模型所属的厂商,转换成对应厂商的原生协议。这个过程很像一个"翻译器":某家厂商的鉴权头叫x-api-key,另一家叫Authorization: Bearer;某家的消息格式是system/user/assistant,另一家可能用messages但字段名不同;某家的参数叫max_tokens,另一家叫max_output_tokens。这些差异全部由内置的 adapter 处理。
第四段是响应回传。模型厂商返回结果后,litellm 再把响应转回 OpenAI 格式返回给客户端。如果开了流式输出,litellm 会把厂商的 SSE 流一条条接收、解析、重新封装成 OpenAI 格式的chunk推给客户端。这也是最容易出问题的一段,因为各家流式协议的事件字段名和结束标记差异不小,一旦处理有误,客户端表现为"输出到一半断掉"。
2.3 为什么偏偏选 OpenAI 格式当"通用语"
我自己做过对比,业界其实有过几种统一方案,比如直接把各家 SDK 封装成一个"超级 SDK",或者定义一个自己的消息结构。但 litellm 选择以 OpenAI 格式为准,这背后是有实际考虑的:OpenAI 的 API 格式在社区里普及率最高,LangChain、LlamaIndex、各类 Agent 框架、甚至很多开源工具都原生支持 OpenAI 格式。你只要把它们的 base_url 换掉,就能立刻接上 litellm,不需要改业务代码,不需要引入额外的客户端库。对于团队来说,这几乎是迁移成本最低的方案。
另外,OpenAI 格式本身就是一套设计得相当规整的 JSON 协议,字段命名直观、消息结构清晰,拿来作为中间层的数据模型很合适。litellm 内部保留这套结构,对外再 adapter 到各家厂商,整个链路维护起来也省心。
3. 部署、配置与第一次跑通
3.1 两种部署方式,怎么选
litellm 官方支持两种主要部署方式:直接用 pip 安装 Python 包启动,或者用 Docker 跑镜像。
本地开发或快速验证阶段,我建议直接用 pip。pip install litellm[proxy]装完,写一个配置文件,命令行启动即可,调起来最快。但生产环境我强烈建议用 Docker。原因很简单:litellm 的依赖数量不算少,直接装在宿主机上容易出现 Python 版本冲突、依赖被其他项目覆盖的问题;而 Docker 镜像把这些都固定好了,升级也方便,拉一个新镜像重启就行。
这里有一个容易被忽略的细节:litellm 的 pip 包有多个安装 extras。如果你只是写脚本调用模型,装pip install litellm就够了;但如果你要跑 Proxy 服务,一定要装litellm[proxy],否则启动时缺一堆依赖。我第一次上手时就是因为只装了基础包,启动服务直接报 ModuleNotFoundError,花了十分钟才反应过来。
3.2 最小可用的配置文件
跑通 litellm 其实只需要一个 config.yaml 和一个环境变量。下面是我常用的最小配置:
model_list: - model_name: gpt-4o litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY - model_name: claude-internal litellm_params: model: anthropic/claude-3-5-sonnet-20241022 api_key: os.environ/ANTHROPIC_API_KEY看到这里你会发现一个关键设计:model_name是你给外部暴露的别名,litellm_params.model才是真实请求的模型标识。比如我可以把内部的claude-internal指向任意厂商的任意模型,客户端根本不知道背后实际的模型是什么。这个设计带来的好处是:以后想换模型,只需要改网关配置,客户端一行代码都不用动。
启动命令也很简单:
export OPENAI_API_KEY=你的Key export ANTHROPIC_API_KEY=你的Key litellm --config config.yaml --port 4000然后用 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": "你好,介绍一下你自己"}] }'注意我上面用的是sk-1234。默认情况下 litellm 开启虚拟 key 校验模式,任意非 master key 的字符串都能作为虚拟 key 使用。生产环境建议把general_settings.master_key配置好,并建议用/key/generate接口生成正式虚拟 key,而不是直接写死一个明文 key。
3.3 环境变量、密钥管理与配置热更新
在真实的团队协作场景里,密钥管理是最容易翻车的一环。litellm 支持在litellm_params里直接写api_key: sk-xxx,这种方式只适合本地测试。生产环境请务必使用os.environ/变量名这种写法,把真实密钥放进部署平台的环境变量或密钥管理系统中,配置文件本身不包含任何明文密钥。
另外很多团队会有多个环境:dev、staging、production。建议每个环境一套 config.yaml,通过 CI/CD 区分注入。litellm 也支持/config/update接口热更新配置,不需要重启服务就能新增模型、调整路由策略。但要注意:热更新只对model_list这类运行时配置生效,涉及监听端口、数据库连接等启动级参数,还是需要重启进程才能生效。
4. 把 litellm 当企业基础设施用:预算、路由与观测
4.1 预算控制:从"事后算账"变成"事前拦截"
直连模型厂商时,预算控制基本靠事后看账单。某个月某条业务线跑了十几万美元的模型调用,等发现时已经晚了。litellm 把预算控制做成了请求链路的一部分,每个请求在发出之前就会检查预算。
配置方式是在 litellm_settings 里做全局限制,然后再给每个虚拟 key 设置独立的预算。比如:
litellm_settings: max_budget: 5000 budget_duration: 30d general_settings: master_key: sk-master database_url: os.environ/DATABASE_URL上面这段的意思是:整个网关每 30 天的总预算上限是 5000 美元,一旦达到就拒绝新的请求。同时,你可以在/key/generate生成虚拟 key 时带上max_budget和budget_duration参数,给每个业务线、每个项目组分配额度。某条业务线的 key 超了预算,只有那个 key 的请求会被拦截,其他业务线不受影响。
这里有个实用经验:预算配置要看清楚单位。不同的budget_duration字段会改变计费周期,30d是 30 天滚动窗口,1mo是自然月,1d是自然日。默认是无限,不设置等于没有预算。上线前一定要做一次"烧钱冒烟测试",用一个只允许花 0.01 美元的 key 去跑几个请求,确认拦截真的生效,再放开真实额度。
4.2 路由策略:fallback 与模型组
模型厂商不可能永远稳定,高并发时段偶尔会 429,服务商也可能临时维护。litellm 的路由策略支持两级兜底:同一模型配置多个后端实例做负载均衡,以及请求失败后自动切换到另一个模型。
先看同模型多实例的配置方式:
model_list: - model_name: gpt-4o-pool litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY_1 - model_name: gpt-4o-pool litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY_2两个条目共用同一个model_name,当客户端请求gpt-4o-pool时,litellm 会在这两个后端之间做负载均衡,某个 key 的配额用尽后会把流量导到另一个 key 上。这招在多账号分摊成本时非常实用。
再看跨模型 fallback:
litellm_settings: fallbacks: - gpt-4o-pool: - claude-internal - gemini-pro这里表达的是:gpt-4o-pool请求失败时,依次尝试claude-internal和gemini-pro。我在生产环境里把这套配置用在关键业务链路上,实际效果很明显,某次厂商故障期间,用户几乎没有感知到模型切换,只是响应偶尔慢了几百毫秒。
4.3 可观测性:数据比直觉可靠
litellm 自带了一套 Prometheus 指标接口,/metrics路径直接暴露请求总数、延迟分布、token 用量、错误码分布等数据。这些指标可以直接接到现有的监控体系里。我们团队就在 Grafana 上做了几个核心面板:按业务线聚合的模型调用量、按模型聚合的延迟 P50/P95、按错误码聚合的失败次数。
除了指标,litellm 的操作日志也能接出去。它支持配置自定义回调,把每次请求的明细(模型、token 数、延迟、用户、虚拟 key)推送到自己的日志系统。这套审计日志在排查"谁在凌晨三点烧了三千块"时是唯一可靠的证据。轻量做法是直接存数据库查询;更规范的做法是接到一个独立日志管道长期保存。
这节重点说说我踩过的坑:litellm 的 metrics 默认包含很详细的标签,但当请求量起来后,高基数标签(比如把虚拟 key 直接作为标签)会让 Prometheus 内存暴涨。我的建议是先在/metrics接口的数据里筛选出必要的标签维度,别一股脑全接进去。
5. 踩坑记录与调优经验
5.1 超时与重试:默认值不等于合理值
第一次用 litellm 做生产接入时,我用默认超时,结果线上频繁出现客户端"转圈很久然后报错"的体验。查了半天,罪魁祸首是 litellm 的默认超时对某些厂商来说太保守。流式请求和非流式请求的超时行为还不一样:非流式请求要等服务端把完整结果拼好才返回,如果模型生成内容很长,耗时就长;流式请求则是持续推流,只要还有数据在传就不算超时。
我的调优做法是分层设置超时:全局设一个大一点的兜底超时,比如 600 秒,避免极端长文本场景被误杀;再给不同模型配置更细的超时。比如快模型设 60 秒,慢模型设 300 秒。开启流式之后,用户感知到的等待时间大幅下降,因为首 token 延迟才是体验关键,而不是整体完成时间。
5.2 重试幂等性:不是所有请求都适合自动重试
litellm 内置了重试机制,请求失败或超时可以自动重试。但我强烈建议你仔细想清楚:你的业务请求是幂等的吗?如果用户在页面上点了一次"生成文案",网关自动重试三次,如果前一次已经成功生成、只是响应在传输中丢失,重试就会造成一次重复扣费、两份结果。这个问题在非流式请求中更容易出现。
我的方案是:在 litellm 前面再放一层业务网关,由业务层决定哪些请求可以安全重试。比如只读类任务(总结、改写)可以重试;有副作用的任务(下单、发消息)绝对不重试。litellm 的自动重试只作为"连接类错误"的兜底,比如 429、503、连接中断,这类错误重试是安全的,因为请求大概率根本没被处理。
5.3 token 统计不一致的怪问题
做成本核算时,我最头疼的就是 token 统计对不上账。litellm 会统一返回 usage 字段,但由于不同厂商的 tokenizer 不一样,同一个 prompt 在 A 厂商那里算 1000 token,到 B 厂商那里可能算 1200 token。这意味着你在网关层看到的 token 数字,和厂商账单上的数字会有偏差。
这不是 bug,是不同模型词表的客观差异。我的处理方式是:成本分析以各家厂商账单为准,litellm 的 token 统计只用于横向对比和监控趋势。如果一定要做实时成本预估,就需要给每个模型配置单独的输入单价和输出单价,litellm 的/cost接口可以做这件事,但价格数据要定期更新,模型调价后别忘记维护。
5.4 并发连接池与高并发下的性能
litellm 默认的并发参数在低流量下没什么问题,但压测到几百 QPS 时就会发现,某些后端连接没有被复用,每个请求都在重新建连,延迟陡增。我调优时重点看了两个方向:litellm 的num_threads参数,以及后端 HTTP 连接池的大小。在配置文件里适当调大线程池,同时留意与上游厂商的速率限制是否匹配。并发开得再大,上游给你 429,反而触发一堆重试,把局面搞得更乱。
这里有个朴素的认知:litellm 是网关,不是挡箭牌。它的并发能力是有上限的,生产部署时我习惯在前面加一层负载均衡,多个 litellm 实例组成集群,数据库统一使用 Postgres,这样才能撑住更大的流量。
5.5 千万别把 master key 当业务 key 用
最后一个坑也是最重要的一个:litellm 的 master key 是管理员凭证,能调所有管理接口、看所有配置、生成任意 key。初期图省事,我直接把 master key 发给前端同学调接口用,后来发现任何人拿到这个 key 都能修改网关配置,这才意识到问题严重性。
正确的做法是:master key 只用于管理端,包括管理后台、生成虚拟 key、更新配置;业务调用统一使用/key/generate生成的虚拟 key,每个虚拟 key 可以绑定预算、限流和用途标签。这样即使某个虚拟 key 泄露,也能在管理后台一键删除,影响范围被限制在一个业务线内。
6. 选型建议:什么时候应该上 litellm,什么时候别上
6.1 值得上的场景
如果你的团队满足下面任意两条,litellm 就值得评估:第一,业务代码里接入了两家以上的模型服务商,且随时可能新增;第二,有多个团队或多个应用共用模型能力,需要一个统一的密钥和预算管理入口;第三,有模型切换或降本诉求,想通过配置而不是改代码来换模型;第四,需要对模型调用做审计和监控。以上场景我都在实际项目里见过,litellm 带来的收益不只是省事,更是把"模型接入"从一个反复返工的编码问题变成了一个可治理的基础设施问题。
6.2 不适合的场景
也有几种情况我不建议用。如果你只是本地脚本偶尔调一次模型,那就没必要部署一个常驻网关服务,多一层就多一份维护成本;如果业务对延迟极度敏感,每一次请求都要额外经过一个网关,网络开销和转发耗时都是要考虑的,尤其在数据量极大的场景下,网关会成为瓶颈;再比如你的团队只有一个模型、一套密钥、没有预算治理需求,那引入 litellm 属于过度设计。
另外还有一个容易被忽视的关键点:litellm 本身也是需要维护的组件。它有自己的升级节奏、依赖变动、数据库迁移操作。团队里至少要有人能看懂它的配置和日志,否则出了问题会变成新的负担。
6.3 自研网关和直接用 litellm 的分界线
有些团队会觉得"我们自己写个统一封装不就行了"。说实话,如果你只是做一个简单的协议转换,自研确实可行,代码量不大。但一旦牵扯到预算控制、多租户密钥管理、Prometheus 指标、数据库持久化、管理后台、配置热更新,自研的成本就开始成倍上升。这些能力 litellm 已经内置,而且经过了大量生产环境的验证。我的建议是:先把 litellm 跑起来,让业务快速接入,等团队在真实场景里积累了足够多的经验后,再评估是否需要在它之上做二次开发或替换。我在实际使用中发现,litellm 最大的价值不是省掉了那几行适配代码,而是把模型接入这件事从"每个团队自己造轮子"变成了"公司统一一条路"。先把路铺好,后面的人走得才快。
最后再分享一个小技巧:生产环境部署 litellm 时,我会把它的服务放在内网,只对业务后端暴露访问地址,不对公网开放。如果业务有外部用户直接调用模型的需求,就在 litellm 前面再挡一层业务网关做用户鉴权和频率控制。litellm 的虚拟 key 能解决"谁在调用"的问题,但用户维度的复杂权限和风控策略,还是交给熟悉业务的网关层去处理更稳妥。