1. 从一个真实困境说起:多模型调用的碎片化之痛
如果你正在做AI应用开发,大概率遇到过这样的场景:项目初期用某一家的大模型API跑通了原型,效果不错,上线后老板说“再加一个备用模型吧,防止单点故障”,于是你开始翻第二家的文档。结果发现,虽然都是“调用大模型”,但两家的接口参数名不一样、返回结构不一样、流式输出的格式不一样、错误码体系更是完全两套。你不得不写一堆if-else来适配,代码里到处是if provider == "a"和elif provider == "b"。
这还只是两家。等到业务需要接入第三家、第四家,或者需要根据成本、延迟、任务类型动态切换模型时,这套适配代码会膨胀到难以维护。更麻烦的是,每次新增一家,测试用例要重写,日志格式要统一,重试逻辑要重新适配——这些重复劳动消耗了大量本该花在业务逻辑上的时间。
litellm这个项目,就是冲着这个痛点来的。它的核心定位可以用一句话概括:用一套统一的OpenAI格式接口,去调用市面上几乎所有主流的大模型服务。你只需要把base_url和api_key换掉,模型名换掉,其余代码几乎不用动。对于需要做多模型对比、多模型路由、成本优化的团队来说,这能省下大量胶水代码。
这篇文章我会从实际使用角度出发,把litellm的核心机制、接入方式、路由策略、成本追踪、踩坑经验完整讲一遍。适合正在做AI应用后端、需要管理多个模型供应商的开发者,也适合刚接触这块、想找一个统一入口的新手。读完你应该能直接上手搭一套自己的多模型调用层。
2. litellm到底解决了什么问题:统一接口的底层逻辑
2.1 它不是一个模型,而是一层“翻译适配层”
很多人第一次看到litellm,会误以为它是某个新出的模型。其实它跟模型本身没关系,它是一个Python库(也有代理服务模式),做的事情是:接收你用OpenAI格式写的请求,把它翻译成目标供应商能听懂的格式,发出去,再把返回结果翻译回OpenAI格式给你。
这个设计思路很关键。OpenAI的接口格式在事实上已经成了行业通用语,绝大多数开发者都熟悉chat.completions.create这套写法。litellm选择兼容这套格式作为统一入口,学习成本几乎为零。你原来怎么调GPT的,现在就怎么调Claude、Gemini、通义、文心,只是model参数换个名字。
从架构上看,它内部维护了一张“供应商映射表”,每个供应商对应一个适配器。适配器负责三件事:请求参数转换、认证信息注入、响应结构归一化。你调用时传入的model="claude-3-5-sonnet",它会识别出这是哪家,然后走对应的适配器。
2.2 为什么是“统一格式”而不是“统一SDK”
这里有个设计取舍值得说。市面上也有其他方案,比如给每家写一个独立SDK,或者做一个抽象基类让各家实现。litellm选了最“薄”的一层:不改变你的调用习惯,只做格式转换。
这样做的好处是侵入性极低。你现有的代码,只要把openai的client换成litellm,或者干脆在openai库里把base_url指向litellm的代理,就能无缝切换。坏处是,它无法覆盖各家所有的高级特性——比如某家特有的“思维链参数”或者“多模态特殊字段”,这些在统一格式里没有对应位置,就需要用extra_body之类的透传字段来处理。
我的经验是:80%的常规调用场景,litellm的统一格式完全够用;剩下20%的高级特性,用透传字段也能解决,只是需要查一下对应供应商的文档。这个取舍对于绝大多数业务应用来说是划算的。
2.3 支持的供应商范围与典型使用场景
litellm覆盖的供应商相当广,主流的闭源模型、开源的推理服务、云厂商的托管服务基本都在列。常见的包括各大国际厂商的模型、国内主流大模型、以及各种自部署的推理框架(只要它们暴露了兼容OpenAI的接口)。
典型使用场景我归纳为四类:
- 多模型对比评测:同一份prompt,批量跑多个模型,比较输出质量和成本。
- 故障转移与降级:主模型超时或报错时,自动切到备用模型。
- 成本优化路由:简单任务走便宜的小模型,复杂任务走贵的大模型。
- 统一日志与计费:所有模型的调用记录、token消耗、费用统计集中在一处。
这四类场景,litellm都有对应的能力支撑,下面会逐一展开。
3. 快速接入:从安装到跑通第一个多模型调用
3.1 安装与最小可用配置
安装本身很简单,标准pip流程:
pip install litellm如果你要用代理服务模式(后面会讲),还需要额外装:
pip install 'litellm[proxy]'最小可用配置就是设置环境变量。litellm约定了一套环境变量命名规则,比如某家供应商的key通常叫XXX_API_KEY。你不需要在代码里硬编码key,只要环境变量设好,调用时它会自动读取。
export OPENAI_API_KEY="你的key" export ANTHROPIC_API_KEY="你的key"这里有个细节要注意:环境变量的名字必须和litellm内部约定的一致,写错了它读不到,会报认证失败。具体每个供应商对应哪个变量名,建议直接查官方文档的provider列表,不要凭感觉猜。
3.2 用统一格式调用不同模型
配置好之后,调用代码几乎和原生OpenAI一模一样:
from litellm import completion # 调用某国际厂商的模型 response = completion( model="gpt-4o", messages=[{"role": "user", "content": "用一句话解释什么是向量数据库"}] ) print(response.choices[0].message.content) # 换成另一家的模型,只改model参数 response = completion( model="claude-3-5-sonnet-20241022", messages=[{"role": "user", "content": "用一句话解释什么是向量数据库"}] ) print(response.choices[0].message.content)注意看,除了model字段,其余代码完全一样。返回结构也是统一的,response.choices[0].message.content这个取值路径对所有模型都成立。这就是统一格式的价值——你的业务代码不需要知道底层是哪家。
流式输出同样统一:
response = completion( model="claude-3-5-sonnet-20241022", messages=[{"role": "user", "content": "写一首关于秋天的短诗"}], stream=True ) for chunk in response: delta = chunk.choices[0].delta.content if delta: print(delta, end="")3.3 接入时的三个常见坑
第一个坑是模型名写错。litellm靠模型名来识别供应商,名字写错了它可能识别成别家,或者直接报“未知模型”。建议把常用模型名整理成一个常量文件,别散落在各处。
第二个坑是超时设置。不同供应商的默认超时不一样,有些慢的模型默认超时可能不够。litellm支持传timeout参数,建议根据实际模型响应时间设置,一般复杂推理任务给到60秒以上。
第三个坑是代理环境变量干扰。如果你的运行环境里设了HTTP_PROXY之类的变量,某些供应商的请求可能会被错误路由。这个在容器化部署时尤其容易遇到,排查时可以先临时清空这些变量验证。
提示:接入新供应商时,先用最简单的单轮对话跑通,确认认证、网络、模型名三件事都没问题,再去接复杂的业务逻辑。这样出问题时排查范围小。
4. 路由与容错:让多模型调用真正稳定下来
4.1 故障转移的配置方式
单模型调用跑通只是第一步。生产环境里,任何一家供应商都可能出现超时、限流、服务抖动。litellm提供了fallbacks参数来做故障转移:
response = completion( model="gpt-4o", messages=[{"role": "user", "content": "你好"}], fallbacks=["claude-3-5-sonnet-20241022", "gemini-1.5-pro"] )这段配置的含义是:优先调gpt-4o,如果失败,依次尝试后面的模型。失败包括网络错误、超时、返回错误码等情况。
这里有个经验:fallback链不要设太长。三个模型已经足够,设太多会导致单次请求的延迟不可控——主模型超时30秒,备用再超时30秒,用户早就等不及了。合理的做法是给每个模型设较短的超时,快速失败快速切换。
4.2 基于成本和任务类型的路由策略
litellm支持更智能的路由,比如根据请求内容自动选择模型。一个实用的场景是:简单问答走便宜的小模型,复杂推理走贵的大模型。
from litellm import Router router = Router( model_list=[ {"model_name": "cheap", "litellm_params": {"model": "小模型名"}}, {"model_name": "powerful", "litellm_params": {"model": "大模型名"}}, ] ) # 业务代码里根据任务复杂度选择 response = router.completion( model="cheap" if is_simple else "powerful", messages=messages )Router还支持负载均衡,同一个model_name可以挂多个部署,请求会轮询分发。这在自部署多实例的场景下很有用。
4.3 重试机制与超时控制
重试是把双刃剑。对于限流类错误(返回429),重试是合理的;对于参数错误(返回400),重试毫无意义只会浪费时间。litellm的num_retries参数控制重试次数,但更精细的控制需要结合错误类型判断。
我的建议配置是:
| 参数 | 建议值 | 说明 |
|---|---|---|
| num_retries | 2 | 重试两次,加上首次共三次 |
| timeout | 30-60秒 | 根据模型复杂度调整 |
| retry_after | 自动 | 限流时按返回的等待时间重试 |
| fallbacks | 1-2个 | 重试仍失败才切换 |
超时控制要分层:单次请求超时、整个fallback链的总超时、以及业务层的超时。三层配合才能保证用户不会无限等待。
4.4 一个真实的路由配置案例
假设一个客服问答系统,要求:日常问题用便宜模型,涉及复杂业务逻辑的用强模型,两者都要有备用。
router = Router( model_list=[ { "model_name": "fast", "litellm_params": {"model": "小模型", "timeout": 15}, }, { "model_name": "fast", "litellm_params": {"model": "另一个小模型", "timeout": 15}, }, { "model_name": "smart", "litellm_params": {"model": "大模型", "timeout": 45}, }, ], fallbacks=[{"fast": ["smart"]}, {"smart": ["fast"]}], num_retries=2, )这个配置里,fast挂了两个部署做负载均衡,fast全挂了降级到smart,smart挂了降级回fast。实际跑下来,日常请求基本都在15秒内返回,成本也可控。
5. 成本追踪与可观测性:把账算清楚
5.1 token计数与费用估算
多模型调用最容易被忽视的就是成本。不同模型单价差几十倍,不做统计的话,月底账单出来才发现超支。litellm内置了成本计算,调用后可以直接拿到:
response = completion(model="gpt-4o", messages=messages) print(response.usage.prompt_tokens) print(response.usage.completion_tokens) print(response._hidden_params["response_cost"]) # 估算费用费用估算依赖litellm维护的价格表。这里要注意:价格表可能滞后于供应商调价,所以它算出来的是估算值,用于内部监控够用,但正式对账还是要以供应商账单为准。
5.2 回调机制与日志落库
litellm支持回调,可以在每次调用后触发自定义逻辑,比如写日志、上报监控:
import litellm def log_callback(kwargs, completion_response, start_time, end_time): # 把模型名、token数、耗时、费用写入数据库 save_to_db({ "model": kwargs.get("model"), "tokens": completion_response.usage.total_tokens, "latency": (end_time - start_time).total_seconds(), }) litellm.success_callback = [log_callback]这个机制是构建可观测性的基础。你可以统计每个模型的平均延迟、错误率、token消耗趋势,为路由策略调整提供数据支撑。
5.3 用代理模式做集中管理
如果团队里多个服务都要调模型,每个服务各自配置key和路由会很乱。litellm的代理模式可以把这些集中到一个服务:
litellm --model gpt-4o --port 4000启动后,其他服务只要把base_url指向这个代理,用统一的key访问即可。代理层统一做认证、路由、限流、日志。这对中大型团队特别有价值——模型配置变更只需要改一处。
代理模式还支持配置文件,把模型列表、路由规则、预算限制都写在一个yaml里,版本化管理。
5.4 预算控制与告警
litellm支持给key或用户设置预算上限,超过后拒绝请求。这个功能在代理模式下配置:
general_settings: max_budget: 100 # 总预算 budget_duration: "30d"配合告警回调,可以在预算用到80%时发通知。这个能力对于防止意外超支很关键,尤其是接了多个模型、又有自动化任务在跑的场景。
6. 踩坑实录:那些文档里不会写的细节
6.1 流式输出在不同供应商下的差异
流式输出是最容易出问题的地方。虽然litellm做了归一化,但不同供应商的流式行为有细微差别。比如有些供应商在流结束时会给一个特殊的结束块,有些不会;有些在首个chunk里就带usage信息,有些要到最后才给。
我遇到过一个典型问题:某供应商的流式返回里,delta.content偶尔是None,如果代码里直接拼接字符串就会报错。正确写法是先判断:
for chunk in response: delta = chunk.choices[0].delta if delta and delta.content: buffer += delta.content还有一个坑是流式模式下的错误处理。非流式调用出错会直接抛异常,流式调用可能已经吐了一部分内容才出错。这时候要决定是丢弃已输出内容还是保留,业务上要提前想清楚。
6.2 参数透传与供应商特有字段
统一格式覆盖不了所有参数。比如某家模型支持“思考预算”参数,统一格式里没有,就要用透传:
response = completion( model="某模型", messages=messages, extra_body={"thinking_budget": 1000} )透传字段的坑在于:不同供应商对同名字段的含义可能不同,而且透传字段不会被litellm校验,写错了可能被静默忽略。建议透传字段单独维护一份文档,标注清楚哪个字段对应哪家。
6.3 模型名映射的维护成本
供应商会不断更新模型版本,旧模型下线、新模型上线。litellm的模型名映射表需要跟着更新。如果你锁定了某个litellm版本,新模型可能识别不了。
我的做法是:在业务层维护一层自己的模型别名,比如业务代码里用"chat-default",在配置里映射到具体的litellm模型名。这样供应商换模型时,只改配置不改业务代码。
6.4 并发场景下的连接池问题
高并发下,如果每次调用都新建连接,性能会很差。litellm底层用的是HTTP客户端,支持连接复用,但需要确认配置。在代理模式下,并发量大的时候要关注连接池大小和超时设置,否则会出现请求排队。
实测下来,代理模式在几百QPS的量级下,调整好连接池参数后表现稳定。再高就需要考虑多实例部署加负载均衡了。
7. 进阶玩法:把litellm用出花来
7.1 结合缓存降低重复调用成本
很多业务场景下,相同的prompt会被反复调用。litellm支持接缓存,命中缓存直接返回,不消耗token:
import litellm from litellm.caching import Cache litellm.cache = Cache(type="redis", host="localhost", port=6379) response = completion( model="gpt-4o", messages=messages, caching=True )缓存key默认基于请求内容生成。要注意的是,带随机性参数的请求(比如temperature>0)缓存意义不大,因为同样输入本来就期望不同输出。缓存更适合确定性任务,比如分类、抽取、翻译。
7.2 自定义供应商适配器
如果litellm不支持你用的某个小众供应商,可以自己写适配器。核心是实现请求转换和响应转换两个方法,注册进去即可。这个能力让litellm的扩展性没有上限。
写适配器时,建议先照着已有的类似供应商的适配器改,比从零写快得多。重点是把认证方式、请求体结构、响应体结构这三块对齐。
7.3 与现有框架的集成思路
litellm可以嵌进各种框架。比如在LangChain里,可以用litellm作为底层LLM实现;在自研的Agent框架里,可以把litellm封装成统一的模型调用工具。关键是把litellm当作“模型访问层”,上层业务不直接依赖任何具体供应商。
这种分层的好处是:将来要换供应商、加供应商、调整路由策略,都只动访问层,业务层无感。
7.4 多租户场景下的key管理
SaaS类产品里,不同租户可能要用不同的模型、不同的预算。litellm的代理模式支持虚拟key,可以给每个租户发一个key,绑定特定的模型权限和预算。这样租户之间隔离,计费也清晰。
虚拟key的管理可以通过API动态创建和吊销,适合租户数量多的场景。
8. 我个人的使用体会
用litellm有一段时间了,最大的感受是它把“多模型”这件事的复杂度从业务层挪到了基础设施层。以前每接一家模型都要改业务代码,现在基本就是加一行配置。这个价值在模型快速迭代的当下特别明显——今天这个模型强,明天那个模型便宜,有了统一层,切换成本几乎为零。
几个实际建议:第一,尽早把模型调用抽象出来,哪怕一开始只用一家,也为将来留好扩展位。第二,成本监控一定要做,多模型场景下费用失控是分分钟的事。第三,fallback链要实测,不要假设配置了就一定生效,要主动制造故障验证切换逻辑。
最后分享一个小技巧:把常用模型的响应时间、错误率、单位成本做成一张对比表,定期更新。做路由决策时看这张表,比凭感觉靠谱得多。这张表也是跟团队沟通模型选型时最有说服力的材料。