多模型接入太难了?用 litellm 统一 OpenAI 兼容格式,打造 LLM 网关
最近在处理一个多模型接入的需求时我最大的感受是真正让人崩溃的其实不是模型效果而是调用方式五花八门。同样是聊天补全有的需要messages数组有的偏要prompt字符串同样是工具调用有的叫tools有的叫functions流式输出的事件结构更是各写各的。如果项目里要同时接几家模型代码会迅速变成一堆 if-else 和适配层维护成本直线上升。这篇文章想聊的litellm就是专门解决这个问题的开源工具。它的核心思路很朴素把各种大模型 API 统一翻译成 OpenAI 风格的请求和响应上层业务只写一套代码底层模型随便换。简单说它是一层“LLM 适配网关”。这篇文章不打算逐条翻译文档而是结合我实际接入、部署、踩坑的经历把litellm从 SDK 到团队网关的完整链路讲透给正打算接入多模型、或者团队需要统一管理模型成本的同学一些参考。1. 先搞清楚它到底解决什么问题1.1 多模型接入时的真实痛感我接过的第二家模型厂商时对接代码就开始不干净了。每家 SDK 的对象结构、鉴权方式、错误码、重试策略都不一样这些差异逼着你写大量胶水代码把请求体转来转去把返回值翻来翻去还要对付各家流式格式的细微差别。这种适配代码通常是项目里最难维护、也最没人愿意碰的部分。另一个痛点是业务方对“模型不可用”是零容忍的。比如某个模型服务方限流、故障或升级如果没有备选方案业务只能干等。而一旦想加一个备选供应商又意味着再加一套适配逻辑。1.2 litellm 的“翻译层”设计litellm的思路是上层统一接收 OpenAI 格式的请求底层由它去做协议转换。你可以把它理解成一个“万能插座”前端只认识一种接口后端由它去接各家不同型号的“插头”。它内部维护了大量模型提供商的转换逻辑不仅处理最基础的文本补全还覆盖 embeddings、image generation、audio transcription 等各类接口。调用时只需要传模型名例如gpt-4o-mini、claude-3-5-sonnet-20241010、gemini/gemini-1.5-flash、ollama/llama3.1这些带前缀或不带前缀的名称它就知道该走哪条转换链路、用哪套认证方式。1.3 SDK 模式和代理模式的区别litellm有两种用法这个要一开始就分清楚因为很多踩坑都源于用错了模式。SDK 模式在代码里import litellm直接调用litellm.completion()。适合单个项目、个人脚本、快速原型。代理模式把litellm起成一个独立服务前端接 OpenAI 兼容接口由这个服务统一转发到真实模型。适合团队共用、生产环境、成本管控、Key 管理。两种模式共用同一套配置逻辑和模型命名规则但部署形态完全不同。下文会先讲 SDK 模式因为它是理解代理模式的基础。2. 用 SDK 模式跑通第一行调用2.1 安装和最小示例安装很简单pip install litellm即可。它的核心调用长这样import litellm response litellm.completion( modelgpt-4o-mini, messages[ {role: system, content: 你是一个简洁的助手}, {role: user, content: 用一句话解释什么是API网关} ] ) print(response.choices[0].message.content)这段代码和 OpenAI SDK 长得几乎一样因为litellm本来就是兼容 OpenAI 的请求结构。但关键在于如果你把model换成anthropic/claude-3-5-sonnet-20241020代码其余部分完全不用动它就能把 OpenAI 格式的请求转成 Anthropic 系模型能认的格式再把返回结果统一成 OpenAI 风格。2.2 环境变量与模型命名规则不同厂商的密钥通过环境变量传入例如OPENAI_API_KEY、ANTHROPIC_API_KEY、GEMINI_API_KEY。litellm读取模型名时自带一套判断规则。传入的模型名实际走向说明gpt-4oOpenAI 系不带前缀直接映射到对应厂商anthropic/claude-3-5-sonnet-20241020Anthropic 系带anthropic/前缀gemini/gemini-1.5-proGoogle 系带gemini/前缀ollama/llama3.1本地 Ollama本地模型不需要云厂商 Keyopenai/自定义接口地址任意 OpenAI 兼容服务配合api_base可以指向本地部署这个前缀规则非常关键。模型名不只是“名字”同时包含了路由信息。我见过不少人第一次用的时候把modelclaude-3-5-sonnet直接传给litellm结果它尝试走某条默认通道报了奇怪的鉴权错误其实加个anthropic/前缀就通了。2.3 响应对象的统一结构litellm返回的是ModelResponse对象字段尽量向 OpenAI 对齐。正文放在response.choices[0].message.contenttoken 用量在response.usage里完成原因在response.choices[0].finish_reason。有个小坑值得注意有些模型会返回推理链路reasoning 内容litellm在转换时不一定把这类字段映射到标准content而是放在response.choices[0].message的扩展属性里。如果你需要把思考过程单独展示给用户最好在拿到返回后用model_dump()看一眼实际数据结构不要假设所有字段名都跟 OpenAI 文档一模一样。2.4 连续对话与流式输出连续对话就是把历史消息整个带上messages数组顺序传递即可。流式输出则需要把streamTrue传进去response litellm.completion( modelgpt-4o-mini, messages[{role: user, content: 写一段关于秋天的短文}], streamTrue ) for chunk in response: delta chunk.choices[0].delta if delta.content: print(delta.content, end)细心的读者会发现这个流式循环体的写法和 OpenAI SDK 一样。litellm会把不同厂商的流式事件统一成这个结构。至于各家流式事件原本长什么样你不需要关心知道在哪取增量文本就够了。3. 把单机调用升级成团队网关3.1 为什么团队需要一层网关SDK 模式虽然方便但它解决不了团队层面的问题成员各自拿云厂商 Key 直连账单分散、权限不可控、模型供应商故障时没有统一降级策略、每个成员对“哪个模型能跑”的理解也不一致。网关模式的核心价值是“收敛入口”。所有请求都打到一个代理服务上这个服务负责鉴权、转发、限流、重试、记录日志。业务方完全不用关心背后有几个模型供应商他们只跟网关打交道网关背后用什么模型只由平台方统一配置。3.2 config.yaml 的基础骨架代理模式的配置核心是一个 YAML 文件。下面是精简但能跑通的基础示例model_list: - model_name: gpt-4o-mini litellm_params: model: gpt-4o-mini api_key: os.environ/OPENAI_API_KEY - model_name: claude-sonnet litellm_params: model: anthropic/claude-3-5-sonnet-20241020 api_key: os.environ/ANTHROPIC_API_KEY litellm_settings: drop_params: true set_verbose: false general_settings: master_key: sk-your-master-key database_url: os.environ/DATABASE_URLmodel_list里每一项的model_name是暴露给调用方的名字litellm_params.model是真实模型名。这给了平台方很大的自由度对外叫claude-sonnet内部实际路由到哪个具体版本可以随时切换而不告诉业务方。master_key是网关的总控密钥所有请求都要带上它才能通过。database_url是可选配置如果要用成本统计、虚拟密钥、预算管理等功能需要配置一个 PostgreSQL 数据库。3.3 用 Docker 或 Python 起服务最简单的启动方式是用官方镜像docker run -d --name litellm-proxy \ -p 4000:4000 \ -v $(pwd)/config.yaml:/app/config.yaml \ -e OPENAI_API_KEYyour-key \ -e ANTHROPIC_API_KEYyour-key \ ghcr.io/berriai/litellm:main-latest \ --config /app/config.yaml --port 4000不用 Docker 的话直接 Python 启动也可以python -m litellm --config config.yaml --port 4000启动成功后验证一下网关是否正常工作curl http://localhost:4000/v1/models \ -H Authorization: Bearer sk-your-master-key能看到配置的模型列表说明网关已经正常工作了。然后就可以用标准的 OpenAI 客户端指定base_urlhttp://localhost:4000来发请求。3.4 虚拟密钥与权限隔离网关模式下不建议直接把master_key发给所有人。litellm提供虚拟密钥机制管理员通过管理 API 或 UI 生成一把带权限控制的子 Key这把 Key 只能调用指定模型还能设定预算上限。生产环境常见做法是每个团队或每条业务线一把虚拟 Key模型权限只开放他们需要的那些预算各自设上限。这样既避免账号共享也方便从账单上看出哪条业务线花得多。4. 模型路由、重试与 fallback 的实战配置4.1 同一模型多实例自动路由真实运营时同一个模型可能会配置多个可用端点比如同一家云厂商有两个区域或者两家厂商都提供同规格模型。可以在model_list里多配几个条目并设置权重model_list: - model_name: gpt-4o-mini litellm_params: model: openai/gpt-4o-mini api_key: os.environ/OPENAI_API_KEY api_base: https://api1.example.com/v1 routing_strategy: weight: 2 - model_name: gpt-4o-mini litellm_params: model: openai/gpt-4o-mini api_key: os.environ/OPENAI_API_KEY_2 api_base: https://api2.example.com/v1 routing_strategy: weight: 1这里weight表示流量占比。权重配比只是最简单的一种litellm还支持根据延迟、健康检查结果做动态路由。当某个端点连续报错或超时代理会自动降低它的流量故障实例权重降为 0 后就不再往它发新请求。4.2 fallback主模型挂了的兜底策略fallback 是生产环境里最有价值的功能之一。它决定了主模型报错后请求是否自动转给备选模型litellm_settings: fallbacks: [ { gpt-4o-mini: [claude-sonnet] } ] num_retries: 3 request_timeout: 60上面配置的意思是当gpt-4o-mini请求失败包括超时、限流、5xx 错误自动把同样的消息转给claude-sonnet再试一次。num_retries是重试次数request_timeout是单次请求的最大等待时间。这个机制对业务方是透明的他们只知道自己请求了网关并不知道背后发生过一次故障切换。要注意的是fallback 不会自动把“失败原因”通知给业务方所以在比较关键的业务场景里我建议在网关日志里做一次失败事件标记方便事后复盘主模型当时的故障表现。4.3 本地模型的接入方式私有化部署、离线环境、数据不出内网等需求都会用到本地模型。litellm对本地模型的支持很直接只要本地模型服务对外提供 OpenAI 兼容接口model_list: - model_name: local-llama litellm_params: model: openai/llama3.1 api_base: http://localhost:8000/v1 api_key: dummy这里的openai/前缀表示“走 OpenAI 兼容协议”并不是说接的一定是某云厂商。api_base指向本地服务的地址即可api_key随便填占位值。如果用的是 Ollama也可以写model: ollama/llama3.1由litellm直接走内置的 Ollama 转换逻辑。我自己的体会是本地模型接入最大的价值不是替代云厂商而是作为云厂商故障时的兜底。即使效果差一些业务也不会完全停摆。这个思路在稳定性要求高的场景里非常实用。5. 成本统计与预算控制5.1 每次请求的 token 是怎么算成钱的litellm会在每次请求结束后记录prompt_tokens、completion_tokens、total_tokens再根据模型名查出对应的单价算出本次花费。前提是它知道这个模型的价格。常用云厂商模型的单价库里基本都有。如果你接的是私有模型、内部自建模型价格表里没有就需要在model_list的litellm_params里手动补充model_list: - model_name: internal-model litellm_params: model: openai/custom-model api_base: http://localhost:8000/v1 api_key: dummy model_info: input_cost: 0.000002 output_cost: 0.000006 max_tokens: 8192这里的单价单位是“美元/千 token”按官方约定填就行。没配model_info的模型费用统计会显示为 0这在事后对账时会很迷惑。接到内部模型时我总是第一步就把价格配上省得后面补数据。5.2 按用户、按 Key、按业务线看账单网关模式下费用天然跟虚拟 Key 绑定。谁花的钱、哪个 Key 花的钱数据库里都有明细。如果一条业务线共用一把 Key账单就按 Key 聚合如果希望更细粒度可以在请求参数里带上user字段网关会把这个用户维度记录下来。litellm的 UI 或管理 API 提供 spend 相关接口最常见的用法是查“某把 Key 本月花了多少”curl http://localhost:4000/spend/logs \ -H Authorization: Bearer sk-your-master-key \ -H Content-Type: application/json日志会返回每条请求的模型、token、花费、用户、时间等信息。把这些数据接进自己的监控系统就能做一个面向老板的用量大盘。5.3 预算额度与超限熔断预算管控是团队引入网关最直接的经济理由。给虚拟 Key 设置月度上限后超过限额的请求会被直接拒绝返回 429。配置方式在生成虚拟 Key 时指定max_budget即可。我给某个测试团队配置过一把月限额 50 美元的 Key。当它突然被业务方写了个死循环脚本狂刷时预算熔断直接拦住了几万美元的潜在账单。这个机制听起来简单实际救过我不止一次。6. 接入过程中的几个常见坑6.1 工具调用的字段兼容各家工具调用格式差异很大litellm会做转换但不是所有新特性都同步支持。部分新款模型支持的strict模式、结构化输出等较新参数在转换到不支持这些参数的老模型时如果配置了drop_params它会把参数默默丢弃如果没有请求会报参数错误。我的建议是关键请求一定要记录转换后的真实请求体。litellm支持在环境变量里开 debug 日志确认工具调用参数有没有被正确转换。不要在没看日志的情况下就认为“工具调用一定没问题”。6.2 流式输出的超时与断连流式输出的连接如果长时间没有返回新 token容易被判定为超时。很多模型思考时间本来就长尤其是一些强推理模型可能几十秒内一个 token 都不吐。这时候不要把timeout设得太小否则会频繁触发重试重试又进一步增加服务端压力。更稳妥的做法是把request_timeout调到 90 秒以上同时依赖应用层的空闲超时保护而不是依赖网关层短超时。短超时“保护”的结果往往是全链路雪崩这个坑是我在生产环境踩过最痛的一次。6.3 输出长度上限不一致各家max_tokens上限差异巨大。有些模型单次输出上限是 4096有些是 8192有些能到 32k 甚至更大。如果你在请求里写死max_tokens16000但路由到了只支持 4096 的模型请求会直接失败而不是被悄悄截断。我处理这类问题一般是在网关层做一次校验不同模型名设置对应的上限档位超出上限的请求直接提示参数错误不让错误请求浪费转发链路。如果只是偶尔使用长输出场景最简单的做法是统一把max_tokens设置成所有模型都支持的安全值虽然损失部分模型能力但换来了稳定性。6.4 不要把网关内部细节暴露给业务方我见过一个团队在业务代码里读取response.model字段来“判断当前用的是哪个模型”这个字段在网关模式下返回的是网关内部真实模型名。一旦平台方做了 fallback 或路由切换业务方会发现“明明调的是 A返回里却写着 B”引发误判。正确的做法是把网关的模型输出当成不透明黑盒。业务方只依赖内容结果和 token 用量不要依赖返回里的模型名去做逻辑判断。这个经验说出来简单但在真实项目中反复被违反。7. 什么场景不建议用 litellm任何工具都有边界litellm也不是万能的。如果只是单个项目、固定一个模型、短期跑通 Demo引入一层网关纯属增加部署和运维成本直接用官方 SDK 更轻快。如果团队已经有完善的内部统一 SDK所有模型都经过自家适配层也没有共享网关、成本归集这类诉求那没必要再从零迁到litellm。对延迟极度敏感的场景也需要慎重。代理模式每多一跳就多一次网络开销虽然litellm本身转发效率不差但相比直连模型厂商总延迟肯定会略有增加。严格来说网关适合“管理价值大于微秒级性能差异”的业务场景而不是所有场景。此外如果某些云厂商刚发布的新模型特有的参数litellm转换层可能还没有及时跟进。对于追求第一时间使用最新特性的团队可能需要自己评估适配的滞后成本。这时候我更倾向于把它当成一个“大部分模型都能管但极少数新特性需要等一等”的工具而不是一个永远零滞后的万能层。我自己现在的使用习惯是个人实验用 SDK 模式团队项目用代理模式所有新的模型调研先跑 SDK确认可用后再把它写进网关配置。这两者的组合让模型接入的边际成本变得很低新模型只要文档支持基本半天内就能让整个团队用上。如果你正被多模型适配和数据碎片化折磨这层“翻译器”值得花一个下午试试。