1. 为什么你的项目也需要一个"LLM中间层"
第一次知道 litellm 这个项目,是在处理一个多模型接入的调度需求时。当时手头要同时对接五六个不同厂商的大模型接口,每个厂商的 SDK 风格都不一样,有的走纯 REST,有的封装了 Python 客户端,有的对话消息格式五花八门。业务代码里塞了一堆 if-else,每接一个新模型就要额外写几百行适配代码,测试排期一次比一次长。后来一个朋友把 litellm 丢给我,说"你先把这玩意儿试跑一下",我才算真正入了门。
这个项目本质上做的事情特别简单:把市面上主流的模型厂商接口全部包一层,统一成同一种调用格式。你只需要会一套 API,就能在几十个模型之间来回切换,而且切换成本低到令人怀疑人生——改一个字符串的事。它既能作为 Python SDK 直接内嵌到你的项目里,也能单独起一个代理服务,给整个团队当网关用。对于正在做 LLM 应用开发、想避免被单一模型厂商绑死、又被多供应商接入折腾得头疼的开发者来说,litellm 基本就是你想要的"中间层"。
我见过很多团队,一开始只用一个模型,代码写得很爽,等产品跑起来想换更便宜的模型或者加个备用模型时,才发现当初的"爽"全变成了债。老实说,这类问题的根源不在模型本身,而在你的业务代码和模型 API 之间缺一个统一的抽象层。litellm 能帮你把这个抽象层补上,而且补得相当稳。
1.1 各家 SDK 差异到底有多痛
很多人没同时接过两个以上模型,体会不到这种痛。拿最简单的对话补全接口来说,厂商 A 的请求体长这样,厂商 B 的长那样,厂商 C 的可能连响应结构都和你以为的不一样。有的用system字段,有的叫instructions,有的干脆让你拼在第一条消息里。参数名也是千奇百怪:temperature、top_p这些都是小事,真正麻烦的是max_tokens、stop_sequences、tools这类字段,各家定义不完全一样,你写业务代码时就得疯狂做兼容。
如果只是几个字段的差异还好说,最怕的是流式响应的数据格式完全不同。一家给你发data: {...}的 Server-Sent Events,另一家发的是 JSON 数组,还有一家是 websocket。你在流式场景下要想做一套通用代码,得额外写一层适配器,把各家流式输出统一成自己的事件结构。我见过有团队这套适配器写了上千行,最后模型一更新,某些字段废弃了,又要改一轮。
litellm 解决这个问题的方式很直接:它把这些差异全部吞掉,对外暴露一个 OpenAI 兼容的请求响应结构。你传messages数组,它负责转换成各家模型需要的格式,返回的时候再转换成统一结构。这样一来,你的业务代码只需要面对一种形态的 API,厂商的差异被隔离在中间层内部。
1.2 模型切换是刚需,不是炫技
你可能觉得,一个模型用得好好的,为什么非要切换?真实场景里,切换模型的原因太多了。
成本是最大的驱动力。同一个任务,用头部大模型和用开源精调小模型,价格能差一个数量级。很多应用的压力测试跑下来,发现 80% 的请求根本不需要最强模型,用便宜模型就够了,于是团队决定做模型路由:简单问题走便宜模型,复杂问题才走强模型。这种路由策略,本质上就是代码里经常要切换模型。
稳定性和容灾也是。模型供应商的服务不是永远可用,特别是某些时段请求量大容易触发限流。如果核心链路只依赖一家,一旦对方出问题,你的产品就挂了。很多团队都开始配"主模型 + 备用模型",主模型失败时自动切换到备用。这种能力靠自己在业务代码里写,工作量不小,因为要处理异常类型识别、重试间隔、熔断标记等等。
litellm 把这些能力做成了内置功能:一个fallbacks参数就能指定失败时切换的模型列表;代理模式里还能做负载均衡,把请求分散到同一个模型的多个部署上。这些能力让模型切换从"需要排期开发的功能"变成了"改个配置就能上线的事"。
1.3 花在管理上的时间,往往比写功能还多
接入大模型之后,团队很快会发现另一个头疼的问题:账号和权限管不过来。每个人的 API Key 直接写在项目配置里,代码一提交就泄露了;有的成员拿 Key 去跑自己的实验,月底账单出来,分不清哪些是业务花的,哪些是乱花掉的。你说要限制预算,但模型接口层根本没有"预算"这个概念,得自己在外面包一层统计。
litellm 代理模式里的虚拟 Key 和预算体系,就是专门解决这个的。你在网关后面创建一堆虚拟 Key,每个 Key 可以绑定模型白名单、设置额度上限、按天/按月限流。开发、测试、不同的业务方各拿各的 Key,网关帮忙记账。后端还有一套完整的用量日志,谁在什么时候调了哪个模型、花了多少 token、折合多少钱,全都拉得出来。这种管理能力,对于一个超过五个人的团队来说,基本是刚需。
2. LiteLLM 的核心机制拆解:统一协议层到底做了什么
前面讲了它解决什么问题,现在聊它是怎么做到的。litellm 不是那种黑盒网关,它的设计其实很朴素,理解了核心机制,你排错的时候会轻松非常多。
2.1 一个函数,包住一百多家模型
litellm 的 SDK 核心就一个函数:litellm.completion()。你只要把model参数传成"厂商名/模型名"的格式,比如"openai/gpt-4o-mini"、"anthropic/claude-3-haiku"、"bedrock/claude-v2",剩下的消息体、参数,全部按 OpenAI 的规范写。litellm 内部根据前缀判断走哪个厂商的适配器,然后完成请求格式转换、鉴权、发送、响应解析。
这个"厂商前缀"的设计是它的命根子。它把一百多家模型的服务商抽象成了字符串前缀,你在代码里切换模型,就是换字符串的事。为了做到这一点,litellm 为每个厂商维护了一套独立的转译规则:入参怎么映射、请求体怎么构造、响应怎么解析成统一结构。这就是它最核心的工作量所在。
有一个很关键的设计细节是:litellm 对外的统一格式不仅仅是"长得像 OpenAI",而是连错误信息、限流状态、流式事件这样的边角料也做了统一。比如所有厂商的限流错误,最终都会归成带RateLimitError类型的异常,你的业务代码只需要统一捕获这一类异常就能处理所有厂商的限流场景。这个细节在排错时价值极高,不用再为每个厂商写一种异常分支。
2.2 SDK 模式和 Proxy 模式,两种用法各有分工
litellm 的第二种玩法是起一个代理服务,在服务器上跑起来,对外暴露一个标准 OpenAI 接口。你的服务只需要配置 base_url 指向这个代理,连 SDK 都不用换,就能让整个团队共享同一份模型接入配置。
SDK 模式和 Proxy 模式的区别,一句话总结:SDK 模式适合单人或者轻量项目,模型配置写在代码里;Proxy 模式适合团队协作和生产环境,配置集中在网关层,权限、预算、监控都在这一层解决。
Proxy 模式还有一个很大的优势:上游模型的 Key 不再需要分发到每个业务服务里。业务服务只需要知道代理的地址和它自己的虚拟 Key,上游厂商的密钥全部锁在网关环境变量里。这样即使某个业务服务被攻破,泄露的也不过是一把权限受限的虚拟 Key,风险面小很多。
2.3 一次转发背后,litellm 帮你做了多少事
很多人以为 litellm 就是个"请求转发器",实际上它在你发一个请求时,背后默默做了很多事。
首先是 token 计数。litellm 会估算请求和响应各消耗了多少 token。估算不是简单数一下字数,它会针对不同的模型加载不同的 tokenizer 规则,尽可能贴近真实消耗。这些计数最终汇入成本统计,这就是它能自动告诉你"这个 Key 这个月花了多少钱"的原因。
其次是重试和超时控制。litellm 内部对某些类型的错误(连接错误、限流错误)会做自动重试,默认带指数退避策略。你不需要在业务层写重试逻辑,它会尽量保证请求完成。
再次是内容安全层面的支持。新版 litellm 支持在网关侧挂入内容审核、敏感信息检测之类的钩子,可以根据需求在请求前或者响应后触发。这块我个人觉得适合把一些通用的审计逻辑沉淀在网关里,而不是在每个业务项目里重复实现。
搞清楚这些机制之后,你会明白 litellm 的定位已经不是简单的"API 聚合器",而是一个偏完整的 LLM 网关中间件。它把接入层、管理层、观测层的能力都打包了,这也是为什么它在开源社区里热度一直在涨。
3. SDK 模式快速跑通:五分钟切换多家模型
如果只是想先体验一下,SDK 模式是最快的路径。我建议所有初次接触 litellm 的人都先从 SDK 模式跑一遍,把核心调用逻辑搞清楚,再决定要不要上 Proxy。
3.1 安装与环境准备
安装非常简单,一条命令的事:
pip install "litellm[proxy]"方括号里的proxy是额外依赖,包含代理服务需要的组件。如果你只打算用 SDK 模式,直接pip install litellm就够了。装完之后,要把你要用的厂商 Key 配到环境变量里。litellm 在请求时如果检测不到对应的 Key,会直接抛异常提醒你。这里有个容易踩的坑:不同厂商的 Key 环境变量名不一样,比如 OpenAI 的是OPENAI_API_KEY,Azure 的是AZURE_API_KEY加一堆AZURE_*参数,千万别只配一个API_KEY就指望所有厂商都能用。
export OPENAI_API_KEY="sk-xxxx" export ANTHROPIC_API_KEY="sk-ant-xxxx"3.2 第一个统一调用
配好环境变量,就可以开始调了:
import litellm messages = [{"role": "user", "content": "用一句话解释什么是 API 网关"}] # 调用 OpenAI 系 resp = litellm.completion( model="openai/gpt-4o-mini", messages=messages, temperature=0.7, ) print(resp.choices[0].message.content) # 换成 Anthropic 系,只改 model 参数 resp = litellm.completion( model="anthropic/claude-3-haiku", messages=messages, temperature=0.7, ) print(resp.choices[0].message.content)注意,两段代码除了model字符串不同,其他一模一样。这就是统一接口的意义——你的业务层只需要关心模型名字,不需要关心厂商差异。实际项目里,你完全可以把模型名写到配置中心里,通过配置动态切换模型,代码一行都不用改。
流式调用也差不多,把stream=True加上就行:
resp = litellm.completion( model="openai/gpt-4o-mini", messages=messages, stream=True, ) for chunk in resp: print(chunk.choices[0].delta.content or "", end="")litellm 会把各家流式输出统一成 OpenAI 的 chunk 结构,逻辑处理起来非常顺手。
3.3 顺手把用量和成本统计也接上
SDK 模式下,litellm 会在响应对象里附带 token 用量信息。你可以直接读:
usage = resp.usage print("输入 tokens:", usage.prompt_tokens) print("输出 tokens:", usage.completion_tokens) print("总 tokens:", usage.total_tokens)如果开了成功日志回调,它还会把 model、messages、响应、用量、延迟等信息全部打到回调函数里,非常适合做流量记录:
def log_usage(model, messages, kwargs, response_obj, start_time, end_time): print(f"模型: {model}, 耗时: {(end_time - start_time).total_seconds():.2f}s") print(f"用量: {response_obj.usage}") litellm.success_callback = [log_usage]这种细粒度的日志在做成本分析和链路排查时非常有用。我建议从一开始就把用量记录接好,等量大了再回头补历史数据,基本补不上。
4. 把 LiteLLM 架成公司网关:Proxy 模式才是重头戏
SDK 模式解决的是"我自己的代码怎么省事",但团队的模型接入管理,最终还是得靠 Proxy 模式。这一节重点讲怎么把 litellm 跑成一个真正的网关服务,以及生产环境里哪些配置是必须的。
4.1 用一份配置文件启动网关
Proxy 模式的入口是一个 YAML 配置文件,里面定义了两类核心内容:模型列表和通用设置。模型列表告诉网关有哪些上游模型可用,通用设置决定网关本身的运行策略。
下面是一份最简配置:
model_list: - model_name: gpt-4o-mini litellm_params: model: openai/gpt-4o-mini api_key: os.environ/OPENAI_API_KEY - model_name: claude-3-haiku litellm_params: model: anthropic/claude-3-haiku api_key: os.environ/ANTHROPIC_API_KEY general_settings: master_key: sk-master-key-123 database_url: postgresql://user:pass@localhost:5432/litellm注意几个细节。model_name是暴露给调用方的名字,你可以起一个跟上游完全无关的别名,比如把上游某个模型叫"text-fast",业务方只认这个别名,以后你在后面偷偷换上游模型,业务方毫无感知。litellm_params.model才是真正的上游模型路径。master_key是管理网关用的管理员密钥,所有虚拟 Key 管理操作都要用它鉴权,这个必须单独设一个足够复杂的值。
启动命令也很简单:
litellm --config config.yaml --port 4000启动之后,你的服务就对外提供了一个标准的 OpenAI 兼容接口,地址是http://localhost:4000。任何支持自定义 base_url 的 OpenAI SDK 客户端,都可以直接连上来。
4.2 虚拟 Key 和预算管控:团队接入的核心
网关起来了,接下来最重要的一步是给团队成员分配虚拟 Key,而不是把上游 Key 直接给他们。通过管理员密钥调用/key/generate接口就能生成:
curl http://localhost:4000/key/generate \ -H "Authorization: Bearer sk-master-key-123" \ -H "Content-Type: application/json" \ -d '{ "models": ["gpt-4o-mini"], "budget": 10, "max_parallel_requests": 100 }'这个 Key 的预算上限是 10 美元,只能调用gpt-4o-mini这一个模型,并发上限 100。也就是说,这个 Key 就算被人偷了,别人最多只能在这个额度内用指定的模型,风险完全可控。
虚拟 Key 的典型使用场景我列一下:
- 给每个开发者一把 Key,额度设成个人月度预算,超了自己去申请。
- 给每个业务方一把 Key,绑定他们实际需要的那几个模型,防止误调到最贵的模型。
- 给 CI/CD 环境一把专用 Key,额度低一点,防止测试脚本失控。
如果配了数据库,litellm 还会记录每个虚拟 Key 的完整调用历史。管理员可以在控制台按 Key、按模型、按时间段查询 token 消耗和费用。出了账单问题,直接按 Key 对账,几分钟就能定位到是哪个业务方在猛调。
4.3 负载均衡和自动回退:生产环境的保命配置
在配置文件里,同一个model_name可以挂多个上游实现。litellm 默认会用加权轮询的方式把请求分发到这些实现上,实现负载均衡。比如你的主供应商和备用供应商各部署了一个同型号模型,可以这样配:
model_list: - model_name: main-model litellm_params: model: openai/gpt-4o-mini api_key: os.environ/OPENAI_API_KEY model_info: priority: 1 - model_name: main-model litellm_params: model: anthropic/claude-3-haiku api_key: os.environ/ANTHROPIC_API_KEY model_info: priority: 2priority决定优先顺序:1是主,2是备。默认情况下网关优先打主供应商,主供应商连续出错达到阈值后,后续请求自动切到备用供应商。
回退配置更简单,直接在litellm_settings里声明:
litellm_settings: fallbacks: [{"main-model": ["claude-3-haiku"]}]意思是main-model失败时,自动用claude-3-haiku顶上。注意,回退逻辑主要解决的是"这个请求有没有成功返回"的问题,它会在请求发出前先按优先级列表逐个尝试,直到有一个模型成功。
我强烈建议任何生产环境都把回退配上。模型供应商的稳定性说实话没有想象中那么高,有回退和没有回退,故障处置时间能差一个数量级。
4.4 缓存机制:省钱和降延迟的利器
litellm 代理内置了响应缓存,支持内存缓存和 Redis 缓存。对于重复性较高的请求,比如固定 prompt 的摘要、分类、信息提取任务,缓存命中后可以直接省掉一次模型调用,既省钱又降延迟。
配置 Redis 缓存:
litellm_settings: cache: true cache_params: type: redis host: localhost port: 6379 ttl: 3600这里有个经验之谈:不要对个性化、实时性要求高的请求开缓存,比如对话聊天场景,缓存会让用户体验变得很奇怪。缓存最适合的是那些输入输出高度确定的任务,比如从文本里提取结构化字段、给工单打标签这类。
5. 我踩过的 LiteLLM 坑位与调优经验
任何工具跑生产环境,都会遇到文档里没写的坑。litellm 我用下来整体很顺手,但下面这几个问题,每一件都让我实打实花过时间排查,写出来帮你省点弯路。
5.1 流式请求的超时配置不能照抄默认值
litellm 默认的请求超时时间对普通短请求够用,但流式生成场景下,模型"首字延迟"和"整体生成时间"完全是两回事。如果你用默认超时去请求一个要生成几千 tokens 的长文本,很可能会出现"超时被杀,但上游其实还在正常生成"的情况。后果就是用户那边看到一半,断了。
我的做法是单独给流式请求配更宽松的超时,并且把超时设置放进模型参数里:
litellm_params: model: openai/gpt-4o-mini api_key: os.environ/OPENAI_API_KEY timeout: 300另外,首字延迟类的排查要看日志里的ttft指标,如果首字延迟就很高,多半是上游模型当时负载大,或者网络链路有问题,这时候调超时没用,得换模型区域或者加自动回退。
5.2 fallback 触发时,小心业务侧重复写入
fallback 配置本身没问题,但有些业务代码会把"请求失败"和"开始重试"这件事记到自己的数据库里。比如请求main-model失败,网关自动切到claude-3-haiku并且成功了,业务侧如果按"每次异常都记录一条失败日志"去写,就会产生误导性的错误数据。
我的调整方案是:在业务侧只关注最终响应。只要最终成功返回了,就不算失败;只有最终全部都失败,才记录一个完整失败。网关内部的多次尝试过程,让它写进网关自己的日志里就行,避免两边数据对不上。
5.3 上游 Key 的管理不能省
配置里如果直接写明文 Key,一旦配置仓库权限管控不严,Key 就泄露了。litellm 支持os.environ/环境变量名这样的语法,在配置文件里引用环境变量。我见过有人图省事直接明文写上去,后来仓库被扫描工具扫出了 Key,被迫全部轮换。这个教训成本挺高。
还有一点,Key 权限也要尽量最小化。给网关用的上游 Key,能不开联网搜索功能就不开,能不开文件访问就不开。毕竟网关是团队共享的入口,它的权限边界越窄,出事时的损失越小。
5.4 高并发下,记得把缓存和数据库一起规划好
litellm 代理的用量记录和虚拟 Key 管理依赖数据库。早期团队可能觉得先用本地 SQLite 跑跑就行,但并发一上来,SQLite 的写锁会成为明显的瓶颈,网关会整体变慢。我建议从部署第一天就接 PostgreSQL,省得后面迁移。
同理,多实例部署的时候,内存缓存是各实例各存一份的,会导致缓存命中率下降。这时候必须上 Redis 做统一缓存层。我的经验是:litellm 一旦跑多实例,Redis 不是可选项,是必选项。
6. 在项目中落地 LiteLLM 的几点建议
工具再好,用不对地方反而增加复杂度。最后聊几个我在实际落地过程中得出的判断,供你参考。
6.1 什么样的场景其实不需要它
如果你只有一个模型、一个人开发、项目也是短期实验性质,那直接用模型厂商自己的 SDK 是最快的,没必要引入 litellm。它的价值在"多模型"和"多人协作"这两个条件下才会充分体现。硬上一个中间层,反而要多维护一份配置,多一个故障点,早期没必要。
还有一种情况是强依赖某个厂商独特功能、完全不考虑切换的项目。比如某模型的视觉理解能力独一档,你的产品就是奔着它去的,那中间层的价值也不大。litellm 擅长的是通用对话、文本生成这类标准化能力,过于独特的模型特性在统一层里往往会被"向下对齐"。
6.2 团队协作里的配置管理纪律
一旦把 litellm 作为团队网关,配置文件的变更管理就要严肃起来。我建议把config.yaml纳入代码仓库,走 MR 评审流程,模型上线前由负责人确认配好的 Key、模型名、预算是否正确。litellm 也支持在数据库里存在 "config hash",配置变化会留痕,方便回溯。
虚拟 Key 的命名规范也值得提前定。比如统一用项目-环境-用途的格式,rec-api-prod、rec-api-test、analyst-personal这样一眼能看出是谁在用什么。否则随着 Key 数量增加,管理后台会变成一团乱麻。
6.3 后续扩展的想象空间
litellm 的扩展能力可以帮你做很多更进阶的事情。比如配合它的落库日志,你能在数据仓库里做模型质量监控,对比不同模型的输出延迟、token 消耗、失败率,辅助模型选型和替换决策。再比如把网关的指标接入监控大盘,设置预算告警,费用冲到一定水位自动提醒,防止月底爆单。
我自己现在所有涉及大模型的项目,基本都默认先接 litellm,就算一开始只接一个模型也这么干。原因很简单:以后加模型的时候不用再改代码,跑数据复盘的时候不用临时补埋点。这个习惯帮我省下的时间,远比当初引入它花掉的时间多。如果你正被模型接入的多样化问题困扰,建议你也拿一个小项目先跑通,感受一下"只写一种调用代码"的舒畅感。