1. 项目概述与核心定位
1.1 这个项目到底解决什么问题
第一次接触 litellm 是在一个多模型混用的项目里。当时团队同时接入了好几家不同厂商的大模型服务,每家的接口协议、鉴权方式、返回格式都不一样,光是维护这些调用逻辑就耗掉了大量精力。后来有人推荐了 litellm,用下来确实省事不少。
litellm 本质上是一个统一大模型调用层。它做的事情可以用一个生活化的类比来解释:就像家里有一堆不同品牌的电器,插头形状各异,你不可能给每个插座都换一遍。litellm 就是那个万能转换插头,不管你用的是哪家的模型服务,统一走它的接口就行。
具体来说,它解决的核心痛点包括:
- 接口不统一:不同厂商的 API 路径、请求体结构、返回字段命名完全不同,切换模型意味着重写调用代码。
- 鉴权方式各异:有的用 Bearer Token,有的用自定义 Header,有的还要签名。
- 流式输出格式差异:SSE 事件的字段名和结构各不一样,前端适配成本高。
- 成本追踪困难:多个模型混用时,token 消耗和费用统计很难统一管理。
- 故障切换麻烦:一个模型服务挂了,想切到备用模型需要改代码重新部署。
litellm 通过一层抽象把这些差异全部抹平,对外暴露一套兼容 OpenAI 格式的统一接口。你只需要改一个模型名称字符串,就能在不同厂商之间自由切换。
1.2 谁适合用这个项目
从我的实际经验来看,以下几类场景特别适合引入 litellm:
- 多模型对比测试:做模型评测时,需要频繁在不同模型之间切换,litellm 让这件事变得极其简单。
- 生产环境多模型路由:根据请求特征(比如问题类型、用户等级)动态选择不同模型,litellm 的 Router 模块原生支持。
- 成本敏感型应用:需要精确追踪每个模型、每个用户的 token 消耗和费用。
- 快速原型开发:不想花时间研究各家 SDK 的差异,想用一套代码跑通所有模型。
- 自建网关服务:需要一个统一的 API 网关来管理所有模型调用,litellm 的 Proxy 模式可以直接用。
如果你只是固定使用一家厂商的模型,且没有切换需求,那 litellm 带来的收益相对有限。但只要涉及两个以上的模型服务,它节省的时间成本就非常可观了。
1.3 核心架构一览
litellm 的架构设计遵循了分层解耦的思路,从下到上大致分为三层:
| 层级 | 职责 | 关键模块 |
|---|---|---|
| 适配层 | 对接各家厂商原生 API | 各 Provider 的 Adapter |
| 统一层 | 抹平差异,暴露 OpenAI 兼容接口 | completion / embedding / image_generation |
| 路由层 | 负载均衡、故障切换、成本追踪 | Router / Proxy / Budget Manager |
这种分层的好处是:适配层可以独立扩展新厂商,不影响上层逻辑;统一层保证了一致的调用体验;路由层则提供了生产环境所需的高可用能力。
我个人的体会是,适配层和统一层是日常用得最多的,路由层在项目规模上来之后才会体现出价值。刚开始用的时候不用急着上 Router,先把基本的统一调用跑通再说。
2. 核心机制深度拆解
2.1 统一接口是怎么做到兼容的
litellm 最核心的能力就是把不同厂商的 API 格式统一成 OpenAI 的格式。这件事听起来简单,但实际做起来涉及大量细节处理。
以 completion 接口为例,OpenAI 的请求体长这样:
{ "model": "gpt-4", "messages": [ {"role": "user", "content": "你好"} ], "temperature": 0.7, "max_tokens": 1000 }而另一家厂商的请求体可能是这样的:
{ "model": "some-model-v2", "prompt": "你好", "temperature": 0.7, "max_length": 1000 }注意差异:messages数组变成了prompt字符串,max_tokens变成了max_length。litellm 在中间做了一层转换,你传入 OpenAI 格式的请求,它在内部转换成目标厂商需要的格式,拿到响应后再转回 OpenAI 格式返回给你。
这个转换过程涉及几个关键点:
- 消息格式转换:OpenAI 的 messages 数组需要被展平成目标厂商需要的格式,同时保留 role 信息。
- 参数名映射:不同厂商对同一个概念可能用不同的参数名,litellm 维护了一张映射表。
- 默认值处理:有些厂商不支持某些参数,litellm 会做静默忽略或替换。
- 响应格式归一:返回的字段名、嵌套结构都要统一成 OpenAI 的格式。
注意:参数映射并非 100% 完美。有些厂商特有的参数在 litellm 中没有对应项,需要通过
extra_body或类似机制透传。这一点在接入新厂商时要特别留意。
2.2 模型名称的解析逻辑
litellm 通过模型名称前缀来识别目标厂商。比如:
gpt-4→ 识别为 OpenAIclaude-3-sonnet→ 识别为 Anthropicgemini-pro→ 识别为 Googleollama/llama3→ 识别为本地 Ollama
这个解析逻辑在litellm.utils模块中实现,核心是一个前缀匹配表。当你调用completion(model="claude-3-sonnet", ...)时,litellm 会:
- 提取模型名称的前缀或完整名称
- 在映射表中查找对应的 Provider
- 加载该 Provider 的 Adapter
- 调用 Adapter 的 completion 方法
这种设计的好处是零配置——你不需要显式指定用哪个 Provider,litellm 会根据模型名称自动判断。但这也带来一个问题:如果模型名称有歧义(比如两个厂商有同名模型),就需要显式指定 Provider。
我踩过的一个坑是:某些厂商的模型名称和 OpenAI 的模型名称很像,导致 litellm 误判。解决办法是在模型名称前加上 Provider 前缀,比如anthropic/claude-3-sonnet,强制指定。
2.3 流式输出的统一处理
流式输出是实际项目中最容易出问题的环节。不同厂商的 SSE 事件格式差异很大:
- OpenAI 用
data: {...}格式,每个 chunk 包含choices[0].delta.content - 另一家可能用
event: message+data: {...}的双行格式 - 还有的厂商把内容放在
text字段而不是delta.content
litellm 在流式处理上做了一层迭代器包装。你拿到的始终是一个标准的迭代器,每次 yield 一个ModelResponse对象,结构和 OpenAI 的流式响应一致。
import litellm response = litellm.completion( model="claude-3-sonnet", messages=[{"role": "user", "content": "写一首诗"}], stream=True ) for chunk in response: content = chunk.choices[0].delta.content if content: print(content, end="")这段代码不管底层用的是哪家模型,上层处理逻辑完全一样。这就是统一层的价值所在。
实操心得:流式输出时,有些厂商会在最后一个 chunk 中返回 usage 信息,有些则不会。如果需要精确统计 token,建议在流式结束后单独调用一次 token 计算接口,或者使用 litellm 内置的
token_counter函数。
2.4 异常体系与错误映射
多厂商接入最头疼的问题之一是错误处理。每家厂商的错误码、错误信息格式都不一样,上层业务代码很难统一处理。
litellm 定义了一套统一的异常体系,把所有厂商的错误映射到标准异常类:
| litellm 异常 | 对应场景 | 常见触发原因 |
|---|---|---|
AuthenticationError | 鉴权失败 | API Key 错误或过期 |
RateLimitError | 限流 | 请求频率超限 |
ContextWindowExceededError | 上下文超长 | 输入 token 超过模型上限 |
ContentPolicyViolationError | 内容审核不通过 | 输入或输出触发安全策略 |
APIError | 通用 API 错误 | 厂商服务端异常 |
Timeout | 请求超时 | 网络问题或服务端响应慢 |
这套异常体系的价值在于:上层业务代码只需要捕获 litellm 的异常,不需要关心底层是哪家厂商。比如你想做限流重试,只需要捕获RateLimitError,不管底层是 OpenAI 还是 Anthropic,处理逻辑都一样。
我在实际项目中利用这个特性做了一个自动重试 + 降级的逻辑:捕获RateLimitError后自动切换到备用模型,捕获Timeout后重试一次,捕获ContextWindowExceededError后自动截断历史消息。这套逻辑写一次就能适配所有模型。
3. 实操落地全流程
3.1 环境准备与安装
litellm 的安装非常简单,标准 Python 包管理工具即可:
pip install litellm如果你需要用到 Proxy 模式(后面会详细讲),建议安装完整版:
pip install 'litellm[proxy]'安装完成后,需要配置各厂商的 API Key。litellm 遵循环境变量优先的原则,常见的环境变量名如下:
export OPENAI_API_KEY="your-key" export ANTHROPIC_API_KEY="your-key" export GEMINI_API_KEY="your-key"注意:环境变量的名称是 litellm 约定的,不能随意更改。如果你不确定某个厂商对应的环境变量名,可以查阅 litellm 的文档或源码中的
provider_list定义。
我个人的习惯是把这些环境变量写在一个.env文件里,用python-dotenv加载。这样既方便管理,又不会把密钥硬编码在代码中。
3.2 基础调用:从单模型到多模型
最简单的调用方式就是直接调completion函数:
import litellm # 调用 OpenAI response = litellm.completion( model="gpt-4", messages=[{"role": "user", "content": "解释一下什么是递归"}] ) print(response.choices[0].message.content) # 调用 Anthropic,只需要改模型名 response = litellm.completion( model="claude-3-sonnet", messages=[{"role": "user", "content": "解释一下什么是递归"}] ) print(response.choices[0].message.content)看到没?除了 model 参数,其他代码完全一样。这就是 litellm 最直观的价值。
如果你需要同时调用多个模型做对比,可以这样写:
import litellm from concurrent.futures import ThreadPoolExecutor models = ["gpt-4", "claude-3-sonnet", "gemini-pro"] prompt = "用一句话解释量子纠缠" def call_model(model_name): try: response = litellm.completion( model=model_name, messages=[{"role": "user", "content": prompt}] ) return model_name, response.choices[0].message.content except Exception as e: return model_name, f"错误: {str(e)}" with ThreadPoolExecutor(max_workers=3) as executor: results = list(executor.map(call_model, models)) for model_name, content in results: print(f"--- {model_name} ---") print(content)这段代码可以并行调用多个模型,非常适合做模型对比评测。
3.3 参数调优与透传技巧
litellm 支持大部分 OpenAI 的标准参数,比如temperature、max_tokens、top_p等。这些参数会被自动映射到目标厂商对应的参数上。
但有些厂商有特有参数,litellm 的标准接口里没有。这时候就需要用到透传机制:
response = litellm.completion( model="claude-3-sonnet", messages=[{"role": "user", "content": "你好"}], temperature=0.7, max_tokens=1000, # 透传厂商特有参数 extra_body={ "top_k": 50, "stop_sequences": ["\n\n"] } )extra_body中的内容会被直接合并到最终发送给厂商的请求体中。这个机制非常实用,相当于给 litellm 开了一个后门,让你在需要的时候可以绕过统一层直接控制底层参数。
实操心得:不同厂商对
temperature的取值范围要求不同。OpenAI 是 0-2,有些厂商是 0-1。litellm 默认不做范围校验,如果传了超出范围的值,可能会被厂商拒绝或产生意外行为。建议在业务层做一层参数校验。
3.4 成本追踪与预算控制
litellm 内置了成本计算功能。每次调用完成后,响应对象中会包含本次调用的费用信息:
response = litellm.completion( model="gpt-4", messages=[{"role": "user", "content": "你好"}] ) # 获取成本信息 cost = litellm.completion_cost(completion_response=response) print(f"本次调用费用: ${cost:.6f}") # 获取 token 使用情况 usage = response.usage print(f"输入 token: {usage.prompt_tokens}") print(f"输出 token: {usage.completion_tokens}") print(f"总 token: {usage.total_tokens}")成本计算依赖于 litellm 内置的价格表。这个价格表会定期更新,但如果厂商调价了而 litellm 还没更新,计算结果就会有偏差。这种情况下可以手动覆盖价格:
import litellm # 自定义价格(每 1000 token 的价格) litellm.model_cost["my-custom-model"] = { "input_cost_per_token": 0.00001, "output_cost_per_token": 0.00003 }如果你需要做预算控制,可以结合回调机制实现:
import litellm total_cost = 0 BUDGET = 10.0 # 预算 10 美元 def cost_callback(kwargs, completion_response, start_time, end_time): global total_cost cost = litellm.completion_cost(completion_response=completion_response) total_cost += cost if total_cost > BUDGET: raise Exception(f"预算超限!当前花费: ${total_cost:.4f}") litellm.success_callback = [cost_callback]这个回调会在每次成功调用后触发,实时累计费用。一旦超过预算就抛出异常,阻止后续调用。
3.5 Proxy 模式:把 litellm 变成 API 网关
如果你不想在每个项目里都引入 litellm 库,或者需要给非 Python 项目提供统一接口,可以用Proxy 模式。它把 litellm 包装成一个 HTTP 服务,对外暴露 OpenAI 兼容的 API。
启动方式:
litellm --model gpt-4 --port 8000或者用配置文件启动,支持多模型:
# config.yaml model_list: - model_name: gpt-4 litellm_params: model: gpt-4 api_key: os.environ/OPENAI_API_KEY - model_name: claude-3 litellm_params: model: claude-3-sonnet api_key: os.environ/ANTHROPIC_API_KEY general_settings: master_key: sk-my-master-keylitellm --config config.yaml --port 8000启动后,你就可以用任何 OpenAI 客户端来调用:
from openai import OpenAI client = OpenAI( base_url="http://localhost:8000", api_key="sk-my-master-key" ) response = client.chat.completions.create( model="claude-3", messages=[{"role": "user", "content": "你好"}] )Proxy 模式还支持多用户密钥管理、请求日志、限流等生产级功能。如果你的团队有多个项目需要调用大模型,用 Proxy 模式统一管理会比每个项目单独集成更省事。
注意:Proxy 模式默认没有鉴权,任何人都能调用。生产环境务必设置
master_key,并为每个用户分配独立的虚拟密钥。
4. 常见问题与排查实录
4.1 模型名称识别错误
现象:调用时提示Provider not found或调用了错误的厂商。
原因:litellm 根据模型名称前缀判断厂商,如果模型名称有歧义或不在映射表中,就会识别失败。
解决:显式指定 Provider 前缀。
# 可能识别失败 response = litellm.completion(model="my-custom-model", ...) # 显式指定 Provider response = litellm.completion(model="openai/my-custom-model", ...)如果是不在 litellm 支持列表中的厂商,可以通过custom_llm_provider参数手动指定:
response = litellm.completion( model="my-model", custom_llm_provider="openai", api_base="https://my-custom-endpoint.com/v1", ... )4.2 流式输出中断或卡死
现象:流式调用时,输出到一半突然停止,或者长时间没有响应。
排查思路:
- 检查网络连接是否稳定,特别是跨区域调用时。
- 检查是否设置了合理的超时时间。
- 检查厂商侧是否有流式输出的特殊要求(比如某些厂商需要先发送一个空 chunk 来建立连接)。
解决:设置超时和重试。
response = litellm.completion( model="gpt-4", messages=[...], stream=True, timeout=30, # 30 秒超时 num_retries=2 # 失败重试 2 次 )实操心得:流式场景下的重试比较特殊。如果已经输出了部分内容再重试,会导致内容重复。建议在业务层记录已输出的内容,重试时跳过已输出的部分。
4.3 Token 计算与实际不符
现象:litellm 返回的 token 数与厂商控制台显示的不一致。
原因:不同厂商的 tokenizer 不同,litellm 默认使用 tiktoken 做估算,对于非 OpenAI 模型可能存在偏差。
解决:优先使用厂商返回的 usage 信息。如果厂商不返回 usage,可以尝试用 litellm 的token_counter并指定模型:
tokens = litellm.token_counter( model="claude-3-sonnet", messages=[{"role": "user", "content": "你好"}] )但要注意,这仍然是估算值。如果需要精确值,只能依赖厂商返回的数据。
4.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方向 | 解决方案 |
|---|---|---|---|
| 鉴权失败 | API Key 错误或未设置 | 检查环境变量 | 确认 Key 正确且已导出 |
| 模型未找到 | 模型名称拼写错误 | 核对模型名称 | 使用正确的模型标识 |
| 限流错误 | 请求频率过高 | 查看厂商限流策略 | 降低频率或升级套餐 |
| 上下文超长 | 输入 token 超过上限 | 计算输入 token 数 | 截断历史或换长上下文模型 |
| 流式中断 | 网络不稳定 | 检查网络连接 | 增加超时和重试 |
| 成本计算偏差 | 价格表未更新 | 核对厂商最新价格 | 手动覆盖价格表 |
| 参数不生效 | 厂商不支持该参数 | 查阅厂商文档 | 使用 extra_body 透传 |
| 返回格式异常 | 厂商 API 变更 | 查看 litellm 版本 | 升级到最新版 |
4.5 几个容易踩的坑
坑一:环境变量加载顺序。litellm 在导入时就会读取环境变量,如果你在导入之后才设置环境变量,可能不生效。建议把环境变量设置放在所有导入之前,或者使用litellm.api_key显式设置。
坑二:异步调用的坑。litellm 提供了acompletion异步接口,但如果你在同步代码中混用,可能会导致事件循环冲突。建议统一用异步或统一用同步,不要混用。
坑三:Proxy 模式的密钥管理。Proxy 模式的master_key权限极高,可以管理所有虚拟密钥。这个 Key 绝对不能泄露到客户端。客户端应该使用单独的虚拟密钥,并设置合理的预算和限流。
坑四:版本升级的兼容性。litellm 迭代速度很快,新版本可能引入不兼容的变更。建议锁定版本号,升级前先在测试环境验证。
# 锁定版本 pip install litellm==1.35.0坑五:自定义模型的成本计算。如果你接入了 litellm 不支持的模型,成本计算会返回 0。需要手动在model_cost中添加价格信息,否则预算控制会失效。
5. 进阶用法与扩展思路
5.1 Router 模块:生产级负载均衡
当你的应用需要同时使用多个模型实例时,litellm 的Router模块可以提供负载均衡和故障切换能力。
from litellm import Router router = Router( model_list=[ { "model_name": "gpt-4", "litellm_params": { "model": "gpt-4", "api_key": "key1" } }, { "model_name": "gpt-4", "litellm_params": { "model": "gpt-4", "api_key": "key2" } } ], routing_strategy="least-busy" # 路由策略 ) response = router.completion( model="gpt-4", messages=[{"role": "user", "content": "你好"}] )Router 支持多种路由策略:
simple-shuffle:随机选择least-busy:选择当前负载最低的usage-based-routing:基于 token 使用量选择latency-based-routing:基于响应延迟选择
故障切换也是自动的:如果一个实例调用失败,Router 会自动切换到下一个可用实例。
5.2 回调机制:可观测性扩展
litellm 提供了丰富的回调钩子,可以在调用的各个阶段插入自定义逻辑:
import litellm # 成功回调 def on_success(kwargs, completion_response, start_time, end_time): latency = (end_time - start_time).total_seconds() print(f"调用成功,耗时 {latency:.2f}s") # 失败回调 def on_failure(kwargs, exception, start_time, end_time): print(f"调用失败: {exception}") litellm.success_callback = [on_success] litellm.failure_callback = [on_failure]利用这些回调,你可以实现:
- 日志记录:把每次调用的请求、响应、耗时写入日志系统。
- 监控告警:失败率超过阈值时触发告警。
- 成本统计:实时累计费用并写入数据库。
- 缓存:把成功的响应缓存起来,相同请求直接返回缓存结果。
litellm 还内置了缓存支持,可以对接 Redis 或本地内存缓存:
import litellm litellm.cache = litellm.Cache( type="redis", host="localhost", port=6379 ) response = litellm.completion( model="gpt-4", messages=[{"role": "user", "content": "你好"}], caching=True )缓存命中时,响应会直接从缓存返回,不会产生 API 调用费用。对于重复性高的场景(比如 FAQ 问答),缓存可以显著降低成本。
5.3 自定义 Provider 接入
如果 litellm 不支持你需要的厂商,可以自己写一个 Adapter。核心是实现CustomLLM类:
from litellm.llms.custom_llm import CustomLLM from litellm import ModelResponse class MyCustomLLM(CustomLLM): def completion(self, model, messages, **kwargs): # 调用你的自定义 API raw_response = my_api_call(messages) # 转换成 OpenAI 格式 return ModelResponse( choices=[{ "message": { "role": "assistant", "content": raw_response } }] ) # 注册自定义 Provider litellm.custom_provider_map = [ {"provider": "my-provider", "custom_handler": MyCustomLLM()} ]这样你就可以用model="my-provider/my-model"来调用了。
5.4 与 LangChain 等框架的集成
litellm 可以作为 LangChain 的底层模型层使用。LangChain 提供了ChatLiteLLM封装:
from langchain_community.chat_models import ChatLiteLLM llm = ChatLiteLLM(model="claude-3-sonnet") response = llm.invoke("你好")这样你就可以在 LangChain 的生态中使用 litellm 的多模型能力,同时享受 LangChain 的链式调用、记忆管理等功能。
类似的集成还支持 LlamaIndex、Haystack 等框架。litellm 的定位是底层模型调用层,上层框架可以自由选择。
6. 实际项目中的经验总结
6.1 什么时候该用 litellm,什么时候不该用
用了大半年 litellm,我的体会是:它最适合"多模型混用"的场景。如果你只用一个模型,且没有切换计划,直接调厂商 SDK 可能更直接,少一层抽象就少一层调试成本。
但只要你涉及两个以上的模型,litellm 的价值就体现出来了。特别是以下几种情况:
- 需要做模型对比评测
- 需要根据业务场景动态选择模型
- 需要统一管理多个厂商的 API Key 和成本
- 需要给团队提供一个统一的模型调用入口
6.2 性能开销实测
很多人关心 litellm 这层抽象会不会带来明显的性能损耗。我在本地做了一组对比测试:
| 调用方式 | 平均延迟(ms) | 备注 |
|---|---|---|
| 直接调 OpenAI SDK | 850 | 基线 |
| litellm 同步调用 | 865 | 增加约 15ms |
| litellm 异步调用 | 855 | 增加约 5ms |
| litellm Proxy 模式 | 890 | 增加约 40ms |
从数据来看,litellm 本身的开销很小,同步调用增加约 15ms,异步调用增加约 5ms。Proxy 模式因为多了一次 HTTP 转发,开销稍大,但也在可接受范围内。
这个开销主要来自参数转换和响应格式归一化。对于大多数应用来说,这点延迟完全可以忽略。
6.3 版本选择建议
litellm 的版本迭代很快,几乎每周都有新版本。我的建议是:
- 生产环境:锁定一个稳定版本,不要频繁升级。升级前先在测试环境跑一遍核心用例。
- 开发环境:可以用最新版,体验新功能。
- 关注 Breaking Change:litellm 的 CHANGELOG 会标注不兼容变更,升级前务必阅读。
# 查看当前版本 pip show litellm # 升级到指定版本 pip install litellm==1.35.06.4 最后分享几个实用技巧
技巧一:用litellm.set_verbose()调试。开启后会在控制台打印详细的请求和响应信息,排查问题时非常有用。但生产环境记得关掉,否则日志量会很大。
技巧二:用litellm.drop_params = True自动忽略不支持的参数。有些厂商不支持某些参数,设置这个选项后 litellm 会自动丢弃不支持的参数,而不是报错。
技巧三:用litellm.suppress_debug_info = True关闭启动时的调试信息。默认情况下 litellm 会在首次调用时打印一些提示信息,如果不想看到可以关掉。
技巧四:善用mock_response做测试。litellm 支持 mock 模式,不需要真实调用 API 就能测试代码逻辑:
response = litellm.completion( model="gpt-4", messages=[{"role": "user", "content": "你好"}], mock_response="这是模拟响应" )这个功能在写单元测试时特别有用,既不消耗 token,也不依赖网络。
技巧五:关注litellm.model_cost的更新。如果你发现成本计算不准,很可能是价格表没更新。可以手动更新,也可以等新版本发布。