做AI应用开发这些年,我最怕听到的一句话就是“换个模型试试”。真不是模型不好,而是切换成本经常把人劝退——代码里到处是某家的协议习惯,换个厂商就要改请求格式、改鉴权方式、改base_url,再加上不同模型的计费口径还不一样,跑一次对比评测光适配就得折腾小半天。后来我把litellm引进来,把它当作一个统一的LLM网关放在所有模型调用前面,这个问题才算真正解决。
litellm是什么?简单说,它是一个开源的、遵循OpenAI接口格式的模型代理服务。你在代码里只认一套API,剩下的厂商协议、密钥分发、路由切换、成本统计全部交给它。这篇文章我会从为什么需要它讲起,把它的核心能力、部署方式、路由策略和常见坑逐个拆开,适合正在做多模型接入、想做模型降级容灾、或者想统一管理团队模型成本的后端开发者。
1. 为什么我最终选了litellm
1.1 多模型接入的混乱,是真实存在的心智负担
先说一个很常见的场景:你负责的项目一开始只接了一家模型,代码里满屏都是对应的SDK调用。后来业务要求对比不同模型的效果,你要临时加上另外一家的模型,再后来公司内部还部署了一个开源模型,需要通过本地网关访问。三套协议摆在一起,问题就冒出来了:
- 各家接口的鉴权方式不同,有的用请求头带固定key,有的要动态签名,还有的走oauth。
- 请求和响应结构不统一,连“消息列表”这个最基础的概念,字段名都各有各的写法。
- 错误格式不一样,同样的限流、超时,返回的状态码和错误体千奇百怪,异常处理逻辑得写三份。
- 想统计“这个月每个业务线花了多少模型钱”,没有统一入口的话,只能去各家后台人工账。
litellm把这一切收敛成一层。客户端只跟litellm通信,litellm通过配置决定把请求转发给哪家厂商,并且用统一格式返回。你的业务代码只需要维护一套OpenAI风格调用逻辑,厂商变化带来的差异被隔离在网关这一层。
1.2 它和直接写SDK的调用方式有什么本质区别
在没有网关的时候,你写的是一个点对点的调用链。每个业务模块都要自己管理对应厂商的客户端实例、密钥和重试逻辑。引入litellm之后,调用模型这件事从“应用直接对接厂商”改成了“应用对接统一网关”,本质上就是把基础设施和业务逻辑解耦了。网关负责做协议翻译、路由选择、配额控制这些事,业务方只关心“我想让模型做这件事”,而不关心“这个请求到底落在哪家”。
这个思路在工程上很常见。类比一下,你家里有电视、空调、灯,你不会给每个设备单独拉一根专线,而是统一接在配电箱里,由配电箱去分配和管理。litellm就是模型调用这块的“配电箱”。
1.3 适合什么场景,不建议什么场景
litellm比较适合这么几类人:
- 产品里要同时用多家模型的,希望用一套代码应对所有厂商。
- 想要给模型调用做自动降级和负载均衡的团队。
- 需要把模型密钥集中管理、按用户或按业务线做配额限制的团队。
- 想让非后端角色也能安全地调用模型(比如数据分析师通过网关调用模型,不需要直接接触厂商原始密钥)。
但如果你只是写个一次性脚本、半夜躺着调一次接口玩,那直接装原厂SDK就完事了,没必要多跑一个服务。litellm的价值在于“长期、频繁、多人、多样”的调用场景,这种场景下它的收益才足够明显。
2. litellm的核心能力拆解
2.1 统一API:一套OpenAI风格接口打天下
litellm对外暴露的接口完全兼容OpenAI的Chat Completions格式。这意味着什么呢?你在项目里原本怎么用OpenAI的,现在就怎么用litellm。Python客户端只需要把base_url指到litellm的地址,剩下的请求体、响应解析、流式处理全部照旧。
from openai import OpenAI client = OpenAI( api_key="sk-your-virtual-key", base_url="http://localhost:4000" ) resp = client.chat.completions.create( model="gpt-3.5-turbo", # 这个名字在litellm配置里定义 messages=[{"role": "user", "content": "你好"}], stream=True )你看到的代码非常清爽,但背后litellm做的事情不少。它会解析你的请求,根据config里model_name的映射关系,找到真正要请求的厂商和模型,然后把OpenAI格式的请求翻译成对应厂商的协议格式。返回的时候再做一次反向翻译,统一回OpenAI格式。这就是“协议适配层”的价值所在。
不仅仅是Chat接口。embedding接口、模型列表接口、密钥管理接口,litellm都有对应的HTTP端点。就算你的业务目前只用到对话生成,把网关提前立起来,后续要加向量化、加音频模型,也不至于推倒重来。
2.2 多模型路由与自动降级
这是litellm最出彩的能力之一。你可以把同一类任务配置成一组可用的模型,指定走哪个,或者让litellm根据策略自动分配。
举一个典型的降级例子。你的产品主线用的是某个高价强模型,偶尔会因为负载高导致限流报错。以前你要自己在代码里写try-except然后切换备用模型,现在只需要在config里声明:
litellm_settings: fallbacks: [{ "gpt-4o": ["claude-3-5-sonnet", "gpt-3.5-turbo"] }]含义是,当请求gpt-4o失败(超时、限流、5xx等),litellm会自动按顺序尝试下一个备胎模型,并把最终结果返回给你。你的业务代码根本感知不到这一层切换。这比在应用层自己做重试要干净得多,因为降级判断、重试间隔、备选排序都由网关统一管理,不会出现每个模块各自为政的混乱。
除了fallback,litellm还支持负载均衡。你把某个模型配置多个上游条目(比如通过不同地区的接入点访问同一个模型),网关会按照你设定的策略把请求分散到不同上游,提升整体吞吐。
2.3 密钥管理与虚拟密钥
直接在生产代码里写厂商原始密钥,是一件迟早出事的操作。密钥一旦泄露,损失是不可控的。litellm提供了一套虚拟密钥机制,你用原始key在网关里生成一把或多把虚拟key,然后把这些虚拟key发给各业务方使用。
虚拟key的价值有三个层面。第一,业务方拿到的不是厂商原始凭证,泄露了也不会直接导致上游账号被滥用。第二,每一把虚拟key都可以单独设置预算上限、速率限制、过期时间,你甚至可以给一个临时项目发一张两天后自动失效、总预算100块的key。第三,每一次调用都会被记录到对应的虚拟key名下,月底复盘成本的时候,你可以清清楚楚看到哪个业务线烧了多少额度。
这在团队协作里非常实用。以前每个人找你要一次key,你只能甩一个原始key出去,现在从网关后台生成虚拟key就行,权限拉满控制权在自己手里。
2.4 成本追踪与用量观测
模型账单一向是“月底吓一跳”的重灾区。litellm内置了成本计算功能,它知道各家厂商的定价规则,会在每次请求完成后根据输入输出token数自动估算费用,并记录本地或推到外部监控系统。
litellm_settings: success_callback: ["langfuse"] # 可替换成prometheus、datadog等你可以在litellm的数据库里查询历史请求,按模型、按用户、按时间段聚合花费。也可以配合仪表盘工具把“每分钟请求量”“各模型失败率”“累计费用”画成可视化的面板。站在做技术管理的角度,这一项能力能帮你少挨很多“模型成本为什么又超了”的灵魂拷问。
3. 快速部署与接入实操
3.1 环境准备与安装
litellm通过pip安装,建议装在独立的Python环境里,别跟你的应用服务混在一起。它本质是一个独立代理进程,单独管理更清晰。
python -m venv .venv source .venv/bin/activate pip install 'litellm[proxy]'这里安装的是带proxy扩展的版本,会额外带上运行网关所需的依赖。装好之后验证一下:
litellm --version如果输出版本号,说明安装成功。接下来就是写配置文件。
3.2 config.yaml配置拆解
litellm的入口是一个YAML配置文件。我们以一个同时接入了两家云厂商模型和一个本地模型的例子展开。
model_list: - model_name: gpt-3.5-turbo litellm_params: model: openai/gpt-3.5-turbo api_key: os.environ/OPENAI_API_KEY - model_name: claude-3-5-sonnet litellm_params: model: anthropic/claude-3-5-sonnet api_key: os.environ/ANTHROPIC_API_KEY - model_name: local-llama litellm_params: model: ollama/llama3 api_base: http://127.0.0.1:11434 litellm_settings: drop_params: true num_retries: 2 request_timeout: 60 general_settings: master_key: sk-your-admin-key database_url: os.environ/DATABASE_URL这里有几个关键点需要说明:
- model_name是你自己定义的“逻辑名”,客户端调用时用这个名字,实际映射到哪个厂商不重要。
- litellm_params里的model字段是真正的上游模型标识,格式是“厂商名/模型名”。litellm内置了几十种厂商的协议处理器,你在标识里写明厂商,它就能用对应的协议去请求。
- api_key可以直接写明文,但更推荐用os.environ/环境变量名这种写法,让litellm启动时从环境变量读取。密钥不落到配置文件里,避免配置文件被上传到代码仓库造成泄露。
- 配置文件里的api_base参数,用于指定本地部署模型的访问地址。凡是走自有网关或私有部署的模型,基本上都要配置这一项。
3.3 启动网关并验证调用
启动命令非常简单:
litellm --config config.yaml --port 4000看到类似“LiteLLM Proxy 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-admin-key" \ -d '{ "model": "gpt-3.5-turbo", "messages": [{"role": "user", "content": "你好,请做一个自我介绍"}] }'主要看两点:请求是否返回了正常的content文本;响应耗时是否在合理范围。第一次调用因为要冷启动加载模型信息,可能会稍微慢一些,这是正常的。
如果你的业务是Python写的,强烈建议直接用OpenAI的包来连litellm,代码改动量最小。原来的代码里把base_url换成你的网关地址就行。Node.js、Java那边同理,凡是支持自定义base_url的OpenAI兼容客户端都能用。
3.4 环境变量的管理与注意事项
启动前记得把该导出的环境变量都准备好,尤其是config里通过os.environ引用的那些变量。一个小习惯是把环境变量写进项目根目录的.env文件,然后用类似direnv或docker compose的env_file机制加载,这样既不出现在代码里,也不会在shell的历史记录里留下痕迹。
我踩过一次坑:在config里写了api_key: os.environ/OPENAI_API_KEY,但启动的时候忘记设置这个环境变量,结果litellm直接报错退出,而不是默默跳过。后来我养成了习惯,启动前先打印确认:
echo ${OPENAI_API_KEY:+set}显示set才启动服务,避免半天查不出原因。
4. 进阶玩法:路由策略与高可用
4.1 多上游实例做负载均衡
负载均衡的配置思路是把同一个逻辑模型映射到多个上游实例。比如你通过两个不同的接入地址访问同一个模型,配置就可以写成这样:
model_list: - model_name: gpt-4o litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY_1 model_info: id: model-instance-1 - model_name: gpt-4o litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY_2 model_info: id: model-instance-2同一个model_name对应两条上游记录,litellm会按照路由策略在它们之间分配请求。有人会问,这不是跟nginx负载均衡差不多吗?是的,但litellm做得更贴合模型场景,它的路由策略可以感知健康状态、延迟、历史出错率,而不是单纯轮询。
4.2 路由策略怎么选
litellm支持多种路由策略,我实际用过且觉得比较有价值的有这么几种:
- simple-shuffle:洗牌后轮流调用,不发健康检查。适合上游都稳定、没有明显差异的场景,开销低。
- least-busy:优先派发给当前未完成请求数最少的那个上游。适合上游服务能力差距较大、想均匀压测的场景。
- usage-based-routing:根据每个上游的tpm(每分钟token数)配额实时调整流量,不会因为某个上游超配额而集体失败。这个策略在对接不同家模型的限流规则时特别有用,我长期用的是这个。
路由策略在config文件里配置:
router_settings: routing_strategy: usage-based-routing enable_loadbalancing: true4.3 重试与超时参数调整
生产环境里请求失败是常态,重要的是把重试和超时限流调好。litellm相关参数有:
litellm_settings: num_retries: 3 request_timeout: 60 retry_after: 5 fallbacks: [{ "gpt-4o": ["claude-3-5-sonnet"] }]- num_retries:某个上游失败后的重试次数,建议2到3次,太多会增加延迟。
- request_timeout:单次批复请求的超时时间。如果模型本身很慢,这个值别设太小,否则每次都提前掐断,白白浪费请求。
- retry_after:重试之间的等待秒数。设太短会在上游还没恢复时疯狂打请求,设太长用户会等不及。5秒是我常用的平衡值。
还有一个我后来才知道的细节:fallback只在“质量失败”或“连接性失败”时触发,如果你的某个上游因为触发了内容安全策略返回了特定状态码,是不是要fallback要视情况看是否配置对应策略。生产环境要提前确定规则,不然可能把一个正常拒绝内容的请求错误地发到另一个模型上。
5. 常见问题与排查技巧实录
5.1 看完这份避坑清单,能帮你省几个小时
我在使用过程中踩过一些坑,也帮朋友排查过类似问题,整理成速查表,按频率从高到低排列:
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| 请求返回model not found | model_name不在配置的model_list里 | 检查config.yaml里有没有这个逻辑名,改完配置要重启网关 |
| 启动报环境变量缺失 | config里os.environ引用的变量没有设置 | 启动前echo检查环境变量,确认后再启动 |
| 调用某家模型超时 | 该上游网络不通或模型本身响应慢 | 调大request_timeout;先用curl直连上游验证能否访问 |
| 部分请求成功部分失败 | 上游限流或负载不均衡 | 开启usage-based-routing,观察上游配额设置 |
| 成本统计为0 | 没有启用成本计算,模型名称不匹配 | 检查模型标识名称是否在litellm的价格表内 |
| 虚拟key无效 | key已过期或预算用完 | 在后台查看虚拟key状态,临时调大配额 |
5.2 排查现场:一个典型的model not found问题
有次朋友找到我,说他的网关服务明明配置了模型,但其他服务调用时一直报错。我让他先查model_name,发现没问题;再查请求头,也没问题。最后一看config文件——他改了配置但没有重启litellm进程,新配置根本没生效。
这就引出我的第一建议:litellm改完config.yaml之后,一定要重启进程。它不是热加载配置的。排查的时候,别一上来就怀疑代码,先把网关日志打开看一眼,大部分问题日志里都有线索。
5.3 超时与重试的“过犹不及”
有段时间我发现自己的服务经常在高峰期出现批量失败,看了日志发现是重试策略太激进了。num_retries设了5次,retry_after设了1秒,上游一出现供不应求就被我这边疯狂尝试,结果越试越堵,把网关到上游的网络都打满了。改成num_retries: 2,retry_after: 3之后,情况立刻缓解。
这里给大家一个建议:重试参数的取向取决于你的业务容忍度。如果业务允许延迟,可以稍微多试几次;如果是实时对话场景,我建议重试最多2次,然后果断把请求降级给备用模型,用户体验比多等几秒强得多。
5.4 密钥安全怎么强调都不过分
litellm虽然提供了虚拟密钥机制,但很多人还是习惯把原始密钥写在代码里图省事。我强烈不推荐这么做。哪怕你只在自己的服务器上跑litellm,也建议把厂商密钥放环境变量,把master_key设置成一个足够复杂的随机串。不要用什么config.yaml带权限校验的“默认密码”思想——默认密码就是危险密码。
顺便提一句,虚拟密钥的创建和使用很简单,调一个HTTP接口就能生成:
curl -X POST http://localhost:4000/key/generate \ -H "Authorization: Bearer sk-your-admin-key" \ -H "Content-Type: application/json" \ -d '{"models": ["gpt-3.5-turbo"], "max_budget": 100, "expires": "2025-12-31"}'返回的key就是给业务方用的虚拟key。给多少预算、限制哪些模型,都由你说了算。这个机制在需要把模型能力开放给外部团队、又不想交出原始控制权的时候,价值尤其明显。
6. 最后想说的几句
litellm解决的不只是“接口统一”这一个表层问题。它更深层的意义在于,让模型供应这件事从应用代码里被“抽象”出来,成为一个独立的基础设施层。有了这层抽象,你的应用可以随时调整模型组合、切换不同厂商、试验新的模型,而不需要大改代码。我做过的项目里,最值的一次改动就是把所有模型调用收敛到litellm后面,之后每次切换模型,改动量从“改几十处代码”变成“改一行配置文件”。
如果你也想引入,我的建议是从最小可用配置开始:一台服务器、一个config文件、接入两家模型,先把链路跑通,再去摸索路由、密钥、成本这些进阶能力。litellm的文档里有很多参数,但别被它淹没,核心功能就那么几个,先让关键链路转起来,其余的能力等踩到对应的坑再逐个研究。
最后分享一个小技巧:litellm的日志会输出每次请求的模型、token数和耗时,建议长期保存。这样一旦线上出现效果波动或成本异常,你能顺着日志回放当时到底请求了什么模型,在哪个环节出了问题。这个习惯帮我解决过不止一次“诡异问题”,现在我已经默认全量开启日志了。