1. LiteLLM 是什么?一个把上百种模型统一起来的接入层
这两年做 AI 应用的人应该都有同一种感受:模型服务越来越多,各家 API 的调用方式却千奇百怪。有的用/v1/completions,有的用/v1/messages,鉴权方式、超时策略、流式返回格式、token 计费方式全都不一样。今天用 A 厂的模型写好的代码,明天想换成 B 厂,基本等于重写一遍对接层。LiteLLM 干的事情就是把这层脏活累活包下来,对外提供一个统一的调用入口,内部再去适配不同模型厂商的协议。
我第一次接触 LiteLLM 是在做一个内部工具的时候。当时项目里要同时接入好几种模型,有的负责写代码,有的负责总结文档,还有本地部署的开源模型要给客户做演示。一开始我老老实实按厂商文档写适配器,接了两家就发现不对头:每家都有自己的一套错误码、重试逻辑、超时配置,光是把这些差异抹平,代码量就已经超过了业务本身。后来在社区看到 LiteLLM,思路一下就通了——它相当于在业务代码和模型厂商之间插了一层网关,你只需要按一套 OpenAI 兼容的格式发请求,它会帮你把请求翻译成目标模型认识的样子,再把结果翻译回统一格式返回。
这个项目本质上是标准的网关模式。不管底层接的是云厂商,还是本地跑的开源模型,对上层业务来说都只是一个base_url加一个 API Key 的事。这样一来,业务代码可以做到完全不知道底层模型的差异,模型切换、灰度、降级都变成配置问题,而不是代码问题。
1.1 为什么需要这样一层“胶水”
你可以把不同模型厂商理解成不同国家的插座标准,形状不一样、电压不一样、接口规范也不一样。LiteLLM 就是那个万能转换头,让你无论飞到哪个国家,插上自己的设备就能用。对于个人开发者来说,这层转换省的是时间;对于团队来说,省的是整个基础设施的复杂度。
没有这层统一抽象时,常见的痛苦是这些:
- 同一段业务逻辑要写好几份适配代码,每接入一个新模型就要复制改一遍。
- 模型厂商升级 SDK 或调整接口,业务代码被动跟着升级。
- 错误处理不统一,有的厂商限流返回 429,有的返回 503,有的把限流信息藏在响应头里。
- 日志格式五花八门,排查一个问题要把不同服务的日志拼起来看。
- 想做一个模型选路和自动降级,只能靠硬编码,改一次路由要发布一次代码。
LiteLLM 把这些横切关注点收敛到一个模块里,业务侧只需要关心“发什么消息、拿什么回复”。后面接哪个模型、要不要切换、哪家便宜、哪家稳定,都不需要业务侧感知。
1.2 SDK 模式与代理模式,怎么选
LiteLLM 提供了两种用法,很多人第一次接触会混淆,这里说清楚。
SDK 模式是直接在 Python 代码里调用litellm.completion()之类的方法,把 LiteLLM 当成一个库来用。请求从你的进程发出,LiteLLM 在进程内部完成协议转换、重试、路由,然后返回结果。这种方式适合换模型、统一接口、简化代码的场景,不需要额外部署服务。
代理模式则是把 LiteLLM 跑成一个独立服务,暴露一个兼容 OpenAI 的 HTTP 接口。业务代码不直接感知 LiteLLM 的存在,它只需要把自己的请求发到一个地址上,剩下的由代理服务完成。适合团队共享模型入口、需要统一鉴权与计费、多业务复用同一批模型配置的场景。
| 对比维度 | SDK 模式 | 代理模式 |
|---|---|---|
| 部署复杂度 | 零额外部署,跟着业务进程走 | 需要起一个独立服务 |
| 接入成本 | 改代码,用 LiteLLM 的方法发起请求 | 改配置,业务侧只换 base_url |
| 适合场景 | 单应用、个人项目、快速验证 | 多应用、多团队、统一管控 |
| 扩展能力 | 依赖进程内配置 | 可以集中管理模型、密钥、预算、日志 |
| 故障隔离 | 模型超时会拖住业务进程资源 | 代理独立承载,业务侧更安全 |
我的建议是:如果只是自己写脚本、做小工具,用 SDK 模式就够了,少维护一个服务。如果团队里有多个项目要共用模型资源,或者需要给非技术同学提供可视化配置入口,直接上代理模式,省心得多。
1.3 核心能力速览
LiteLLM 能做的事远不止协议转换,这里列几个我实际用下来觉得最值钱的能力:
- 统一接口:所有模型都走 OpenAI 风格的 Chat Completions 格式,参数命名一致,返回结构一致。
- 协议适配:内部封装了各家厂商的鉴权、请求签名、响应解析逻辑,相当于把各家的 SDK 都替你包了一层。
- 失败重试:统一的重试策略、超时控制、指数退避,不用自己在业务代码里写循环。
- 模型路由:支持按策略分发到不同模型,也可以配置故障转移,主模型挂了自动切备用模型。
- 成本统计:按模型单价和 token 消耗估算每次调用的成本,代理模式还能把花费记录到数据库。
- 缓存:可以对相同请求做缓存,降低重复调用成本,缩短响应时间。
2. 五分钟跑通第一个补全调用
先给没接触过的朋友一条完整的上手路径。假设你的机器上已经有 Python 3.9 以上的环境,我们直接用 pip 装一个最小可用的 LiteLLM,然后发起第一次请求。
2.1 安装与环境准备
安装很简单,一条命令:
pip install litellm装完之后,在终端里确认一下版本:
python -c "import litellm; print(litellm.__version__)"然后准备 API Key。LiteLLM 约定通过环境变量加载密钥,这样密钥不会写死在代码里。你可以在终端 export,也可以写进.env文件用dotenv加载。我习惯在项目根目录放一个.env,格式大概是这样的:
OPENAI_API_KEY=sk-xxxx ANTHROPIC_API_KEY=sk-ant-xxxx GOOGLE_API_KEY=AIzaxxxxPython 这边加一行:
from dotenv import load_dotenv load_dotenv()这里有个容易被坑的地方:LiteLLM 默认会按照环境变量的名字去判断该用哪个厂商的密钥。比如请求openai/gpt-4o,它默认找OPENAI_API_KEY;请求anthropic/claude-3.5-sonnet,它默认找ANTHROPIC_API_KEY。如果变量名不对,请求会报环境变量未找到的错误。
2.2 最简单的 completion 请求
安装好之后,写一个最基础的脚本:
import litellm response = litellm.completion( model="openai/gpt-4o", messages=[ {"role": "user", "content": "用一句话介绍你自己"} ] ) print(response.choices[0].message.content)这个调用和 OpenAI SDK 的写法几乎一样,区别在于model参数变成了openai/gpt-4o这种带厂商前缀的格式。LiteLLM 看到openai/前缀,就知道要把请求转成 OpenAI 的协议,于是去读OPENAI_API_KEY,向对应的服务端点发起请求。
如果你想调用别家的模型,比如 Anthropic 的 Claude,只需要把 model 参数改成:
response = litellm.completion( model="anthropic/claude-3-5-sonnet-20241022", messages=[ {"role": "user", "content": "用一句话介绍你自己"} ] )其他代码完全不用动。这就体现出统一接口的价值了,业务逻辑不用关心底层模型是谁,只需要维护好 messages 的结构。
2.3 流式输出与对话补全的区别
很多模型厂商都支持流式输出,也就是响应内容分块推回来,而不是等全部生成完再一次返回。LiteLLM 把流式行为也统一了,开启方式非常简单:
response = litellm.completion( model="openai/gpt-4o", messages=[{"role": "user", "content": "写一段 500 字的短文"}], stream=True ) for chunk in response: delta = chunk.choices[0].delta.content if delta: print(delta, end="", flush=True)注意,流式返回的对象和一次性返回的对象结构不一样。一次性返回里,内容是放在response.choices[0].message.content;流式返回里,每一块的内容是放在chunk.choices[0].delta.content。新手第一次用流式,最容易在这个字段上写错。
还有一点:流式输出时,token 用量统计通常会放在最后一个 chunk 的usage字段里,如果你在大量使用流式接口,记得处理这个细节,否则成本统计会漏掉最后一段。
2.4 model 参数背后的 provider 解析逻辑
很多人第一次看model="openai/gpt-4o"会好奇,这个斜杠前面的名字是不是必须的。是必须的,但它的作用不是区分模型,而是区分厂商协议。
LiteLLM 内部有一个厂商列表,它通过斜杠前面的部分找到对应的 provider 处理器。如果你不写前缀直接传gpt-4o,LiteLLM 会尝试自动猜测厂商,但这种猜测不是百分百可靠。比如某些模型名在多家厂商都有同名型号,猜错就麻烦了。所以我建议任何时候都明确写上前缀,宁可长一点,也不要让路由逻辑靠猜。
市面上也有一些纯开源模型,跑在本地的 Ollama 或者兼容 OpenAI 协议的本地服务上,LiteLLM 同样支持。例如本地跑一个聊天模型,可以这样指定:
response = litellm.completion( model="ollama/llama3.1:8b", messages=[{"role": "user", "content": "你好"}], api_base="http://localhost:11434" )这种场景下,前缀变成了ollama/,同时通过api_base告诉它本地服务的地址。LiteLLM 强大的一个体现,就是它给本地模型和云端模型保留了同样的使用姿势,切换体验非常顺滑。
3. 代理模式:把多模型接入做成一个网关
SDK 模式解决的是“代码里别写死某一家模型”,代理模式解决的是“基础设施层面把模型入口统一收口”。如果你的团队有多套系统都要用大模型,让每套系统单独接 LiteLLM SDK 并不划算,因为模型配置、密钥管理、预算控制都是重复的。这时候把 LiteLLM 跑成一个网关,让所有业务都通过这个网关访问模型,会顺手很多。
3.1 为什么要起一个代理服务
代理模式最大的好处是业务侧演化成了零代码接入。业务系统只需要把 OpenAI SDK 的base_url指向 LiteLLM 代理地址,其他什么都不用改。对于已经用了 OpenAI SDK 的老项目,改动量常常只是一个环境变量。
举个例子,原先业务代码里可能是这样调 OpenAI 的:
from openai import OpenAI client = OpenAI(api_key="sk-xxx") resp = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "你好"}] )接入 LiteLLM 代理后,只需要改成:
from openai import OpenAI client = OpenAI( api_key="sk-any-key", base_url="http://your-gateway-host:8000" ) resp = client.chat.completions.create( model="gpt-4o", # 这个值由网关解析 messages=[{"role": "user", "content": "你好"}] )你甚至可以把api_key写成一个固定的虚拟密钥,真正的厂商密钥只存在网关服务端,业务侧永远接触不到。这对安全管理来说是个巨大的加分项。
代理模式还能顺带解决一个现实问题:业务团队每个人都去厂商官网申请密钥,充值的额度分散在好多个人头上,账单管理很痛苦。统一收口之后,密钥统一由运维或平台负责,成本也统一核算,避免资源浪费。
3.2 一份最小可用的代理配置
代理模式需要一个 YAML 配置文件,用于声明你能使用哪些模型、模型的真实名字是什么、密钥从哪来。先看一个非常精简的示例:
model_list: - model_name: gpt-4o litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY - model_name: claude-3-5 litellm_params: model: anthropic/claude-3-5-sonnet-20241022 api_key: os.environ/ANTHROPIC_API_KEY - model_name: local-llama litellm_params: model: ollama/llama3.1:8b api_base: http://localhost:11434这里面的逻辑是:model_name是业务侧看到的别名,litellm_params.model是 LiteLLM 内部真正要访问的模型标识。业务调用的时候传gpt-4o,网关收到后把它映射成openai/gpt-4o再发起请求。这种别名的设计很有用,比如你想要在某个时间窗口把业务从 GPT-4 平滑切到另一个模型,只要在网关配置里把gpt-4o这个别名的 target 改掉,业务代码一行不用动。
启动代理服务:
litellm --config config.yaml --port 8000看到监听日志之后,就可以用 OpenAI SDK 或 curl 来测试了:
curl http://localhost:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer sk-any-key" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "你好"}] }'3.3 业务代码接入代理的方式
代理暴露的接口是 OpenAI 兼容的,所以几乎所有语言的 OpenAI SDK 都能直接连上来。Python 侧的 OpenAI SDK 设置方式上面已经演示过,其他语言也大同小异,无非是设置base_url和api_key。
如果业务代码原来是直接调 HTTP 接口的,也没问题。LiteLLM 代理对外暴露的路径基本能对齐 OpenAI 的路径结构,比如/v1/chat/completions、/v1/embeddings、/v1/models这些都会提供。你可以先用/v1/models看一下网关注册了哪些模型,确认配置生效了。
3.4 鉴权与多用户隔离
代理默认为了内网调试方便,可能会让请求直接绕过鉴权。但一旦多人使用,就不能裸奔了。LiteLLM 代理支持配置 master key,也就是管理用的密钥,同时可以为不同团队或应用生成虚拟密钥。
虚拟密钥的好处是可以控制某个密钥能用哪些模型、每分钟最多请求多少次、总共能花多少钱。这相当于在网关层面做了一层细粒度的访问控制。比如 A 团队只能用通用模型,B 团队可以用更强的模型但不能超过月预算,都能通过密钥策略表达出来。
我个人建议,即使是团队内部使用,也至少配上 master key,不要开放无鉴权模式。否则一旦网关地址被外部扫描到,你就等于把花钱的入口免费送给了别人。
4. 进阶玩法:路由、重试、预算与可观测性
跑通基础调用之后,LiteLLM 真正拉开差距的地方在于那些生产环境才用得上的能力。下面按路由、重试、成本、可观测性四个维度分别展开。
4.1 路由策略:让流量自动找到最合适的模型
代理模式下,业务侧只需要传入一个抽象的模型名。至于这个请求到底发给哪个厂商的哪个模型,可以由网关动态决定。LiteLLM 支持多种路由策略,最常见的几种:
- 优先级路由:根据
model_list中配置的顺序选模型,前面的优先,挂了才用后面的。 - 加权随机路由:给每个模型配一个权重,按权重比例分配流量。
- 最少请求路由:选当前并发请求数最少的模型,适合负载均衡。
- 延迟优先路由:根据历史平均响应延迟选最快的一个。
路由配置放在litellm_settings里。举个例子:
litellm_settings: routing_strategy: "latency-based-routing" model_list: - model_name: chat-model litellm_params: model: openai/gpt-4o model_info: priority: 1 - model_name: chat-model litellm_params: model: anthropic/claude-3-5-sonnet-20241022 model_info: priority: 2这个配置的意思是:业务侧调用chat-model,网关会优先发往优先级为 1 的 GPT-4o;如果 GPT-4o 不可用或者超时,再切换到优先级为 2 的 Claude。对于不想让业务感知具体模型变化的场景,这个机制非常实用。
4.2 重试、超时与故障转移的配置权衡
生产环境里,模型厂商的接口大概率不会一直稳定。LiteLLM 把重试和超时也集中管理了。最常用的几个参数是timeout、num_retries和max_retries。
import litellm litellm.timeout = 30 litellm.num_retries = 3这里的逻辑是:单个请求等待上限 30 秒,失败后最多自动重试 3 次。LiteLLM 内部对重试做了随机抖动和指数退避,不是傻傻地立刻重发,因此可以在一定程度上降低触发厂商限流的概率。
配置重试时有一个权衡:重试次数设太多,用户侧感知的响应时间会被拖长,请求队列也会越积越多;设太少,偶发的抖动直接变成失败返回,用户体验不好。我一般先将超时控制在 20 到 30 秒,重试控制在 2 到 3 次,再根据线上监控调整。
故障转移和重试不太一样,它指的是主模型连续失败后,把请求转给备用模型。上面说的 fallback 配置就是干这个的。举个例子:
model_list: - model_name: chat-model litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY fallbacks: ["anthropic/claude-3-5-sonnet-20241022"]当openai/gpt-4o超时或返回错误时,代理自动转给 Claude。备用模型也需要在配置里准备好。这种机制比业务侧自己写 try-except 去切换要干净得多,因为重试和切换状态都被代理收拢了,业务侧拿到的是一个正常成功响应。
4.3 成本追踪与实际落地的预算控制
大模型应用上线之后,成本往往是最让人焦虑的问题之一。LiteLLM 内置了价格表,可以根据模型、输入 token 和输出 token 估算每次调用的成本。SDK 模式下,响应对象里一般可以拿到 usage 信息和 Cost 相关信息;代理模式下,花费数据可以写入数据库,方便后续对账。
一个比较落地的做法是在代理配置中启用预算控制:
general_settings: master_key: sk-master-xxx database_url: postgresql://user:pass@localhost:5432/litellm启动后,代理会把每次调用的模型、token 数、估算成本写入数据库。配合虚拟密钥层面的限额,可以做到某个团队或某个应用的钱花到一定数额之后自动拒绝请求。这种预算护栏在多人共用网关时极其重要,否则月底账单会让人措手不及。
4.4 留痕:日志、缓存与追踪注意点
日志是排查问题的基本手段。代理模式下,LiteLLM 会把请求和响应的核心信息记录下来,包括模型、耗时、token 数、错误信息。开启数据库后,这些记录会自动持久化。团队内部可以基于这些日志做告警和统计,比如某个模型连续报错就通知运维。
缓存方面,如果同一段提示词反复请求,相同参数和相同内容的结果可以缓存一段时间,减少重复调用和费用。配置 Redis 之类的缓存服务后,可以在代理配置中声明缓存策略。需要注意的是,缓存命中后你拿到的可能是历史结果,如果业务对实时性敏感,比如想通过提示词动态调节输出风格,就必须谨慎开启缓存。
延迟监控可以通过_response_ms这类字段获取单次请求的毫秒耗时。我习惯把请求耗时、token 数、成本三个指标一起塞进日志,这样排查慢请求和费用异常时可以快速定位。
5. 我踩过的坑:常见报错与排查记录
LiteLLM 用起来不算难,但生产环境里细节很多。下面把我在实际项目里遇到的问题整理成表格和清单,希望你能躲开我走过的弯路。
5.1 高频报错对照表
| 报错或现象 | 典型原因 | 处理办法 |
|---|---|---|
AuthenticationError/ 401 | 密钥不存在、变量名不对、key 被吊销 | 检查环境变量名与 model 前缀是否匹配 |
RateLimitError/ 429 | 超出厂商调用配额 | 提高重试间隔,降低并发,或启用备用模型 |
TimeoutError | 模型响应太慢,超时阈值太短 | 调大timeout,检查模型服务是否过载 |
NotFoundError/ 404 | model 参数中的模型名不存在 | 核对厂商官方模型名,确认前缀正确 |
| 环境变量相关的报错 | .env没加载或命名拼写错误 | 先用print(os.environ)确认加载成功 |
| 流式输出取不到内容 | 用了非流式的字段读流式结果 | 检查delta.content字段与stream=True是否匹配 |
| 代理模式下业务报“模型不存在” | model_list里没配置这个别名 | 检查 YAML 配置,确认model_name正确 |
| 本地模型请求失败 | api_base没设置或本地服务没启动 | 确认本地服务端口可达,地址是否正确 |
5.2 部署性能调优的几条经验
网关本身是一个 Python 服务,并发能力取决于你启动了多少 worker。默认单进程跑,可能承载不了太高的 QPS。生产环境建议使用多 worker 启动,比如用 gunicorn 或者 uvicorn 的多 worker 模式。
数据库存储会带来额外写入开销,如果请求量很大,建议把日志写入做成异步的,不要让它阻塞核心请求路径。我在一个项目里曾经因为日志写入太慢,导致整体响应时间翻倍,后来改成异步批量写入才算解决。
缓存命中率对成本和延迟影响很大,但缓存设置也不能一概而论。对于相同提示词的高频调用,比如内容审核规则固定、关键词抽取模板固定,缓存效果很好。对于发散性强的对话类请求,缓存命中率很低,配置了反而增加复杂度。
5.3 工程化接入时的避坑清单
这部分是综合多个项目经验整理出来的,优先级从上到下:
- 模型名称一律写完整前缀,不要靠自动推断。
- 密钥不要留在代码仓库里,统一走环境变量或密钥管理服务。
- 代理模式必须配置鉴权,哪怕是内网环境。
- 重试策略、超时时间在 LiteLLM 层统一设置,不要在每段业务代码里重复写。
- 大并发场景下,给代理前面的负载均衡预留健康检查接口。
- 定时任务类流量要设置合理的并发限制,避免瞬时打爆厂商配额。
- 成本记录不要依赖最终账单,在代理层先做一版预估成本。
- 模型版本升级后,在代理配置里确认模型名没有过期,厂商经常悄悄废弃旧版本。
6. 用了一段时间之后的真实感受
把 LiteLLM 引入实际项目之前,我一直是手动维护模型适配层,每天都跟各家厂商的文档打交道。换成 LiteLLM 之后,最直观的感受是:写业务代码的时候不需要再想“这个模型是哪个厂商的”,模型切换、故障转移、成本统计都变成了配置和运维问题。尤其是代理模式,团队里多个应用共用同一个网关,密钥管理、预算控制、日志审计都集中了,省下的不只是代码量,还有跨团队协调的成本。
如果说有什么需要适应的地方,那就是它的配置项很多,一开始容易陷入文档里出不来。我的建议是从最小配置起步,先把一个模型的调用跑通,再慢慢加路由、重试、预算、缓存。避免一上来就追求大而全,配置复杂了排错反而困难。如果你正在为多个模型服务的差异化接口发愁,或者想给团队搭一个统一的大模型入口,LiteLLM 值得花一个下午试试。