ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

基于litellm构建AI网关:多模型统一接入、容灾与成本管控实践

基于litellm构建AI网关:多模型统一接入、容灾与成本管控实践

年初接了一个多模态问答项目,业务方拍板要求同时在三家模型供应商之间做切换和容灾。我第一版做了一个专门转发请求的内部服务,两周之后那个转发服务比业务代码还难维护——新接一家模型要改代码、重发版,限流重试逻辑散落在各个适配器里,月底对账全靠手工拉账单。后来我把整条调用链路重构为基于 litellm 的 AI 网关方案,多模型统一调用、自动重试、预算管控这些事才算真正收敛。

这篇文章就把我对 litellm 的理解,以及从 SDK 到生产网关的完整落地过程写出来。如果你也在做多模型接入、模型切换、成本管控这类事,或者正在纠结"要不要在业务和模型之间插一层",应该能从中找到直接能抄的配置和几条实在的避坑经验。

1. 多模型接入的混乱,从第一版转发服务说起

1.1 一百多个供应商,意味着什么

很多人第一次听到 litellm 的时候,会觉得"这不就是一个统一封装吗"。但真正把业务同时接入多家模型之后才会发现,问题远不是"多写几个 if 分支"那么简单。

每个模型供应商都有自己的一套认证头、模型命名、超时语义、限流返回码,甚至同一个temperature、stop参数在不同平台上的实际行为都不一样。OpenAI 系用Authorization: Bearer,Anthropic 系用x-api-key,本地部署的 vLLM 和 Ollama 又各自有不同的兼容层。你在业务代码里写适配逻辑,第一遍还能勉强跑通,等某个供应商改了模型版本、换了限流策略,维护成本立刻失控。

litellm 做的事情本质上只有一件:把"调用一个大模型"收敛成统一接口。无论对面是 OpenAI 系、Anthropic 系,还是本地起的 vLLM、Ollama,它都用同一套 messages 结构、同一套参数命名去转发,再把返回结果归一化成 OpenAI 风格。听起来简单,做起来非常繁琐——各家在系统提示词处理、多模态输入格式、推理参数命名上的差异是琐碎而真实的。

1.2 我在适配层里踩过的具体坑

我的第一版内部服务用的是"供应商适配器列表"模式,每个适配器单独负责认证和参数映射,请求进来之后按模型名分发。问题集中在三处:

第一,新接入一个模型必须改代码、重新发布。本来只是配置层面的事,却硬生生变成了发布流程,业务催得急的时候特别被动。第二,各家的限流返回码不一致,有的返回 429,有的返回 503,有的在错误 body 里给出quota_exceeded。我的重试逻辑只好到处复制粘贴,最后还经常重试错对象,把上游平台打到更严厉的限流。第三,成本统计完全没有统一口径,财务要数据的时候我只能导每一家的账单手工做透视表,一个月一次,每次都痛。

后来我把这套东西全部换成 litellm,相当于把适配层从"业务代码的一部分"抽离成"基础设施的一部分"。

1.3 litellm 的三个核心抽象

  • 统一模型标识:用provider/model的格式区分模型来源,比如openai/gpt-4o、anthropic/claude-3-5-sonnet。
  • 统一请求结构:始终使用 OpenAI Chat Completions 风格的消息体和参数,多模态内容、函数调用、流式输出都走同一套格式。
  • 统一返回与错误:成功响应里的 choices、usage,错误响应里的状态码和错误类型,都被归一化。

这三个抽象让上层业务可以彻底忘记"这个模型是从哪家买的"。你只需要关心模型叫什么、参数怎么传、返回怎么解析,剩下的认证、协议适配、错误映射全由 litellm 处理。

2. 三种使用姿势:SDK、CLI、网关

2.1 作为 SDK 用时,代码长什么样

最简单的方式是在 Python 项目里直接装包:

pip install litellm

然后调用不同家的模型,代码几乎是一样的:

import litellm response = litellm.completion( model="openai/gpt-4o", messages=[{"role": "user", "content": "用一句话介绍杭州"}], max_tokens=512, ) print(response.choices[0].message.content)

换成另一家,只需要改 model 前缀和对应的 API Key:

response = litellm.completion( model="anthropic/claude-3-5-sonnet", messages=[{"role": "user", "content": "用一句话介绍杭州"}], max_tokens=512, )

API Key 可以通过环境变量注入,也可以在调用时用api_key参数显式传入。多模态内容同样沿用 OpenAI 的格式,不用为每家单独写一套 content 结构。

这种 SDK 模式适合的场景是"你自己就是一个独立应用,模型切换频率不高,也没有多团队共享需求"。它最大的价值是让代码里不再出现针对具体厂商的 import,后续换模型只需要改模型名。

