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(modelclaude-3-sonnet, ...)时litellm 会提取模型名称的前缀或完整名称在映射表中查找对应的 Provider加载该 Provider 的 Adapter调用 Adapter 的 completion 方法这种设计的好处是零配置——你不需要显式指定用哪个 Providerlitellm 会根据模型名称自动判断。但这也带来一个问题如果模型名称有歧义比如两个厂商有同名模型就需要显式指定 Provider。我踩过的一个坑是某些厂商的模型名称和 OpenAI 的模型名称很像导致 litellm 误判。解决办法是在模型名称前加上 Provider 前缀比如anthropic/claude-3-sonnet强制指定。2.3 流式输出的统一处理流式输出是实际项目中最容易出问题的环节。不同厂商的 SSE 事件格式差异很大OpenAI 用data: {...}格式每个 chunk 包含choices[0].delta.content另一家可能用event: messagedata: {...}的双行格式还有的厂商把内容放在text字段而不是delta.contentlitellm 在流式处理上做了一层迭代器包装。你拿到的始终是一个标准的迭代器每次 yield 一个ModelResponse对象结构和 OpenAI 的流式响应一致。import litellm response litellm.completion( modelclaude-3-sonnet, messages[{role: user, content: 写一首诗}], streamTrue ) 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_KEYyour-key export ANTHROPIC_API_KEYyour-key export GEMINI_API_KEYyour-key注意环境变量的名称是 litellm 约定的不能随意更改。如果你不确定某个厂商对应的环境变量名可以查阅 litellm 的文档或源码中的provider_list定义。我个人的习惯是把这些环境变量写在一个.env文件里用python-dotenv加载。这样既方便管理又不会把密钥硬编码在代码中。3.2 基础调用从单模型到多模型最简单的调用方式就是直接调completion函数import litellm # 调用 OpenAI response litellm.completion( modelgpt-4, messages[{role: user, content: 解释一下什么是递归}] ) print(response.choices[0].message.content) # 调用 Anthropic只需要改模型名 response litellm.completion( modelclaude-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( modelmodel_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_workers3) 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( modelclaude-3-sonnet, messages[{role: user, content: 你好}], temperature0.7, max_tokens1000, # 透传厂商特有参数 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( modelgpt-4, messages[{role: user, content: 你好}] ) # 获取成本信息 cost litellm.completion_cost(completion_responseresponse) 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_responsecompletion_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_urlhttp://localhost:8000, api_keysk-my-master-key ) response client.chat.completions.create( modelclaude-3, messages[{role: user, content: 你好}] )Proxy 模式还支持多用户密钥管理、请求日志、限流等生产级功能。如果你的团队有多个项目需要调用大模型用 Proxy 模式统一管理会比每个项目单独集成更省事。注意Proxy 模式默认没有鉴权任何人都能调用。生产环境务必设置master_key并为每个用户分配独立的虚拟密钥。4. 常见问题与排查实录4.1 模型名称识别错误现象调用时提示Provider not found或调用了错误的厂商。原因litellm 根据模型名称前缀判断厂商如果模型名称有歧义或不在映射表中就会识别失败。解决显式指定 Provider 前缀。# 可能识别失败 response litellm.completion(modelmy-custom-model, ...) # 显式指定 Provider response litellm.completion(modelopenai/my-custom-model, ...)如果是不在 litellm 支持列表中的厂商可以通过custom_llm_provider参数手动指定response litellm.completion( modelmy-model, custom_llm_provideropenai, api_basehttps://my-custom-endpoint.com/v1, ... )4.2 流式输出中断或卡死现象流式调用时输出到一半突然停止或者长时间没有响应。排查思路检查网络连接是否稳定特别是跨区域调用时。检查是否设置了合理的超时时间。检查厂商侧是否有流式输出的特殊要求比如某些厂商需要先发送一个空 chunk 来建立连接。解决设置超时和重试。response litellm.completion( modelgpt-4, messages[...], streamTrue, timeout30, # 30 秒超时 num_retries2 # 失败重试 2 次 )实操心得流式场景下的重试比较特殊。如果已经输出了部分内容再重试会导致内容重复。建议在业务层记录已输出的内容重试时跳过已输出的部分。4.3 Token 计算与实际不符现象litellm 返回的 token 数与厂商控制台显示的不一致。原因不同厂商的 tokenizer 不同litellm 默认使用 tiktoken 做估算对于非 OpenAI 模型可能存在偏差。解决优先使用厂商返回的 usage 信息。如果厂商不返回 usage可以尝试用 litellm 的token_counter并指定模型tokens litellm.token_counter( modelclaude-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 litellm1.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_strategyleast-busy # 路由策略 ) response router.completion( modelgpt-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( typeredis, hostlocalhost, port6379 ) response litellm.completion( modelgpt-4, messages[{role: user, content: 你好}], cachingTrue )缓存命中时响应会直接从缓存返回不会产生 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()} ]这样你就可以用modelmy-provider/my-model来调用了。5.4 与 LangChain 等框架的集成litellm 可以作为 LangChain 的底层模型层使用。LangChain 提供了ChatLiteLLM封装from langchain_community.chat_models import ChatLiteLLM llm ChatLiteLLM(modelclaude-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 SDK850基线litellm 同步调用865增加约 15mslitellm 异步调用855增加约 5mslitellm Proxy 模式890增加约 40ms从数据来看litellm 本身的开销很小同步调用增加约 15ms异步调用增加约 5ms。Proxy 模式因为多了一次 HTTP 转发开销稍大但也在可接受范围内。这个开销主要来自参数转换和响应格式归一化。对于大多数应用来说这点延迟完全可以忽略。6.3 版本选择建议litellm 的版本迭代很快几乎每周都有新版本。我的建议是生产环境锁定一个稳定版本不要频繁升级。升级前先在测试环境跑一遍核心用例。开发环境可以用最新版体验新功能。关注 Breaking Changelitellm 的 CHANGELOG 会标注不兼容变更升级前务必阅读。# 查看当前版本 pip show litellm # 升级到指定版本 pip install litellm1.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( modelgpt-4, messages[{role: user, content: 你好}], mock_response这是模拟响应 )这个功能在写单元测试时特别有用既不消耗 token也不依赖网络。技巧五关注litellm.model_cost的更新。如果你发现成本计算不准很可能是价格表没更新。可以手动更新也可以等新版本发布。