1. 为什么需要 litellm:多模型接入的"翻译层"问题
我从去年开始频繁对接各类大模型 API,最头疼的不是某个模型的效果好坏,而是切换模型时的适配成本。OpenAI 用一套请求格式,Anthropic 是另一套,本地部署的模型又可能走不同的协议。每次想对比一下不同模型的效果,都要改代码、调参数、重试逻辑各自写一遍。这种碎片化体验持续了一段时间后,我决定把 litellm 正式引入自己的技术栈。
litellm 本质上是一个开源的 LLM 网关服务。它做的事情可以概括为三件:统一 API 格式、统一模型接入、统一治理策略。你可以把它理解成一个"翻译层+路由层":对外提供一套 OpenAI 兼容的接口,对内把请求翻译成各个模型厂商或开源框架对应的协议格式,再配合路由策略、预算控制、请求重试、负载均衡等能力,把多模型管理变成一个集中式操作。
这么说可能有点抽象。举个例子:我的团队需要在应用里接入 GPT、Claude、国产开源模型和本地部署的 Qwen 系列。以前每条通道各写一套封装,鉴权和重试逻辑重复三遍。引入 litellm 之后,所有请求都发给 litellm 这一个入口,代码里只需要维护一套 OpenAI SDK 的调用方式,切换模型时只改一个 model 名称,其他逻辑完全不动。
这个项目适合谁?如果你只需要一个模型的 API,litellm 对你的价值有限;但如果你在评估多个模型、做效果对比、要给不同团队或业务线分配 token 额度、想集中观测成本与延迟,那这个网关层几乎就是刚需。我的经验是,当接入的模型超过两家时,统一网关带来的收益就已经能覆盖部署成本。
2. 设计思路拆解:litellm 为什么值得作为基础设施
2.1 核心痛点:一百个模型一百种协议
各家大模型的 API 风格并不一致。OpenAI 的接口以 chat/completions 为核心,Anthropic 的 Messages API 在结构上相近但字段不同,Cohere 又有自己独特的参数设计,更别提一些开源模型走的是非标准 REST 风格。开发者如果直接对接多个服务商,每个服务商都要处理超时、限流、报错格式和鉴权方式。
litellm 的解决思路非常直接:内部维护了一个"模型到提供商"的映射表,并且实现了大量提供商 SDK 的适配逻辑。它把请求统一转换为每个提供商期望的格式,再把响应统一转回 OpenAI 格式。这意味着,只要 litellm 支持某个模型,使用者就不需要了解该模型背后的协议细节。
这个思路其实很像数据库领域的 ORM。ORM 让你用同一种对象操作语法访问 MySQL 或 PostgreSQL,litellm 让你用同一套 OpenAI 客户端代码调用任何底层模型。我比较看重的也是这一点:它不只是省了几个接口封装函数,而是把模型的接入成本从"每个模型一次开发"降到了"每个模型一次配置"。
2.2 企业级需求:路由、限流与预算的统一治理
光做协议转换,litellm 和普通的封装库没什么区别。它的价值重心其实在治理能力上。当你的模型调用不再只是开发者本地的测试请求,而是一个团队甚至多个团队在生产环境中共享时,以下几个问题就变得绕不开:不同业务线如何分配 token 预算?如何防止某个异常任务打爆账户额度?如何在一个模型不可用时自动切换?如何在调用量指数级增长时保持可控?
litellm 把这些能力内置了。它支持虚拟密钥(Virtual Key)机制,每个密钥可以绑定独立的预算、速率限制和模型白名单;支持模型级别的负载均衡与故障切换;支持按用户、按项目的成本追踪。这些能力单靠"在代码里封装一个 openai client"是做不到的,必须要有一个中心化的服务来做状态管理和策略执行。
我自己感受最深的场景是预算控制。之前没有网关时,模型调用的费用是粗放统计的,月底拿到账单才知道超支了多少。把 litellm 架起来之后,每个业务线配一个虚拟密钥,设置月度上限,限额快耗尽时自动告警;还可以在仪表盘上实时看到每分钟的 token 消耗量。这种"看得见、控得住"的能力,是在多模型场景下必须的基础设施。
2.3 两个关键概念:数据面与控制面
litellm 的架构里有两个基础概念值得先理解,分别是数据面(Data Plane)和控制面(Control Plane)。
数据面负责处理模型调用请求本身,包括请求转发、响应转换、流式传输、重试与fallback。控制面则负责管理配置、密钥、预算、模型路由规则等内容。litellm 开源版提供了一个基于 Django 的 Admin UI,用于管理虚拟密钥、预算、模型名称映射等;代理服务本身是一个 FastAPI 应用,处理 realtime inference 请求。愿意花时间理解这个划分的话,后续做生产部署时会非常清晰:控制面的数据需要持久化,数据面的节点可以横向扩容。
3. 核心细节拆解:模型路由、虚拟密钥与请求转换机制
3.1 模型路由是怎么工作的
litellm 在初始化服务时,会读取一个 model_list 配置,将"对外模型名"与"实际提供商+模型名"建立映射关系。你可以把同一个对外名称映射到多个实际部署的后端,形成一个候选组。请求到达后,litellm 按预设策略从一个组里选出一个后端执行转发。
举个例子,我在配置里这样写:
model_list: - model_name: my-gpt-role litellm_params: model: openai/gpt-4o-mini api_key: ${OPENAI_API_KEY} model_info: mode: chat cost_per_token: 0.00000015 - model_name: my-claude-role litellm_params: model: anthropic/claude-3-5-sonnet-20240620 api_key: ${ANTHROPIC_API_KEY}这时候我调用client.chat.completions.create(model="my-gpt-role", ...),litellm 会找到 my-gpt-role 映射,把它翻译为 OpenAI 的 gpt-4o-mini 请求。
如果我想做 fallback 和负载均衡,可以这样配置:
model_list: - model_name: routed-model litellm_params: model: openai/gpt-4o-mini api_key: ${OPENAI_API_KEY} model_info: mode: chat - model_name: routed-model litellm_params: model: anthropic/claude-3-5-sonnet-20240620 api_key: ${ANTHROPIC_API_KEY} model_info: mode: chat router_settings: routing_strategy: simple-shuffle fallbacks: []两个 backend 共用同一个对外名称 routed-model,请求到达后 litellm 在两者之间简单轮询。假如某个 backend 持续报错,litellm 会自动把请求转给另一个人。
实际生产配置里,我还会配合routing_strategy选择更聪明的方式,比如usage-based-routing(根据 token 消耗调节权重)、latency-based-routing(低延迟优先)。这些策略的适用场景不同,需要结合业务形态去选。
3.2 请求转换的底层机制
litellm 的转换逻辑核心是"先归一化为自己的内部表示,再转换为目标服务商格式"。这个设计和很多爬虫框架的 item pipeline 类似。请求进来时,litellm 会把消息、参数、工具定义解析为一个标准结构;发送前,再根据 provider 类型将标准结构序列化为目标格式;收到响应后,再把目标格式转回 OpenAI 的 ChatCompletion 结构。
这样做有个直观的好处:如果未来出现新厂商,只需要实现该厂商和标准结构的转换模板,而不需要改动上层代码。litellm 仓库里维护了大量 provider 的转换函数,这也是它版本迭代比较频繁的原因之一——新模型出现后通常很快就能被支持。
流式响应(stream)是另一处容易出问题的细节。有些服务商的流式格式不是标准 SSE,字段名或分段粒度不同。litellm 在流式场景下会做逐块(chunk)转换,把每个 chunk 的增量内容重新包装为 OpenAI 的 chunk 格式。这个转换有性能开销,但对客户端而言,所有的流式接口体验是一致的。
3.3 虚拟密钥:把配额管理细化到业务线
虚拟密钥是 litellm 控制面里最实用的功能之一。每创建一个虚拟密钥,实际上是在 litellm 内部生成一个 UUID,并把它与预算、速率限制、模型访问范围绑定。你把这个密钥给到业务方,他们在请求头里用Authorization: Bearer <virtual_key>调用 litellm,litellm 鉴定身份后对请求执行配额和速率限制。
我通常是这样设计密钥层级的:
- 平台级密钥:用于管理后台和运维操作,权限最大。
- 业务线密钥:每个业务线一把,月度 token 预算独立,模型白名单按需开放。
- 临时密钥:用于测试、CICD 或一次性任务,设置了极短的过期时间和小额预算。
这样设计的好处是,某个业务线上线了一个有问题的定时任务,最多耗尽它自己的配额,不会影响其他业务。出现费用异常时,按密钥排查也能直接锁定责任方。
如果某个请求的虚拟密钥超预算,litellm 默认返回 429 并包含说明信息。我建议在客户端把 429 当作正常业务状态处理,在任务重试策略中不要无限重推,否则预算限流机制本身的告警价值会被掩盖。
4. 实操过程:从本地部署到生产接入
4.1 部署方式选择
litellm 的部署方式很灵活。Python 包方式安装最简单,适合开发调试;Docker 部署更适合生产;Kubernetes 部署适合多副本场景;企业版还有 Helm Chart 和专门的控制面组件。
我建议从 pip 安装开始,把逻辑跑通再迁移到 Docker:
pip install 'litellm[proxy]'或者你也可以用官方 Docker 镜像,拉下来直接起:
docker run -d --name litellm-proxy \ -p 4000:4000 \ -e OPENAI_API_KEY=sk-xxx \ -e ANTHROPIC_API_KEY=sk-xxx \ ghcr.io/berriai/litellm:main-latest如果你只是临时测试某个模型,也可以用LITELLM_MODELS环境变量注入模型配置,省去写 config.yaml 的步骤:
export LITELLM_MODELS='[{"model_name": "gpt-test", "litellm_params": {"model": "openai/gpt-4o-mini", "api_key": "sk-xxx"}}]' litellm --config /dev/null --port 4000不过这种一次性的方式不适合生产。生产环境我强烈建议用 YAML 配置文件,因为控制面需要识别每个模型的 cost 属性、模式等元数据,环境变量方式的可维护性会随着模型数量增加急速下降。
4.2 配置文件的核心字段
一个最小可用的 config.yaml 如下:
model_list: - model_name: gpt-4o-mini litellm_params: model: openai/gpt-4o-mini api_key: os.environ/OPENAI_API_KEY - model_name: claude-sonnet litellm_params: model: anthropic/claude-3-5-sonnet-20240620 api_key: os.environ/ANTHROPIC_API_KEY litellm_settings: drop_params: true set_verbose: true num_retries: 3 request_timeout: 60 general_settings: master_key: sk-litellm-master database_url: postgresql://user:pass@localhost:5432/litellm这里有几个我反复踩过坑的地方值得说明。
第一,api_key用os.environ/XXX形式比明文更安全,litellm 在读取配置时会解析环境变量。不要在 YAML 里直接写密钥,除非你确定配置文件不会进入版本控制。
第二,drop_params是一个容易被忽略但很有用的开关。不同模型的参数集合差异很大,你在请求里传了 temperature、max_tokens、tools 等参数,目标模型不一定支持全部字段。打开 drop_params 后,litellm 会丢掉目标模型不支持的参数,避免请求因为多余字段报错。代价是请求参数可能被静默忽略,所以如果要保证一致性,建议业务侧还是先按模型白名单控制参数。
第三,database_url不是必填的。不配置时,litellm 使用本地 SQLite 存储虚拟密钥和日志,适合开发;生产环境一定要切换到 Postgres,否则多副本部署时密钥和配置状态无法共享。
4.3 启动代理并用真实请求验证
配置完成后,启动服务:
litellm --config config.yaml --port 4000然后用 curl 验证基本连通性:
curl http://localhost:4000/v1/models \ -H "Authorization: Bearer sk-litellm-master"能列出模型列表,说明控制面已经正常读取 model_list。接着发一个完整的对话请求:
curl http://localhost:4000/v1/chat/completions \ -H "Authorization: Bearer sk-litellm-master" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o-mini", "messages": [{"role": "user", "content": "你好,介绍一下你自己"}], "max_tokens": 100 }'如果返回正常的 ChatCompletion JSON,说明代理已经完成了一次完整的协议转换和转发。
值得注意的一点是,litellm 暴露的/v1/models和/v1/chat/completions接口与 OpenAI 的结构一致。这意味着你几乎可以把base_url改成http://localhost:4000/v1,然后继续使用官方 OpenAI Python SDK。下面这段代码是我实际在项目中用的:
from openai import OpenAI client = OpenAI( api_key="sk-litellm-master", base_url="http://localhost:4000/v1" ) response = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "用一句话解释什么是路由网关"}], temperature=0.3 ) print(response.choices[0].message.content)这种兼容性极大地降低了迁移成本。团队里有大量现有代码基于 OpenAI SDK 开发时,不需要改业务逻辑,只需要在客户端配置层换 base_url 和 token,就能把流量切到 litellm 上。
4.4 配置 fallback 与预算限制
下面这套配置是我的常用模板,兼顾了故障转移和预算控制:
model_list: - model_name: primary-model litellm_params: model: openai/gpt-4o-mini api_key: os.environ/OPENAI_API_KEY model_info: mode: chat - model_name: primary-model litellm_params: model: anthropic/claude-3-5-sonnet-20240620 api_key: os.environ/ANTHROPIC_API_KEY model_info: mode: chat router_settings: fallbacks: [ {"primary-model": ["primary-model"]} ]这种写法的含义是,两个后端共享 primary-model 名字,第一个请求随机转发;如果主后端连续失败,litellm 会把请求切到第二个。
预算限制主要由控制面 UI 或 API 创建虚拟密钥时配置。比如,要给某个内部工具创建一把月预算 20 美元的密钥,用 curl 调用管理接口即可:
curl http://localhost:4000/key/generate \ -H "Authorization: Bearer sk-litellm-master" \ -H "Content-Type: application/json" \ -X POST \ -d '{"models": ["primary-model"], "max_budget": 20, "budget_duration": "30d"}'响应里会返回 key 值和 key 的 id。这把 key 将绑定 30 天 20 美金的预算,并且只能调用 primary-model。实际项目中我会再通过控制面把该 key 标记为"某个业务线的私有密钥",这样在日志面板中就能按 key 分组查看消耗。
我还遇到过需要按用户追踪的场景。litellm 支持自定义 OpenAI 之外的 metadata 字段传参。开发侧可以在请求参数里传入user字段,litellm 会结合虚拟密钥形成更细粒度的成本归因。这在实际治理中很实用。
5. 常见问题与排查技巧实录
5.1 模型响应用不了
最常见的报错是404 model not found。出现这个错误通常不是模型不存在,而是 litellm 配置文件里的model_name和客户端请求里的model不一致,又或者是配置更新后代理没有 reload。litellm 服务读取配置后常驻内存,修改 yaml 后需要重启或触发配置热加载。我一开始经常忘记重启,排查了许久才发现是配置没生效。
提示:如果修改了 config.yaml 后没有生效,先确认是否是在原有进程上重启,而不是再开了一个新端口占用进程。
另一个高频原因是drop_params未开启。比如你用某个请求参数调用 Claude,Claude 不支持该字段,litellm 默认会报错而不是自动丢弃。遇到参数兼容性问题时,先打开drop_params: true测试,再决定是否需要从业务代码层统一收敛参数。
5.2 超时与重试导致的雪崩
litellm 默认的请求超时是 600 秒,对实时性要求较高的应用来说不太合适。我在代理层把request_timeout调到了 60 秒,加上num_retries: 3,并在业务侧设置了应用级超时上限。如果底层模型持续慢,litellm 的重试会被触发;但要注意,重试是完整重发请求,对于幂等性要求较高的业务(如扣费、状态变更)需要谨慎使用,最好由路由侧做一次 fail-fast 而不是盲目的多次重试。
我踩过一个典型问题:某个模型服务在高峰期响应时间超过 100 秒,litellm 按重试策略把同一请求发多次,结果模型后端负载更高,引起连锁雪崩。后来我调整思路:把 retries 设为 1,打开allowed_fails和cooldown_time参数,让故障后端进入冷却期,而不是一直重试。
5.3 流式响应在客户端显示不完整
有的模型在流式模式下会返回最后一段 usage 信息,有的不会;有的会把 content 拆成极小片段,有的则把多段内容合并为一个 chunk。正常情况下 OpenAI SDK 可以正常消费这些 chunk,但如果你的业务在中间层做了字符串拼接或关键词识别,就要注意兼容层在转换流式 chunk 时的字段映射可能与原生 API 有细微差异。
我的经验是:不要在 litellm 代理层做太多的流式后处理。代理层本身已经承担了解析、转换和转发开销,再做内容层加工会成倍放大性能压力。流式内容的业务处理放在服务端或客户端各自负责,代理只做转发和转换。
5.4 生产环境配置速查表
下面这张表是我在维护 litellm 过程中总结的高频排查清单,建议保存一份:
| 现象 | 可能原因 | 排查思路 |
|---|---|---|
| 404 model not found | 对外模型名配置错误或未加载 | 检查 model_list、确认配置更新 |
| 401 unauthorized | 虚拟密钥无效或权限不足 | 检查 key 是否被删、模型白名单是否匹配 |
| 429 too many requests | 超预算或触发速率限制 | 查控制面 key 用量,调整预算或 rate limit |
| 500 internal error | 提供商 API 返回异常 | 看日志中 provider 报错详情,先绕开该 provider |
| 响应过慢 | 无 timeout、没有冷却期 | 配置 request_timeout、cooldown_time |
| 参数被静默忽略 | drop_params 生效 | 确认业务是否依赖该参数,必要时关闭 drop_params |
5.5 用日志与观测面板定位问题
litellm 自带每请求日志,记录模型名、虚拟密钥、延迟、token 使用量和成功/失败状态。字段信息非常细致,包括model、messages、response、spend、total_tokens等。生产环境建议把日志持久化到 Postgres 或通过企业版接入外部可观测性系统。
我自己在做成本归因时,最常看的是"按密钥分组的 tpm/spend 曲线"。这个视图能帮我发现某个业务线是否突然出现了消费陡增,也能定位是否有人拿共享密钥在跑大规模批处理任务。如果配置了database_url,这些数据会定期写入数据库,配合仪表盘查看很直观。
6. 落地建议:从个人项目到团队基础设施
litellm 的部署门槛不高,但真正落地到团队级别还是有一些细节值得提前规划。
第一个建议是,从第一天就把模型名当作 API 契约来管理。业务代码里不要直接写gpt-4o-mini这种厂商模型名,而是定义业务含义的名字,比如assistant-chat、image-caption-base。后续升级模型时,只需要在 litellm 侧调整映射关系,业务代码零改动。这样做对灰度发布也很友好——新模型先在后端列表里增加权重,观察几天再切换全部流量。
第二个建议是密钥规划。不管团队规模大小,一律走虚拟密钥,禁止在代码仓库里保存真实厂商 API Key。可以在 CI 环境里通过环境变量注入 litellm 所需的上游密钥,应用运行时只感知虚拟密钥。即使某个业务线的虚拟密钥泄漏,也能在 litellm 侧一键吊销,影响面可控。
第三个建议是在引入早期就建立成本基线。litellm 会在日志表里记录每次请求的 spend 字段。通过定期对 spend 做汇总,可以发现用量增长趋势、哪些业务线成本偏高、哪些模型性价比不理想。这些数据在做模型选型和资源优化时很有价值。
我在实际项目中还做了一个小扩展:写了一个定时任务,每天凌晨拉取 litellm 的 spend 汇总,按项目和模型维度写入内部报表系统。这样管理层能看到"上周各业务线模型调用花了多少钱",而不是每次等到月底账单出来才追悔莫及。
7. 写在最后的个人实战体会
litellm 使用起来顺手,但不代表没有坑。我印象最深的一点是版本更新频繁,意味着你需要关注 release notes。升级代理版本前,先在测试环境跑一遍回归,重点验证流式响应、fallback 路由和虚拟密钥逻辑是否正常,避免生产环境被一个小版本升级弄挂。
另一个小技巧是,如果你在 Docker 里运行 litellm,不要只暴露 4000 端口。管理接口和控制面接口同样有暴露面,至少要用防火墙或安全组限制来源 IP。全部接口用 master key 保护是基本要求,但网络层隔离仍然值得做。
如果你已经有现成的日志系统,建议把 litellm 日志接入进去。很多问题不一定是 litellm 本身出了问题,而是上游模型或网络链路的波动。有一个统一的日志入口,能更快判断故障到底出在哪一层。
按照我的经验,litellm 最适合进入技术栈的时机,是在你还只有两个模型、但已经预感到要接第三个的时候。早点统一入口,比接了一堆模型再迁移要省力得多。接入成本不算高,花一两天配置好路由、密钥和预算,后面省下的是每个接入周期里的重复劳动。