今天聊一个我在多个项目里反复复用的工具:litellm。简单说,它是一个把大量模型服务商的接口收敛成标准HTTP API的“模型网关”。你只需要按同一套参数写业务代码,剩下的鉴权、路由、重试、限流、成本记录,都由litellm在中间层搞定。不管是AI原型Demo、内部工具,还是给整个团队统一接模型出口,litellm都足够轻。这篇文章适合两种人:一是后端或算法工程师,想让多模型接入不再绑定某家SDK;二是团队的技术负责人,想在成本和质量之间拿到可控度。我会从为什么需要它讲起,一直讲到生产环境里能直接抄的配置和排坑。
1. 为什么开发团队会需要一个“模型网关”
1.1 真实项目里接入多模型的混乱状态
之前做一个智能客服项目,刚开始只需要一个文本生成模型,代码里直接import对应厂商的SDK,调两三个接口就完事。不到一个月,产品说要对比另一个厂商的效果;再后来,还需要接一个开源模型自己部署。麻烦就来了:每个厂商的SDK风格不一样,有的用chat对象,有的用messages,参数叫法也不一样,超时、重试、流式返回的细节全是差异。业务代码里开始出现一坨判断分支:“如果用A厂商,走A初始化逻辑;如果用B厂商,走B处理返回”,甚至还有把不同厂商的返回强行结构化成统一对象的工具函数。这个函数越写越长,最后连测试都不敢动了。
这种状态下,最需要的不是再封装一层,而是把“所有模型调用”统一到一个入口,让上层只面对一种接口。litellm就是干这件事的。它的核心思路很简单:把各家模型服务商的请求和响应都转换成同一套“chat/completions风格”的格式,业务端只需要知道一个端点、一种请求体、一种返回结构。你不需要关心背后到底是哪个厂商的哪款模型,换模型只是把配置里的模型名改掉,代码一行不动。
1.2 litellm的定位:不是SDK,而是一层代理
有的人会问,这会不会和某些模型编排框架里的模型调用封装差不多?我在项目里也用过编排组件,但两者定位不一样。编排框架更像是一个应用开发框架,把Prompt、Chain、Agent、Memory都编排在一起;litellm则更薄,只聚焦在“把一次模型调用发出去并拿回结果”。它既可以作为Python包嵌入你的服务里,直接调用litellm.completion();也可以作为一个独立的网关服务启动,暴露一个HTTP接口给所有客户端。
我理解litellm的价值在于“接口收敛”和“治理能力”。接口收敛好理解,所有模型统一格式;治理能力是说,当团队里几十个服务都要调模型时,你不能让每个服务自己配API Key、自己处理重试,那会出现有的服务疯狂调用、烧钱失控,有的服务没有重试导致线上报错。你希望所有流量经过一个点,然后在这个点上统一做鉴权、限流、预算、日志。这个点就是litellm网关。
1.3 它解决掉的主要问题清单
从实际收益看,litellm帮我解决了几类问题。
第一是兼容与迁移成本。如果有一天要更换模型厂商,不需要改业务代码,甚至可以把两个厂商配置成同一个model_name,通过权重把一部分流量切到新模型,做灰度切换。第二是稳定性和容错。某个厂商出现限流或宕机,litellm可以自动重试、自动切换到备用模型,上层服务几乎无感知。第三是成本管控。可以给每个应用、每个API Key设置预算上限,超了直接拒绝调用,避免有人误跑了超大Prompt导致账单爆炸。第四是观测性。它会把每一次请求的模型名、Token用量、延迟、费用记录到日志里,方便后面做分析。这些功能单独实现每一件都不难,但集中在一起、还能用配置文件声明式管理,就会省非常多精力。
2. 3分钟跑通最小可用链路
2.1 安装和环境变量准备
先讲最直接的用法,不搞复杂架构。安装很简单,Python 3.8以上,执行pip install litellm。我建议在虚拟环境里装,别直接装到系统环境。装完可以用litellm --version看一眼版本,如果打印出版本号就说明装好了。
然后需要准备一个测试用的API Key,用环境变量方式传给litellm。注意:litellm的很多上游服务商都会自动从环境变量读取Key,比如你设置DEMO_PROVIDER_API_KEY=sk-123456,然后在配置里写api_key: os.environ/DEMO_PROVIDER_API_KEY,它就会读取。这一步很容易被忽略,我见过好几次“明明配置了Key但一直鉴权失败”的问题,最后发现是环境变量名没对上。建议所有Key先放到环境变量里,不要硬编码进config文件。
2.2 用Python SDK发起第一次调用
如果你只是想快速验证某个模型能不能通,可以不用启动服务,直接用Python调用。示例:
import litellm response = litellm.completion( model="demo-provider/chat-model-v1", messages=[ {"role": "system", "content": "你是一个简洁的助手。"}, {"role": "user", "content": "用一句话解释什么是API网关"} ], temperature=0.7, max_tokens=200 ) print(response.choices[0].message.content)这段代码里模型名写的是demo-provider/chat-model-v1,实际使用时要替换成litellm支持的厂商前缀加具体模型名。第一次跑通后,你会看到返回的是一个常见的标准化响应结构,choices[0].message.content就是文本内容。如果模型服务商支持流式结果,你可以加stream=True,然后循环读取增量,后面做打字机效果会很自然。
2.3 把litellm作为HTTP代理网关启动
更常见的做法是直接启动一个网关服务,让多个业务共用。新建一个config.yaml:
model_list: - model_name: "main-chat" litellm_params: model: "demo-provider/chat-model-v1" api_key: "os.environ/DEMO_PROVIDER_API_KEY"然后在终端执行:
litellm --config config.yaml --port 4000看到启动日志里出现HTTP服务进程监听信息,说明网关起来了。现在业务端完全不用管背后的模型厂商,只需要向http://localhost:4000发标准的chat/completions请求,模型名填main-chat即可。用curl验证特别直观:
curl http://localhost:4000/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer sk-demo-master-key" \ -d '{ "model": "main-chat", "messages": [{"role": "user", "content": "你好"}], "stream": false }'这里Authorization里的key,默认可以先用配置文件里指定的master key,后面再做成虚拟Key分发给不同应用。到这里,你已经拥有了一个最简模型网关:所有客户端都往它转发请求,网关统一包揽转发、鉴权和响应格式化。
3. 生产级网关配置拆解:config.yaml里到底要写什么
3.1 多模型注册与模型映射关系
生产环境基本不可能只有一个模型,所以需要同时注册多个模型,并映射成一个统一对外名称。下面是一个多模型配置片段:
model_list: - model_name: "main-chat" litellm_params: model: "demo-provider/chat-model-v1" api_key: "os.environ/DEMO_PROVIDER_API_KEY" api_base: "https://api.demo-provider.example.com" model_info: max_tokens: 8192 - model_name: "main-chat" litellm_params: model: "demo-provider/chat-model-v2" api_key: "os.environ/DEMO_PROVIDER_API_KEY" - model_name: "embedding-one" litellm_params: model: "demo-provider/embedding-model-v1" api_key: "os.environ/DEMO_PROVIDER_API_KEY"我在这里对外统一叫做main-chat,但底下挂了两个不同版本的模型。这样做的意义在于:你可以随时把chat-model-v1的配置改成chat-model-v2,而调用方不用感知变化。对外暴露的模型名、实际调用的后端模型,通过config就分离了。很多团队上线时喜欢做法是,创建main-chat-stable和main-chat-canary两个名字,分别指向老版本和新版本,然后通过网关层调整权重流量,这样比直接改代码安全得多。
另外要注意api_base。如果服务商提供的是兼容主流接口风格的端点,可以通过这个字段指向自定义地址;不管是自建推理服务还是第三方兼容平台,都能用同一套方式接入。
3.2 限流和预算参数该怎么设
模型网关最容易失控的就是流量。没有限流时,某个业务方写了个死循环,每分钟发几千次请求,账单立刻起飞。litellm支持在模型级别和全局级别配置限流参数。我常用的配置:
general_settings: master_key: sk-demo-master-key max_parallel_requests: 50 model_list: - model_name: "main-chat" litellm_params: model: "demo-provider/chat-model-v1" api_key: "os.environ/DEMO_PROVIDER_API_KEY" rpm: 120 rpd: 50000字段含义也很直白:rpm是每分钟最大请求数,rpd是每天最大请求数。你可以根据业务预估的峰值来设定。比如一个客服机器人,平均每秒2个请求,峰值每秒5个,那每分钟120个基本够用;如果怕某个团队刷爆预算,还可以配合rpd限制单日总量。
预算部分,litellm支持给每个Key设置max_budget,用美元计。当这个Key的累计消费超过上限,网关会直接返回429或403,阻止后续调用。我建议预算别拍脑袋设,先看一周的实际Token用量曲线,再在均值上乘以1.5到2倍作为上限,既不会太紧,也能防住异常。
3.3 重试、超时和备用模型降级
真实生产里没有一个上游服务能保证100%可用。litellm默认在遇到限流、网络抖动、HTTP 5xx时可以做重试,你可以通过retry_policy控制。一个比较稳妥的配置:
litellm_settings: retry_policy: TimeoutError: retries: 2 APIError: retries: 3 RateLimitError: retries: 3 APIConnectionError: retries: 2 request_timeout: 30这里我按错误类型分别设置重试次数。比如RateLimitError表示上游限流,重试时很有可能是间歇性的,多试几次能扛过去;TimeoutError则要少重试,因为超时说明链路本身慢,重试太多会把网关线程占满。request_timeout设为30秒,既照顾了长思考模型,又不会让请求无限挂起。
备用模型降级也非常实用。可以在调用代码里设置fallback列表,也可以在config里配。代码方式:
response = litellm.completion( model="main-chat", messages=[{"role": "user", "content": "你好"}], fallbacks=["backup-chat"] )当main-chat对应的上游连续失败达到阈值后,litellm会自动尝试backup-chat。这个机制特别适合“主模型贵但效果好,备用模型便宜但可用性强”的场景。要注意的是,fallback的模型必须提前在config里注册,并且在逻辑上保证结果返回格式一致,否则上层接收到的字段会不一致。
3.4 缓存策略:把重复请求成本降下来
很多业务会反复问相同的问题,比如首页FAQ、产品介绍,这些内容完全可以走缓存。litellm默认支持简单内存缓存,也支持外部独立缓存服务。开启方式:
litellm_settings: cache: true cache_ttl: 3600 cache_type: "external" cache_host: "localhost" cache_port: 6379这里cache_type我写的是external,表示接入外部缓存组件;具体填什么值,要看你实际用的缓存服务类型和litellm版本。开启后,只要请求的model、messages、参数组合完全一致,命中缓存就直接返回结果,不再向上游收费。这在做测试、演示、客服冷启动时能省下一大笔。
但我给个直接建议:不要在涉及个性化内容的场景里开全局缓存,比如“根据当前用户资料生成推荐”,每个用户内容都不一样,缓存命中率极低,还会因为TTL导致展示旧内容。更合理的做法是对特定接口开,或者用cache_key精确控制。
3.5 虚拟Key与多团队权限隔离
当网关被多个团队共用时,不能把主Key分给所有人。litellm提供一个虚拟Key的机制:管理员持有master key,然后为每个应用生成一个虚拟Key,每个虚拟Key可以绑定模型访问白名单、预算和限流规则。生成方式:
curl http://localhost:4000/key/generate \ -H "Authorization: Bearer sk-demo-master-key" \ -H "Content-Type: application/json" \ -d '{ "models": ["main-chat"], "max_budget": 100 }'返回结果里会有一串sk-litellm-demo-xxx,这就是要分发给下游应用的Key。这个Key只能访问允许的模型,并且消费预算独立。实际体验里,这套机制帮团队避免了好几次“同事误用了生产Key去调试,结果污染了统计”的问题。你在做生产环境改造时,一定要尽早把虚拟Key分配做进去,而不是让所有服务共用master key。
4. 常见问题与排查技巧实录
4.1 鉴权失败:Key没生效的几类原因
我在部署litellm网关时,遇到最多的就是鉴权问题。第一种情况是配置文件里用了api_key: "os.environ/XXX",但启动litellm的进程里没有设置这个环境变量。解决方案很简单,在启动前用echo $XXX确认,或者干脆在启动命令里带上XXX=... litellm --config。第二种情况是虚拟Key生成后再修改了模型列表,老的虚拟Key不会自动适配新的权限,需要重新生成。第三种情况是客户端没带Authorization头,就拿着裸的model名来问网关,网关当然拒掉。排错时将litellm的debug模式打开,能直接看到具体拒绝原因。
4.2 超时、卡死与流式连接不断开
还有一个常见现象:跑了一个大Prompt,等了30秒没返回,客户端以为挂了。这里要先区分是非流式还是流式。非流式可以通过request_timeout控制;流式场景的问题往往是服务端一直在等上游返回,但上游返回的是流式数据,litellm转成HTTP SSE流过程中,因为客户端不消费或断连导致连接堆积。建议设置一个空闲超时,比如SSE连接超过120秒没有任何新token就断开。另外如果有反向代理层,还要注意代理的proxy_read_timeout也要同步调大,默认60秒会杀掉长思考连接。这个坑很隐蔽,导致明明litellm没问题,前端就是老是断。
4.3 成本统计不准:模型单价配置要提前维护
litellm的成本统计依赖每个模型在model_info里的cost_per_token字段。如果你只是把模型接进来,但没有填单价,后续的“预算控制”就会不准确,甚至按0计算。我的习惯是在接入每个模型时,就根据服务商价格把cost_per_token和cost_per_output_token配好,或者在启动前调litellm.get_model_cost_map()看看当前内置覆盖情况。别忘了,自建私有化模型没有内置价格,一定要手动填。否则你会发现账单报告和实际账单差距很大,那就失去成本管控的意义了。
4.4 与既有模型编排框架集成时的兼容点
很多人是在一个模型编排框架项目里补一个litellm网关。这类框架里的模型客户端组件,默认会连到厂商原生接口,如果用litellm网关,需要把组件的base_url指到http://localhost:4000,api_key填master key或虚拟Key就行。这一步很多教程没讲,导致大家以为不兼容。另外,如果你在编排框架里用了消息历史、工具调用,要确认litellm转发的上游模型本身支持这些参数,否则网关不会替你做降级。litellm只是原样转发消息,不要指望它把一个不支持工具的模型包装成支持工具的模型。
5. 选型建议与我的落地体会
5.1 litellm和自研封装、模型编排框架怎么选
我在项目里见过三种方案。第一种是每个服务自己写一层模型调用封装,简单直接,但一换厂商就要改多处;第二种是引入模型编排框架,功能很全,但对很多轻量应用太重,尤其只想调个接口时,为了模型调用引入一整个编排框架不划算;第三种就是litellm,它把模型调用这层单独抽出来,既能做SDK,又能做独立网关,而且暴露的接口风格是很多人已经熟悉的chat/completions。
我给团队的建议是:如果你的业务逻辑不复杂,就是“给Prompt取结果”,直接上litellm网关;如果你要做复杂的Agent编排、多步工具调用,那可以在litellm之上再搭编排框架,让litellm管模型接入,编排框架管业务编排,两者不冲突。
5.2 生产环境部署时我用到的几件事
生产环境我不会只用单进程跑litellm。首先,litellm是无状态的,前面挂一个负载均衡器,起多个副本,这样就算单实例重启,也不会中断业务。其次,外部缓存要独立部署,不要用默认内存缓存,否则多实例缓存不一致。再次,日志要接入统一日志平台。litellm本身的日志已经包含模型名、Token、耗时、费用,我通常会用一个小脚本轮询它的数据库或日志,把关键指标推到监控系统。如果团队对成本特别敏感,可以每天跑一个定时任务,统计每个虚拟Key的消耗,发一条日报到内部群,这样消费情况完全透明。
5.3 几个我踩过坑后的实操建议
最后分享几个实打实的建议。
第一,刚开始做POC时,不要一上来就配十几个模型,先用一个模型跑通链路,确认日志和成本统计都正常,再逐步加。第二,模型命名要有规范,比如main-chat-stable、main-chat-canary、embedding-prod,不要在配置里写什么model1、model2,否则过两周没人看得懂。第三,主Key和虚拟Key严格分开,主Key只放在部署机器和管理端,面向应用全部用虚拟Key。第四,如果遇到奇怪的上游响应,用litellm --debug模式跑一次,能看到详细的请求与响应详情,这比盲猜高效得多。
到这一步,litellm已经从“能跑通的Demo”变成了“团队模型调用的统一出入口”。它不复杂,但能扛住真实生产里的鉴权、限流、成本、容错这些琐碎问题。我个人的体会是,先把网关做成一个薄薄的中立层,不要过早把业务逻辑塞进去,后续换模型、灰度切换、成本治理都会轻松很多。