LiteLLM实战:统一网关搞定多模型接入、路由与成本控制
这段时间在帮团队搭大模型应用的统一接入层折腾了一圈网关方案最后留在生产环境里的是 LiteLLM一个开源的 LLM 网关项目。这玩意儿解决的事说起来特别简单现在市面上的大模型厂商太多了OpenAI、Anthropic、Google、国内几家的开放平台、本地部署的模型服务各自有各自的请求格式、鉴权方式、SDK业务代码每次接一个新厂商就得改一遍谁会写谁痛苦。LiteLLM 的做法是把这一堆模型全部封装成一个统一的 OpenAI 兼容接口你只需要把客户端的 base_url 指到 LiteLLM它帮你转发、重试、记账、限流背后连的是哪家模型业务代码完全不关心。这篇文章不打算给你念官方文档我直接把从部署到生产落地的完整路径、配置细节、以及我踩过的一些坑全部摊开来讲。无论是想给个人项目做一个模型切换开关还是给团队搭一套多模型网关这篇内容都能帮你省下不少排查时间。1. LiteLLM 到底解决的是哪一类问题1.1 从一次痛苦的模型切换说起先说一个特别常见的场景。你一开始在项目里直接调某家头部大模型厂商的接口代码里写的是某家 SDK 的调用方式请求结构、消息格式、流式返回的解析逻辑全都按那家的文档来。后来项目要上线了客户说预算有限希望换成国内某家更便宜的大模型或者希望把敏感数据留在本地要求接入内部部署的模型服务。这时候你打开业务代码发现所有涉及模型调用的地方都在用一种专用格式要换一家等于把通信层重写一遍。更麻烦的是SDK 依赖也得跟着换消息结构、参数命名、报错类型都不一样。我第一次搞这种迁移的时候光是把流式输出解析那块改完就花了整整一个下午而且改完心里还没底总觉得哪个边界情况漏了。做 AI 应用的人应该都有过这种体验模型厂商是在快速迭代的而你的业务代码不应该被某个具体厂商绑架。LiteLLM 的核心定位就是把模型调用这个“通信层”抽出来放到一个独立的服务里。你只跟一个标准接口打交道换模型是改网关配置的事不是改业务代码的事。1.2 为什么偏偏是 OpenAI 格式成了标准LiteLLM 对外暴露的接口格式是 OpenAI 的 /v1/chat/completions 格式。有人说这算不算“偏心”还真不是偏爱这是市场选择的结果。OpenAI 这套 API 设计在聊天补全、流式输出、工具调用这几个维度上做得早、用得多、生态成熟导致几乎所有后发的模型厂商——包括开源模型的推理服务、云厂商的模型平台——都在主动兼容这套格式。这对我们使用者来说是极大的利好。不管懂不懂 OpenAI 的协议细节社区里各种成熟的客户端、框架、IDE 插件默认支持 OpenAI 格式你只需要换 base_url 就能复用整个生态的轮子。比如你现在用的是 Python 的 openai 库原本指向官方接口把 base_url 指向 LiteLLM代码几乎不用动背后请求的却是另一个厂商的模型。这就是 LiteLLM 聪明的地方。它不去发明一套新协议而是顺应了事实标准然后把适配各种模型的脏活累活揽在自己身上。你在 LiteLLM 的模型列表里配置哪个模型走哪家的接口、用什么鉴权、格式怎么转换业务层永远只看到 OpenAI 风格的请求响应。1.3 它跟 LangChain 这类框架不是一回事很多人刚接触 LiteLLM 会把它跟 LangChain 放在一起比较其实这俩根本不冲突。LangChain 是应用层的编排框架负责把工具调用、检索、记忆、Agent 这些拼起来LiteLLM 更靠下它管的是“你的应用到底怎么跟各种模型服务实际通信”。用一个好懂的比喻LangChain 像餐厅里的点餐系统帮你设计套餐、组合菜品而 LiteLLM 像后厨统一的传菜口所有灶台不同模型供应商做好的菜都从这一个窗口送出来你作为服务员不需要知道每个菜是哪个灶台做的。在 LangChain 里你可以直接集成 LiteLLM 的 SDK更推荐的方式是把 LangChain 也配置为通过 LiteLLM 网关的标准 OpenAI 端口去连模型这样架构更清爽上层永远不变下层自由切换。2. 五分钟部署一个属于自己的模型网关2.1 部署前的准备安装方式和应用场景取舍LiteLLM 有两种用法。一种是在代码里直接 import把它的 completion() 方法当成 SDK 来用另一种是启动它内置的 proxy server把 LiteLLM 跑成一个常驻的 HTTP 服务团队内所有项目共用。如果你的目标是“多个项目共用一个网关”“集中管理 key 和预算”“在服务端做模型路由”那直接上 proxy 模式。我自己的选择是 proxy 模式。因为只有跑成服务你才能把模型切换、权限控制、成本统计这些能力真正复用起来而不是每个项目各写一套配置。推荐用 Docker 方式部署干净也方便迁移docker run --name litellm \ -p 4000:4000 \ -v $(pwd)/config.yaml:/app/config.yaml \ -e OPENAI_API_KEYsk-xxxx \ -e ANTHROPIC_API_KEYsk-ant-xxxx \ -e DASHSCOPE_API_KEYsk-xxxx \ ghcr.io/berriai/litellm:main-latest \ --config /app/config.yaml如果你不用 Docker也可以直接 pip 安装代理版本然后命令行启动pip install litellm[proxy] litellm --config config.yaml --port 4000启动之后默认会在 4000 端口拉起一个服务它对外提供 /v1/chat/completions 等 OpenAI 风格接口同时还有一个管理界面和一套管理 API 用来生成 key、查用量、看配置。2.2 最小可用配置一个文件说清楚LiteLLM 的配置是一个 YAML 文件核心就两个大块model_list 定义网关里有哪些模型可选litellm_settings 定义网关本身的全局行为。下面是我搭的第一个能跑通的配置里面同时接了一家海外大模型、一家 Anthropic 模型、国内通义千问的兼容接口model_list: - model_name: gpt-4o-mini litellm_params: model: openai/gpt-4o-mini api_key: os.environ/OPENAI_API_KEY - model_name: claude-3-5-sonnet litellm_params: model: anthropic/claude-3-5-sonnet api_key: os.environ/ANTHROPIC_API_KEY - model_name: qwen-plus litellm_params: model: openai/qwen-plus api_base: https://dashscope.aliyuncs.com/compatible-mode/v1 api_key: os.environ/DASHSCOPE_API_KEY这里有几个关键点必须说清楚。第一model_name 是暴露给你的客户端的名字它完全由你自定义客户端传 model 参数时用的是这个名字它不一定是真实模型名。第二litellm_params 里的 model 字段格式是 “提供商/模型名”LiteLLM 看到 openai/ 前缀就走 OpenAI 兼容协议看到 anthropic/ 前缀就走 Claude 的协议看到 openai/ 前缀但配了 api_base就走你指定的兼容地址比如国内模型的 OpenAI 兼容模式。第三api_key 可以写成 os.environ/XXX服务器启动时从环境变量读取这样可以避免密钥直接硬编码在配置文件里。弄好这个配置重启服务接着用 curl 验证一下是否通了curl http://localhost:4000/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer sk-1234 \ -d { model: gpt-4o-mini, messages: [{role: user, content: 说一句话介绍你自己}] }注意上面的 Authorization 里默认的 sk-1234 是 LiteLLM 的 master key在环境变量里设置 PROXY_MASTER_KEY 可以改成自定义值。如果你没有设置过有些版本默认是 sk-1234。返回里出现 content 字段说明这一条链路已经通了你。2.3 验证和接入客户端代码基本不用改服务跑起来之后你在 Python 代码里接入的方式和直接调 OpenAI 几乎一模一样只是 base_url 换成了网关地址from openai import OpenAI client OpenAI( base_urlhttp://localhost:4000/v1, api_keysk-your-proxy-key ) response client.chat.completions.create( modelgpt-4o-mini, messages[ {role: user, content: 帮我把这句翻译成英文今天天气不错} ] ) print(response.choices[0].message.content)像我这种已经把 openai SDK 用得熟门熟路的人第一次看到代码完全不改只换地址的时候其实是有点震惊的。这意味着你团队里的人不需要学习任何新知识原来怎么写 OpenAI 还是怎么写唯一变的是配置里的 base_url。这种“低学习成本”是我选型时特别看重的一点它直接决定了团队能不能快速接受这套东西。3. 生产环境的路由和高可用配置3.1 自动故障转移主模型挂了谁顶上单个模型接入网关只是入门生产环境真正需要的是当某个模型服务出问题的时候网关能自动把请求转移到备用模型。比如你的主模型是某家大厂的它不稳定的时候如果业务代码直接连它那就只能等超时然后用户体验就是转圈圈、报错。有网关之后可以设置 fallback 策略。LiteLLM 的 fallback 配置很简单在 model_list 的同级加上 router_settingsrouter_settings: default_fallbacks: [gpt-4o-mini] num_retries: 2 timeout: 30 cooldown_time: 60 allowed_fallbacks: - gpt-4o-mini - claude-3-5-sonnet - qwen-plus这里的意思是当客户端请求的模型不可用、超时或者返回错误的时候LiteLLM 会自动按顺序尝试 allowed_fallbacks 里列出的模型重试次数是 2 次。cooldown_time 是熔断时间如果某个模型连续出错LiteLLM 会让它进入冷却冷却时间内不再给它发请求防止一直在坏模型上干耗。配置完 fallback 之后有一个细节要特别留意不同模型的输出格式、能力边界肯定有差异fallback 到另一个模型时context 窗口、工具调用能力、输出长度上限都不一样。所以 fallback 列表尽量选能力相近的模型比如主模型支持工具调用备选模型也最好支持主模型 context 是 128k备选不要选一个只有 8k 的否则平时没问题关键时刻反而因为长度超限继续失败。3.2 负载均衡和路由策略多把钥匙分摊压力如果同一个模型配了多个上游 Key比如你有两个账号或者只用一个账号但配额有限LiteLLM 还支持负载均衡。做法很简单就是在同一个 model_name 下配置多个 litellm_params每个带各自的 api_keymodel_list: - model_name: gpt-4o-mini litellm_params: model: openai/gpt-4o-mini api_key: os.environ/OPENAI_API_KEY_1 model_info: rpm: 100 - model_name: gpt-4o-mini litellm_params: model: openai/gpt-4o-mini api_key: os.environ/OPENAI_API_KEY_2 model_info: rpm: 80这样配置之后两个 key 会轮流接请求。每一条记录可以写 model_info 里的 rpm每分钟请求数限制和 tpm每分钟 token 数限制LiteLLM 会根据这些值来分配流量避免某个 key 超过配额被上游限流。路由策略也有讲究。默认是轮流分配如果你对延迟敏感可以改用 least-busy各个模型能力、价格差异比较大时想根据历史表现自动把请求路由到又快又便宜的模型上可以试 usage-based-routing 或 latency-based-routing。这些策略在 router_settings 里用 routing_strategy 字段设置router_settings: routing_strategy: usage-based-routing不要迷信某一个固定策略。我实测的时候发现least-busy 在请求量大的时候表现稳定usage-based-routing 在混合模型场景下成本控制更聪明你应该按自己的业务节奏去压测而不是直接抄别人的配置。3.3 超时、重试和冷却机制的经验值这一节我想把几个边界情况串起来讲因为它们在生产环境往往是一起发生的。先说超时。LiteLLM 的默认超时时间偏长如果你做的是实时对话类应用30 秒超时显然不合适。在 router_settings 里把 timeout 调成 10 到 15 秒用户在界面上能感知的等待极限也就是十秒上下。大模型推理不是普通 API 请求30 秒以上的耗时经常发生特别是在输出很长或者服务端排队的时候所以不要设太短。我生产环境用的是 15 秒硬超时加上 2 次重试整体最坏等待 45 秒对大部分业务场景来说这个上限是可以接受的。再说重试。num_retries 的重试是在“同一个候选模型”上重试还是换一个模型重试LiteLLM 默认是同模型重试加 fallback 顺序如果第一次失败会把它当作一次路由决策重新走候选列表。如果你的上游对错误响应很快比如 429 限流LiteLLM 会立即触发重试如果上游一直不返回就会一直等 timeout 到了再重试。所以超时配置直接决定了你整个链路的恶化速度。最后说冷却注意点。cooldown_time 设太短坏模型可能刚恢复又被当成可用设太长模型都恢复了还被晾着。一般建议 60 秒起步如果是那种偶尔抽风的模型放个 120 秒更稳。另外冷却只针对具体的模型实例key 级不是整个 model_name 级这样比较精确。4. 成本追踪和权限管控做不好会被打爆4.1 用量统计和模型成本计算网关一个很大的价值是“所有请求都从你这里过”那所有成本数据也自然都沉淀在网关这一层。LiteLLM 内置了一个成本字典记录了市面上主流模型每百万 token 的输入输出单价。当一个请求完成后它会根据 model_list 里对应的模型名、输入输出 token 数自动计算这个请求花了多少钱并按模型维度聚合成报表。想看实时监控打开网关的 UI 就能看到按模型、按 key、按团队统计的 token 消耗也可以在 litellm_settings 里开启 Prometheus 指标输出方便接入监控告警litellm_settings: set_verbose: true prometheus: true开启后LiteLLM 会在 /metrics 暴露标准的监控指标。把这些指标接到你的监控系统里就能对“哪个模型调用量暴涨”“哪个 key 预算快烧完”做到心里有数。成本数据这件事等到月底对账单的时候再重视就晚了。我见过不止一次有人把某个高参数模型的价格看漏了一个数量级结果一晚上调了几千次账单直接起飞。有了网关的单价统计至少每一笔花费都清清楚楚。4.2 虚拟 Key给团队发钥匙而不是发真金白银LiteLLM 提供了一套 Key 管理体系你可以在网关里生成很多把“虚拟钥匙”每一把 key 都可以绑定允许访问的模型范围、预算上限、有效期。使用方拿到的不是真实的模型厂商 key而是这个虚拟 key他说到底根本接触不到你的真实密钥。比如你要给 A 项目组发一把只能访问低成本模型、一个月上限 50 美元的钥匙只需要一条命令curl -X POST http://localhost:4000/key/generate \ -H Authorization: Bearer sk-1234 \ -H Content-Type: application/json \ -d { models: [gpt-4o-mini, qwen-plus], max_budget: 50, budget_duration: 30d }返回结果里有一个新的 key把它给对应使用方就行。如果某个项目预算超了这个 key 接下来的请求会被拒绝不影响其他团队的使用。从管理角度讲这是“把权限收回到网关把便利留给使用方”的典型做法。一开始可能会觉得多一步流程麻烦但当你同时管着两三个项目组、几十个开发者的访问权限时这套机制会帮你省掉大量沟通成本。4.3 多团队隔离和管理 API 的实战经验多团队共用网关的时候除了发 key最好按照团队维度去管理。LiteLLM 里有 Team 的概念一个团队可以挂多把 key一把 key 也可以绑定多个模型。你可以在配置里预设 team 的预算、模型白名单然后通过管理接口批量生成 key分配给对应成员。实际操作中我比较推荐按“模型分组”而非“按团队”来组织 model_name。比如所有便宜通用模型统一叫 cheap-model所有长文本模型统一叫 long-context-model所有图片理解模型统一叫 vision-model。这样团队在代码里只需要根据业务场景选组不需要了解具体是哪个模型网关配置怎么调整都不会惊动业务方。同时做权限控制时也只需要按组管理模型增减不需要挨个通知团队。权限上注意一点登录网关 UI 和管理接口用的是 master key这个 key 权限极高别把它放在前端代码里。真实的部署场景你应该把 master key 放在环境变量或者专门的密钥管理系统里只有运维和管理员能接触到。5. 真的跑起来之后的那些坑和排查实录5.1 认证错误百分之九十是环境变量没传到位LiteLLM 最常见的报错就是 Authentication Error。很多人第一个配置写完启动服务信心满满地一测直接 401。我排查过很多次这个问题原因基本一模一样config.yaml 里写了 os.environ/OPENAI_API_KEY但启动 Docker 容器时忘了加 -e 参数或者环境变量名拼错了。给你一个我自己的排查顺序第一步先在宿主环境里 echo $OPENAI_API_KEY 看有没有值第二步看 Docker 启动日志里有没有加载 key 的记录第三步直接在 curl 请求里临时用明文 api_key 塞进去测。如果明文能通而环境变量读不到那一定是环境变量这层的问题。把这些步骤走一遍五分钟内能定位到问题。还有一个容易犯的错检查一下你的 api_key 值开头有没有多余的引号或空格。读环境变量不像读 YAML 字符串那么严格有时候你在启动脚本里写了 KEYsk-xxx 带引号引号会被当成 key 的一部分传进去这类错误日志很难看出来问题在哪。5.2 请求超时和性能瓶颈的排查思路请求超时的表现通常是客户端收到 Timeout Error但网关日志里可能什么都没打。记住一个原则当你看到超时先确认是网关到上游超时还是客户端到网关超时。如果客户端到网关就超了多半是网关本身处理不过来如果网关到上游超时那可能是上游大模型服务端排队或者网速问题。排查的时候把 set_verbose 打开LiteLLM 会打印出每一次请求的完整生命周期包括打到哪个上游、耗时多少、返回了什么。这些日志是定位问题的一手资料。另外如果你的业务里确实有一些令牌数很大的请求比如一次让模型读一整本书不要只想着调大全局超时更好的做法是给这个模型单独配置一个超时参数model_list: - model_name: long-context-model litellm_params: model: openai/gpt-4o-mini api_key: os.environ/OPENAI_API_KEY timeout: 120LiteLLM 允许在单个模型的 litellm_params 里覆盖全局超时这能避免一个特殊场景拖垮所有普通请求的体验。5.3 工具调用Function Calling容易踩的兼容性坑模型工具调用是现在 Agent 应用的核心能力但在网关转发场景里这里坑不少。最大的问题在于不是所有模型对工具调用的支持方式都一样。OpenAI 的接口原生支持 tools 参数而有些模型走的兼容接口对工具调用的格式支持很弱甚至完全不支持。如果你在主配置里给一个工具调用能力很差的模型发 tools 参数它要么报错要么直接忽略。LiteLLM 有个非常实用的开关叫 drop_params。打开这个开关之后如果某个模型不支持你传入的参数LiteLLM 会自动把那个参数剔除掉再发请求而不是硬传给上游导致报错。litellm_settings: drop_params: true这个开关在模型训多样性很高、你又不确定每个模型支持哪些参数时能救你很多次。但在生产环境里我建议你审慎使用因为它也会悄悄抹掉一些你本来想传的参数让你以为模型已经收到了。更稳的做法是维护一份“每个模型支持哪些参数”的清单网关配置里对每个模型单独控制。以工具调用来说优先保证主模型支持 function callingfallback 模型也尽量选支持能力一致的这样不至于一个核心参数在切换模型后就丢失。再补充一个容易忽视的细节模型返回值里的 tool_calls 格式也未必完全一致。OpenAI 的 tool_calls 字段是数组格式有些模型的兼容 API 虽然在请求侧支持的挺好响应侧却把工具调用塞到 content 里返回。这时候你直接解析 tool_calls 就会拿到空。实践里最好在业务代码里做一层容错优先解析 tool_calls拿不到就看看 content 里有没有按约定格式输出的 JSON。5.4 问题速查表运维高频问题的定位清单问题现象常见原因处理方式返回 401 Authentication Error环境变量未传、key 错误检查环境变量与启动参数用 curl 直接指定明文 key 验证模型报错 Not Foundmodel_name 与 model_list 里定义不一致确认客户端传的 model 参数是网关 model_name不是真实模型名请求超时 Timeout全局超时设太短或上游模型处理慢调大全局超时给特殊模型单独配 timeout所有请求都失败候选模型都被冷却查看路由日志确认上游状态重启或调高 cooldown_time成本数据看不到未开启成本统计或模型未匹配内置计价表确认配置里 model 前缀正确升级到最新版本tools 参数报错或被忽略上游模型不支持或 drop_params 影响检查目标模型文档必要时换支持工具调用的模型5.5 推荐的最小生产配置模板最后给出一份可以直接抄作业的生产级最小模板包含路由、预算、监控、日志四个维度model_list: - model_name: cheap-model litellm_params: model: openai/gpt-4o-mini api_key: os.environ/OPENAI_API_KEY model_info: rpm: 200 - model_name: strong-model litellm_params: model: anthropic/claude-3-5-sonnet api_key: os.environ/ANTHROPIC_API_KEY model_info: rpm: 100 - model_name: local-model litellm_params: model: openai/qwen-plus api_base: https://dashscope.aliyuncs.com/compatible-mode/v1 api_key: os.environ/DASHSCOPE_API_KEY litellm_settings: drop_params: true set_verbose: false prometheus: true max_budget: 500 budget_duration: 30d router_settings: routing_strategy: least-busy default_fallbacks: [cheap-model] num_retries: 2 timeout: 15 cooldown_time: 60 allowed_fallbacks: - cheap-model - strong-model - local-model这份配置在网关层设了 30 天的总体预算上限一旦整月消费超过 500 美元所有请求都会停掉这是一个保底安全阀。生产环境我强烈建议加上一个保底预算宁可某天误伤正常请求也好过月底收到天价账单再去跟老板解释。就我个人来说用了 LiteLLM 网关之后最大的心态变化是再也不怕换模型了。之前接到“换个模型试试”这种需求心里都是咯噔一下现在只是改几分钟配置业务方甚至感知不到模型变了。如果你也被多模型接入和切换折磨过建议先按这篇文章的最小配置把网关跑起来花不了半小时但后面省下来的时间会是这半小时的几十倍。最后再分享一个小技巧就算你现在只用一个模型也值得把 LiteLLM 架在中间因为客户端代码写死一个 SDK 之后未来任何新模型出现时你永远只需要在网关里加一行配置。