1. 项目概述与核心定位
litellm 这个项目,最早是我在做一个多模型路由网关时被同事安利的。当时我们的业务需要同时对接好几家不同厂商的大模型接口,每家的 SDK、鉴权方式、请求参数、返回结构都不一样,代码里到处是 if-else 分支,维护起来非常痛苦。litellm 的出现基本解决了这个痛点——它用一套统一的 OpenAI 格式接口,把不同厂商的大模型调用统一封装起来,你只需要改一个模型名称字符串,就能在多个模型之间切换。
简单来说,litellm 是一个轻量级的 Python 库,同时也可以作为一个代理服务部署。它的核心价值在于统一接口层:不管你底层用的是哪家的模型,上层业务代码只需要按照 OpenAI 的格式来写,剩下的适配工作交给 litellm 处理。这对于需要做模型对比、成本优化、故障降级的团队来说,实用性非常高。
适合谁来参考这篇内容?如果你正在做以下事情,litellm 值得花时间研究:一是需要在多个大模型之间做动态切换或 A/B 测试;二是想统一管理多个厂商的 API Key 和调用配额;三是希望在不改动业务代码的前提下,快速接入新模型;四是需要一套统一的成本追踪和日志记录方案。哪怕你只是个人开发者,想在自己的小项目里灵活切换模型,litellm 也能帮你省掉大量适配代码。
2. 核心架构与设计思路拆解
2.1 为什么需要统一接口层
在没有 litellm 这类工具之前,多模型接入的典型做法是每个厂商写一个适配器类,然后在业务层根据配置去选择调用哪个适配器。这种做法的问题在于:每接入一个新模型,就要新增一个适配器,业务层的选择逻辑也要跟着改。更麻烦的是,不同厂商的返回结构差异很大,有的返回choices[0].message.content,有的返回output.text,有的甚至直接返回一个字符串。业务层如果直接依赖这些结构,后续换模型时改动量会非常大。
litellm 的设计思路是在业务代码和厂商 SDK 之间插入一个标准化层。这个层做的事情包括:把统一的 OpenAI 格式请求翻译成各厂商的实际请求格式;把各厂商的返回结果翻译回 OpenAI 格式;统一处理鉴权、重试、超时、错误码映射等横切关注点。这样一来,业务代码只需要面向 OpenAI 格式编程,底层的差异被完全屏蔽。
这个思路其实和数据库领域的 ORM 很像。你写 SQL 的时候不需要关心底层是 MySQL 还是 PostgreSQL,ORM 帮你做方言转换。litellm 就是大模型调用领域的“ORM”,只不过它转换的不是 SQL 方言,而是各家 API 的请求和响应格式。
2.2 核心模块拆解
litellm 的代码结构不算复杂,核心模块可以分成几块来理解。
第一块是completion 入口。这是最常用的功能,对应litellm.completion()方法。你传入模型名称和消息列表,它返回一个标准化的响应对象。模型名称的格式通常是厂商前缀/模型标识,比如openai/gpt-4o、anthropic/claude-3-5-sonnet、deepseek/deepseek-chat这样的形式。litellm 根据前缀去路由到对应的适配器。
第二块是provider 适配器。每个厂商对应一个适配器模块,负责把标准请求转换成该厂商的实际请求。适配器里会处理该厂商特有的参数映射,比如某些厂商不支持temperature参数,适配器就会在转换时忽略或做替代处理。适配器还负责把厂商的返回结果标准化,包括 token 用量统计、finish_reason 映射等。
第三块是router 路由层。这是 litellm 作为代理服务时的核心组件。Router 支持多种路由策略,比如按权重分配、按成本最低优先、按延迟最低优先、按可用性降级等。你可以配置多个部署实例,Router 会根据策略自动选择。这个功能在生产环境中非常实用,比如你可以配置主模型和备用模型,主模型调用失败时自动切换到备用模型。
第四块是成本追踪与日志。litellm 内置了成本计算功能,会根据模型名称和 token 用量自动计算本次调用的费用。它还支持回调机制,可以把调用日志推送到各种可观测性平台。对于需要做成本核算的团队来说,这个功能省去了自己维护价格表的麻烦。
2.3 部署形态的选择
litellm 有两种使用方式,选择哪种取决于你的场景。
第一种是作为 Python 库直接调用。适合单体应用或者脚本场景,你直接在代码里import litellm,然后调用completion()方法。这种方式最简单,不需要额外部署服务,但缺点是每个调用方都需要自己管理 API Key 和配置。
第二种是作为代理服务部署。litellm 提供了一个 proxy server,你可以把它部署成一个独立的 HTTP 服务,所有业务方通过这个代理来调用模型。代理层统一管理 API Key、做访问控制、记录日志、执行路由策略。这种方式适合多团队共用的场景,业务方不需要关心底层用的是哪家模型,只需要调用代理的统一接口。
我个人的建议是:如果是个人项目或者小团队,直接用库的方式就够了,没必要引入额外的服务部署复杂度。如果是公司级别的多团队共用,或者需要做精细的成本管控和访问审计,那就上代理服务。
3. 核心细节解析与实操要点
3.1 模型名称的命名规则
litellm 的模型名称格式是provider/model_name,这个命名规则是整个库的路由基础,理解清楚很重要。
常见的 provider 前缀包括:openai、anthropic、azure、bedrock、vertex_ai、deepseek、mistral、cohere、ollama等。每个前缀对应一个适配器实现。如果你用的是 OpenAI 兼容的第三方服务,比如某些国内厂商提供的 OpenAI 兼容接口,可以用openai/前缀,然后通过api_base参数指定实际的服务地址。
这里有个容易踩的坑:不同 provider 对模型名称的写法要求不一样。比如 Azure OpenAI 的模型名称需要包含部署名,格式是azure/你的部署名,而不是直接用模型本身的名称。再比如 Bedrock 的模型名称需要包含区域前缀和版本号,格式类似bedrock/anthropic.claude-3-sonnet-20240229-v1:0。这些细节在官方文档里都有说明,但第一次配置时很容易搞错。
提示:如果你不确定某个模型的正确名称格式,可以用
litellm.model_list查看当前版本支持的所有模型列表,或者在代码里调用litellm.get_model_info("模型名称")来验证。
3.2 API Key 的配置方式
litellm 读取 API Key 的方式遵循环境变量优先的原则。每个 provider 对应一个或多个环境变量名称,比如 OpenAI 对应OPENAI_API_KEY,Anthropic 对应ANTHROPIC_API_KEY,Azure 对应AZURE_API_KEY加上AZURE_API_BASE和AZURE_API_VERSION。
你也可以在代码里显式传入,比如litellm.completion(model="openai/gpt-4o", api_key="sk-xxx", ...)。但显式传入的方式不利于密钥管理,生产环境建议用环境变量或者密钥管理服务。
对于代理服务模式,litellm 支持在配置文件里定义多个模型和对应的密钥,代理层会根据请求中的模型名称自动选择对应的密钥。配置文件通常是一个 YAML 文件,结构大致如下:
model_list: - model_name: gpt-4o litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY - model_name: claude-sonnet litellm_params: model: anthropic/claude-3-5-sonnet-20241022 api_key: os.environ/ANTHROPIC_API_KEY这种配置方式的好处是,业务方只需要知道gpt-4o或claude-sonnet这样的别名,不需要关心底层的实际模型名称和密钥。密钥轮换时也只需要改配置文件,不需要通知所有业务方。
3.3 请求参数的标准化处理
litellm 会把 OpenAI 格式的请求参数翻译成各厂商的实际参数。大部分参数是直接映射的,比如temperature、max_tokens、top_p这些通用参数。但有些参数在不同厂商之间差异较大,需要特别注意。
比如max_tokens在 OpenAI 里表示生成的最大 token 数,但在某些厂商的接口里叫max_output_tokens或者max_new_tokens。litellm 的适配器会做名称转换,你只需要按 OpenAI 的写法传max_tokens就行。
再比如stop参数,OpenAI 支持传一个字符串或字符串列表,但有些厂商只支持单个字符串。litellm 会在适配器里做兼容处理,如果厂商只支持单个字符串,它会取列表的第一个元素。
还有一个容易忽略的点是多模态输入。OpenAI 的格式是用content数组来表示文本和图片的混合输入,每个元素有type字段。litellm 会把这种格式转换成各厂商支持的多模态格式。但不同厂商对图片的格式要求不一样,有的要求传 URL,有的要求传 base64 编码,有的对图片尺寸有限制。这些细节在跨厂商切换时容易出问题,建议在切换模型前先用小规模请求做验证。
3.4 流式输出的处理
流式输出是大模型应用中很常见的需求,litellm 对流式的支持比较完善。你只需要在调用时传stream=True,返回的就是一个迭代器,每次 yield 一个 chunk。
但这里有个细节需要注意:不同厂商的流式返回格式差异很大。OpenAI 的流式返回是 SSE 格式,每个 chunk 是一个 JSON 对象,包含choices[0].delta.content。Anthropic 的流式返回格式不同,事件类型更多,有message_start、content_block_delta、message_stop等多种事件。litellm 会把这些差异统一成 OpenAI 的流式格式,你拿到的 chunk 结构是一致的。
不过,统一格式也意味着某些厂商特有的信息可能会丢失。比如 Anthropic 的流式返回里会包含usage信息,但 OpenAI 的流式格式默认不包含 usage。litellm 在转换时会尽量保留这些信息,但如果你依赖某些厂商特有的字段,建议先测试确认。
注意:流式模式下,litellm 的某些功能(比如自动重试)可能不会生效,因为流式请求一旦开始返回,就很难在中途重试。如果你的场景对可靠性要求很高,建议在流式外层自己做重试逻辑。
4. 实操过程与核心环节实现
4.1 环境准备与安装
安装 litellm 很简单,一条 pip 命令搞定:
pip install litellm如果你需要用到代理服务功能,还需要安装额外的依赖:
pip install 'litellm[proxy]'安装完成后,建议先设置好环境变量。以 OpenAI 和 Anthropic 为例:
export OPENAI_API_KEY="你的密钥" export ANTHROPIC_API_KEY="你的密钥"如果你用的是其他厂商,参考官方文档设置对应的环境变量。环境变量设置好之后,可以先跑一个最简单的测试:
import litellm response = litellm.completion( model="openai/gpt-4o", messages=[{"role": "user", "content": "你好,请用一句话介绍你自己"}] ) print(response.choices[0].message.content)如果这一步能正常返回,说明基础环境没问题。如果报错,大概率是 API Key 没设置对,或者模型名称写错了。
4.2 多模型切换的实操演示
假设你有一个业务场景,需要根据用户的问题类型选择不同的模型。简单问题用便宜的小模型,复杂问题用能力强的大模型。用 litellm 实现起来很直接:
import litellm def ask(question, complexity="simple"): if complexity == "simple": model = "openai/gpt-4o-mini" else: model = "anthropic/claude-3-5-sonnet-20241022" response = litellm.completion( model=model, messages=[{"role": "user", "content": question}], temperature=0.7, max_tokens=1024 ) return response.choices[0].message.content这段代码里,切换模型只需要改一个字符串。如果你后续想换成 DeepSeek 或者别的模型,也只需要改model参数的值,其他代码完全不用动。这就是统一接口层的价值。
4.3 代理服务的部署与配置
如果你决定用代理服务模式,部署流程也不复杂。首先创建一个配置文件config.yaml:
model_list: - model_name: fast-model litellm_params: model: openai/gpt-4o-mini api_key: os.environ/OPENAI_API_KEY - model_name: strong-model litellm_params: model: anthropic/claude-3-5-sonnet-20241022 api_key: os.environ/ANTHROPIC_API_KEY general_settings: master_key: sk-your-master-key database_url: "sqlite:///litellm.db"然后启动代理服务:
litellm --config config.yaml --port 4000启动后,你就可以像调用 OpenAI 接口一样调用这个代理:
from openai import OpenAI client = OpenAI( api_key="sk-your-master-key", base_url="http://localhost:4000" ) response = client.chat.completions.create( model="fast-model", messages=[{"role": "user", "content": "你好"}] ) print(response.choices[0].message.content)注意这里用的是标准的 OpenAI SDK,只是把base_url指向了 litellm 代理。业务方完全不需要安装 litellm,也不需要知道底层用的是哪家模型。这种解耦程度对于多团队协作来说非常友好。
4.4 路由策略的配置
litellm 的 Router 支持多种路由策略,配置方式是在litellm_params里指定routing_strategy。常见的策略包括:
| 策略名称 | 含义 | 适用场景 |
|---|---|---|
| simple-shuffle | 随机选择 | 多实例负载均衡 |
| least-busy | 选择当前请求数最少的实例 | 避免单实例过载 |
| usage-based-routing | 根据 token 用量选择 | 成本敏感场景 |
| latency-based-routing | 根据历史延迟选择 | 延迟敏感场景 |
| cost-based-routing | 根据成本选择 | 成本优化场景 |
配置示例:
model_list: - model_name: smart-route litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY - model_name: smart-route litellm_params: model: anthropic/claude-3-5-sonnet-20241022 api_key: os.environ/ANTHROPIC_API_KEY router_settings: routing_strategy: latency-based-routing这个配置定义了两个部署实例,都叫smart-route。当业务方请求smart-route时,Router 会根据延迟策略选择当前延迟较低的实例。如果某个实例调用失败,Router 会自动重试另一个实例。这种配置在生产环境中很实用,可以在不中断服务的情况下做模型切换和灰度发布。
4.5 成本追踪的实操
litellm 内置了成本计算功能,每次调用后可以通过response._hidden_params或者回调函数获取本次调用的成本。更常用的方式是在代理服务里开启成本追踪,然后在数据库里查看统计。
配置文件中开启成本追踪:
general_settings: master_key: sk-your-master-key database_url: "sqlite:///litellm.db" cost_tracking: true开启后,每次调用的 token 用量和费用都会记录到数据库。你可以通过代理的管理接口查询统计信息,也可以直接查数据库。对于需要做成本分摊的团队来说,这个功能可以省去自己维护价格表和计算逻辑的工作量。
提示:litellm 的成本计算依赖内置的价格表,如果某个模型的价格有变动,或者你用的是自定义价格的模型,需要手动更新价格配置。建议定期检查成本统计的准确性。
5. 常见问题与排查技巧实录
5.1 模型名称报错怎么排查
最常见的报错是litellm.exceptions.BadRequestError或者Invalid model name。遇到这种报错,按以下顺序排查:
第一步,确认模型名称格式是否正确。必须是provider/model_name的形式,provider 前缀要在 litellm 支持的列表里。你可以用litellm.model_list查看支持的模型列表。
第二步,确认该 provider 的 API Key 是否设置。如果环境变量没设置,litellm 会报鉴权错误。你可以用litellm.get_model_info("模型名称")来验证模型信息是否能正常获取。
第三步,如果是 Azure 或 Bedrock 这类需要额外配置的 provider,确认api_base、api_version、region等参数是否设置正确。这些 provider 的配置项比较多,容易遗漏。
5.2 超时和重试的配置
litellm 默认的超时时间可能不适合所有场景。你可以在调用时传timeout参数,也可以在全局设置里配置:
import litellm litellm.request_timeout = 60 # 全局超时60秒 litellm.num_retries = 3 # 全局重试3次对于代理服务模式,可以在配置文件里设置:
litellm_settings: request_timeout: 60 num_retries: 3 retry_after: 5这里有个经验:重试次数不是越多越好。如果某个模型服务本身不可用,重试太多次只会增加延迟。建议根据业务场景设置合理的重试次数,一般 2 到 3 次就够了。另外,对于流式请求,重试可能不会生效,需要在业务层自己做处理。
5.3 常见报错速查表
| 报错信息 | 可能原因 | 解决方法 |
|---|---|---|
| AuthenticationError | API Key 未设置或错误 | 检查环境变量或配置文件中的密钥 |
| BadRequestError | 模型名称格式错误或参数不合法 | 检查模型名称格式,确认参数在支持范围内 |
| RateLimitError | 触发厂商的速率限制 | 降低请求频率,或配置多个部署实例做负载均衡 |
| Timeout | 请求超时 | 增加 timeout 配置,或检查网络连接 |
| ContextWindowExceeded | 输入 token 超过模型上限 | 截断输入或换用上下文窗口更大的模型 |
| ContentPolicyViolation | 内容触发了厂商的安全策略 | 调整输入内容,或换用策略更宽松的模型 |
5.4 几个容易踩的坑
第一个坑是环境变量命名不一致。不同 provider 的环境变量名称不一样,有的用_API_KEY后缀,有的用_KEY后缀,有的还需要额外的_BASE和_VERSION。建议在项目里统一用.env文件管理,避免手动设置时遗漏。
第二个坑是代理服务的数据库迁移。litellm 代理服务用数据库存储配置和日志,版本升级时可能需要做数据库迁移。建议在升级前先备份数据库,并查看官方文档的升级说明。
第三个坑是流式模式下的 usage 统计。某些 provider 在流式模式下不返回 usage 信息,导致成本统计不准确。如果成本统计对你很重要,建议在流式请求结束后额外发一个非流式请求来获取 usage,或者使用 provider 提供的 usage 查询接口。
第四个坑是多模态输入的格式兼容。不同厂商对图片输入的格式要求不同,有的要求 URL,有的要求 base64,有的对图片格式和尺寸有限制。在跨厂商切换时,建议先用小规模请求验证多模态输入是否能正常工作。
5.5 性能优化的几个方向
如果你发现 litellm 的调用延迟比较高,可以从几个方向优化。
一是连接复用。litellm 底层用的是 HTTP 客户端,默认会复用连接。但如果你在代码里频繁创建新的客户端实例,连接复用可能不会生效。建议在应用启动时创建一个全局的客户端实例,后续调用都复用这个实例。
二是并发控制。litellm 支持异步调用,用acompletion()方法可以在异步框架里并发调用多个模型。但要注意控制并发数,避免触发厂商的速率限制。
三是缓存。litellm 支持缓存功能,可以把相同请求的响应缓存起来,避免重复调用。对于某些确定性较高的场景(比如 temperature 设为 0),缓存可以显著降低成本和延迟。配置方式是在调用时传caching=True,或者全局开启缓存。
import litellm litellm.cache = litellm.Cache(type="local") response = litellm.completion( model="openai/gpt-4o", messages=[{"role": "user", "content": "你好"}], caching=True )缓存虽然好用,但要注意缓存的失效策略。如果模型更新了或者你的 prompt 模板变了,缓存可能会导致返回旧结果。建议在关键场景下谨慎使用缓存,或者设置合理的缓存过期时间。
6. 扩展玩法与进阶思路
6.1 自定义 Provider 适配器
litellm 虽然支持很多 provider,但总有一些小众的或者自建的模型服务不在支持列表里。这种情况下,你可以自己写一个适配器。litellm 的适配器接口比较清晰,核心是实现请求转换和响应转换两个方法。
自定义适配器的基本步骤是:继承BaseLLM类,实现completion()方法,在里面处理请求转换和响应标准化。然后在litellm的 provider 注册表里注册你的适配器。这样你就可以用custom/your-model这样的名称来调用自建服务了。
这个功能对于有自建模型服务的团队来说很实用。你可以把自建服务和第三方模型统一到同一个接口下,业务方不需要区分调用的是哪个。
6.2 与可观测性平台的集成
litellm 支持多种回调机制,可以把调用日志推送到各种可观测性平台。常见的集成方式包括:推送到日志服务、推送到指标监控系统、推送到链路追踪系统。
配置方式是在litellm_settings里设置success_callback和failure_callback:
litellm_settings: success_callback: ["custom_callback"] failure_callback: ["custom_callback"]你也可以自己写回调函数,在函数里做自定义的日志处理。比如记录每次调用的模型名称、token 用量、延迟、成本等信息,然后推送到你的监控系统。这对于做模型性能对比和成本分析很有帮助。
6.3 多租户场景下的权限控制
如果你用 litellm 代理服务给多个团队或客户提供服务,权限控制是一个需要考虑的问题。litellm 支持基于 API Key 的权限控制,你可以为每个租户分配不同的 Key,并限制他们可以访问的模型和调用配额。
配置方式是在数据库里创建虚拟 Key,并设置models和max_budget等字段。这样每个租户只能访问被授权的模型,超出配额后会被拒绝。对于 SaaS 类产品来说,这个功能可以省去自己实现权限系统的工作量。
6.4 模型降级与故障转移
生产环境中最怕的就是某个模型服务突然不可用。litellm 的 Router 支持故障转移配置,你可以设置主模型和备用模型,主模型调用失败时自动切换到备用模型。
配置示例:
model_list: - model_name: primary litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY - model_name: fallback litellm_params: model: anthropic/claude-3-5-sonnet-20241022 api_key: os.environ/ANTHROPIC_API_KEY router_settings: fallbacks: [{"primary": ["fallback"]}]这个配置表示:当primary调用失败时,自动降级到fallback。对于可用性要求高的场景,这种配置可以显著降低服务中断的风险。不过要注意,不同模型的能力和输出风格可能不同,降级后的结果可能和主模型有差异,需要在业务层做兼容处理。
6.5 本地模型与云端模型的统一调用
litellm 也支持本地部署的模型,比如通过 Ollama 或者 vLLM 部署的模型。你只需要把 provider 前缀设为ollama或openai(vLLM 兼容 OpenAI 接口),然后指定api_base为本地服务地址即可。
这样一来,你就可以在同一个接口下同时调用本地模型和云端模型。对于某些对数据隐私要求高的场景,可以把敏感请求路由到本地模型,非敏感请求路由到云端模型。这种混合部署的方式在成本和合规之间找到了一个平衡点。
# 调用本地 Ollama 模型 response = litellm.completion( model="ollama/llama3", messages=[{"role": "user", "content": "你好"}], api_base="http://localhost:11434" ) # 调用云端模型 response = litellm.completion( model="openai/gpt-4o", messages=[{"role": "user", "content": "你好"}] )两种调用的代码结构完全一致,只是模型名称和api_base不同。这种统一性对于需要灵活切换部署方式的团队来说非常方便。
6.6 批量请求与异步处理
如果你的场景需要批量调用模型,比如对一批文档做摘要或者分类,litellm 的异步接口可以显著提升效率。用acompletion()配合asyncio.gather()可以并发处理多个请求。
import asyncio import litellm async def process_one(text): response = await litellm.acompletion( model="openai/gpt-4o-mini", messages=[{"role": "user", "content": f"请摘要:{text}"}] ) return response.choices[0].message.content async def process_batch(texts): tasks = [process_one(t) for t in texts] results = await asyncio.gather(*tasks) return results这种方式的效率比串行调用高很多,但要注意控制并发数。如果并发太高,可能会触发厂商的速率限制。建议用asyncio.Semaphore来控制并发数,或者用 litellm 内置的批量接口。
提示:批量请求时建议开启重试和错误处理,避免单个请求失败导致整个批次中断。litellm 的
acompletion()支持num_retries参数,可以在请求失败时自动重试。
7. 个人实操体会与建议
我在多个项目里用过 litellm,踩过一些坑,也总结了一些经验。最大的体会是:litellm 的价值不在于它支持多少模型,而在于它把多模型调用的复杂度收敛到了一个统一的接口层。在没有它之前,每接一个新模型都要写适配代码、改业务逻辑、重新测试。有了它之后,接入新模型的成本从“天”级别降到了“分钟”级别。
另一个体会是,代理服务模式虽然功能强大,但不要为了用而用。如果你的场景只是单应用调用几个模型,直接用库的方式就够了。代理服务引入了额外的部署复杂度和运维成本,只有在多团队共用、需要精细权限控制和成本审计的场景下才值得上。
还有一个建议是:在生产环境使用前,一定要做充分的测试。不同厂商的模型在参数支持、返回格式、错误码、速率限制等方面都有差异,litellm 虽然做了统一,但不可能覆盖所有边界情况。建议在切换模型或升级 litellm 版本前,先用小规模流量做验证,确认没有兼容性问题后再全量切换。
最后分享一个小技巧:如果你不确定某个模型是否支持某个参数,可以在调用时传drop_params=True,litellm 会自动忽略该模型不支持的参数,而不是报错。这个参数在跨模型切换时特别有用,可以避免因为参数不兼容导致的调用失败。
response = litellm.completion( model="anthropic/claude-3-5-sonnet-20241022", messages=[{"role": "user", "content": "你好"}], temperature=0.7, drop_params=True # 自动忽略不支持的参数 )这个参数虽然方便,但也要注意:忽略参数可能会导致模型行为和你预期的不一致。比如某个模型不支持temperature,忽略后它会用默认值,可能和你想要的效果有差异。所以建议在开发阶段先确认参数支持情况,生产环境再决定是否开启drop_params。