如果现在的项目里只是接一个大模型服务直接写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存所有真实keydata/放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_baseimport litellm response litellm.completion( modelchat-main, api_basehttp://localhost:4000/v1, api_key你的key, messages[ {role: user, content: 你好请一句话介绍你自己} ], ) print(response.choices[0].message.content)这段代码的好处是后续如果想绕过网关直接调后端比如做某些离线分析只需要把api_base换掉业务逻辑完全不用动。3.6 首次上手容易踩的配置细节我帮别人排查过不少网关启动问题排在最前面的几个都是配置细节。第一漏了provider/前缀。model直接写gpt-4olitellm会按默认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( modelchat-main, api_basehttp://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跑通再把一个模型接入进去其他配置等有了真实流量再慢慢养这条路比一开始就追求面面俱到要稳得多。