如果现在的项目里只是接一个大模型服务,直接写SDK就够了,什么网关、路由、代理,都是过度设计。但当你需要同时服务几个不同模型、给不同业务线分配额度、时不时还得切换服务商时,事情就没那么简单了。我第一次在团队里拉起litellm,就是为了解决这种“模型一多就乱”的窘境。它本质上是一个开源的LLM网关层,对内用一套OpenAI兼容的HTTP接口收齐Chat、Embedding、Images这些能力,对外把路由、重试、密钥、预算、监控这些公共需求变成配置项,而不是每个项目各写一套胶水代码。这篇文章不讲官方文档已经讲透的部分,而是从落地实操的角度,讲清楚litellm怎么选型、怎么配、怎么避坑,适合正在评估多模型统一方案,或者已经在用但想优化路由与成本管理的开发者。
1. 模型多了之后,第一波痛点全在这三件事上
1.1 API风格不一致:真正的成本不是“接入”,而是“维护”
如果只是对接一家大模型服务,不管它给的SDK多简陋,总能靠复制示例跑通。麻烦从第二家开始。每家服务商都有自己的参数风格、错误结构和特殊约定,有的把图片参数放在消息内容里,有的要求你单独传一个image字段;有的错误体里有明确定位的error_code,有的只有一个泛泛的HTTP 500。你在业务代码里给每家写一套适配封装,表面上是多几百行代码,实际是给自己埋了三份需要长期维护的“方言”。更麻烦的是上层逻辑一旦依赖这些差异化细节,后面新增模型的时候就得把所有调用方都翻出来改。
我第一次用litellm的直观感受,就是它把这层差异收走了。调用方看到的永远是一个OpenAI风格的请求结构、一个统一的错误格式,后台是哪家模型、哪家SDK,业务代码不用关心。它做的本质上是一个“协议翻译层”:把OpenAI风格请求翻译成每个服务商能理解的格式,再把各家的响应和错误翻译回统一结构。真正做到这一步之后,新增模型就只是网关配置里加几行,而不是改一版代码。
1.2 密钥散落与预算失控
团队小的时候,模型的真实key经常直接躺在环境变量里,甚至写进配置仓库。某个脚本用一份key,某次demo又复制一份,等到要轮换key的时候,你根本不知道哪些地方还在用旧key。而且真实key一旦泄露,等于把整个账号的额度都交给别人,服务商后台的告警时常是滞后的。
litellm的关键做法是把真实key收敛在网关里。对外签发的是虚拟key,虚拟key可以和团队、项目、预算、有效期绑定,权限不足或者额度用完的时候,网关直接拒绝请求。这样即使一个虚拟key泄露,影响范围也局限在它绑定的那部分额度里,吊销一个虚拟key不会影响其他业务线,更不需要去动真实账号的根密钥。这个设计在多人协作的团队里几乎是刚需。
1.3 没有兜底的重试机制
多模型场景里,某个服务商偶尔限流、偶尔超时,几乎是常态。自己写重试逻辑,容易写成一团乱麻:无脑重试可能放大故障,退避策略设置不合理会拖慢响应,fallback到另一个模型时又发现数据格式对不上。litellm在代理层把重试、超时、fallback这些行为配置化,让我可以把状态判断和降级策略从业务代码里剥离出来,统一放到网关配置里管理。这也是我后面愿意把它正式接入核心链路的核心原因。
简单总结我当时的需求痛点:
| 痛点 | 典型表现 | litellm提供的解法 |
|---|---|---|
| API差异 | 每家SDK风格不一致,错误结构混乱 | 统一OpenAI风格请求与错误结构 |
| 密钥管理 | 真实key散落,泄露波及整个账号 | 网关收敛真实key,下发虚拟key |
| 故障处理 | 单点限流/超时,业务代码重试逻辑混乱 | 代理层统一重试、超时、fallback |
| 成本统计 | 各项目费用没有口径,月底难对账 | 按key/team记录花费并查询报表 |
2. litellm的核心设计:一张OpenAI协议“调度表”通吃全家
2.1 两条使用路径:SDK直调与Proxy网关
litellm提供两种用法,理解它们的区别能帮你少走弯路。第一种是当Python SDK用,在代码里直接调用litellm.completion()或者litellm.acompletion()(异步版本),传入模型名和消息,拿到响应。这种形态适合个人脚本、离线任务、快速试验,不需要额外起服务。第二种是当Proxy网关用,启动一个本地HTTP服务,把所有模型注册进配置,客户端只需要知道网关地址和一个虚拟key。这种形态适合团队共享、需要权限隔离和集中监控的场景。
我自己的路径是先用SDK跑了两天试验,确认它能覆盖日常调用的各种参数风格之后,才搭了网关。两者的底层能力其实是同一套,网关侧会把收到的一个个请求当作一次“在线调用”,只是多了路由、密钥和统计这些管理功能。如果你一开始就把所有调用都连到某个官方SDK上,后面再想上网关,改动量会成倍增加。
2.2 两层映射:对外稳定名与对内路由标识
litellm配置里有一点最容易让人绕晕,就是model_name和litellm_params.model这两个概念的区别。以下面这段配置为例:
model_list: - model_name: chat-main litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY这里chat-main是对外暴露的名字,业务代码请求时model字段写它就行。openai/gpt-4o才是litellm真正要转发的内部标识,斜杠前面的openai/表示协议提供方,斜杠后面是模型名。为什么非要分成两层?因为它给了运维一个“在不改业务代码的前提下切换后端模型”的开关。今天chat-main指向某个主流模型,明天你想换成另一家的同档次模型,只需要改网关配置,然后把新key配上,所有业务侧请求不变。
这个设计很像DNS:域名对外保持稳定,背后的IP随时可以换。很多人刚开始不理解,总是把chat-main当成了模型本身的ID,结果换服务商时要同步改一堆代码。记住一条原则:业务代码只认外部稳定名,后端真实模型永远通过网关配置来调整。
2.3 为什么选用OpenAI兼容协议
litellm对外提供的是OpenAI兼容协议,这大概是我最认可的一个设计决策。原因很实在:OpenAI风格的聊天补全、Embedding和图片接口在社区里几乎是事实标准,开源项目和商业化应用大多支持自定义base_url,也就是说,它们本身就能指向一个自定义网关。你如果换成其他格式,很多现成客户端根本不认识,接入门槛会瞬间抬高。
从实现角度看,OpenAI协议的payload结构也比较通用:model、messages、temperature这些字段能很自然覆盖大多数文本类请求。litellm再在这个协议之上补了路由、用量统计、成本估算、密钥检查等扩展点,两层各司其职:协议负责统一表达,扩展能力负责管理。这也是为什么它能作为团队内部的“模型总线”存在,而不仅仅是一个API转发器。
3. 从零搭一个多模型网关:配置、启动与首次请求
3.1 安装与目录规划
安装很简单,一条命令:
pip install 'litellm[proxy]'带[proxy]是因为要启动HTTP网关,需要FastAPI那一套依赖。如果只是在Python脚本里当SDK调用,直接pip install litellm就够了。装完可以用litellm --help确认命令行可用。
我习惯把网关项目放在独立目录里,结构大概是:
~/gateway/ ├── config.yaml ├── .env └── data/.env存所有真实key,data/放litellm的SQLite数据库文件,用来记录key、花费、日志等。把配置和密钥分开的好处是,config.yaml可以放心进入版本库,.env单独放到服务器的安全目录里,不提交。如果团队成员拿到配置,也不会看到任何真实密钥。
3.2 写一个最小可用配置
一个能跑通的最小config.yaml长这样:
model_list: - model_name: chat-main litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY - model_name: claude-main litellm_params: model: anthropic/claude-3-5-sonnet api_key: os.environ/ANTHROPIC_API_KEY general_settings: master_key: os.environ/LITELLM_MASTER_KEY database_url: sqlite:///data/litellm.db我特意用了os.environ/前缀,这是litellm读取环境变量的标准写法,而不是把key明文写进YAML。master_key是管理员的根密钥,用来访问/key/generate、/spend这些管理接口。如果只是自己试用,master_key也可以直接当普通调用key用;要多人协作,就应该给每个团队生成虚拟key,这一点后面细说。
3.3 启动服务和健康检查
启动命令:
cd ~/gateway set -a; source .env; set +a litellm --config ./config.yaml --port 4000先加载.env,再启动服务。看到日志里出现“Uvicorn running on”之类的输出就说明起来了。接着可以用/health确认模型注册状态:
curl http://localhost:4000/health返回信息里会带上litellm版本和每个模型组的状态。养成上线后先看/health的习惯,可以第一时间发现配置里的前缀写错、key没加载上去之类的问题。
3.4 用curl发起第一次请求
现在用master_key(或之后分配的虚拟key)发起一个聊天补全请求:
curl http://localhost:4000/v1/chat/completions \ -H "Authorization: Bearer <你的key>" \ -H "Content-Type: application/json" \ -d '{"model": "chat-main", "messages": [{"role": "user", "content": "你好,请一句话介绍你自己"}]}'注意model字段填的是chat-main,不是openai/gpt-4o。这一步想明白,后面路由和fallback都会很顺畅。返回结构和OpenAI风格一致,有choices、usage这些字段,业务侧可以直接复用之前为单模型场景写的解析代码。
3.5 在Python项目里接入网关
如果你的项目原本用官方SDK,大多数情况下可以把base_url指到网关地址,key换成虚拟key,模型名换成外部名。即使是用litellm自己的SDK,也支持指定api_base:
import litellm response = litellm.completion( model="chat-main", api_base="http://localhost:4000/v1", api_key="<你的key>", messages=[ {"role": "user", "content": "你好,请一句话介绍你自己"} ], ) print(response.choices[0].message.content)这段代码的好处是,后续如果想绕过网关直接调后端(比如做某些离线分析),只需要把api_base换掉,业务逻辑完全不用动。
3.6 首次上手容易踩的配置细节
我帮别人排查过不少网关启动问题,排在最前面的几个都是配置细节。
第一,漏了provider/前缀。model直接写gpt-4o,litellm会按默认provider解析,虽然有时也能通,但一旦涉及服务商特有的参数,行为就会变得不可控。我建议所有模型都写成完整前缀形式,例如openai/gpt-4o、anthropic/claude-3-5-sonnet、gemini/gemini-1.5-pro,这些前缀本质上是路由标识,不是对某家公司做背书,你完全可以按自己环境改成其它别名。
第二,key放错位置。有人会把服务商key填到master_key,或者把master_key当服务商key用,结果启动时数据库都建不起来。记住:master_key是网关自己的管理员密钥,litellm_params.api_key才是后端模型的真实key。
第三,改完配置不重启。litellm有些配置需要重启进程才生效,改完配置文件后别急着发请求,先重启再查/health。
4. 路由与容灾实验:优先级、按成本调度和fallback链路
4.1 路由策略不是玄学
当同一个对外模型名下挂着多个后端实例时,litellm就可以在它们之间做路由。官方文档里列了多种策略,大体上分为几类:随机/轮询这类简单负载均衡、最小并发这类偏向空闲实例的策略,以及按延迟、按成本、按预算用量做决策的智能路由。它们各有适用场景,比如成本敏感的业务适合低成本优先,响应速度敏感的业务适合低延迟优先。
我在团队里实践下来的经验是:不要一上来就迷信智能路由。先把随机或轮询跑通,确认两种模型返回的格式在你的业务里都能被解析,再切换按成本或按延迟的策略。否则一个模型偶尔输出结构不同,会让智能路由的收益瞬间被兼容成本吃掉。
4.2 用同一个外部名挂多个后端
litellm允许多个后端实例注册在同一个model_name下,这相当于把一个模型组看成一个池子。下面是一个简化的示例:
model_list: - model_name: chat-pool litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY - model_name: chat-pool litellm_params: model: anthropic/claude-3-5-sonnet api_key: os.environ/ANTHROPIC_API_KEY两个后端都叫chat-pool,业务侧请求chat-pool时,网关会根据当前路由策略决定走哪个。注意不同版本对同名模型组的具体实现略有一点差异,有的版本需要用额外的分组别名配置,但这套思路是通用的:对外一个名字,对内多个实例备选,这也是做灰度切换和容灾的基础。
4.3 fallback链路与重试
比路由更常用的是fallback。在litellm里可以给请求配置一组备选模型:
import litellm response = litellm.completion( model="chat-main", api_base="http://localhost:4000/v1", api_key="<你的key>", fallbacks=["claude-main", "gemini-main"], messages=[ {"role": "user", "content": "你好"} ], )主模型失败或超时后,网关会依次尝试claude-main和gemini-main。这里的“失败”包括网络错误、限流、HTTP 5xx这类服务端错误,业务层的错误码通常不会触发fallback。所以如果两个模型的输出结构差异很大,你还要在上层做一轮校验,确保fallback回来的数据格式能继续走后续流程。
4.4 我做过的一次容灾实验
之前为了验证全链路可靠性,我做过一个很简单的实验:在网关里注册两个可用模型,把其中一个模型的key故意改错,然后让这个坏模型成为首选。连续发了几十条请求,观察响应内容,确认每次都fallback到了另一个模型,同时日志里有清晰的模型切换记录。
做这类实验时,我建议关注三点:响应的model字段是否真的发生了切换、响应耗时是否因为fallback增加了、以及上游错误是否被正确地记录到告警系统。如果只验证“请求没报错”,容易忽略fallback链路上的性能损耗和日志质量问题。实验完成后记得把测试key吊销,别让它留在环境里。
5. 虚拟密钥和预算管控:把模型费用切给不同业务线
5.1 为什么虚拟key是必须的
网关一旦铺开,真实服务商key就更不能直接发给开发了。litellm的虚拟key体系相当于一个身份代理层:每个key绑定某个用户、团队或项目,网关根据key决定你能够使用哪些模型、有多少预算、多久过期。这样一来,真实key的暴露范围被限制在网关上,业务侧拿到的是一个权限受控、额度受限的临时身份。
我见过一些团队觉得“我就几个开发,用master_key算了”,结果某个人在脚本里硬编码了master_key,轮换的时候又要全体改一遍。虽然小团队起步阶段可以用master_key,但至少从第一天就打开key生成接口,凡是机器对机器调用全部用虚拟key,这个习惯能省掉后面大量的治理成本。
5.2 生成一个带预算的虚拟key
管理接口调用示例如下:
curl http://localhost:4000/key/generate \ -H "Authorization: Bearer <master_key>" \ -H "Content-Type: application/json" \ -d '{ "team_id": "team-image", "max_budget": 50, "budget_duration": "30d" }'返回里会包含一个sk-开头的新key。这个key在30天内最多消耗50美元(默认币种),超过额度后网关会直接拒绝请求并返回配额错误。每次生成的key都归属team-image这个团队,后续查询花费时就能按团队看到明细。
这里有个容易忽略的点:max_budget的口径是网关侧的估算,基于litellm内置的单价表和token统计,而不是服务商的最终账单。正因为服务和计费口径存在差异,预算更适合作“软上限”和告警线,而不是财务结算的精确依据。
5.3 预算口径与对账习惯
litellm会基于模型单价表和token消耗来计算花费,这个数据适合做趋势监控,但不要指望它和每家服务商的账单逐分吻合。不同服务商对图片、缓存、提示词的计费方式各有差异,尤其多模态输入,估算偏差可能很可观。我的做法是每周从/spend导出一份报告,再和服务商后台的消费明细做比对。偏差持续偏大时,就去调整模型配置里的单价假设,让它尽量贴近真实成本。
另外一个常见需求是给某个标书项目单独记账。你只需要给它单独生成一个team的key,所有相关调用都通过它转发,月底想看项目成本,直接查这个team的花费总和。这种按业务线分摊成本的能力,在内部结算场景里非常实用。
5.4 最小权限自查清单
- master_key只保存在网关服务器的环境变量或密钥管理服务里,不写进仓库。
- 每个业务线至少一个team_id,避免所有调用混在同一个key下。
- 对只读类任务,生成权限受限的key,而不是直接复用管理key。
- 定期轮换长期key,可以用管理接口重新生成并吊销旧key。
- 打开花费查询权限,给需要对账的运维同学使用,不要在服务器上对所有人开放shell。
6. 那些常规文档不会告诉你的坑
6.1 token口径不一致导致成本报表失真
这是我会给后面接入的人提醒得最多的一个坑。litellm计算的成本基于它内置的token估算和单价表,而各家服务商实际计费有自己的口径,尤其是图片token,往往不是简单按固定值就能估算准的。结果就是,网关报表里显示的费用可能和账单有百分之十几甚至更多的偏差。如果你把预算设得很紧,可能会因为这个偏差误伤正常业务。
对策分两层。第一,把预算值作为“告警线”而不是“硬截断线”,留出缓冲。第二,定期对账,发现偏差大时调整模型配置里单价相关的自定义项。如果你只是需要相对趋势,那直接用litellm报表即可;如果你要做财务级核算,还是以服务商账单为准。
6.2 大payload与超时
多模态接口很容易踩超时。一张图片转成base64塞进messages后,请求体可能轻松到几兆字节,有些时候还要传多张,网关在解析、转发、转码之间消耗的时间会明显变长。如果你用默认的HTTP超时配置,就会发现大文件请求偶尔“随机”失败,其实都是超时。
我的经验是两条:一是尽量把图片传到对象存储,给网关传URL而非base64;二是确实要传base64时,在SDK和网关两端都把超时时间调大,并给这类请求单独配置一个较长的超时阈值,不要和普通文本请求混用一个超时配置。同样重要的是,不要把base64完整写进日志,否则一段日志可能几MB,磁盘和日志平台都撑不住。
6.3 配置里的密钥泄露
我见过有人在config.yaml里直接写api_key,启动日志会把配置内容打印出来。如果当时开着详细调试模式,key就这样出现在日志文件里。更隐蔽的是,如果这类文件被误提交到版本库,历史记录里就永远有这份明文key。正确做法是用os.environ/变量名语法,把真实key放到环境变量或容器密钥文件里,config.yaml里只保留引用。
用容器部署的时候,建议把密钥文件挂载为只读,不要在镜像构建时把密钥打进环境变量。litellm读取环境变量时也支持常规方式,配合容器平台的secret机制可以做到配置文件和密钥完全分离。别贪图方便在本地把key写死,这个坑一旦踩了,清理成本很高。
6.4 健康检查的“虚假繁荣”
/health接口能告诉你某个模型是否已注册、网络是否通、后端是否可达,但健康不代表发送真实请求一定成功。有一次服务商把一个模型下线,但网关的健康检查仍然显示正常,因为检查请求只做连通性测试,没有真正发起推理请求。结果一批生产流量打到这个模型上,直到错误率飙升我们才发现。
从那以后,我的线上策略是把“健康检查”当作“注册检查”,把“请求错误率”当作真正的健康指标。新模型上线先灰度,让少量相关流量试跑,确认正常后再全量切。健康检查可以辅助你快速发现连接性问题,但不要把它当成模型可用性的唯一依据。
6.5 重试风暴
litellm默认会有一定次数地重试,这在单请求场景下很友好,但如果大量请求同时失败,统一重试可能会形成放大效应。最典型的场景是某家服务商限流,网关里的几十个并发请求同时进入重试,几乎在同一时间再次打到限流接口上,反而延长了故障时间。
缓解方法是在网关上放开合理的速率限制,并对重试延迟做退避处理。业务侧也要有降级意识:如果是面向用户的低延迟场景,宁可快速失败并返回提示,也不能让用户等三次重试的时间。
7. 可观测性:把网关看透彻再谈优化
7.1 指标暴露到Prometheus协议
litellm可以按Prometheus协议暴露指标,对外提供/metrics端点。这个设计让我可以把它纳入已有的监控体系,直接用告警规则覆盖“错误率突增”“延迟过高”“成本日环比异常”这些场景,而不是只靠人工登录看日志。
常用指标主要围绕请求数、成功率、延迟、token用量、成本这几个维度。按团队和模型拆分后,可以快速定位是哪个业务线、哪个模型把成本拉高了。我一般会按模型分组看延迟曲线,一旦某个模型p95延迟爬升,结合路由策略就能判断要不要调整流量分配。
7.2 一次请求的完整追踪路径
可观测性的另一个关键是日志。litellm的日志里包含request_id、命中模型、路由结果、耗时和token消耗等字段。线上问题排查时,我习惯先按时间窗找到这个request_id,然后拉出这条请求的完整链路,看它到底走了哪个后端、有没有触发fallback、最终耗时多少。这条链路比起去服务商后台大海捞针,定位速度快得多。
有条件的话,建议把请求级指标和业务告警打通。例如当某个模型错误率高到阈值,自动生成一个事件,同时在指标里标记对应的模型名。这样问题还没传到用户耳里,值班的人已经从曲线和日志里知道是哪条链路出了问题。
7.3 请求与响应的抽样存储
为了复盘prompt和输出质量,可以开启请求/响应摘要的存储或转发。这里尤其要注意脱敏:别把完整聊天内容全存下来,存关键字段、摘要、token用量和路由结果就够复盘用了。我见过因为把用户原始输入存进日志,后来在合规审查环节非常被动的团队。日志和追踪系统只保留业务需要的信息,是网关上线前就要想清楚的事。
最后聊一点我自己的体会。litellm的配置项很多,刚接触很容易被路由、预算、回调这些概念唬住。我的建议是先把架构想清楚:业务代码只认外部model_name,后端真实模型在配置里随便换,密钥按团队分,预算先设一个宽裕的软上限跑起来。等流量稳定了,再把基于成本的路由、灰度切换这类高阶玩法加进去。这个工具解决的是多模型管理的复杂度,真正决定网关稳不稳的,还是你对自己的路由策略和成本口径有多了解。如果你正在评估要不要上模型网关,我的意见很直接:先把一个最小可用的litellm跑通,再把一个模型接入进去,其他配置等有了真实流量再慢慢养,这条路比一开始就追求面面俱到要稳得多。