1. 项目概述与核心定位
litellm 这个项目,第一次接触是在一个多模型混合调用的需求场景里。当时团队需要在一个业务系统里同时对接多家大模型服务商的接口,每家的 SDK 参数格式、返回结构、鉴权方式都不一样,代码里到处是 if-else 分支,维护成本极高。litellm 就是在这个背景下进入视野的——它本质上是一个统一大模型调用层,用一套 OpenAI 兼容的接口格式去调用不同厂商的模型服务。
说白了,它做的事情就是:你按照 OpenAI 的请求格式写代码,litellm 在中间做协议转换,把请求转发到目标模型服务商,再把返回结果统一成 OpenAI 格式吐回来。对上层业务代码来说,切换模型只需要改一个模型名称字符串,不需要动任何业务逻辑。
这个项目解决的核心问题有三个。第一是接口碎片化,不同厂商的 API 参数命名、消息格式、流式返回结构都有差异,litellm 做了标准化适配。第二是迁移成本,业务代码一旦绑定了某家 SDK,换模型就要重写调用层,litellm 让这件事变成改配置。第三是多模型编排,比如 fallback、负载均衡、成本追踪这些需求,litellm 提供了开箱即用的支持。
适合谁来用?如果你正在做 AI 应用开发,需要对接多个模型服务商,或者你希望业务代码和具体模型解耦,再或者你需要做模型调用的成本监控和路由策略,litellm 值得花时间研究。哪怕你只用一家模型服务,用它来做统一的调用封装和日志追踪也是划算的。
2. 整体架构设计与选型考量
2.1 为什么选择 OpenAI 格式作为统一标准
litellm 最核心的设计决策是以 OpenAI 的 API 格式作为统一接口标准。这个选择不是随便拍的,背后有几个很实际的考量。
OpenAI 的 Chat Completions 接口格式在事实上已经成为行业通用语言。绝大多数模型服务商在提供 API 时,都会主动兼容 OpenAI 的请求格式,因为这样能降低用户的迁移成本。这意味着 litellm 的适配层工作量会小很多——很多服务商只需要改 base_url 和 api_key 就能直接对接,不需要做复杂的协议转换。
从用户角度看,开发者对 OpenAI 的 SDK 最熟悉,messages数组、role字段、stream参数这些概念已经深入人心。litellm 沿用这套格式,学习成本几乎为零。你原来怎么调 OpenAI,现在就怎么调 litellm,只是把openai.chat.completions.create换成litellm.completion。
还有一个隐性好处是生态兼容性。大量现有工具链、框架、中间件都是围绕 OpenAI 格式构建的,litellm 采用同样的格式,就能直接接入这些生态,不需要额外的适配层。
2.2 代理模式与 SDK 模式的双轨设计
litellm 提供了两种使用方式,这个设计很值得说道。
SDK 模式是直接在 Python 代码里 import litellm,然后调用completion()函数。这种方式适合快速集成,代码侵入性小,适合脚本、Notebook、小型服务。它的优势是轻量,不需要额外部署服务,pip install 之后就能用。
代理模式是启动一个 litellm 的 proxy 服务,对外暴露 OpenAI 兼容的 HTTP 接口。你的业务代码通过 HTTP 请求访问这个代理,代理再转发到实际的模型服务。这种方式适合团队协作场景——多个项目、多种语言的服务都可以通过统一的 HTTP 接口调用模型,配置集中管理,密钥不落地到各个业务方。
两种模式的选择逻辑很清晰:个人开发、快速验证用 SDK 模式;团队协作、多语言环境、需要集中管控用代理模式。代理模式还额外提供了管理后台、用量统计、密钥分发等能力,这些在 SDK 模式下是没有的。
2.3 适配层的工作原理
litellm 的适配层做的事情可以拆解成几个步骤。当你调用completion(model="某模型", messages=[...])时,litellm 首先解析 model 字符串,识别出目标服务商。model 字符串的格式通常是provider/model-name,比如openai/gpt-4、anthropic/claude-3这种形式。
识别出服务商之后,litellm 会加载对应的适配器。每个适配器负责三件事:把统一的 OpenAI 格式请求转换成目标服务商的原生格式,调用目标服务商的 API,再把返回结果转换回 OpenAI 格式。这个转换过程包括参数映射(比如max_tokens在不同厂商可能叫不同名字)、消息格式转换(比如 system message 在某些厂商需要特殊处理)、流式响应的分块重组等。
适配器的实现方式有两种:一种是直接 HTTP 调用,litellm 自己封装了 HTTP 客户端;另一种是调用厂商的官方 SDK。具体用哪种取决于厂商 SDK 的成熟度和 litellm 的适配策略。对于 OpenAI 兼容的服务商,适配器可能只是改一下 base_url 和鉴权头。
3. 核心功能细节与实操要点
3.1 基础调用:从单模型到多模型切换
最基础的用法就是替换 import。原来你写:
from openai import OpenAI client = OpenAI(api_key="your-key") response = client.chat.completions.create( model="gpt-4", messages=[{"role": "user", "content": "你好"}] )用 litellm 之后变成:
import litellm response = litellm.completion( model="openai/gpt-4", messages=[{"role": "user", "content": "你好"}] )看起来只是换了个函数名,但关键在于model参数。你可以把它改成anthropic/claude-3-sonnet、gemini/gemini-pro、deepseek/deepseek-chat等等,业务代码的其他部分完全不用动。litellm 会根据 model 字符串自动路由到对应的服务商。
这里有个实操细节:不同服务商的 API Key 设置方式。litellm 支持通过环境变量读取,比如OPENAI_API_KEY、ANTHROPIC_API_KEY这些。你也可以在调用时显式传入api_key参数。如果同时用多家服务,建议用环境变量管理,避免密钥硬编码在代码里。
注意:model 字符串的前缀必须和服务商匹配,写错了 litellm 会报错找不到对应的适配器。建议在项目里维护一个模型名称常量表,避免手写出错。
3.2 流式输出与异步调用
流式输出在对话类应用里几乎是标配。litellm 的流式调用和 OpenAI 的用法一致,设置stream=True即可:
response = litellm.completion( model="openai/gpt-4", messages=[{"role": "user", "content": "写一段介绍"}], stream=True ) for chunk in response: content = chunk.choices[0].delta.content if content: print(content, end="")异步调用用acompletion(),配合async for使用。在高并发场景下,异步调用的吞吐量比同步调用高一个数量级。我实测过一个场景,同样的硬件条件下,异步方式处理 100 个并发请求的耗时大约是同步方式的十分之一。
流式输出有个容易踩的坑:不同服务商的流式返回格式在细节上有差异,比如有的会在最后一个 chunk 里带 usage 信息,有的不带。litellm 做了统一处理,但如果你需要精确的 token 统计,建议在流式结束后单独做一次计数,或者开启 litellm 的 usage 追踪功能。
3.3 重试、降级与负载均衡
这是 litellm 比较有特色的能力。通过fallbacks参数可以配置降级链:
response = litellm.completion( model="openai/gpt-4", messages=[{"role": "user", "content": "你好"}], fallbacks=["anthropic/claude-3-sonnet", "gemini/gemini-pro"] )当主模型调用失败时,litellm 会自动按顺序尝试降级模型。这个机制在生产环境里非常实用——某家服务商偶尔抽风的时候,业务不会直接挂掉。
重试策略通过num_retries参数控制,默认是 0。建议至少设置 2 次重试,配合指数退避。litellm 内部实现了退避逻辑,你只需要设置重试次数。
负载均衡在代理模式下支持得更好。你可以在配置文件里为同一个模型名配置多个部署(比如多个 API Key 或多个区域端点),litellm 会按策略分发请求。支持的策略包括简单轮询、加权分配、基于延迟的路由等。
| 功能 | SDK 模式 | 代理模式 |
|---|---|---|
| 基础调用 | 支持 | 支持 |
| 流式输出 | 支持 | 支持 |
| 降级链 | 支持 | 支持 |
| 负载均衡 | 有限支持 | 完整支持 |
| 用量统计 | 需自行实现 | 内置 |
| 管理后台 | 无 | 有 |
3.4 成本追踪与预算控制
litellm 内置了成本计算功能。每次调用之后,可以通过response._hidden_params或者回调机制获取本次调用的 token 数和预估费用。它维护了一个模型价格表,覆盖主流服务商的常见模型。
代理模式下,可以设置每个 API Key 的预算上限,超出后自动拒绝请求。这个功能在多团队共用一套模型服务时特别有用,能防止某个团队意外刷爆账单。
实操建议:成本追踪的准确性依赖于价格表的更新。模型价格会变动,新模型会不断出现,建议定期检查 litellm 的版本更新,或者自己维护一份价格覆盖配置。我遇到过价格表滞后导致统计偏差的情况,后来在配置里手动覆盖了几个常用模型的价格,数据就准了。
4. 代理模式部署与配置实战
4.1 最小化部署流程
代理模式的部署比想象中简单。安装之后,准备一个配置文件:
model_list: - model_name: gpt-4 litellm_params: model: openai/gpt-4 api_key: os.environ/OPENAI_API_KEY - model_name: claude-3 litellm_params: model: anthropic/claude-3-sonnet api_key: os.environ/ANTHROPIC_API_KEY然后启动服务:
litellm --config config.yaml --port 4000服务启动后,你就有了一个运行在 4000 端口的 OpenAI 兼容接口。任何支持 OpenAI 格式的客户端都可以直接连过来,把 base_url 指向http://localhost:4000即可。
配置文件里的model_name是对外暴露的模型名,litellm_params.model是实际的目标模型。这种映射关系让你可以自定义对外名称,比如把gpt-4映射到某个具体的部署,业务方不需要知道底层用的是哪家服务。
4.2 多实例与高可用配置
生产环境不能单实例部署。litellm 代理本身是无状态的,可以水平扩展多个实例,前面挂负载均衡。配置数据可以通过数据库共享,litellm 支持 PostgreSQL 作为后端存储,把配置、密钥、用量数据都持久化。
数据库配置在 yaml 里加上:
database_url: postgresql://user:pass@host:5432/litellm启用数据库之后,管理后台的密钥管理、用量统计、预算控制这些功能才能完整使用。不用数据库的话,这些数据只存在内存里,重启就丢了。
高可用还有一个层面是模型服务的高可用。通过为同一个model_name配置多个litellm_params条目,实现多端点冗余:
- model_name: gpt-4 litellm_params: model: openai/gpt-4 api_key: os.environ/OPENAI_API_KEY_1 - model_name: gpt-4 litellm_params: model: openai/gpt-4 api_key: os.environ/OPENAI_API_KEY_2这样配置之后,对gpt-4的请求会在两个端点之间分发,单个 Key 触发限流时另一个还能顶上。
4.3 密钥管理与访问控制
代理模式支持生成虚拟密钥。业务方不直接持有上游服务商的真实密钥,而是使用 litellm 生成的虚拟 Key。虚拟 Key 可以设置预算、有效期、可访问的模型列表、速率限制等。
创建虚拟 Key 通过管理接口:
curl -X POST http://localhost:4000/key/generate \ -H "Authorization: Bearer <master-key>" \ -H "Content-Type: application/json" \ -d '{"models": ["gpt-4"], "max_budget": 100, "duration": "30d"}'这个机制解决了密钥分发的安全问题。真实密钥只存在于 litellm 服务端,业务方拿到的是受限的虚拟 Key,即使泄露,影响范围也可控。
注意:master key 是管理接口的凭证,权限最高,务必通过环境变量设置,不要写在配置文件里。生产环境建议把管理接口限制在内网访问。
4.4 日志与可观测性
litellm 支持多种回调机制,可以把调用日志推送到外部系统。常见的集成包括 Langfuse、Helicone、自定义 Webhook 等。配置方式是在 yaml 里加litellm_settings:
litellm_settings: success_callback: ["langfuse"] failure_callback: ["langfuse"]回调数据包含请求内容、响应内容、token 数、延迟、模型名称等。这些数据对于排查问题、分析成本、优化调用策略都很有价值。
如果不想接入第三方,也可以配置自定义回调函数,把日志写到自己的数据库或日志系统。litellm 提供了CustomLogger基类,继承之后实现log_success_event和log_failure_event方法即可。
5. 常见问题与排查技巧实录
5.1 模型名称解析失败
这是新手最常遇到的问题。报错信息通常是找不到对应的 provider 或者 model。排查思路:先确认 model 字符串的格式是否正确,前缀是否在 litellm 支持的 provider 列表里。然后确认对应的环境变量是否设置,比如ANTHROPIC_API_KEY没设置的话,调用 anthropic 的模型就会失败。
还有一种情况是模型名称拼写错误。不同服务商的模型命名规则不一样,有的用连字符,有的用点号,有的带版本号。建议直接查阅 litellm 的文档或者源码里的模型列表,不要凭记忆写。
5.2 流式输出中断或乱序
流式输出在网络不稳定的时候容易出现中断。litellm 本身有重试机制,但流式请求的重试比较特殊——已经输出的部分不能撤回。建议在业务层做处理:捕获异常后,把已输出的内容保留,从断点处重新发起请求。
乱序问题一般出现在异步并发场景。如果多个流式请求共用一个输出通道,需要自己做序列化处理。litellm 的每个流式响应是独立的迭代器,只要不混用就不会乱序。
5.3 成本统计与实际账单不符
前面提到过价格表滞后的问题。除此之外,还有几个原因:一是缓存命中,litellm 支持响应缓存,缓存命中的请求不会产生实际费用,但统计时可能会计入;二是流式请求的 token 计数方式,有些服务商在流式模式下不返回精确的 usage,litellm 只能用估算值;三是不同服务商的 token 计算规则不同,同样的文本在不同模型下 token 数可能不一样。
排查方法:先用非流式请求做对比测试,确认基础统计是否准确。然后检查是否有缓存配置。最后对照服务商后台的实际账单,找出偏差最大的模型,手动校准价格配置。
5.4 代理模式下的超时问题
代理模式多了一层转发,超时设置需要特别注意。litellm 代理本身有超时配置,上游服务商也有超时限制,业务客户端还有自己的超时。三层超时需要合理配置,原则是:客户端超时 > 代理超时 > 上游超时。
如果代理超时设置得太短,长文本生成任务会被截断。如果设置得太长,上游已经挂了但代理还在等,浪费连接资源。我的经验值是:代理超时设置为上游超时的 1.5 倍,客户端超时设置为代理超时的 1.5 倍。
| 问题现象 | 可能原因 | 排查方向 |
|---|---|---|
| 报错找不到 provider | model 前缀错误 | 检查 model 字符串格式 |
| 鉴权失败 | 环境变量未设置 | 确认 API Key 环境变量 |
| 流式中断 | 网络不稳定 | 检查网络,增加重试 |
| 成本统计偏差大 | 价格表滞后 | 手动校准价格配置 |
| 请求超时 | 超时配置不合理 | 调整三层超时比例 |
| 并发上不去 | 同步调用阻塞 | 改用异步调用 |
5.5 几个容易忽略的实操细节
第一个细节是消息格式的兼容性。OpenAI 格式的 messages 数组里,content 可以是字符串,也可以是数组(多模态场景)。但有些服务商不支持数组格式的 content,litellm 会尝试转换,但转换可能丢失信息。如果用到多模态输入,建议先确认目标模型是否支持。
第二个细节是system message 的处理。OpenAI 的 system message 是独立的 role,但有些服务商把 system prompt 作为单独的参数传递。litellm 做了适配,但行为可能因服务商而异。如果发现 system message 没生效,检查一下目标服务商的文档。
第三个细节是参数透传。litellm 支持通过extra_body或者**kwargs传递服务商特有的参数。比如某些服务商支持的top_k、repetition_penalty这些非 OpenAI 标准参数,可以通过这种方式传递。但要注意,不同服务商对同名参数的解释可能不同,跨模型切换时这些参数的行为可能不一致。
6. 进阶用法与扩展思路
6.1 自定义适配器
litellm 支持自定义 provider。如果你的模型服务不在内置列表里,可以继承CustomLLM类实现自己的适配器。需要实现的方法包括completion、acompletion、streaming等。实现完成之后,通过litellm.custom_provider_map注册进去。
自定义适配器的价值在于,你可以把内部自建的模型服务也纳入 litellm 的统一管理。比如团队自己部署了一个推理服务,只要实现一个适配器,就能和外部服务商用同样的方式调用,享受 litellm 的路由、降级、统计等能力。
6.2 与现有框架的集成
litellm 可以作为很多框架的底层模型调用层。比如 LangChain 有 litellm 的集成包,用 litellm 作为 LLM 后端,就能在 LangChain 里自由切换模型。类似的还有 LlamaIndex、CrewAI 等框架。
集成方式通常是替换框架里的 LLM 类。以 LangChain 为例,用ChatLiteLLM替代ChatOpenAI,然后 model 参数写 litellm 的格式即可。这样框架层面的代码不用改,模型切换在 litellm 层完成。
6.3 路由策略的定制
代理模式下的路由策略支持自定义。除了内置的轮询、加权、延迟路由,你还可以实现自己的路由逻辑。比如根据请求内容路由到不同模型——简单问题用便宜的小模型,复杂问题用贵的大模型。
这个能力通过Router类的扩展实现。litellm 的路由层是开放的,可以注入自定义的路由函数。对于成本敏感的场景,这种精细化路由能显著降低费用。
6.4 缓存策略
litellm 支持响应缓存,可以配置内存缓存、Redis 缓存等。缓存命中的请求直接返回结果,不调用上游模型,既省钱又快。
缓存的 key 默认是请求内容的哈希。对于相同的请求,第二次调用就能命中缓存。但要注意,缓存会掩盖模型的实时性——如果模型更新了,缓存里的旧结果还会返回。建议设置合理的 TTL,或者在模型更新后主动清理缓存。
提示:缓存对调试不友好。开发阶段建议关闭缓存,避免改了 prompt 但返回的还是旧结果,白白浪费时间排查。
7. 个人实操体会与建议
用了 litellm 一段时间之后,有几个体会比较深。
第一,不要过度设计。刚开始用的时候,我试图把所有能配的功能都配上——降级链、负载均衡、缓存、回调,结果配置文件复杂到没人看得懂。后来精简到只保留必要的降级和统计,维护成本一下子降下来了。litellm 的能力很多,但按需使用才是正道。
第二,版本升级要谨慎。litellm 迭代很快,新版本可能引入不兼容的变更。生产环境升级前,先在测试环境跑一遍核心调用流程,确认没问题再上。我遇到过一次升级后某个适配器的参数映射变了,导致请求报错,排查了半天。
第三,成本统计要交叉验证。不要完全依赖 litellm 的统计数字,定期和服务商后台的账单对一下。偏差大的时候,先检查价格配置,再检查是否有缓存或重试导致的重复计数。
第四,代理模式的管理接口要保护好。master key 泄露等于所有模型密钥泄露。生产环境一定要限制管理接口的访问来源,定期轮换 master key。
第五,日志要留够。出问题的时候,详细的调用日志是排查的关键。建议至少保留请求的模型名称、token 数、延迟、错误信息这几个字段。请求内容是否记录看合规要求,如果记录,注意脱敏。
这个项目后续还可以这样扩展:把 litellm 的调用日志接入自己的监控告警系统,当某个模型的错误率或延迟超过阈值时自动告警;或者基于用量数据做一个内部的成本分摊看板,让各团队看到自己的模型消耗情况。这些都是在 litellm 提供的基础能力之上,结合自己业务需求做的二次开发,投入不大但效果明显。