用litellm搭建统一模型网关,解决多模型接入难题 1. 做了个模型网关解决了我最头疼的多模型接入问题先交代一下背景。上个月要给一个内部工具接大模型能力一开始需求很简单调一个对话接口把结果返回给前端就行。结果做着做着就失控了。产品那边今天说换某家的新模型试试明天说哪个便宜切哪个后天又要求同一个请求失败后自动换备用模型。代码里全是 if-else 分流每个厂商的 API 格式还不一样鉴权方式、超时时间、错误码全都各搞各的。改到第三版的时候我实在撑不住了决定去找一个统一的接入层。一圈看下来最后锁定了 litellm。litellm 是个开源的模型网关Gateway也叫 LLM Proxy核心作用是把各种大模型 API 收敛成一个统一格式的接口。你的业务代码只需要按照它给的规范发请求至于背后调的是哪家模型、哪个版本、怎么鉴权、超时多久、失败要不要重试全部交给网关处理。用了一段时间之后我的感受是它真正解决的其实不只是接口统一这个表面问题而是把模型调用这件事从一个到处打补丁的脏活变成了一个可以配置、可以观测、可以控制成本的平台工程问题。这篇文章我打算从一个实际使用者的角度把 litellm 从原理、配置、部署到排障的完整链路讲一遍。内容会比较长但我尽量把每一步为什么这么做讲透。如果你正在做 AI 应用开发、负责内部模型的统一接入或者单纯被多模型切换折磨过这篇应该能帮你省不少时间。1.1 这个项目本质是干什么的很多第一次接触的人会把 litellm 理解成又一个模型聚合库。确实它原本最早是个 Python 库提供了统一的调用接口让你用一套代码兼容几十家模型服务。后来项目演进核心能力越来越偏向网关化它起一个服务接管所有模型请求然后在服务端做路由、鉴权、限流、重试、缓存、日志、费用统计这些事。我用一个生活化的类比来解释。假设你的应用是公司前台大模型服务商就是不同的供应商。没有网关的时候你要分别记住每个供应商的电话号码、接电话的人是谁、需要用什么暗号对接每个供应商的业务流程还不一样。有了 litellm 之后你只需要知道总机号码报上工号剩下的事总机会帮你转接、谈判、记录通话内容。甚至某个供应商忙线了总机还会自动帮你换一家。你的业务方完全不用关心供应商换了谁。从这个角度看litellm 解决的是下面这几类核心问题接口统一业务方只面对一个 /chat/completions 风格接口不用为每家厂商写适配层。路由与切换可以在不改业务代码的情况下按模型名、按权重、按优先级把请求分发到不同后端。稳定性兜底支持超时、重试、fallback后端挂了自动切备用模型。可见性每一次调用都有日志、耗时、token 用量、费用记录方便做监控和成本核算。权限与配额可以给不同团队、不同用户发不同的 key设置速率限制和预算上限。对团队来说引入它相当于把怎么调模型这件事从业务代码里彻底剥离出去变成一个独立的基础设施。这个思路和以前做数据库中间层、消息网关是同一个套路。模型厂商会变、模型版本会迭代但你的接入方式始终不变。1.2 我为什么最终选型它而不是自己造轮子其实在最开始我也心动过自己写一个统一调用层。无非就是每个厂商包一层适配函数出错了统一抛异常再写个简单的路由表。做起来似乎不难。但真往深处想有几件事是自研很容易翻车的第一是厂商接口的细节太多。你以为的适配就是把 a 的请求参数映射成 b 的但实际上每家对上下文窗口、多模态输入、工具调用function calling、流式输出的处理方式都有差异。这些细节自己踩一遍坑的成本非常高即使踩完了厂商接口一升级你又要跟一遍。第二是看起来简单做起来很重的功能重试策略、退避算法、熔断、并发控制、token 计量、成本统计。这些要做得好用涉及很多边缘情况。比如流式请求中途断了怎么算、重试时 token 怎么算、预算控制如何在接近上限前告警而不是事后罚款。我评估下来在 litellm 成熟的开源方案上做二次开发比从零写要划算得多。第三是社区和维护成本。litellm 背后有活跃的维护团队和大量生产环境使用者厂商接口变动时基本当天就会有人提 issue、出补丁。这个维护密度是自研根本比不了的。我只需要跟着升级版本蹭到这些修复就行。当然自研也不是完全没价值。如果你的需求极其简单只调一两家模型而且长期不会变那自研几行代码就够用了没必要引入网关。可如果你的场景跟我一样模型注定要换来换去还要多团队共用那选 litellm 这类成熟网关是更稳妥的路。2. 核心细节拆解一次搞懂网关的配置模型litellm 虽然提供了 Python SDK但真正让它在生产环境发挥威力的是网关模式。网关模式的基础是一份 YAML 配置文件里面声明了模型路由、后端地址、鉴权信息、预算策略等。说实话我第一次打开配置模板的时候是有点懵的因为它同时存在好几套概念model name、model_info、litellm_params、deployment 等等。这一节我把这套配置模型拆开来讲。2.1 三层配置结构master key、模型配置、部署路由理解 litellm 的配置关键是记住三个层级第一层网关通用配置。包括 master key主密钥所有请求的通行证、数据库连接、缓存开关、日志级别、管理后台地址等。这些配置直接挂在顶层相当于网关本身的系统设置。第二层模型逻辑名model name。这是你暴露给业务方的名字比如统一的对话接口叫 chat-model不管后面实际用的是哪家厂商的哪个模型。业务方永远只认这个名字。第三层数据中心deployment。同一个逻辑模型名可以挂多个具体部署每个部署对应一个真实的后端模型。litellm 会在这些部署之间做路由、负载均衡和 fallback。举个例子我内部把所有对话需求统一叫 chat-model然后在它下面配了三个 deployment某厂商的旗舰对话模型、某开源模型的本地推理服务、某云厂商的轻量模型。业务方调用 chat-model 时网关默认把流量按权重分给三个后端旗舰挂了就自动切到轻量模型。这是配置带来的核心价值。对应到 YAML 上结构大概是这样的model_list: - model_name: chat-model litellm_params: model: provider-a/biz-flagship-v2 api_key: os.environ/A_PROVIDER_KEY model_info: mode: completion supports_function_calling: true cost_per_million_tokens: 15 - model_name: chat-model litellm_params: model: provider-b/opensource-70b api_key: os.environ/B_PROVIDER_KEY api_base: http://10.0.8.21:8000/v1 model_info: mode: completion你注意看这里的奥妙model_name 相同代表它们是同一个逻辑模型的不同后端litellm_params 里的 model 字段前半段是服务商标识后半段是具体模型版本。api_base 可以指向自建推理服务也可以指向云厂商的兼容端点。model_info 用来声明这个模型支持什么能力比如是否支持工具调用、按什么价格计费。2.2 路由与权重用 yaml 描述生产环境的模型调度策略光有后端列表还不够生产环境需要的是可控的路由策略。litellm 在这里做得很像负载均衡器你可以给每个 deployment 配权重权重高的分到更多请求可以指定优先级编号小的优先只有它失败才用下一个还可以写自己的路由函数。我实际用的比较多的是权重和优先级两种模式。权重模式适合成本优化场景比如我想把 70% 的请求打到便宜的模型上30% 打到高质量模型上就可以在 litellm_params 同级的地方配 weights。优先级模式适合稳定性场景优先用自建模型万一自建服务挂了再走云厂商。配置片段如下model_list: - model_name: chat-model litellm_params: model: provider-a/cheap-model api_key: os.environ/A_PROVIDER_KEY model_info: mode: completion weights: 0.7 - model_name: chat-model litellm_params: model: provider-a/flagship-model api_key: os.environ/A_PROVIDER_KEY model_info: mode: completion weights: 0.3这里要提醒一句如果你的业务对结果质量有严格要求千万别把权重搞得太随意。我在测试中就遇到过一个问题便宜的模型在某些复杂指令下输出质量明显下降但因为权重高大部分请求打到它身上导致整体效果变差。后来我把配置改成按场景拆分逻辑模型名创作类走高质量模型摘要类走便宜模型成本和质量反而都兼顾了。这个经验后面我还会展开。2.3 关键参数备忘超时、重试、缓存、预算除了路由litellm 里最值得花时间理解的是四个隐藏能力超时控制、重试策略、响应缓存、预算管理。这几个参数平时不起眼但真正出问题时全靠它们兜底。超时控制在 general_settings 里配general_settings: request_timeout: 30 connect_timeout: 10 read_timeout: 60我习惯把 connect_timeout 设得短一点因为连接超时通常意味着后端服务有问题没必要等太久read_timeout 设得长一点因为模型生成本来就需要时间太短容易把正常请求误杀。重试策略可以通过 deployment 级别的 retry_after 或者请求参数里的 max_retries 控制。需要注意重试必须配合超时否则一个慢请求会反复占用线程。我踩过一次坑某个后端响应特别慢网关在等待超时后立刻重试结果重试请求又排队最后把整个网关的并发占满别的请求全堵住了。后来我把重试次数限制为 1并且把 read_timeout 调大问题才缓解。响应缓存是个省钱神器。对于摘要、分类、信息抽取这类结果高度可重复的任务开缓存能省掉大部分重复调用费用。配置方式是在请求里加参数或者在部署级默认开启general_settings: default_cache: true缓存默认以请求参数模型名为 key我建议根据自己的业务改成语义相关的方式避免无关参数不同导致缓存命中率低。不过缓存也有副作用如果你用了动态随机参数比如 temperature 随机结果可能是套用旧的要按需取舍。预算管理是 litellm 相对独特的功能。它可以给某个 key、某个团队设置 max_budget美元超过后自动拒绝请求。我第一次配的时候觉得这个功能可有可无直到一个月后账单出来才发现某个测试环境连续几天跑批任务费用超了预期三倍。从那以后所有环境一律先配预算、再开放使用。3. 实操过程从零搭建一个可用网关理论讲完下面进入动手环节。我按自己当初搭建的顺序从安装、配置到接入客户端完整走一遍。你在复现的时候照着做就行大部分步骤不需要改。3.1 安装与初始化litellm 以 Python 包分发安装非常简单前提是你本地有 Python 3.9 以上的环境以及一个虚拟环境管理工具venv 或 conda 都行。mkdir lite-gateway cd lite-gateway python -m venv .venv source .venv/bin/activate pip install litellm[proxy]我先解释一下为什么用 [proxy] 这个扩展名。litellm 本身包含核心库但要让服务以网关模式跑起来还需要加载代理相关的依赖比如 Web 框架、数据库驱动、密钥管理等模块。直接 pip install litellm 也能装但启动网关的时候会提示缺依赖。所以我第一次搭就用了 [proxy] 这个完整版本省得后面补装。装好后可以用一条命令验证litellm --version如果正常输出版本号说明环境就绪了。注意 litellm 的版本迭代比较快我建议先锁定一个版本用不要每次升级都跟否则配置文件的写法可能因为大版本更新而变动。3.2 编写第一份完整配置我建了一个 config.yaml内容尽可能简单目的是先跑通链路再加高级功能。假设我有两个后端厂商 A 的对话模型以及我自己拿开源模型在内部服务器上起的推理服务。model_list: - model_name: chat-model litellm_params: model: provider-a/biz-chat api_key: os.environ/PROVIDER_A_KEY model_info: mode: completion supports_function_calling: true - model_name: local-chat litellm_params: model: provider-b/open-llm api_key: fake-key-not-used api_base: http://10.0.8.21:8000/v1 model_info: mode: completion general_settings: master_key: sk-my-master-key-0001 database_url: sqlite:///litellm.db这里有几个字段单独说明一下model_name 是暴露给调用方的逻辑名字可以随便起但要保证后续请求里用这个名字。litellm_params.model 前半段的 provider-a、provider-b 是我为了写示例做的代称实际使用时会是你选择的模型服务商标识后半段是对应模型版本名。api_key 可以通过环境变量引用也可以直接写明文但我强烈建议用环境变量避免配置泄漏。api_base 指向自建服务的地址因为这类服务本身可能不需要鉴权所以填了占位 key。master_key 是所有请求都需要携带的网关密钥相当于你给网关设的总密码。database_url 用来存储 key、预算、日志等数据。先用 sqlite 快速跑通生产环境建议换 MySQL 或 Postgres。你可能会疑惑为什么自建推理服务也叫 provider-b/open-llm其实 litellm 所有后端不管是不是固定在某个云服务商都会走统一的接入规范只不过 api_base 指向了本地地址而已。这也是它设计上比较聪明的地方自建和云服务在配置上的差异在 litellm 里被弱化到最小。3.3 启动、验证、接入客户端配置写完启动服务只需要一行命令litellm --config config.yaml --port 4000启动日志如果能看到类似 Uvicorn running on http://0.0.0.0:4000 的内容说明网关已经起来了。我建议在正式接入业务前先用 curl 做一轮冒烟测试确认路由和鉴权是否正常。curl http://127.0.0.1:4000/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer sk-my-master-key-0001 \ -d { model: chat-model, messages: [ {role: user, content: 用一句话介绍你自己} ] }如果一切正常网关会把请求转发到 provider-a 的后端并把响应原样返回给你。你会在返回里看到一个正常的 chat completion 结构包含 content、usage 等字段。curl 通了之后接入业务代码就非常简单了。litellm 客户端库支持 OpenAI 兼容的调用方式基本零成本接入。我用 Python 写了一个最小调用示例import os from openai import OpenAI client OpenAI( api_keysk-my-master-key-0001, base_urlhttp://127.0.0.1:4000, ) resp client.chat.completions.create( modelchat-model, messages[{role: user, content: 写一段产品文案}] ) print(resp.choices[0].message.content)你没有看错这里用的是 openai 这个客户端库但它连接的是 litellm 网关而不是某个具体厂商的服务。这就是统一接口的好处只要你的代码里用的是 OpenAI 兼容的客户端无论后端模型怎么换业务代码一个字符都不用改。3.4 一个真实场景给某内部 Demo 项目做多模型切换为了让你更直观地理解生产环境怎么用我举一个我实际做过的场景给内部一个 Demo 项目统一接入多个模型并且支持按请求参数切换。背景是这样某内部 Demo 项目需要同时给用户提供快速问答和深度写作两个入口。快速问答要求响应快、成本低深度写作要求质量高、能长文本生成。一开始方案是让前端传两个不同的 model 名但这样前端就要感知后端模型体系不太合理。我在 litellm 里的做法是定义两个逻辑模型名qa-model 和 writer-model分别路由到不同后端。请求只传场景名qa 或 writer网关内部帮我分流。qa-model 指向轻量模型同时开了缓存和较短的超时writer-model 指向旗舰模型关闭缓存允许更长的生成时间。效果非常明显快速问答的平均首字延迟从原来的 3 秒降到了 1 秒出头轻量模型 缓存命中深度写作的质量也稳定在旗舰模型水平。而后端要换模型时我只需要改 YAML重启网关业务方完全无感。这个体验让我确定了一点网关模式不只是在多模型切换时才需要它在场景化路由上同样能发挥很大价值。4. 常见问题与排查技巧实录这部分把我在使用过程中遇到的典型问题、排查思路和一些独家经验整理一下。 litellm 整体比较稳但配置和运维上还是有一些容易翻车的点。4.1 高频报错速查表报错信息大概率原因处理方法Authentication Error请求头里没带 master key或 key 错误检查 Authorization 头确认为 Bearer master keyUnknown model name使用了 YAML 里未定义的 model_name检查逻辑模型名拼写或确认配置已加载Connection error后端 api_base 无法访问或网络不通从网关所在节点直接 curl 后端地址确认连通性Timeout error后端响应太慢超过了配置的超时时间调整 read_timeout或排查后端负载429 rate limit后端或网关触发了限流检查预算是超过限额或后端自身限流策略404 route not found请求路径写错比如 /v1/chat/completions 与 /chat/completions 混用核对网关暴露的接口路径统一加 /v1/ 前缀遇到问题的时候我第一件事永远是看网关日志而不是直接改配置。litellm 的日志里会记录每一次请求的路由方向和出错阶段错误发生在网关定位后端还是后端返回异常处理方式完全不同。4.2 三个容易踩的坑第一个坑是api_base 末尾的 /v1 路径混乱。有些自建推理服务要求请求路径是 /v1/chat/completions有些则是 /chat/completions。如果你在 litellm 里配了 api_base 又同时配了额外的前缀路径请求可能变成 /v1/v1/chat/completions。我的建议是先在浏览器或 curl 里手动确认后端真实路径再填到配置里不要凭记忆写。第二个坑是环境变量没加载导致鉴权失败。我在配置里用了 os.environ/PROVIDER_A_KEY 这种写法但如果网关进程没有正确地读取到环境变量请求会一直报鉴权失败。排查这种问题时可以在启动前先手动打印一下环境变量是否为空或者把 api_key 临时改成明文做对照测试。别笑这个坑我踩过两次。第三个坑是流式输出时的重试导致重复内容。如果你开了重试而原始请求已经部分消耗了 token后端可能在第二次重试时因为请求不完整而产生异常输出。我在生产环境的策略是流式请求默认关闭重试或者只在连接阶段重试不在读取阶段重试。这是个很容易被忽略但影响不小的细节。4.3 我习惯留着的几个调优技巧最后分享几个我一直在用的调优习惯算不上什么官方推荐但实际效果很好。第一给不同场景拆逻辑模型名而不是在一个逻辑模型下挂太多后端。虽然 litellm 支持一个 model_name 多个 deployment但你硬把质量要求完全不同的场景塞到同一路由下权重怎么调都别扭。拆开后每个场景的缓存、预算、超时都能独立配置运维清晰很多。第二把 master key 和应用 key 分开。我习惯把 master key 只留给管理员操作比如调管理接口、查看仪表盘日常业务用单独创建的 API key。这样即使业务 key 泄漏影响范围也有限不至于被人拿着 master key 乱改配置。第三预算坐上线的速度要比想象中快。我在测试环境设过 5 美金的日预算以为够用结果某次批量任务一小时就触顶了。现在所有 key 我都会配上日预算总预算并且写一个定时任务读取 litellm 的统计接口预算用到 80% 就发钉钉告警。提前发现和事后救火完全是两种心情。第四升级前看 change log 而不是直接覆盖升级。litellm 发版速度很快有些版本会调字段名、改默认行为。我踩过一次升级后配置不兼容的坑就是从那时起每次升级前都会把 change log 里的 breaking change 部分先看一遍再决定升不升。最后说点实在的litellm 用到现在最大的体会是它把一个充满脏活的工程问题转化成了配置和运维问题。以前每次切换模型我都要改跑业务代码、重新部署还可能因为各厂商 API 差异吵架现在只需要改 YAML、优雅重启网关产品想怎么换模型我都敢接。如果你也打算引入它我的建议是不要一上来就上全家桶。先装一个最小实例跑通单一模型调用再慢慢加路由、加缓存、加预算、加日志。把基础链路摸清楚后再放大规模遇到问题的概率会小很多。等你跑了一段之后回头看大概率会和我一样觉得这一层网关是值得的。