2.2 用 CLI 做快速验证

除了在代码里调用,litellm 还提供了命令行工具,适合做快速连通性测试。比如我想验证某个 API Key 有没有配错、某个模型在当前网络环境下能不能通,直接跑:

litellm --model openai/gpt-4o --messages '[{"role": "user", "content": "ping"}]'

不用起一个 Python 工程,也不用写测试脚本,几秒钟就能确认一个模型是否可用。这个习惯帮我省了不少事——每次新接入一家供应商,我都先用 CLI 打通,再进配置层,最后才是写业务代码,避免拿着没验证过的配置去改代码,出了问题分不清是配置错还是代码错。

2.3 网关模式才是大多数生产环境的选择

litellm 真正的重头戏是它的代理服务模式,也就是 AI 网关。起一个常驻服务,业务方不再直连各家模型,而是统一打到这个网关,网关再按配置转发到真实模型供应商。

pip install 'litellm[proxy]' litellm --config config.yaml --port 4000

启动之后,网关会暴露一个 OpenAI 兼容的接口。业务代码里只需要把base_url指向网关地址:

from openai import OpenAI client = OpenAI( api_key="sk-虚拟密钥", base_url="http://localhost:4000", ) resp = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "你好"}], ) print(resp.choices[0].message.content)

业务团队拿着标准的 OpenAI SDK 就能访问所有模型,这解决了前面说的"新接模型要改代码发布"的问题——接入新模型,只需要在网关配置里加一行,业务代码一行不动。

三种使用姿势的选型逻辑我整理成一张表:

使用方式适合场景主要优势主要代价
SDK 模式独立应用、模型切换不频繁轻量、无额外组件每台机器都要管密钥,切换仍需发版
CLI 模式调试、连通性测试零成本快速验证不适合生产调用
网关模式多服务共享、成本管控、模型容灾集中配置、统一审计、灵活切换多一个组件要运维,需要设计容错

3. 网关的核心配置拆解:模型注册、路由与密钥

3.1 config.yaml 的基本骨架

网关模式的入口是一份 YAML 配置,它决定了网关知道哪些模型、怎么转发、按什么策略路由。一个最简配置是这样的:

model_list: - model_name: gpt-4o litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY - model_name: claude-sonnet litellm_params: model: anthropic/claude-3-5-sonnet api_key: os.environ/ANTHROPIC_API_KEY litellm_settings: drop_params: true

这里有两个字段容易混淆,我具体解释一下:

  • model_name是暴露给业务方的"别名",业务代码里请求的就是这个名字。
  • litellm_params.model是真正要调用的"上游模型",必须带provider前缀,用于告诉 litellm 走哪一家的协议。

把这两层分开设计,是 litellm 用得好的关键。比如你上游从openai/gpt-4o迁到openai/gpt-4.1,业务里请求的gpt-4o这个别名可以不动,只改网关配置里的上游模型,就完成了透明升级。

3.2 model_list 的路由与负载均衡设计

当同一个别名背后挂多个上游模型时,litellm 会自动按策略分发请求。比如我希望 gpt-4o 承担主要流量,同时把一部分请求分流到另一个模型做压测,可以做这样一个配置:

model_list: - model_name: gpt-4o litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY rpm: 1000 tpm: 100000 - model_name: gpt-4o litellm_params: model: anthropic/claude-3-5-sonnet api_key: os.environ/ANTHROPIC_API_KEY rpm: 800 router_settings: routing_strategy: usage-based-routing num_retries: 2 fallbacks: [{"gpt-4o": ["fallback-model"]}]

routing_strategy可以配置为简单轮询、最少请求数、基于用量、基于延迟等不同策略。rpm和tpm是每模型每分钟请求数和 token 数的上限,litellm 会基于这些参数决定要不要把流量切到另一个可用模型,避免把某个上游打爆。

这里有一个很重要的设计思路:别把路由策略当成负载均衡器用,它首先是容灾工具。我在实际项目里习惯让主模型承接绝大多数请求,备模型兜底,只有主模型触发限流或返回 5xx 时才切换。这样行为可预期,不会出现因为路由策略太智能导致线上模型行为忽高忽低的情况。

3.3 虚拟密钥、预算与多租户隔离

网关模式还有一个业务上很实用的能力:虚拟密钥。它相当于给每一条调用链发一张独立的"子卡",可以单独设预算、单独看用量。

curl -X POST http://localhost:4000/key/generate \ -H "Authorization: Bearer sk-admin-key" \ -H "Content-Type: application/json" \ -d '{"max_budget": 100, "tpm_limit": 50000}'

