先说个场景:如果你手头的项目同时接了四五家大模型的 API,你会立刻发现,最麻烦的根本不是模型本身效果怎么样,而是各个服务商的接口格式不一样、报错风格不一样、限流策略不一样。今天聊的这个项目叫 LiteLLM,命令行工具名是 litellm,它本质上是做一个非常轻量的 LLM 网关与代理层,把一百多家模型服务统一收敛成一个接口,顺便把负载均衡、失败重试、预算控制、多租户、缓存这些事一起解决了。这篇文章没有任何广告,就是我把 LiteLLM 部署进真实业务之后的完整复盘,从为什么需要它,到最小配置怎么起,再到各种坑怎么填,尽量一次说透。
不管是做 AI 应用的开发者、维护模型平台基础能力的后端团队,还是想在公司内部搞一套统一模型入口的架构师,这篇文章的内容都能直接用上。下面从最核心的问题开始讲。
1. 先想清楚:这一层到底解决的是什么问题
1.1 多模型接入的真实痛点
很多团队在引入大模型后的第一个月,代码库就会长成这副模样:一个模块封装了服务商 A 的调用,另一个模块封装了服务商 B 的调用,还有一个人偷偷在脚本里写死了服务商 C 的临时接口。大家各自调各自的,写出来的代码完全没法复用。
问题不止是代码乱。不同服务商的 API 差异,比很多人想象中更大。请求格式有的要求消息列表,有的要求单独传系统提示;返回结果里有的直接把答案放在统一字段里,有的要你自己去解析;超时和限流的报错方式更是五花八门,有的返回一个状态码,有的直接返回一段让人看不懂的文本。你为了兼容这些差异,写了大量胶水代码,而这部分代码除了维护成本,没有任何业务价值。
更头疼的是切换困难。今天觉得平台 A 的效果好,想切到 A,明天觉得平台 B 更便宜,想切到 B。每次切换都意味着改代码、改测试、重新发布。业务方可不管你在后台怎么折腾,他们只关心你承诺的响应速度还稳不稳定。时间一长,团队就会抗拒尝试新模型,AI 应用的能力进化被自己的集成成本卡住了。
1.2 LiteLLM 的核心定位与组成部分
LiteLLM 解决的思路很直接:在所有模型服务商和你的业务代码之间,插一层统一的代理。你的业务代码只认一种请求格式、一种返回格式、一种错误格式,剩下的适配工作全部由代理层完成。
这个项目由两个主要部分组成。第一部分是 Python SDK,你可以在自己的代码里直接调用,用一套函数完成对多个模型服务商的请求,适合脚本、后台任务、单元测试这类场景。第二部分是可独立部署的代理服务,也就是 Proxy Server,它会读取一份 YAML 配置文件,对外暴露一个 HTTP 接口,对内负责把请求转发给真正的模型服务商。业务团队完全不用关心代理后面到底有几个服务商,他们只需要把请求发到代理,然后在控制台上看到“可用模型”列表。
代理模式是我最推荐的生产环境用法。原因很简单:业务代码侧完全无感切换模型,新增一个上游模型只需要改配置文件,不需要重新发版。而且代理层天然就是一个集中治理点,密钥、预算、日志、限流全都可以在这里统一管控,不用再指望各个团队自觉遵守规范。
2. 两种接入方式与最小可用配置
2.1 SDK 模式与代理模式的取舍
刚开始接触 LiteLLM 的人,很容易被它的文档绕晕,因为同一个项目给了你两条完全不同的使用路径。
SDK 模式适合快速验证和临时脚本。你只需要安装 Python 包,然后通过统一接口传目标模型名和消息内容,就能发请求。这个模式最大的好处是轻,不需要额外的服务进程。坏处也很明显:每个需要用到模型的应用都要自己维护同一套配置,密钥散落在各处,负载均衡和预算管理基本靠自觉。
代理模式则是一个独立的 HTTP 服务。你在服务器上启动代理进程,业务代码里的请求地址改成代理的地址,密钥改成代理给你生成的虚拟密钥,剩下的让代理去处理。这个模式适合生产环境,因为它把所有的治理逻辑集中到了一处。你可以给不同的团队发不同的虚拟密钥,每个密钥有不同的模型访问范围和预算上限;你可以在代理里配置失败自动重试、模型故障转移、语义缓存;你还可以让代理把每个请求的耗时、Token 用量、费用明细全部记到数据库里。
我的建议是:如果你想在笔记本电脑上快速测一个想法,直接用 SDK 模式就行。如果这个想法要变成线上服务的核心链路,第二天就应该把代理搭起来,别等业务流量来了之后再补课。
2.2 配置文件长什么样
代理模式的一切核心都在一个 YAML 文件里。第一次看到这个文件的人通常会问:就这么几段配置,真能管住那么多模型服务商?答案是能。
下面是一个最小可用的配置示例:
model_list: - model_name: "chat-main" litellm_params: model: "provider-a/latest-chat" api_key: os.environ/PROVIDER_A_KEY model_info: max_input_tokens: 32768 max_output_tokens: 4096 - model_name: "chat-main" litellm_params: model: "provider-b/flagship-chat" api_key: os.environ/PROVIDER_B_KEY model_info: max_input_tokens: 200000 max_output_tokens: 8192 general_settings: master_key: os.environ/LITELLM_MASTER_KEY database_url: os.environ/LITELLM_DATABASE_URL litellm_settings: num_retries: 2 request_timeout: 30 set_verbose: false这里有一个关键概念先解释清楚:model_name是对外暴露的名字,model是上游服务商真正识别的模型标识。上面配置里有两个上游模型,但对外都叫chat-main。业务方只调chat-main,代理会在两个上游模型之间自动做路由和负载均衡。provider-a、provider-b这些不是真实的服务商标识,在你自己落地的时候,要把它们替换成你实际开通的模型服务商标识,latest-chat、flagship-chat也要换成该平台真实可用的模型 ID。
2.3 启动代理并完成第一次调用
配置文件写好后,启动代理只需要一条命令:
litellm --config config.yaml --port 4000如果是想部署在服务器上,建议用容器方式运行,实际上就是把上面的命令封装成基础镜像,再统一管理环境变量。启动后,可以用curl快速验证一下代理是否正常。
curl http://localhost:4000/health/liveliness看到正常返回后,再发一个真实的对话请求:
curl http://localhost:4000/v1/chat/completions \ -H "Authorization: Bearer sk-1234" \ -H "Content-Type: application/json" \ -d '{ "model": "chat-main", "messages": [{"role": "user", "content": "你好,请简单介绍下你自己"}] }'这个请求格式是全篇最值得记住的东西:它和业界通用的 Chat 接口格式保持一致。也就是说,原来那些只需要改base_url就能切换服务商的 SDK,现在直接把base_url改成代理地址,代码几乎不用动。这就是 LiteLLM 最讨喜的地方,集成成本低到离谱。
3. 把模型路由和故障转移玩明白
3.1 一个对外模型名对应多个上游
LiteLLM 的对外模型名和上游模型是解耦的,这个设计给了路由能力一个很好的基础。你可以在同一个对外模型下面挂多个上游,然后让代理根据实时状态决定把请求发给谁。
配置里可以通过router_settings控制路由策略:
router_settings: routing_strategy: "least-busy"常见的策略有几种。
simple-shuffle是最简单的方式,代理会尽量平均地把请求分发到每个上游,适合各服务商能力相近、价格相近的场景。least-busy会看当前每个上游正在处理的请求数,优先发给最空闲的那个,适合某些上游响应慢、容易堆积的场景。usage-based-routing是按每个上游配置的每分钟请求数、每分钟 Token 数的已用比例来路由,谁的配额剩余最多就发给谁,适合有明确套餐上限的场景。
还有一种更实用的场景是模型按优先级划分。比如你有一个主力模型和一个备用模型,主力效果最好但贵,备用便宜但效果略差。你可以把两个模型放在同一个路由组里,正常情况下全走主力,主力故障时自动切到备用。
3.2 故障冷却与自动重试
真实生产环境中,模型服务商不可能永远稳定。限流、超时、返回异常,什么情况都会发生。LiteLLM 的处理机制是两层。
第一层是请求级别的重试。litellm_settings里配置num_retries,代理会在请求失败或超时后自动重试,这是最简单的一层保护。需要注意的是,重试要配合幂等逻辑,否则同一笔扣费可能会发生多次。
第二层是上游级别的故障冷却。代理会持续统计每个上游的成功率和失败率,如果某个上游在指定时间内连续失败达到阈值,代理会把它加入冷却名单,在冷却期内不再把新请求发给它。对其他请求来说,这段时间流量会被自动转移到健康的上游,用户完全感知不到。
这是我实际用下来觉得最值钱的功能。在没有代理层之前,上游抖动一次,我们的客服就要收到一波“AI 没反应”的投诉。有了冷却机制之后,这种故障直接被透明消化掉了。
3.3 参数透传与模型差异
很多人会担心:各家模型的参数差异这么大,统一接口是不是意味着很多能力被阉割了?LiteLLM 的做法是“核心参数统一,特殊参数直通”。
所有主流对话接口共有的参数,比如温度、最大输出 Token 数、停止符,代理会把它们转成上游认识的名字再发出去。模型特有的参数,比如某些平台支持的“思考模式开关”“搜索开关”,可以通过额外的配置项传进去,代理不会吃掉这些参数。如果你用的是 SDK 模式,甚至可以直接写平台相关的参数以便调试。
这里提一个非常容易忽略的配置:model_info里的max_input_tokens和max_output_tokens。很多刚上手的人不配这两项,导致代理无法准确判断一个请求会不会超过上游的上下文限制。一旦某个模型的实际上下文比较小,业务方把超长文本传进来,请求就会被上游拒绝,报错信息直接暴露给用户,体验很差。正确的做法是在配置里把每个模型的上下文上限写清楚,这样代理在转发前就能做拦截,返回一个清晰的业务提示,而不是一串晦涩的技术报错。
4. 虚拟密钥、预算与多租户管控
4.1 虚拟密钥怎么发
如果让所有团队共用同一个上游密钥,那预算、审计、限流全都无从谈起。LiteLLM 的代理模式提供了虚拟密钥机制,简单说就是:真正的上游密钥只存在代理配置里,业务方拿到的是一串代理自己生成的虚拟密钥。
启动代理时,general_settings里的master_key是管理员密钥,用它去调密钥管理接口,就可以生成不同权限的虚拟密钥。
curl http://localhost:4000/key/generate \ -H "Authorization: Bearer sk-1234" \ -H "Content-Type: application/json" \ -d '{ "models": ["chat-main"], "budget": { "budget_limit": 100, "time_period": "1mo" }, "info": { "team": "content-platform" } }'生成的虚拟密钥会在返回值里展示。把这个密钥给对应的团队,他们只能用它访问被授权的模型,调用记录也会被计入这个密钥名下。上游真实密钥不落地到业务代码,泄露风险一下就小了很多。
这里要注意一点:budget_limit的币种在大部分版本里是按美元计费的。如果你们公司内部用的是人民币预算,一定要在生成密钥的代码里做一次换算,否则你给团队批的 100 元预算,实际可能是 100 美元,这个月成本直接翻几倍。
4.2 预算与限流
虚拟密钥可以带预算限额,预算耗尽后代理会直接拒绝请求,避免出现“月底看账单吓一跳”的情况。预算粒度可以分多层:密钥级别、模型级别、用户级别。你既可以限制某个密钥整体一个月花多少钱,也可以限制某个密钥在某个模型上每天最多用多少 Token,还能在请求参数里带上用户标识,针对单个用户做限额。
限流也是代理层治理的重头戏。上游模型服务商一般都有每分钟请求数、每分钟 Token 数限制。如果你有多个业务方共用同一个上游,某个团队突发流量很可能把整个上游的配额全部打满,导致其他团队的请求被限流。在 LiteLLM 的配置里,可以给上游模型显式声明配额上限,让代理在转发前就根据已用配额做判断,宁可内部排队,也不触发上游的全局限流。
4.3 多租户实践
我把 LiteLLM 部署成一个平台能力之后,内部好几个团队都开始接入。每个团队有自己独立的虚拟密钥和预算,模型资源池共享,但数据互相隔离。由于每个请求都会记录密钥归属,审计的时候一眼就能看出是哪个团队在调用哪个模型、花了多少钱。
这个模式下有几个细节值得注意。启用数据库后,虚拟密钥、预算、请求日志都会持久化,否则代理一重启,所有密钥和消费记录全部丢失。数据库连接建议用独立的账号,别和业务库混在一起,否则日志量一大,很容易拖慢正常业务查询。
另外,虚拟密钥的吊销一定要做成可自助化的接口,让团队管理员可以自己负责自己团队成员的密钥生命周期,不要让平台管理员整天处理这类琐事。
5. 缓存与并发优化
5.1 缓存配置
模型调用的延迟通常比普通 HTTP 接口高,费用也跟 Token 量直接挂钩。如果业务上存在不少相同或相似的请求,缓存能同时优化延迟和成本两个指标。
LiteLLM 支持在代理内开启缓存,默认可以直接使用内存缓存,适合单实例场景。多实例部署时,建议把缓存放到独立的缓存中间件里,比如 Redis。配置很简单:
litellm_settings: cache: true cache_params: type: redis host: redis-host port: 6379 ttl: 3600TTL 根据业务场景来配。如果是智能客服,用户问题重复率可能很高,可以设长一点;如果是数据分析类助手,问题基本不重复,缓存意义不大,没必要占用内存。
5.2 语义缓存
普通缓存只能命中完全相同的问题,但很多请求虽然字面上不一样,意思是一样的。比如“帮我总结一下这篇文章”和“请给我一个这篇文章的摘要”,问的是同一件事。语义缓存就是先给请求文本计算一个向量,然后在缓存里做相似度匹配,达到阈值就直接返回缓存中的结果。
开启语义缓存需要单独配置一个用于向量化的模型服务。好消息是,LiteLLM 本身支持通过代理的方式调用这类模型服务,不需要额外维护另一个接入流程。语义缓存的代价是每次请求都要多一次向量计算,所以适合那些请求量大、重复语义多的场景。如果你们的业务是低频咨询,先别开,收益不明显还增加延迟。
5.3 并发与速率限制
代理层本身要支持高并发,这意味着你需要根据上游能力配置好每个模型的并发上限。litellm_params里可以配置每分钟请求数和每分钟 Token 数,代理会在内部做队列控制,避免上游被突发流量打爆。
我见过很多团队把代理部署好之后,压测发现成功率上不去,追查了半天,发现是上游限流了,因为代理转发时把请求数原封不动地打给了上游。正确的思路是给每个模型声明它真实的上游配额,再给不同的虚拟密钥配置不同的调用频率限制。这样即使某个业务方疯狂调用,受影响的也只是他自己的队列,其他团队不会被殃及。
6. 日志、观测与费用核算
6.1 统一日志与数据库
日志功能是 LiteLLM 的隐藏价值之一。代理会在每个请求完成后,记录这次请求用了什么模型、花了多少 Token、耗时多久、费用是多少、命中缓存还是直连上游。这些日志不仅是排查问题的基础,也是成本核算的原始数据。
只要配置了数据库,代理就会把所有记录写进去。日志量会增长得非常快,尤其是业务量上来以后。建议定期做日志归档,把旧的明细备份到低成本存储,数据库里只保留最近一到三个月的数据,查询性能会稳定很多。
费用核算方面,代理内置了花费统计接口,可以按照密钥、模型、用户、时间范围查询花费明细。这意味着财务对账的时候,你不需要去各家服务商后台手工拉账单,直接在代理上导出一份完整报表就行。
6.2 UI 与指标
代理自带一个管理后台,登录后可以看到模型健康状态、请求量、失败率、响应耗时等基础信息。对于团队自用来说,这个后台已经够用。
如果需要接入公司已有的监控体系,代理也暴露了标准指标接口,Prometheus 这类监控系统可以直接采集。我们当时是给每个模型的请求延迟、失败率、缓存命中率各配了一条告警规则,一旦某个上游连续几分钟失败率超过阈值,告警自动发出,运营同学可以第一时间处理,不用等用户来抱怨。
7. 常见问题与排查速查
7.1 排查清单
我把实际运维 LiteLLM 过程中遇到的高频问题整理成了一个速查表,遇到问题可以对照排查。
| 现象 | 可能原因 | 排查与处理 |
|---|---|---|
| 代理启动后没有任何日志 | 日志级别设置过低或配置没加载 | 检查set_verbose是否打开,启动时指定--detailed_log观察 |
| 请求返回认证失败 | 请求头里的密钥不正确,或 master key 未设置 | 确认请求头和代理里的 master key 一致,检查环境变量是否加载 |
| 返回预算超限错误 | 虚拟密钥预算耗尽,或预算字段配错币种 | 查询该密钥的花费明细,确认预算配置的币种和数值 |
| 提示上下文长度超限 | 上游模型上下文较小,或代理未配置模型上下文上限 | 在model_info里配置准确的输入输出 Token 上限 |
| 频繁触发上游限流 | 代理没有按上游真实配额做流量控制 | 在litellm_params中显式配置每分钟请求数和每分钟 Token 数 |
| 路由结果不均衡 | 路由策略不适合当前流量模型 | 换用usage-based-routing或least-busy,观察一段时间 |
| 代理重启后虚拟密钥丢失 | 没有配置持久化数据库 | 配置database_url,重启后密钥自动恢复 |
| 压测时大量请求超时 | 重试次数过多叠加故障冷却时间 | 区分业务超时和上游故障,适当调低重试次数,缩短冷却时间 |
7.2 几个我踩过的坑
第一个坑是版本升级。LiteLLM 的迭代速度非常快,配置字段在版本之间有过变化。我们曾经在一个旧版本上稳定运行了几个月,某天升了个小版本,发现部分配置项被提示不推荐了,路由策略的行为也有变化。从那以后,我养成了一个习惯:升级前先在隔离环境跑一遍回归用例,确认配置兼容性再上生产。这个项目的文档很全,但版本差异文档更要仔细看。
第二个坑是重试导致的重复扣费。设置了自动重试之后,如果上游实际已经处理了第一个请求,只是响应超时了,代理重试就会造成同一请求被处理两次,费用自然也会翻倍。这个问题本身不是代理的 bug,而是调用方接口没有幂等标识。我们后来在上游配置里加上了幂等相关的请求头参数,尽量降低重复扣费的概率。
第三个坑是预算设置过度严苛。刚开始为了控制成本,我把某些模型的每日预算设置得很低,结果业务方一早上就把预算打满了,下午所有请求全部被拒,客户体验直接崩掉。现在我的做法是预算先宽后紧,观察一周真实消耗,再按实际量的 1.5 倍去设限制,给突发流量留出缓冲。
最后一个建议是把配置纳入版本管理。config.yaml就是整个模型网关的“操作规范”,每次改模型、调参数、设预算之前,先看一眼变更历史,别直接在生产环境在线编辑。我们内部现在规定所有配置变更必须走代码评审流程,代理本身支持配置热加载,但改之前先让同事看一眼,能避免很多低级失误。
这些坑我基本都踩过一遍,踩完之后最大的体会是:LiteLLM 本身是个非常实用的工具,真正决定它好不好的,是你有没有想清楚自己的治理需求。先设计好模型分组、密钥体系、预算模型,再动手配置,比把文档翻个底朝天再摸索要高效得多。