这段时间我一直在做一个内部的多模型调度平台,见得最多的一个词就是 litellm。它不是一个具体的大模型,也不是某个框架的插件,而是一个把各种模型 API 统一成一套格式的网关层工具。说白了,你只需要学会接一个服务,剩下的几十个模型都由它帮你转成标准格式。这个工具很适合正在做 AI 应用、又不想被某一家模型 SDK 绑死的开发者,也很适合团队里要统一管理模型密钥、统计成本的负责人。哪怕你只是个刚接触 LLM 开发的新手,把它当成一个“API 转发服务”来理解,也完全够用。
这套东西我用了一段时间,从单机调试到生产环境部署都踩过不少坑,也实打实验证过它的稳定性。这篇文章不打算贴一大堆官方文档,而是把我实际配置、运行、排查的过程拆开来讲,包括为什么这么设计、哪些参数必须调、哪些功能看着有用但实际容易翻车。你要是正准备引入 litellm,或者已经在用但被配置和路由搞到头大,这篇文章应该能省你不少时间。
1. 为什么需要 litellm:多模型时代的基础设施需求
1.1 被不同模型 API 格式支配的痛苦
先聊一个很实际的问题:如果你手头同时接了 GPT 系列、Claude 系列、Gemini 系列,再加上国产的 DeepSeek、文心、通义、豆包的一两个模型,你的代码会变成什么样?
我最早做多模型接入时,给每个厂商写了一个封装类。每个类的请求参数不一样,鉴权方式不一样,返回结构不一样,工具调用的字段也不一样。今天模型 A 的 SDK 升级了,明天模型 B 的接口加了新参数,后天你自己代码里要加流式输出,每个封装类都要跟着改一遍。到后来改一个公共字段,能改出十几个文件,测试跑半天。最崩溃的是不同模型对 system prompt 的优先级处理不一致,同一个指令在这家有效、在那家无效,排查起来完全靠试。
这种情况持续了大概两个月,我就意识到必须把“模型接入”这件事从业务代码里剥离出去。我需要一个统一的入口:不管底层是哪个模型,业务代码只关心一套 API 格式。这也是 litellm 最核心的价值。
1.2 统一网关做的事:从“改代码”到“改配置”
litellm 做的事情,可以类比成电源转换头。你去国外出差,不会因为插座形状不一样就重新装修酒店,你会带一个转换头。litellm 就是 AI 领域的转换头:上游接各家模型的原生 API,下游统一暴露成 OpenAI 风格的接口。你的业务代码只要适配这一种风格,后续想换模型、加模型,全部在网关配置里改,一行业务代码都不用动。
它的第二个价值是集中管理。密钥统一放在网关侧,业务侧拿到的都是网关下发的独立 key,可以随时吊销、限流、统计用量。第三个价值是策略化路由:同一个请求可以配置主模型、备用模型、负载均衡策略、超时重试机制,这些原本要写在业务代码里的逻辑,全部下沉到网关层。
我自己后来的经验是:凡是准备长期做 AI 应用的团队,不管现在接了几家模型,都值得在最前面放一个 litellm 这样的网关。它前期多花半天配置,后期能省下一整个“模型兼容性维护”的岗位。
2. 快速上手:把 litellm 跑起来
2.1 安装与基础配置文件
litellm 的安装很简单,pip 一行命令:
pip install 'litellm[proxy]'这里我建议直接装带 proxy 的版本,因为单独装纯 SDK 版还得自己写服务封装,没必要。装完之后你会有两个核心东西:一个是litellm命令行工具,用来启动代理服务;一个是 Python 模块,可以在代码里直接调用。我们这里主要用代理服务模式。
接着创建一个config.yaml,这是整个网关的核心配置。我第一次写的时候以为越复杂越好,结果发现最小可用配置其实只有几行:
model_list: - model_name: gpt-3.5-turbo litellm_params: model: gpt-3.5-turbo api_key: os.environ/OPENAI_API_KEY - model_name: claude-3-haiku litellm_params: model: claude-3-haiku api_key: os.environ/ANTHROPIC_API_KEY注意几个容易踩的坑:
model_name是你在网关里对外暴露的名字,客户端请求时填这个名字。它不必和上游真实模型名一样,你可以把 gpt-3.5-turbo 改成你喜欢的别名。litellm_params.model是真正传给上游服务的模型标识,必须和厂商模型名完全一致。api_key不要直接写在 yaml 里,用os.environ/变量名的方式引用环境变量,否则配置文件一泄露,所有厂商密钥全暴露。
这背后的逻辑其实很清晰:model_name是网关的“门牌号”,litellm_params.model是“真实房间号”,两者解耦之后,你随时可以把门牌号指向另一个房间,客户端无感知。
2.2 发出第一个请求
配置写好后,启动服务:
litellm --config config.yaml --port 4000看到类似 “Uvicorn running on http://0.0.0.0:4000” 的日志就说明起来了。然后你可以用 curl 验证:
curl http://localhost:4000/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer sk-your-master-key" \ -d '{"model":"gpt-3.5-turbo","messages":[{"role":"user","content":"你好,请做个自我介绍"}]}'正常情况下,你会拿到一个 OpenAI 风格的 JSON 返回,里面的model字段可能显示的是gpt-3.5-turbo。如果你请求claude-3-haiku,返回格式也是一模一样的。
如果你业务代码已经用了 OpenAI 的 Python SDK,那就更简单了,只需要改两个地方:
from openai import OpenAI client = OpenAI( base_url="http://localhost:4000/v1", api_key="sk-your-master-key" ) resp = client.chat.completions.create( model="gpt-3.5-turbo", messages=[{"role": "user", "content": "你好"}] ) print(resp.choices[0].message.content)这就完了。之前我为各家 SDK 写的兼容层全部可以删掉,以后所有模型调用统一走这一套。
2.3 最少必要配置背后的设计逻辑
为什么 litellm 要用一个 yaml 文件集中管理,而不是像很多工具那样提供一大堆编程接口?我理解它的设计哲学是:模型接入本质上是一个运维配置问题,而不是业务代码问题。你换模型、加模型、改密钥,这些都不应该触发代码发布流程。用配置文件管理,意味着运维同事也能参与维护,改完重启或热加载即可。
另外你可能注意到,litellm 对外暴露的接口完全对齐 OpenAI 格式,这不是因为它“抄袭”,而是因为 OpenAI 格式已经事实上成了 LLM API 的通用语言。几乎所有模型厂商都提供了 OpenAI 兼容接口,或者有转换中间层。litellm 选择把自己的输出统一成这个格式,等于借力了整个生态——LangChain、各类框架、各种开源工具都能直接对接,不需要额外适配。
3. 核心功能深挖:路由、密钥与成本
3.1 模型路由与回退策略
litellm 能火起来,靠的不只是格式统一,还有路由能力。生产环境里最怕的一件事是:上游模型突然抖动,或者限流,而你的业务是强依赖它的。如果没有网关层,你就只能眼睁睁看着线上报错,临时改代码切备用模型,等发布完高峰已经过了。
在 litellm 里,这种逻辑只需要在config.yaml里声明。比如我想实现:主调用 GPT 系列某模型,如果它报错或者超时,自动切换到 DeepSeek 的模型:
model_list: - model_name: gpt-4o-mini litellm_params: model: gpt-4o-mini api_key: os.environ/OPENAI_API_KEY - model_name: deepseek-chat litellm_params: model: deepseek-chat api_key: os.environ/DEEPSEEK_API_KEY router_settings: fallbacks: [ {"gpt-4o-mini": ["deepseek-chat"]} ]这里我把主模型命名为gpt-4o-mini,备用模型命名为deepseek-chat。实际请求时,litellm 会先尝试主模型,如果出现连接错误、限流、超时等情况,就自动把请求转发给备用模型。客户端拿到的还是一样的 OpenAI 风格结果,只是模型变成了备用的。
我实测过这种 fallback 的效果:模拟把上游密钥改成错误的,网关层会在几十毫秒内感知并切换到备用模型,业务侧完全无感。这种能力在生产环境的价值怎么强调都不过分。
3.2 密钥管理与多租户隔离
很多团队接 litellm 之前,都是直接把厂商密钥写在前端或客户端里,这其实非常危险。密钥一旦泄露,别人可以拿你的额度疯狂调用,账单分分钟爆掉。litellm 提供虚拟密钥机制,可以很好地解决这个问题。
启动服务时要设置一个master_key,这个 key 是管理员权限,用来调管理接口和创建其他 key。你可以给每个业务线、每个客户、甚至每个功能模块单独生成一个虚拟 key:
curl -X POST http://localhost:4000/key/generate \ -H "Authorization: Bearer sk-master-key" \ -H "Content-Type: application/json" \ -d '{ "models": ["gpt-4o-mini"], "max_budget": 10, "budget_duration": "1d" }'返回结果里会包含一个新的sk-开头的 key,这个 key 只能调用指定的模型,并且有每天 10 美元的预算上限,超过就自动拒绝。这些虚拟 key 都有一个唯一标识,你可以随时查询它的用量、手动吊销、修改模型权限。听起来是不是很像云平台的 API key 管理?对,litellm 做的是就是这一层,只不过它管的是模型网关。
这个功能对 To B 场景尤其重要。假设你做了一个 SaaS 应用,有几十个企业客户,你不可能为每个客户单独去注册一家模型厂商的账号,更不可能把自己的主密钥发给客户。正确做法是:全公司共用一个上游账号,通过 litellm 给每个客户下发独立的虚拟 key,各自限额、各自统计、各自审计。真出问题,吊销一把钥匙即可,不影响其他客户。
3.3 成本追踪与用量统计
多模型接入之后,你很快会遇到一个头疼的问题:钱花哪儿了?以前每家模型一个账单,出自不同平台、不同币种,对账能对到怀疑人生。litellm 会把每一次请求的 token 数、模型、用户、key 都记录下来,并提供查询接口和界面。
主要看两个东西:
/spend/logs接口:按时间、key、模型查询调用日志和费用。- Prometheus 指标:litellm 内置了
/metrics端点,可以接入监控系统,看请求量、延迟、错误率、tokens 消耗趋势。
我自己习惯每天看一眼按模型维度的费用排行。这一步很有用:有时候你以为某个模型很便宜,结果调用量一大,费用远超预期;有时候一个新的模型性能差不多,价格只有原来的一半,这时候直接在配置里加一个路由规则,把流量切过去,成本立刻降下来。
4. 生产环境的关键设置:从“能用”到“扛得住”
4.1 超时、重试与并发控制
本地调试时,litellm 默认配置已经够用。但一旦上生产,上游模型的抖动、慢响应、限流都是家常便饭,你需要显式地配置超时和重试策略。
我推荐的router_settings配置:
router_settings: timeout: 30 retry_policy: Timeout: 2 RateLimitError: 3 max_retries: 3 allowed_fails: 2 cooldown_time: 30逐个解释一下:
timeout:单个请求等待上游响应的最大秒数。模型推理通常很慢,30 秒是个比较合理的值,太长会拖垮你的服务,太短大模型容易超时。retry_policy.Timeout:超时后的重试次数。注意,重试不是无脑越多越好,因为重试请求同样消耗上游配额,而且如果你的业务对实时性要求高,重试 2 次就够表心意了。retry_policy.RateLimitError:遇到上游限流(HTTP 429)时重试 3 次。限流通常是瞬时峰值造成的,隔几秒再试成功率很高。allowed_fails和cooldown_time:当某个上游实例连续失败达到一定次数后,litellm 会把它拉入冷却状态,暂停流量一段时间,避免继续往已经挂掉的服务上打请求。
这些参数看着不起眼,但生产环境稳定性全靠它们托底。
4.2 缓存策略:降本增效的关键
LLM 调用贵,这是绕不开的痛点。如果一个 prompt 在短时间内被重复请求,每次都去上游算一遍,既浪费钱又增加延迟。litellm 内置了缓存机制,支持精确缓存和语义缓存。
精确缓存的配置很简单,在litellm_settings里加上:
litellm_settings: cache: true cache_params: type: redis host: localhost port: 6379开启后,完全相同的请求会在缓存有效期(默认 24 小时)内直接命中缓存返回,不再请求上游。这个功能对 FAQ 机器人、文档问答这类场景效果显著——用户翻来覆去问的就那几个问题,命中率轻松超过 50%。
语义缓存的差别是:它会对用户的文本做 embedding,然后算相似度,意思相近的提问也能命中同一个结果。这配置更复杂,而且有误判风险,比如两个相似但意图不同的问题可能被当成同一个返回,所以我的建议是:先开精确缓存,命中率不够再考虑语义缓存。
实际使用后你会发现,缓存带来的不只是省钱,还有响应速度的质变。原本要等 3 秒的请求,命中缓存后 100 毫秒内返回,用户体验完全两个档次。
4.3 部署形态:网关该放在哪里
litellm 是一个 Python 服务,部署形态很灵活。开发环境直接跑在本机就行,生产环境我建议用容器化部署,挂到 Kubernetes 或者直接用 Docker Compose 跑。
关键配置项有这么几个:
- 日志持久化:默认日志打到标准输出,容器一重启就丢了。建议配置日志文件挂载到持久卷,方便追查历史问题。
- 环境变量注入:所有密钥用环境变量注入,不要写死在镜像或 yaml 里。
- 健康检查:litellm 暴露了
/health/liveliness和/health/readiness端点,容器编排时可以配置探针。
网关部署的位置要尽量靠近你的业务服务,别跨个大洲去调远端网关。因为 litellm 本身是一个代理,每多一层网络跳转就多一份延迟,把它跟业务部署在同一个内网,延迟几乎可以忽略。
5. 常见问题与排查技巧实录
5.1 上游限流与配额报错
这是我遇到最多的一类问题。现象是:请求偶尔成功,偶尔返回 429 或者上游的错误信息提示 rate limit,表现为线上接口成功率不稳定。
排查思路:
- 先看 litellm 日志,确认 429 来自哪个上游模型。有时候不是整个厂商限流,而是某个模型本身并发额度低。
- 看自己有没有设置
num_retries和重试策略。如果没配,429 直接透传给客户端,业务表现就是偶发失败。 - 如果上游确实频繁限流,最有效的解法不是调大重试次数,而是配置多模型 fallback。让主模型限流时自动切到备用模型,这样用户无感,成功率直接拉满。
5.2 响应格式不一致
不同模型返回的格式细节差异很大,尤其是工具调用(function calling)和流式输出。litellm 虽然统一了外层格式,但某些字段语义还是会有差异。
常见情况:GPT 系列的工具调用参数是 JSON 字符串,Claude 系列某些版本的工具调用结构不同。litellm 在大多数情况下能正确转换,但遇到模型特定扩展字段时,偶尔会丢失信息。
我的排错方法是:先用网关的日志接口查看原始上游响应,再用网关返回的结果对比,逐字段检查。如果确实有字段差异,最简单的做法不是改网关代码,而是尽量避免直接依赖模型特定的返回字段,统一在业务层做解析。
5.3 配置加载与热更新问题
我最初以为改完config.yaml重启服务就完事了,后来发现生产环境重启代价很高。litellm 支持部分配置的热更新,可以通过管理接口动态新增 key、修改预算,但model_list的变更通常还是需要重启。
这里分享一个降低痛点的做法:不要把配置写死成一个文件,而是分环境管理。开发环境用最小配置,生产环境用完整配置,通过环境变量切换。再配合 CI/CD 流程,在集群里做滚动重启,把影响降到最低。
6. 实测心得与扩展脑洞
6.1 我踩过的坑
用了一个多月,我遇到过几个比较隐蔽的问题,这里列一下供参考。
虚拟 key 和上游 key 的鉴权关系容易搞混。虚拟 key 是 litellm 自己管理的,它只负责决定“你能不能用网关”,而真正调用上游的是配置里的厂商 key。也就是说,即使虚拟 key 有效,如果上游 key 过期或余额不足,请求照样失败。排查时别只盯着虚拟 key,上游 key 的状态也要一起看。
缓存误判的问题。开启语义缓存后,我发现有一个问“如何申请退款”的请求,命中了“如何申请发票”的缓存结果,用户直接看懵了。后来我把语义缓存关掉,只保留精确缓存,虽然命中率降了一点,但正确性优先。如果确实要用语义缓存,建议只在 FAQ 这类高风险敏感度低的场景开启。
日志量过大的问题。litellm 默认记录每次请求的完整输入输出,在流量大的时候会产生海量日志,存储成本肉眼可见地涨。后来我调低了日志级别,只记录错误日志和关键指标,完整内容按需抽样记录。
6.2 这个工具后续还能怎么玩
litellm 的能力不止上面这些。我还试过把它和本地部署的开源模型接在一起,作为云端模型不可用时的最后兜底。也试过用它的管理接口,配合内部的审批流,让前端动态申请和释放模型权限。这其实就是把模型资源当成云资源来管理,思路打开之后能玩出很多花样。
根据我个人的经验,如果你所在团队的业务和模型深度绑定,litellm 这种网关层工具越早引入越好。与其天天切到不同平台去配模型、统计账单、处理限流,不如把这些问题全部收敛到一个入口解决。架构上简化的不只是代码,更是团队协作方式和排障路径。
最后再分享一个小技巧:litellm 提供了和 LangChain 的兼容接口,意味着你原来用 LangChain 写的应用,只需要把 base_url 改成本地网关地址,其他代码基本不用动。你要是已经在用 LangChain 做开发,这个接入成本几乎为零,值得马上试一试。