生成出来的sk-开头的密钥给业务方用。每个密钥可以绑定某个团队、某个项目或某条业务线,网关会把这段密钥的调用量和费用单独记账。业务方用同一个模型,但不同的虚拟密钥配不同的预算上限,超了就不放行。这让成本管控从"月底算总账"变成了"实时拦截超支"。

多租户隔离是很多团队忽略的点。如果你们有多个项目组都要用大模型,又不希望 A 项目超支影响 B 项目的额度,虚拟密钥几乎是必须的。它的安全性同样重要——数据库里存的是哈希后的密钥,内存里才能看到明文,避免数据库泄露导致密钥直接暴露。

4. 生产环境必须处理的韧性细节

4.1 超时、重试与 fallback 的正确打开方式

模型调用天然有不确定性:上游可能限流、可能超时、可能返回 5xx。网关落地的第一件事就是把重试策略想清楚。

litellm 的router_settings里可以直接配重试次数:

router_settings: num_retries: 3 retry_after: 5 cooldown_time: 30

重试逻辑我建议遵循一个原则:对用户可见的长请求,重试次数别超过 2 次。模型生成动辄几十秒,每次重试都在成倍增加用户的等待。与其依赖重试解决所有问题,不如把重心放在 fallback 上——第一个模型不行,第二个模型顶上,而不是同一个模型反复撞墙。

fallback 配置很直接:

router_settings: fallbacks: [{"gpt-4o": ["claude-sonnet", "local-llama"]}]

意思是gpt-4o这个别名失败了,依次尝试claude-sonnet、local-llama。这里有一个经验:fallback 链上不要放同一家供应商的另一个模型,否则上游故障时一起挂,失去容灾意义。

4.2 缓存:省钱的甜头与业务正确性的代价

litellm 网关支持结果缓存,配置文件里打开就行:

litellm_settings: cache: true cache_params: mode: default

默认是精确缓存——请求的 messages 完全一样,直接返回之前的答案,不再调用上游。更进阶的语义缓存会做向量相似度匹配,近似问题也走缓存。

我在项目里对缓存的选用非常谨慎。对知识库问答、固定话术类场景,精确缓存能省不少 token 费用,这个收益很可观。但对创作类、决策类场景,缓存会让用户拿到"别人的答案",体验很怪。另外,模型版本升级后,旧缓存内容可能已经过时。建议给缓存设 TTL,并且在模型版本升级后主动清理缓存池。

说一个实际的教训:有一次我在一个客服场景开了语义缓存,结果新政策发布后,部分用户仍然拿到旧政策答案,因为相似度匹配把新问题的提问识别成了旧问题。从那以后,涉及时效性信息的场景我再也不开语义缓存,只保留短 TTL 的精确缓存。

4.3 成本与 token 统计的真实口径

litellm 网关在配置数据库后,会记录每一笔请求的模型、token 用量和估算费用。这里的"估算"两个字很关键——有些上游返回的 usage 字段并不完整,litellm 会根据模型定价表补算,和实际账单之间存在偏差。

三个口径问题我在生产环境里真实遇到过:

  • 上游返回的prompt_tokens和completion_tokens之和,不等于账单上的 token 数,因为有些平台把多模态输入的图像计算成额外 token,并不体现在这两个字段里。
  • 流式请求的 usage 可能为空,litellm 会在结束后用最后一段 chunk 去补算,但如果请求中途断了,这笔用量就可能缺失。
  • 网关记账是"近似预算",真正的财务对账还是要以供应商账单为准。

所以我的建议是:用网关做实时预算拦截和趋势分析,用供应商账单做最终对账。两条线并存,谁都不能替代谁。记账数据库我强烈建议用 Postgres 而非 SQLite,尤其是多副本部署的时候。SQLite 在并发写入和高可用上都会成为瓶颈。

5. 线上跑一段时间后,我复盘出的踩坑清单

5.1 模型名不一致导致的"隐性降级"

这是我在生产上踩过最深的一次坑。当时为了业务侧好记,把别名直接设成了gpt-4o,但上游实际配的是某家兼容 OpenAI 协议的模型。某天那家平台限流,fallback 触发,流量悄悄切到了另一个模型。业务方看到调用记录里的模型名还是gpt-4o,完全没意识到实际回答已经换了一个模型,导致线上出现一批风格迥异的回答。

正确做法是让别名带有明确的业务语义,比如main-qa-model、heavy-reasoning,而不是直接暴露具体模型名。这样即使内部把上游从 A 换成 B,业务侧看到的名字不会变化,也就不会有"名字没变但行为变了"的困惑。

5.2 流式响应下超时判断失准

