1. 从一个“模型调用地狱”说起:litellm 到底解决了什么问题
如果你最近半年在折腾大模型应用,大概率会遇到这样一个场景:项目一开始只想接一家模型服务,代码写得干干净净,一个 SDK 走天下。结果产品经理跑过来说,能不能再加一家做备份,A 家挂了自动切 B 家;过两天又说,C 家的某个模型在中文场景下效果更好,能不能按任务类型路由;再过一周,财务说成本太高,能不能把简单请求都丢给便宜的小模型,复杂请求才走大模型。
于是你的代码里开始出现一堆 if-else,每个厂商的 SDK 各有一套鉴权方式、一套参数命名、一套返回结构、一套错误码。OpenAI 用messages数组,某家厂商用prompt字符串;OpenAI 的返回是choices[0].message.content,另一家可能是output.text;流式返回的 chunk 结构更是五花八门。你写了一个适配层,结果每接一家新厂商就要改一次,测试用例翻倍,线上出问题排查半天。
litellm 就是冲着这个痛点来的。它本质上是一个 Python 库(也有代理服务形态),核心价值一句话概括:用一套 OpenAI 格式的接口,去调用上百家不同的大模型服务。你写一次completion(model="xxx", messages=[...]),换模型只需要改model这个字符串,其余代码几乎不动。它把各家 API 的差异——鉴权、参数映射、返回结构、流式格式、异常类型——全部在内部抹平,对外暴露一个统一的抽象层。
这个项目适合谁?我梳理了一下,大概三类人收益最明显。第一类是做 AI 应用的后端开发者,尤其是需要多模型对比、灰度、降级、成本优化的团队,litellm 能省掉大量胶水代码。第二类是做评测和研究的同学,同一套测试脚本要跑十几个模型,litellm 让切换成本降到几乎为零。第三类是搭建内部 AI 网关的运维或平台工程师,litellm 的 proxy 模式可以直接当轻量级网关用,统一鉴权、限流、记账、日志。
需要提前说明的是,litellm 不是模型本身,也不是推理框架,它不负责跑模型,只负责“把请求翻译成各家能听懂的方言,再把回答翻译回统一格式”。理解这一点,后面所有的设计取舍就都顺了。
2. 核心设计思路拆解:为什么是“统一接口”而不是“统一 SDK”
2.1 统一到 OpenAI 格式,是一个务实的赌注
litellm 最关键的决策,是把 OpenAI 的接口格式当作“事实标准”来对齐。这个选择背后有很现实的考量。过去两年,OpenAI 的chat/completions接口几乎成了行业默认方言,大量开源模型服务(比如各类本地推理框架的兼容接口)、云厂商的托管服务,都主动提供了 OpenAI 兼容层。也就是说,对齐 OpenAI 格式,等于天然对齐了大半个生态,适配成本最低。
反过来,如果 litellm 自己发明一套格式,那所有厂商都得为它写适配,生态根本推不动。所以它的策略是:对外完全模仿 OpenAI 的请求和响应结构,对内做一层“翻译 + 归一化”。你传messages,它帮你转成某家需要的prompt;你收choices,它帮你从某家的output里拼出来。
提示:这个设计意味着,你只要熟悉 OpenAI 的接口,就几乎零学习成本上手 litellm。但也要注意,OpenAI 格式本身在演进(比如工具调用、结构化输出),litellm 的跟进速度会直接影响可用性,选版本时尽量用较新的稳定版。
2.2 模型命名规则:一个字符串承载路由信息
litellm 里最巧妙的设计之一,是model参数的命名约定。它通常长这样:provider/model-name,比如openai/gpt-4o、anthropic/claude-3-5-sonnet、azure/your-deployment、ollama/llama3。这个前缀就是路由依据,litellm 靠它决定用哪个适配器、读哪组环境变量、走哪套鉴权。
为什么用字符串而不是枚举或配置对象?因为字符串最灵活,可以动态拼接,可以从数据库读,可以从环境变量注入。你在做多租户系统时,完全可以把用户选的模型直接存成这个字符串,运行时透传给 litellm,不需要维护一张映射表。代价是拼写错误只能在运行时暴露,所以生产环境建议做一层白名单校验。
2.3 适配器模式 + 注册表:新增厂商不用改核心
从架构上看,litellm 用的是典型的适配器模式。每个厂商对应一个 Provider 类,负责三件事:把统一请求转成厂商请求、发 HTTP 请求、把厂商响应转回统一响应。所有 Provider 注册到一张表里,主流程根据model前缀查表分发。
这样做的好处是开闭原则:新增一家厂商,只需要加一个 Provider 文件并注册,核心调用逻辑一行不改。对使用者来说,这意味着社区贡献新适配的速度很快,你遇到冷门厂商时,甚至可以自己照着现有 Provider 写一个,几十行代码就能接进来。
2.4 重试、降级、路由:把可靠性做进调用层
真正让 litellm 从“玩具适配层”变成“生产可用”的,是它内置的可靠性机制。它支持配置多个部署(同一个模型的多组密钥或多个区域),请求失败时自动重试或切换到下一个部署;支持按权重、按延迟、按成本做路由;支持设置超时、最大重试次数、退避策略。
这些能力如果自己实现,要处理并发、状态、熔断,工作量不小。litellm 把它们收敛到配置里,比如用一个fallbacks列表声明降级链,用num_retries控制重试。这就是它比手写 if-else 高一个档次的地方:不只是格式统一,还把生产环境需要的容错逻辑一起打包了。
3. 核心细节与实操要点:从安装到第一次调用
3.1 安装与依赖管理
安装本身很简单,但有几个坑要提前避开。基础安装用pip install litellm即可,但它默认不会装所有厂商的 SDK,而是按需依赖。如果你要用某家厂商,可能需要额外装对应的包。这种设计是为了控制体积,避免你为了用一个模型装一堆用不上的依赖。
我的建议是:在项目里明确锁定 litellm 版本,因为它的迭代非常快,小版本之间都可能有行为变化。用requirements.txt或pyproject.toml固定版本,升级前先在测试环境跑一遍回归。另外,如果你打算用 proxy 模式,安装时通常需要带上额外依赖,具体以官方文档为准。
3.2 密钥配置:环境变量是首选
litellm 读取密钥的默认方式是按厂商约定的环境变量名,比如 OpenAI 系读OPENAI_API_KEY,Anthropic 系读ANTHROPIC_API_KEY,Azure 系读AZURE_API_KEY加AZURE_API_BASE。这种约定让你不用在代码里写密钥,符合十二要素应用的原则。
实操中我强烈建议:永远不要把密钥硬编码进代码或提交到仓库。用.env文件配合python-dotenv在本地加载,线上用密钥管理服务注入环境变量。litellm 也支持在调用时显式传api_key参数,但这只适合临时测试,生产环境不要这么干,否则密钥会散落在日志和调用栈里。
3.3 第一次调用:最小可用示例
下面是一个最简调用,感受一下统一接口的写法:
from litellm import completion import os os.environ["OPENAI_API_KEY"] = "your-key-here" response = completion( model="openai/gpt-4o-mini", messages=[ {"role": "system", "content": "你是一个简洁的助手。"}, {"role": "user", "content": "用一句话解释什么是适配器模式。"} ] ) print(response.choices[0].message.content)注意返回结构和 OpenAI 完全一致,response.choices[0].message.content直接可用。换成另一家厂商,只需要改model字符串和对应的密钥环境变量,其余代码不动。这就是它最大的价值。
3.4 流式输出:chunk 结构也被统一了
流式场景是差异最大的地方,各家 chunk 结构千奇百怪。litellm 把它们统一成 OpenAI 的流式格式,你只需要加stream=True:
response = completion( model="anthropic/claude-3-5-sonnet", messages=[{"role": "user", "content": "写一首关于秋天的短诗"}], stream=True ) for chunk in response: delta = chunk.choices[0].delta if delta and delta.content: print(delta.content, end="", flush=True)注意:流式场景下错误处理更麻烦,因为连接可能在中途断开。建议给流式调用单独设置超时,并在外层包 try-except,把中断的响应标记出来,避免前端显示半截内容却以为成功了。
3.5 参数映射:那些“看起来一样其实不一样”的参数
统一接口最大的挑战是参数语义差异。比如temperature、top_p、max_tokens这些名字各家都有,但取值范围和默认值可能不同;max_tokens在某些厂商那里叫max_output_tokens;工具调用(function calling)的 schema 格式也有细微差别。litellm 会尽量做映射,但不是所有参数都能完美对齐。
我的经验是:跨厂商通用的参数尽量只用最基础的几个(temperature、max_tokens、stream),厂商特有的高级参数通过extra_body或厂商专属参数传递。这样既能享受统一接口的便利,又不会因为强行对齐而丢功能。
4. 进阶玩法:路由、降级与代理网关
4.1 多部署与自动降级
生产环境最怕单点故障。litellm 允许你为同一个逻辑模型配置多个部署,比如两组不同区域的密钥,或者同一家的两个模型版本。配置好后,主部署失败会自动切到备用。降级链可以用fallbacks声明,例如主模型超时就切到更便宜更快的模型,保证服务不中断。
这里的关键是区分“可重试错误”和“不可重试错误”。限流、超时、5xx 通常可重试;参数错误、鉴权失败重试也没用,反而浪费配额。litellm 内置了错误分类,但不同厂商的错误码映射不一定完美,建议在测试环境故意制造各类错误,观察它的重试行为是否符合预期。
4.2 基于成本与延迟的路由
当你有多个可用部署时,可以按策略路由。比如按成本:简单请求走便宜模型,复杂请求走贵模型;按延迟:优先选当前响应最快的部署;按权重:做灰度发布,把 10% 流量导到新模型观察效果。这些策略在 litellm 的 router 模块里有对应实现。
我踩过的一个坑是:延迟路由需要持续采集各部署的响应时间,如果采集窗口太短,会因为偶发抖动频繁切换,反而增加不稳定。建议窗口设长一点,并加一个最小样本数阈值,样本不够时走默认部署。
4.3 Proxy 模式:把 litellm 当内部网关
如果你不想在每个服务里都引入 litellm 库,可以跑它的 proxy 模式:启动一个服务,对外暴露 OpenAI 兼容的 HTTP 接口,所有后端服务通过这个网关调用模型。这样做的好处是密钥集中管理、统一限流、统一记账、统一日志,业务代码里连 litellm 都不用装,直接用标准 OpenAI 客户端即可。
配置通常是一个 YAML 文件,声明模型列表、密钥、路由策略。启动后,业务方拿到的就是一个标准的/v1/chat/completions端点。这个模式特别适合中大型团队,因为模型接入这件事从“每个业务团队各自折腾”变成“平台团队统一维护”。
提示:proxy 模式下的鉴权要单独设计,别让网关裸奔。可以给每个业务方发独立的虚拟密钥,绑定额度和权限,方便追踪成本归属。
4.4 成本追踪与预算控制
litellm 会尽量计算每次调用的 token 数和预估成本,这对做预算控制非常有用。你可以按用户、按项目、按模型维度统计花费,设置预算上限,超了就拒绝或降级。实操中要注意:不同模型的计价单位不同,有的按输入输出分别计价,有的有缓存折扣,litellm 的估算值仅供参考,最终还是要以厂商账单为准。
5. 常见问题与排查技巧实录
5.1 典型问题速查表
| 问题现象 | 可能原因 | 排查方向 |
|---|---|---|
| 报“model not found” | 模型字符串前缀拼错或未注册 | 检查provider/model格式,确认版本支持该厂商 |
| 鉴权失败 | 环境变量名不对或密钥无效 | 对照厂商约定检查变量名,确认密钥未过期 |
| 返回内容为空 | 参数映射丢失或厂商返回结构变化 | 打印原始响应,对比统一格式,必要时升级版本 |
| 流式中断 | 网络抖动或超时设置过短 | 加大超时,外层加重连逻辑 |
| 成本统计偏差大 | 计价规则未更新 | 以厂商账单为准,定期校准 |
| 降级没生效 | 错误被判定为不可重试 | 检查错误分类,必要时自定义重试条件 |
5.2 独家避坑经验
第一个坑是版本升级的隐性破坏。litellm 迭代快,某些小版本会调整默认行为,比如默认超时、默认重试次数。升级后一定要跑一遍核心用例,尤其是流式和工具调用场景。
第二个坑是参数透传的边界。有些厂商支持response_format做结构化输出,有些只支持 JSON mode,有些完全不支持。你以为传了统一参数就万事大吉,结果某家直接报错。建议对每个要用的厂商单独做一次参数兼容性测试,把不支持的参数在路由层过滤掉。
第三个坑是并发下的连接池。高并发场景如果每次调用都新建连接,性能会很差。litellm 底层用 HTTP 客户端,建议复用客户端实例,并合理设置连接池大小。我见过因为连接池太小导致请求排队、延迟飙升的案例。
第四个坑是日志里的敏感信息。调试时打开详细日志很方便,但请求头里的密钥、用户输入的内容都可能被记录下来。生产环境务必关闭详细日志,或对日志做脱敏处理。
5.3 性能优化的小技巧
如果延迟敏感,可以开启连接复用、合理设置超时(别用默认的无限等待)、对幂等请求做结果缓存。litellm 支持缓存后端,把相同请求的响应缓存起来,重复问题直接命中缓存,既省钱又快。但要注意缓存键的设计,别把不同用户的私有上下文混在一起。
另外,批量请求场景下,如果厂商支持 batch 接口,优先用 batch 而不是循环单条调用,成本和延迟都会好很多。litellm 对部分厂商的 batch 有支持,用之前确认一下。
6. 我个人的使用体会与扩展方向
用了一段时间 litellm,我最大的感受是:它把“多模型”这件事从架构问题降级成了配置问题。以前接第二家模型要改代码、加测试、重新部署,现在改一行配置就行。这种杠杆效应在快速试错的阶段特别值钱。
当然它也不是银弹。统一接口必然意味着某些厂商的高级特性被抽象掉,遇到需要深度定制的场景,还是得绕过它直接调原生 SDK。我的做法是:通用场景走 litellm,特殊场景走原生,两者并存,不强行统一。
后续如果要扩展,我会考虑几个方向:一是把 litellm 的调用指标接入监控,做模型健康度看板;二是基于它的成本数据做自动路由,让系统自己学会“什么任务用什么模型最划算”;三是把 proxy 模式和内部权限系统打通,做到按人按项目精细化管理。这些都不难,因为 litellm 已经把最脏最累的适配工作做完了,剩下的都是业务逻辑。