做 AI 应用的人,这两年最深的感受应该就是:模型越来越多了,接口也越来越乱了。今天接了一家大模型服务的 API,明天产品经理说要换另一家的开源模型试试;这个场景适合快一点的模型,那个任务需要更强的推理能力;还有团队协作时密钥怎么管理、成本怎么统计——这些问题堆在一起,比调模型本身还让人头大。我第一次见到 litellm 这个项目的时候,第一反应是"又一个封装库",但真正用起来才发现,它解决的远不止是接口统一这么简单。这篇文章我把自己的使用经验完整梳理一遍,从原理到配置再到踩坑,给正被多模型接入折磨的同学一个可以直接照做的参考。
1. 为什么需要统一模型网关:碎片化时代的真实痛点
1.1 每多接一家模型,就多一层痛苦
先还原一个很典型的场景。你是一家创业公司的后端开发,产品里集成了三个语言模型:一个负责实时对话,要求低延迟;一个负责长文档总结,要求上下文够长;还有一个是内部微调过的模型,部署在自建的推理服务上。这三家服务的请求格式不一样,鉴权方式不一样,错误码不一样,连流式返回的数据结构都不一样。
写业务代码的时候,你不得不针对每一家分别封装一层 client,然后在上层再做一次适配。更麻烦的是,任何一家服务升级了模型版本、改了限流策略,你的代码可能就要跟着改。我见过一个项目里光是一段"调用模型"的逻辑就写了三个条件分支,而且每个分支里还各自处理超时、重试、异常转换。这种代码短期能跑,长期就是技术债。
litellm 的核心理念其实很朴素:它把所有主流模型服务的 API 统一成一个格式,让你用一套调用代码就能对接不同服务商。底层不管你怎么变,上层永远是一份稳定的接口。它本质上是一个"模型网关",放在你的应用和各家的模型 API 之间,专门负责格式转换、路由转发、错误归一化这些脏活累活。
1.2 它解决的五个核心问题
我用了半年之后,觉得 litellm 解决的核心问题可以归纳成五类:
第一是接口统一。这是最表层的能力,但也是最省事的地方。所有模型调用都长一个样,新接入一家服务商只需要在配置里加一行,不需要写新代码。
第二是故障转移。某一家模型服务不稳定是常有的事。原来你可能需要自己写 failover 逻辑,在 litellm 里可以通过路由策略自动降级到备用模型,用户无感知。
第三是密钥安全。团队成员各自持有各家 API 密钥,本身就是安全隐患。通过代理集中管理密钥,业务方拿到的只是一个受限访问令牌,方便做权限隔离。
第四是成本与用量统计。各家服务商的计费口径不同,手工统计费用很麻烦。litellm 能在调用层统一记录 token 消耗,按模型、按用户、按时间段聚合,月底对账非常方便。
第五是团队标准化。前后端、算法、产品都通过同一个代理访问模型,请求格式、错误格式、日志格式全都统一了,联调和排查问题的效率高很多。
1.3 什么时候不需要 litellm
当然,这个工具也不是银弹。如果你的项目只是固定调用一家模型服务,而且没有团队协作、成本统计、模型切换这些需求,那直接用原厂的 SDK 反而是更轻的选择。引入 litellm 意味着多维护一个中间层,多了解一套配置体系,这是需要权衡的。但我个人判断,一旦你的项目里有"第二家模型接入"这个计划,或者需要多人共用密钥,统一网关的收益就远远大于成本了。
2. 核心机制拆解:它是怎么做到"一套代码打天下"的
2.1 统一的请求入口
litellm 最基础的使用方式是作为 Python 包直接调用。它的核心函数只有一个,叫做 completion,看起来长这样:
from litellm import completion response = completion( model="provider-a/model-large", # 这里的 provider-a 是占位符,实际换成你的服务商标识 messages=[{"role": "user", "content": "介绍一下你自己"}], api_key="你的访问密钥", )这个函数的入参风格模仿了聊天补全接口的通用写法。litellm 拿到你的请求之后,会根据 model 参数里斜杠前面的前缀,判断出你要调用哪家服务,然后把你的请求转换成那家服务要求的原生格式,再发出去。
也就是说,你写的 messages 结构是通用的,temperature、max_tokens 这些参数也是通用的。不同的服务商对参数命名有细微差异,比如有的叫 max_tokens,有的叫 max_output_tokens,有的还限制枚举值,这些差异都由 litellm 在内部消化掉了。
2.2 模型命名规则
要正确使用 litellm,必须先理解它的模型命名规则。规范是"服务商/模型名",斜杠前面是提供方的标识,斜杠后面是具体的模型名。
举几个例子你就会发现规律:provider-a/model-large 表示服务商 A 的大尺寸模型;provider-b/model-fast 表示服务商 B 的快速模型;本地部署的托管服务则可以把服务商标识配置成你自己起的名字。
这个设计最聪明的地方在于,模型名本身就成了路由的依据。你不需要额外写 if-else 去判断走哪条链路,litellm 看到前缀自然就知道该往哪里转发。而且它还支持给模型起别名。假设你的代码里到处都写了 model="primary-model",后来发现一家服务商不够稳,想换成另一家,不需要改代码,只要在配置里把 primary-model 这个别名重新指向新的服务商即可。
2.3 代理模式和 SDK 模式的区别
litellm 有两种使用形态,很多人刚接触时会困惑。
第一种是 SDK 模式,也就是在 Python 代码里直接 import litellm,当作一个库来调用。这种方式适合单机脚本、快速原型、或者已经用 Python 做 AI 应用的团队。它的优点是轻量,不需要额外起服务,缺点是密钥还是分散在各处,也没有统一的访问控制。
第二种是代理模式,部署一个独立的 litellm 代理服务,所有请求都打给这个代理,由代理再去转发给各个模型服务商。这种方式才是完整的"网关"形态。业务端只需要配置一个代理地址和一个代理密钥,真正各家的 API 密钥都集中存放在代理这一侧。
代理模式最大的价值在于,它把一个团队级别的模型接入问题,变成了一个"配置并部署一个服务"的运维问题。前端、后端、算法同学不再需要各自去申请各家模型服务的密钥,只需要找管理员申请一个代理令牌。后续要调模型路由、限流、日志审计,也都在代理层统一完成。
2.4 路由器:从单点调用到策略分发
litellm 的代理内置了一个路由器组件。它的作用不仅仅是转发请求,还可以根据你定义的策略做智能分发。
最常见的三种策略是:
基于权重的路由。你给同一个模型别名配置两个真实模型,设置权重。比如 main-model 背后指向两家不同服务商的同级别模型,权重各占 50%,请求会被近似均匀地分发过去。这天然就能实现负载均衡。
基于优先级的回退。你配置一个主模型,再配置一个或多个备用模型。主模型超时或者返回错误时,litellm 自动重试备用模型。这种机制在关键业务里非常实用,能大幅提高调用成功率。我实际用过一次,某家服务商下午出现大面积抖动,我那段时间的线上请求因为自动降级,成功率几乎没掉。
基于用量的路由。litellm 会统计各个模型最近的延迟和错误率,自动把流量导向表现更好的模型。这种策略适合有多家同类模型且希望"哪个好用用哪个"的场景。
这些路由策略都是通过 yaml 配置声明的,不需要写业务代码,改配置即可调整。对应到团队协作场景中,产品想调整模型分配比例,只需要提一个配置变更,不用等开发排期。
3. 实操:从零搭建一个可用的统一模型网关
3.1 安装与最小启动
litellm 的安装非常简单,直接用 pip:
pip install "litellm[proxy]"注意我推荐加 [proxy] 这个扩展标志,因为代理模式需要额外的 server 依赖。如果只用 SDK 模式,直接 pip install litellm 就够了。
装好之后,先不急着配置,运行一下命令行工具验证安装是否成功:
litellm --help能看到帮助信息就说明装好了。此时你可以尝试用 SDK 模式做一次最小调用,验证环境整体正常:
import litellm resp = litellm.completion( model="provider-a/model-large", messages=[{"role": "user", "content": "测试一下"}], api_key="你的密钥", ) print(resp.choices[0].message.content)这里能用起来,基础调用链路就通了。下面我们进入代理模式的完整搭建。
3.2 编写网关配置文件
代理模式的灵魂在配置。我建议从最小可用的配置开始,然后逐步扩展。先看一个基础的 config.yaml:
model_list: - model_name: main-chat litellm_params: model: provider-a/model-large api_key: os.environ/PROVIDER_A_KEY - model_name: fast-chat litellm_params: model: provider-b/model-fast api_key: os.environ/PROVIDER_B_KEY general_settings: master_key: sk-local-test-key database_url: sqlite:///litellm.db这个配置做了三件事:定义了模型列表;把 main-chat 和 fast-chat 两个别名分别映射到两家真实模型;开启了一个本地数据库用于记录日志。
启动代理只需要一条命令:
litellm --config config.yaml --port 4000启动成功后,你会看到代理监听在本地的 4000 端口。现在用 curl 测一下:
curl http://localhost:4000/chat/completions \ -H "Authorization: Bearer sk-local-test-key" \ -H "Content-Type: application/json" \ -d '{ "model": "main-chat", "messages": [{"role": "user", "content": "你好"}] }'注意请求体里的 model 字段传的是别名 main-chat,而不是原来的"provider-a/model-large"。代理看到别名后,会去 model_list 里找到对应的路由目标,然后完成转发。
到这里,一个最简模型网关就跑起来了。接下来你可以让其他同事把接口地址指向你的代理,把密钥换成 master_key,他们不需要再关心 provider-a 是什么了。
3.3 配置回退和负载均衡
前面说的还是单点转发,真正体现 litellm 优势的是复杂路由。我们在模型列表里增加一个回退配置:
model_list: - model_name: main-chat litellm_params: model: provider-a/model-large api_key: os.environ/PROVIDER_A_KEY model_info: mode: completion - model_name: backup-chat litellm_params: model: provider-b/model-medium api_key: os.environ/PROVIDER_B_KEY litellm_settings: fallbacks: main-chat: - backup-chatfallbacks 配置的意思是:调用 main-chat 时如果出现超时或特定错误,自动改用 backup-chat。这个机制对生产环境极其重要。模型服务商的稳定性不受你控制,但你可以在网关层面给业务上一道保险。
如果想做负载均衡,也有对应的配置方式。可以在同一个 model_name 下面挂多个 litellm_params:
model_list: - model_name: balanced-chat litellm_params: model: provider-a/model-large api_key: os.environ/PROVIDER_A_KEY - model_name: balanced-chat litellm_params: model: provider-b/model-large api_key: os.environ/PROVIDER_B_KEY router_settings: routing_strategy: simple-shuffle此时请求会轮询到这两家真实模型上。如果你更希望"谁快用谁",可以把 routing_strategy 改成 usage-based-routing,让路由器根据实时延迟和错误率自动决策。
3.4 多副本部署和监控
代理本身是无状态的,但依赖数据库来记录日志和 key 信息。所以生产环境里做多副本部署是水到渠成的事:
general_settings: master_key: sk-prod-master-key database_url: postgresql://user:password@db-host:5432/litellm数据库从 sqlite 换成 postgres 之后,多个代理副本可以共享同一份配置和日志存储。前方再挂一个负载均衡器,就完成了一个小规模但完整的模型网关集群。
在这套架构下,流量路径是:业务应用 → 负载均衡 → litellm 代理副本 → 各家模型服务。任何一层出了问题,都可以通过扩容或重启来恢复,不会直接波及业务代码。
3.5 预算控制与用量观测
网关集中流量的一个额外好处是可以做预算管理。litellm 提供了按 key 限制配额的能力。比如给某个内部项目发一个专用 key,限制它每分钟最多请求 100 次:
general_settings: master_key: sk-prod-master-key database_url: postgresql://user:password@db-host:5432/litellm litellm_settings: max_budget: 1000 # 总预算 1000 美元 budget_duration: 30d同时,每次请求后,litellm 会在数据库中记录模型的输入 token 数、输出 token 数、响应延迟等信息。基于这些数据,你可以直接查出"上个月各个模型花了多少钱""哪个用户调用最频繁",比人工统计各家服务商账单要省心太多。这一点在成本敏感的业务里属于刚需。
4. 常见问题与排查技巧实录
4.1 拿到 404 或 "Unknown provider" 报错
最常遇到的问题是模型名写错了。我已经不知道多少次在代码里写了一个不存在的 provider 前缀,然后看到 404。litellm 对未知前缀的报错信息还算明确,会直接提示 unknown provider。
排查思路很简单:先确认 model 参数斜杠前面的标识符在 litellm 的支持列表里;再确认斜杠后面的模型名是那家服务真实存在的版本号。如果用的是自定义托管服务,确认 config.yaml 里是否正确配置了服务商标识和 base_url。
我自己的习惯是,先在 SDK 模式下测试最小调用,确认某个 provider 前缀能通,再把它写进代理配置。不要直接一步到位改 yaml,不然错误发生在代理转发层,排查链路会拉长。
4.2 请求超时和重试风暴
代理模式下,超时配置是一个容易踩坑的地方。默认情况下,litellm 代理的请求超时时间可能不太符合你的业务预期。比如你调用的模型是个长文本生成模型,正常情况下需要两分钟才能返回,但代理在 60 秒就超时了,结果上游模型还在继续生成,代理已经返回错误。客户端如果设置自动重试,就会造成请求重复堆积,费用翻倍。
这个问题的解法是分层设置超时。当你使用回退功能时,建议给每层模型单独设置合理的超时时间:
litellm_settings: request_timeout: 600 fallbacks: main-chat: - backup-chat如果某个模型服务固定需要较长时间,还可以在模型配置里单独指定:
- model_name: slow-model litellm_params: model: provider-a/model-extra-long api_key: os.environ.PROVIDER_A_KEY timeout: 900另外一个实操心得是:客户端不要做过于激进的自动重试。网关层已经有回退机制了,客户端只需要处理最终的超时错误即可。如果两边都在重试,一旦上游服务抖动,重试流量会被放大好几倍,反而是帮倒忙。
4.3 密钥管理不当导致的安全隐患
代理模式的初衷之一是集中管理密钥,但很多人用着用着就会犯一个错误:把各家服务商的密钥直接写在 yaml 明文里,然后整个仓库提交到代码管理平台。我第一次部署时也这么干过,后来意识到这就是给公司的账单送人头。
正确做法是利用环境变量注入:
model_list: - model_name: main-chat litellm_params: model: provider-a/model-large api_key: os.environ/PROVIDER_A_KEY服务商密钥只放在服务器环境变量或者密钥管理服务中,yaml 文件本身可以安全入库。另外,代理的 master_key 务必使用高强度随机串,不要用 sk-test 这类弱口令。之前就有团队把默认 master_key 暴露在公网,结果被人盗用疯狂调用模型,月底账单直接爆掉。
4.4 同一模型在不同服务商之间的效果差异
很多人误以为同一个模型名在不同服务商那里差不多。实际上,即使是同级别的模型,不同服务商的提示词格式、默认参数、上下文裁剪策略都不一样。
我在一次迁移中发现,同一段提示词在服务商 A 上输出正常,换到服务商 B 后格式明显变差。排查了半天,最后发现是服务商 B 的接口对 system 消息和 user 消息的处理方式与 A 不完全一致。litellm 虽然统一了调用格式,但它不会替你做提示词层面的适配。切换模型后,一定要针对新模型的输出做一些回归测试,尤其是涉及结构化输出时。
4.5 数据库连接池和日志写入瓶颈
如果你用了 sqlite 作为代理的数据库,在请求量上来之后很快会遇到写入瓶颈。sqlite 不擅长高并发写入,日志记录本身会拖慢请求响应。
我的建议是,生产环境至少换到 postgres,并且给 litellm 的数据库表做定期归档。因为 token 使用量和请求日志都是增长型数据,长时间不清理会让查询变慢,甚至影响代理本身的持久化操作。早期我部署时没在意,结果跑了三个月之后,日志表已经有几千万行,控制台打开都要等好几秒。
4.6 本地测试和远程模型服务的网络问题
另外一个小经验:本地起代理、远程调模型服务时,要确认你的服务器能稳定访问模型服务商的接口。有些云环境的出网策略比较严格,需要配置白名单或者网络代理。很多同学在本地跑通了,一到服务器上就各种超时,八成不是 litellm 的问题,而是网络出网被限制。排查这类问题,可以先不通过 litellm,直接在服务器上用 curl 测试模型服务商的接口,看能不能通,再回过来排查配置。
5. 一些值得深入的方向
如果你已经把 litellm 跑起来了,后面有几个方向值得继续探索。
第一是缓存。litellm 支持对历史请求做语义缓存,重复的 prompt 可以直接返回缓存结果。对于业务中重复率高的场景,比如客服问答、知识库检索,缓存可以显著降低成本和延迟。配置上一两行就能开起来,但需要想清楚缓存命中条件和失效策略,避免给用户返回过期内容。
第二是模型质量看板。加上数据库之后,你已经有了所有请求的延迟、token 消耗、错误率数据。可以基于这些数据做一个简单的可视化看板,按模型维度看调用量和成本趋势。这个对技术管理者和预算决策特别有帮助,比月底翻账单靠谱得多。
第三是多环境管理。开发、测试、生产三套环境用不同的代理配置,通过环境变量区分。开发环境可以用便宜的模型,生产环境用质量更高的模型,而业务代码完全不用改,因为统一用的是别名。这几乎是无感切换,也是我推荐每个团队都试一下的做法。
我自己在实际操作中最深的体会是:litellm 真正省下的不是写那几行适配代码的时间,而是团队协作中的沟通成本。以前接一个新模型,要拉群、对文档、联调,折腾一两天。现在配置里加一行,测试几分钟,再灰度上线,整个链路顺畅很多。而且因为错误格式统一了,排查问题也不再需要对照不同服务商的文档去翻译报错信息,定位速度提升非常明显。
如果你正在为多模型接入的事发愁,建议先拿一个小项目把代理跑起来,感受一下"所有模型长一个样"的开发体验。等用顺手了,再把路由策略、预算控制、监控面板一个个加上去。这个工具不难,但能把很多人从模型接入的泥潭里解放出来,把精力放回真正的业务逻辑上。