网关模式下,业务方与网关之间通常是 HTTP 流式连接,网关与上游之间也是流式连接。默认请求超时如果设得太短,长上下文的首次 token 等待很容易超时;设得太长,上游真正挂死时又无法及时感知。

我的调法是分两层:网关到上游的请求超时按模型上下文长度分级——短上下文模型 30 秒,长上下文模型放宽到 90 秒;网关到业务方的连接不设硬超时,而是让业务侧根据首 token 延迟做感知。首 token 超过 20 秒还没有任何数据返回,就可以主动断开并走业务侧重试。

另一个容易漏的点是流式 chunk 之间的空闲间隔。上游生成过程中,如果长时间没有新 chunk,连接可能在半路假死但没有关闭。litellm 本身有读超时机制,但我在业务侧仍然加了兜底——客户端持续 60 秒收不到任何数据就主动断开。两边都设保护,才敢放心上生产。

5.3 计费数据更新延迟

网关的虚拟密钥配额是按记账数据实时扣减的,但记账数据写入数据库存在延迟。如果业务方在同一秒内并发打大量请求,有可能在记账落库之前就突破预算上限。对预算管控比较严格的场景,我建议把虚拟密钥的 rpm 也设置一个低于预算上限的硬限制,用两道闸来防止超支,而不是只依赖预算这一个软限制。

5.4 网关本身的故障怎么隔离

litellm 网关虽然是无状态的,但引入它之后,它就成为了业务链路里的一个关键节点。网关挂掉,所有模型调用一起挂。我的处理方式分三层:

  • 网关多副本部署,前面挂负载均衡,实例健康检查用网关自带的健康检查接口,而不是只检查端口。
  • 网关的配置和数据都放在外部数据库里,不让单实例的本地状态影响整体可用性。
  • 业务侧做超时熔断,网关连续失败达到阈值就降级为直连某个固定模型,保证核心链路不完全断开。

这种设计思路是:网关应该帮你隔离上游故障,而不是变成一个新的故障点。

6. 部署形态与选型建议

6.1 单机容器部署和集群部署怎么选

如果只是团队内部调试或者流量不大,用 Docker 单容器起一个网关就够了。关键是把配置文件和数据库目录挂载出来,避免容器重启后配置丢失。

流量上来之后,网关要按无状态服务部署。我目前的生产形态是 Kubernetes 里跑多个副本,数据存 Postgres,配置存在 ConfigMap 里,密钥通过环境变量注入。这里有个容易踩的坑:apex这类带状态的功能(比如跨副本共享的密钥缓存)需要依赖数据库或 Redis,如果只部署多个纯内存副本,虚拟密钥的实时扣减会各自为政。所以多副本部署的第一个检查项,就是把数据库和缓存组件配齐。

6.2 与自研网关、云厂商原厂网关的对比

我在选型 litellm 之前,认真对比过三个方向:自研转发服务、云厂商原厂网关、litellm 开源网关。

维度自研转发服务云厂商原厂网关litellm 网关
接入新模型成本高,要写适配代码低,但局限于云厂商生态低,配置即可
跨厂商容灾要自己实现部分支持,绑平台原生支持
成本追踪自己建表依赖平台账单按密钥/团队/项目拆分
运维成本最高最低中等
灵活性最高但代价大最低较高

自研服务的优势是逻辑完全可控,但代价巨大——你写的每一个适配器、每一段重试逻辑,都要长期维护。云厂商原厂网关胜在省心,但要注意绑定问题,往别的模型迁移时要重新配置。litellm 的定位恰好是中间档:比自研省事,比原厂网关灵活。

6.3 什么时候应该不用它

不是所有项目都需要插一层网关。我见过不少团队把 litellm 网关引进来,仅仅因为"别人都在用",最后发现自己只有一个模型、一个应用、也没人对账需求,白白多维护一个组件。

我的判断标准是三选一,命中任何一条再考虑网关:是有多个模型要切换,是有多个团队要共享模型能力,是有预算管控和成本审计需求。如果以上都不沾边,直接在应用里用 litellm SDK 就够了,不要为了架构的"完整感"去增加复杂度。

部署调试的时候有一个小习惯很实用:先在本地用 CLI 把上游模型打通,再起一个临时网关实例验证配置,最后才部署到集群。这样每层问题都被隔离在对应层,不会出现"上游不通还是网关配错"互相甩锅的情况。

我在实际项目里使用 litellm 最重的功能其实是预算管控,而不是多模型统一调用。真正跑起来之后你会发现,统一调用只是第一步,成本可视、超支拦截、团队分摊这些管理能力,才是网关在长期维护里最有价值的部分。如果你现在正好在规划接入第二家模型供应商,建议把成本统计和密钥管理这件事从第一天就放进方案,而不是后面再来补。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进