最近我一直在折腾多模型接入的事情,做应用开发时最让人头疼的往往不是提示词本身,而是“接模型”这一步:今天要对接这个平台,明天又得调另一家供应商的接口,每家平台的鉴权方式、请求参数、错误码全都不一样。我在一个内部项目里同时接了三个不同来源的模型,光整理差异和写适配逻辑就花了两天,后来在开源社区找到 LiteLLM 这个工具,才算把问题彻底理顺。
LiteLLM 说白了,就是一层统一的大模型接入层。它把几十种不同模型供应商的接口差异藏在内部,对外提供一套风格统一、使用简单的接口。你可以把它当成一个 Python 库直接嵌进项目,也可以把它当作独立服务部署出来,让团队内部所有应用共用同一个模型入口。这篇文章我会从最基础的 SDK 调用讲起,再带你把一个实际可用的代理网关跑起来,最后把限流、预算、日志这些生产环境真正用得上的配置一起过一遍。过程中穿插一些我实际踩过的坑,希望能帮你省点时间。
1. LiteLLM 是什么,为什么我最后选了它
1.1 多模型接入的烦琐到底在哪
我先说一个具体场景。假设你现在要在业务里接入三家模型供应商,A 平台的 SDK 直接返回 JSON,B 平台要求你自己手工拼 HTTP 请求,C 平台走的是另一套消息结构。此时你的代码大概会长这样:
if provider == "A": client = AClient(api_key) resp = client.chat(messages=messages) elif provider == "B": resp = requests.post(B_URL, headers=..., json=...) elif provider == "C": resp = CClient(api_key).completion(...)这还只是最理想的状况。真实世界里,每个平台对消息格式、角色字段、参数命名、超时策略、错误返回的定义都不太一样。有人用system字段,有人用instruction字段;有人返回choices[0].message,有人返回output.text。再加上密钥管理、成本统计、模型版本迭代,你很快就会发现:业务代码里真正处理“业务逻辑”的部分没多少,全在跟各家接口打架。
我当时就是这么被折腾的。每次想换一个更便宜的模型跑跑看,都要重新改一遍调用逻辑。后来我意识到,问题不在于某个平台好不好用,而在于所有平台都在用各自的“方言”说话,缺少一个翻译层。
1.2 LiteLLM 的两副面孔
LiteLLM 解决的就是这个翻译问题。它有两种使用形态,刚接触时容易混淆,我分开说:
第一种是 SDK 模式。你直接pip install litellm,然后在自己的 Python 进程里调用litellm.completion(...)。这种方式最轻,适合写脚本、做测试、或者单个服务内部使用。
第二种是代理模式(Proxy Server)。你把 LiteLLM 部署成一个独立服务,它对外暴露一个 HTTP 接口,你的应用只需要按照这个接口格式发请求就行。底层到底用了哪家模型、配了几个模型、密钥放在哪里,业务侧完全不用关心。
这两种形态不是互斥的。很多人先拿 SDK 跑通验证,等要开放给团队其他人用的时候,再切成代理模式。我自己的习惯是:凡是只跑一次的脚本,用 SDK 就够了;凡是需要长期运行、多人调用、要控成本的服务,一律走代理模式。
1.3 它真正替你省下的是什么
刚开始我以为 LiteLLM 只是省了写适配代码的时间,用久了才发现,它最大价值是让“模型”这个概念从代码里解耦出来。
原来你的代码里一旦写了model="某个平台的某型号",这个模型的切换就变成了研发任务,得发版、上线、测试。有了 LiteLLM 之后,模型切换变成配置项:改一行 YAML,重启服务,同一个对外接口的底层模型就换掉了。业务侧甚至无感知。
另一个被很多人忽略的好处是成本归集。模型调用一旦散落在各个服务的代码里,想统计这个月花了多少钱,只能各种翻账单。有了统一入口之后,每次请求用了多少 token、花了多少钱、成功还是失败,都可以集中记录。这对团队管理预算非常重要。
2. 先从 SDK 用起,一个函数把模型都串起来
2.1 最小可运行样例
我建议所有刚接触 LiteLLM 的人,都先别急着上代理模式,第一步先在本地跑通一段最原始的调用代码。
先安装:
pip install litellm然后新建一个 Python 文件,写一段最基础的对话补全请求:
import litellm import os # 在你的环境变量里配置好某个模型平台的密钥 os.environ["PRIMARY_MODEL_KEY"] = "sk-xxxx" messages = [ {"role": "system", "content": "你是一个简洁的助手。"}, {"role": "user", "content": "用一句话介绍你自己"}, ] resp = litellm.completion( model="provider-prefix/model-alpha", # 占位格式:供应商前缀/模型标识 messages=messages, api_key=os.environ["PRIMARY_MODEL_KEY"], ) print(resp.choices[0].message.content)注意我在这里用了占位写法provider-prefix/model-alpha。LiteLLM 的模型名格式通常是“供应商标识/模型标识”两段式,前半部分告诉 LiteLLM 该走哪个适配器,后半部分是该供应商体系下的真实模型代号。你在实际使用时,把前半部分替换成你选定的模型供应商标识,把后半部分替换成你在该平台开通的具体模型名即可。
跑通这段代码之后,你会看到返回对象的结构非常眼熟:choices、message、content。这就是 LiteLLM 刻意设计的——它把主流模型平台的返回结构统一成一种风格,让你按直觉写代码就行。
2.2 参数里的常用开关
litellm.completion能透传大部分生成参数,这句话的意思是:你在别的 SDK 里怎么调温度、上限、流式等参数,在 LiteLLM 里也怎么写。
比较常用的几个:
resp = litellm.completion( model="provider-prefix/model-alpha", messages=messages, temperature=0.7, max_tokens=512, stream=False, timeout=30, num_retries=2, )temperature:控制随机性,越高越发散,越低越稳定。max_tokens:控制生成长度。不同平台对这类参数的名称和默认值不完全一样,LiteLLM 会帮你做适配。timeout:请求超时秒数。平台有时候会慢,不给超时的话请求可能一直挂着。num_retries:失败自动重试次数。这个我强烈建议配一下,因为很多模型 API 偶尔会抖动,重试一次往往就成功了。
如果你需要流式输出,把stream=True打开,返回值会变成一个可迭代对象:
resp = litellm.completion( model="provider-prefix/model-alpha", messages=messages, stream=True, ) for chunk in resp: print(chunk.choices[0].delta.content, end="")流式模式在对话类产品里几乎必用,因为用户等不了长时间的白屏。LiteLLM 会把各家平台不同的流式格式统一成上面的迭代形式,这点做得比较省心。
2.3 换模型只改一个字段
SDK 模式最爽的一点,就是切换模型真的只改一个字段。我举个例子:原来我用provider-a/model-alpha跑一批测试,效果不错但价格不便宜,后来发现provider-b/model-beta在中文任务上效果更好,价格还低一半。这时候我只需要把model参数换成provider-b/model-beta,再把对应平台的密钥配好,其他代码完全不动。
如果你想做得更稳一点,还可以配置后备模型,让主模型失败时自动降级:
resp = litellm.completion( model="provider-a/model-alpha", fallbacks=["provider-b/model-beta"], messages=messages, )这段逻辑的意思是:如果主模型因为限流、超时、服务器错误等原因没调通,LiteLLM 会自动去调用后备模型,不需要你在业务代码里写 try-except 重试逻辑。
我实际用下来,这种简单粗暴的切换方式对新项目探索特别友好。你不需要在早期就纠结“到底选谁家的模型”,先把链路跑通,之后拿真实业务数据去对比,选中哪个就换哪个。
3. 代理模式实操,把多模型包装成一套对外接口
3.1 环境准备与目录习惯
SDK 模式你自己用没问题,但团队里其他同事也要调模型时,总不能让大家各自配密钥、各自维护一套 SDK 代码。这时候就该上代理模式了。
代理模式本质上是一个跑在服务器上的进程,它读取配置文件,对外提供 HTTP 接口。你的应用只需要向这个接口发请求,完全不接触底层模型平台的细节。
我的建议目录习惯是这样的:
my-gateway/ config.yaml .env logs/config.yaml放模型列表和网关配置,.env放各种密钥。把密钥和配置分开是底线,不然配置文件一旦被提交到代码仓库,密钥就泄露了。
安装代理模式需要的依赖:
pip install "litellm[proxy]"3.2 写一份最小可用配置
复制下面这段到一个config.yaml文件里,注意字段缩进必须保持 YAML 规范:
model_list: - model_name: my-chat litellm_params: model: provider-prefix/model-alpha api_key: os.environ.PRIMARY_MODEL_KEY max_tokens: 2048 timeout: 30 - model_name: my-chat-fast litellm_params: model: provider-prefix/model-beta api_key: os.environ.SECONDARY_MODEL_KEY这里有三个关键概念,我逐个解释。
model_name是对外暴露的模型名,你的业务代码只认这个名字。内部到底用什么真实模型,业务侧完全不用关心。
litellm_params是传给 LiteLLM 的参数列表,里面写真正调用的模型标识、密钥等。你可以理解成“对外一个名字,对内一套真实参数”。
api_key: os.environ.PRIMARY_MODEL_KEY表示从环境变量里读取密钥。LiteLLM 支持在配置里直接写os.environ路径引用,这是比把密钥明文写进 YAML 更稳妥的方式。
3.3 启动代理并验证请求
配置文件准备好后,启动服务:
litellm --config ./config.yaml --port 4000看到类似启动成功的日志之后,先用健康检查接口确认服务活着:
curl http://localhost:4000/health然后发一个真正的对话请求:
curl http://localhost:4000/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "my-chat", "messages": [{"role": "user", "content": "你好"}] }'注意我这里请求的路径是/chat/completions,请求体里model写的是my-chat。如果你曾经直接调过某家主流平台的标准接口,会发现这个请求格式几乎一模一样。这就是 LiteLLM 代理最巧妙的地方:它把各家平台乱七八糟的接口风格全部统一成一种,让你的客户端代码可以用一种固有习惯去对接所有模型。
如果返回结果里带上了choices[0].message.content,说明整条链路已经通了。
3.4 用别名把底层模型“藏”起来
上面配置里model_name: my-chat这个设计,我单独拿出来说,因为我觉得它是代理模式最值得用好的功能。
假设你的业务代码里到处写的都是model: my-chat。某一天,底层那个provider-a/model-alpha突然涨价,或者效果变差,你只需要修改服务端config.yaml,把model-alpha换成provider-c/model-delta,然后重启代理进程。业务代码一行不用动,团队其他成员甚至不知道底层换了模型。
这相当于在你和多个模型供应商之间加了一个“开关面板”。想换哪一路,去面板上拨一下就行,不用重新接线路。
如果你不想用 curl 测试,也可以直接把某些应用原本指向模型平台的 base_url 改成http://你的网关地址:4000,因为 LiteLLM 代理的接口风格做到了一致兼容,大多数现成的 SDK 客户端可以直接切过来。
4. 限流、预算与路由,生产环境才关心的配置
4.1 模型路由怎么设置
当你的模型列表里有多个真实模型时,路由策略就开始起作用了。
最简单的方式是“一对一”:一个对外模型名对应一个真实模型,就像前面示例里那样。但真实场景里,你往往希望某些请求走更便宜的小模型,某些请求走更强的大模型。LiteLLM 支持在model_list里配置多个条目,通过不同的model_name对外暴露。
另一种情况是“一组模型互相备份”。你可以让一个对外模型名下挂多个真实模型,并在配置里指定优先顺序。当第一个模型不可用或报错时,LiteLLM 自动把请求转到下一个候选模型上。这在很多模型服务不稳定的时候非常有用。
更高级一点,LiteLLM 也支持按请求负载做简单轮询或随机分配,避免把所有流量都打进同一个模型实例。我不会建议你在初期就把路由策略搞得太复杂,先把“主模型 + 后备模型”用起来,已经能覆盖大部分稳定性需求。
4.2 限流、预算和超时控制
模型 API 是有成本的和有频率限制的,尤其是多个团队共用一个网关时,如果不做控制,某一天某个同事的循环脚本可能直接刷出一笔大账单。我在代理配置里通常会加上这些限制:
model_list: - model_name: my-chat litellm_params: model: provider-prefix/model-alpha api_key: os.environ.PRIMARY_MODEL_KEY rpm: 1000 max_budget: 100rpm:每分钟最多请求次数,超出后网关直接拒绝或者排队,具体行为取决于你的设置。max_budget:该模型累计费用上限,比如 100 美元,达到后停止调用。这个参数特别适合防止异常代码失控。
还有几个全局参数经常一起配:
router_settings: num_retries: 3 timeout: 30 fallbacks: - my-chat: ["my-chat-fast"]num_retries的作用我之前提过,在这里是全局生效的。timeout控制整个请求的最大等待时间。fallbacks的意思则是:如果my-chat挂了,就降级到my-chat-fast。
我的建议是:刚开始不要把rpm和预算值配得太高,先用保守数字跑一阵,观察真实业务曲线之后,再逐步放宽。限流配置加多了可以调,但如果一开始不设,等爆出账单就晚了。
4.3 日志与用量统计
统一网关还有一个天然好处:所有请求都会经过同一个进程,因此你可以在网关层面统一记录日志和用量数据。
在config.yaml里加上:
litellm_settings: success_callback: ["console"] failure_callback: ["console"]这样每次成功的请求和失败的请求都会在控制台输出日志。你能看到模型名、请求耗时、token 用量、状态码等关键信息。
我第一次看到完整用量统计时还挺惊讶的:原来同一个功能,两个模型在 token 消耗上能差好几倍。有了这些数据,我就能判断到底哪个模型更适合当前业务,而不是凭感觉选型。
更进一步的玩法,是把这些回调接到你现有的日志或监控系统里,把每次调用的 token 数与成本汇总成看板。这一步需要一些额外开发,但长期来看很值得。毕竟模型成本在 AI 应用里不是小数目,能看见、能度量,才能控制得住。
5. 踩坑记录与问题排查实录
5.1 我的排查顺序
我在接入和使用 LiteLLM 的过程中踩过的坑不算少,前前后后花了不少时间。后来我总结出一套固定的排查顺序,照着做基本能定位大部分问题。
第一步,先开调试日志。启动代理时加上调试参数,或者在 SDK 里设置:
import litellm litellm.set_verbose = True这样 LiteLLM 会打印出实际发往底层平台的请求详情,你能看到真实的目标地址、请求头和请求体。很多“为什么报错”的问题,到这里就解决了一半。
第二步,看返回体。不要只看 HTTP 状态码,要把返回体里的错误信息完整读一遍。不同平台的报错信息风格不一,但大多数会明确指出是密钥无效、模型不存在,还是频率超限。LiteLLM 会把某些错误信息透传出来,你认真看就行。
第三步,确认模型标识。检查model参数是不是“前缀/模型名”的完整格式。这是最容易被忽略的问题,比如只写了model-alpha而漏掉了前缀,或者把供应商前缀写错,都会导致找不到对应适配器。
5.2 典型报错速查表
我把实际使用中比较高频的问题整理成了一张表,方便你直接对照:
| 现象 | 大概率原因 | 处理方式 |
|---|---|---|
| 返回 401 | 密钥无效或未配置 | 检查环境变量是否注入,密钥是否过期,权限是否开通 |
| 返回 404 | 模型名不正确 | 确认前缀是否正确,模型名是否真实存在 |
| 返回 429 | 触发限流或频率超限 | 调低并发、增大重试间隔;检查rpm、max_budget配置 |
| 请求超时 | 平台响应慢或网络不稳定 | 增大timeout,配置后备模型 |
| 启动时报 YAML 错误 | 缩进或字段拼写有误 | 检查配置里每一项的缩进,建议用 YAML 校验工具过一遍 |
| 中文超短或截断 | 没显式设置max_tokens | 在litellm_params中配置合理的max_tokens |
| 本机能调通,服务器上报错 | 服务器缺环境变量 | 检查部署环境里的密钥配置,不要手动复制到代码里 |
这张表不能覆盖所有情况,但覆盖了最常见的 80%。我遇到疑难问题时,基本都会先对照一遍这些可能性。
5.3 三个容易被忽略的细节
第一个细节:密钥千万别写死在 YAML 文件里。我之前见过有人图省事,直接在配置里写明文密钥,结果不小心把配置发到仓库里,泄露得干干净净。正确的做法是使用os.environ.xxx的方式引用环境变量。这属于一次养成、长期受益的习惯。
第二个细节:不同平台对消息格式的校验宽松程度不一样。有时候你在一个平台传messages内容带有多余字段能通过,另一个平台可能直接报 400。我踩过这个坑之后,养成了一个习惯:在业务侧固定只传role和content两个字段,把其他非标准字段都放在别的参数里,避免格式歧义。
第三个细节:测试阶段一定要把限流和预算配小。我自己的教训是,调试流式输出时写了一个死循环,不小心把请求刷爆了,当天的额度直接见底。后来我无论做什么测试,都会先在配置里把rpm和预算压到足够低,确认逻辑无误后再放开。这个习惯帮我省了很多不必要的开销。
6. 我的使用心得与扩展建议
6.1 什么场景适合用 LiteLLM
结合我自己的经验,如果你属于下面几类情况,我很建议用 LiteLLM:
第一类,业务里已经用了两个或更多模型平台的 API。这时候每多接一个平台,代码复杂度都会非线性增长,LiteLLM 正好能把复杂度收拢。
第二类,需要频繁做模型对比和替换。我今天想用 A 平台试试效果,明天想换 B 平台看看成本和延迟,这时候统一接口的价值就体现出来了。
第三类,团队内部多人或多项目都要调模型。把网关部署出来,统一管理密钥、限流、预算和日志,比每个人自己对接一家平台要安全得多。
6.2 什么场景不建议急着上
也不是所有场景都适合立刻引入。如果你的项目只有一个固定的模型平台,短时间内不可能换,而且调用量很小,那引入 LiteLLM 反而多了一层依赖和配置成本。这种情况下,直接用平台官方 SDK 反而更轻。
另外,如果你对每次请求的延迟极其敏感,希望链路越短越好,那么多一层网关确实会带来轻微的性能开销。虽然这个开销通常不大,但追求极致性能的场景需要自己权衡。
6.3 如果你也想从零开始试
我的建议是,不要一上来就部署完整的代理网关。先用 SDK 模式跑通一次最简单的调用,把模型名怎么填、密钥怎么配搞清楚。然后写一个非常小的 demo,验证你常用的两三个功能,比如普通对话、流式输出、参数透传。跑通之后,再根据实际需要逐步加上代理模式和限流预算。
我试过很多次,直接照搬复杂配置反而容易出问题。一步一步来,反而更快。
这个方向还可以继续扩展的地方也很多。比如把网关的用量数据整理成成本看板,每天自动汇总各项目消耗;比如结合流量切换,做模型的灰度发布,先让 10% 的流量走新模型,对比效果后再全量放开。LiteLLM 只是一个基础底座,上面能盖什么楼,取决于你团队的需求和想象力。
跟你一样从零接入多模型的朋友,建议把这篇文章收藏下来,按顺序操作一遍。我自己就是这么过来的,中间踩过的坑,希望你不用再踩一遍。