litellm 开源大模型网关:统一API调用,实现多模型无缝切换与降本增效
做 AI 应用这几年最让我头疼的一件事不是模型效果而是接口。最近项目中试用了一个叫 litellm 的开源项目这个问题算是找到了比较彻底的解法。简单说litellm 是一个开源的大模型网关和统一调用层它能把 OpenAI、Claude、Gemini 以及各种本地模型接口全部统一成 OpenAI 风格的 API 格式。业务侧只需要改一个 base_url就能在不同模型之间无缝切换同时它还内置了路由、缓存、限流、成本追踪这些生产级能力。如果你正在做多模型聚合、想降低模型切换成本或者需要把大模型接入企业业务系统这个工具值得仔细看一看。下面把我实际部署和使用的经验拆开讲讲。1. 为什么需要 litellm多模型时代的接口碎片化1.1 各家模型接口到底有多乱先回忆一下没有 litellm 的时候接一个模型要做什么。如果业务要同时接 GPT 系列、Claude 系列、Gemini 系列和本地模型你至少要写四套完全不同风格的调用代码。比如最基础的非流式对话请求# OpenAI 风格 from openai import OpenAI client OpenAI(api_keysk-...) resp client.chat.completions.create( modelgpt-4o, messages[{role: user, content: 你好}], ) print(resp.choices[0].message.content)# Anthropic 风格 import anthropic client anthropic.Anthropic(api_keysk-ant-...) resp client.messages.create( modelclaude-3-5-sonnet-20241022, max_tokens1024, messages[{role: user, content: 你好}], ) print(resp.content[0].text)# Gemini 风格 import google.generativeai as genai genai.configure(api_keyAI...) model genai.GenerativeModel(gemini-1.5-pro) resp model.generate_content(你好) print(resp.text)这还只是最简单的调用。一旦涉及流式返回、工具调用、多轮对话、超时重试差异会更加明显。响应结构里GPT 的内容在choices[0].message.contentClaude 在content[0].textGemini 在text字段里。就算你给三个模型传同一个 prompt拿回来的数据结构也完全不一样业务代码根本没法统一处理。更麻烦的是参数语义差异。OpenAI 的 temperature 范围是 0 到 2Claude 是 0 到 1OpenAI 老版本用max_tokens新模型上叫max_completion_tokensClaude 也用max_tokensGemini 的接口里还有generationConfig这种包装。如果把文档翻译成代码适配层工作量并不小。我之前在某模拟项目 X 里花了整整两天时间写了一个内部封装专门把三家厂商的 SDK 统一成自己的格式。写完当时觉得挺满意结果后来要接入一个新模型发现还是要改适配层那感觉就像千层饼永远铺不完。1.2 litellm 的两种使用模式litellm 解决的正是这个痛点。它把各家 SDK 的差异收敛到内部对外提供统一接口。实际使用时有两种模式适用场景也不同模式使用方式适合场景SDK 模式在代码里直接import litellm调用统一方法脚本、后端服务内部调用不想额外部署组件代理模式启动一个 litellm 服务对外暴露 OpenAI 兼容接口多团队共用、需要路由/预算/日志管控、前后端分离场景我第一次使用是 SDK 模式因为它最快一个 pip 包就能解决。但后来发现只要业务里出现多个服务都要调模型或者产品经理说“这个接口换成另一个模型试试”代理模式才是更合适的方案。代理模式会把模型调度逻辑收敛到一个独立服务里业务代码只认一个地址模型相关配置全部在服务端完成。后续想切换模型、加限流、做成本统计都不需要重新发版。这两种模式并不是互斥的。比如代理模式内部也可以继续用 SDK 模式作为底层调用通道在快速原型阶段你用 SDK 模式验证验证完了再平滑迁移到代理模式整体成本很低。2. 核心功能与原理拆解它到底做了什么2.1 统一调用协议一次适配处处可用litellm 内部的核心逻辑可以理解成一个“协议翻译器”。请求进来时它先把外部请求转成内部统一的消息结构再根据 model 参数中的前缀翻译成对应厂商的 SDK 调用格式响应回来后再统一包装成 OpenAI 格式返回。举个直观的例子。litellm SDK 模式下你只需要这样写import litellm resp litellm.completion( modelanthropic/claude-3-5-sonnet-20241022, messages[{role: user, content: 你好}], temperature0.7, ) # 响应结构是 OpenAI 风格 print(resp[choices][0][message][content])注意model 参数里有anthropic/这个前缀litellm 就是靠它识别上游提供商的。在内部它会把这个请求转换成 Anthropic 的 SDK 格式把 temperature 做一次范围映射然后调 Claude拿到结果后再把content[0].text翻译成choices[0].message.content。这个过程对上层业务是透明的。也就是说你在项目里先接 GPT后面想换成 Claude只需要把 model 参数改成anthropic/claude-3-5-sonnet-...再配好对应的 API Key业务代码一行都不用动。这听起来简单但实际解决了很大的适配成本问题。你可以把 litellm 理解成万能插座转换器不同国家插座标准不一样但你的手机充电器只需要一种插头。2.2 模型前缀与路由规则litellm 支持的 provider 非常多常见的有openai/OpenAI 全系列模型anthropic/Claude 系列gemini/Google Gemini 系列azure/Azure OpenAI 部署ollama/Ollama 本地模型vllm/vLLM 部署的模型huggingface/HuggingFace Inference APIbedrock/AWS Bedrock 上的模型这里要注意一个常见误区model 参数不带前缀时litellm 默认按 OpenAI 处理。如果你把claude-3-5-sonnet直接传给 litellm它不会自动识别成 Claude而是当成 OpenAI 模型去找大概率会报错。所以 model 名必须带上前缀这是最容易踩的第一个坑。前缀之外litellm 还支持通配符模型名。你可以配置一个可用的模型列表让请求方只用模型家族名来请求比如openai/*litellm 会自动选择一个当前可用的具体模型。这个机制在模型故障自动切换时特别有用。路由策略方面litellm 内置了多种策略最常见的是这几个simple-shuffle随机轮询适合模型能力差不多的场景least-busy选择当前并发请求数最少的模型最大化吞吐cost-based优先选择成本最低的模型latency-based优先选择延迟最低的模型usage-based-routing根据历史 token 使用量动态调整我曾经用一组模型接口做过对比同样一批请求用least-busy策略比默认轮询的整体响应时间稳定不少。原因是不同模型厂商的上游处理速度不一样如果固定轮询慢的模型会拖慢整个队列。least-busy 会把请求尽量分给当前空闲的实例整体波动小很多。2.3 企业级网关能力缓存、限流、密钥与预算litellm 不只是“接口翻译器”代理模式下它还是一个完整的 AI 网关。我自己最常用的几个能力是缓存。litellm 支持本地内存缓存、Redis 缓存和语义缓存。对重复的 prompt 可以直接命中缓存不用再调上游模型。实测下来相同问题二次请求的响应时间能降到 50ms 以内成本也省了。Redis 缓存适合多实例部署语义缓存则会对 embedding 相近的请求做模糊匹配适合客服问答这类场景。限流和配额。可以在配置里给每个 API Key 设置 RPM每分钟请求数和 TPM每分钟 token 数。之前团队里有人写脚本不小心死循环调模型如果没有限流几分钟就能烧掉几百块。后来我在网关层面把单 Key RPM 限制住了问题直接消失。虚拟 API Key 管理。litellm 可以生成自己的虚拟 Key业务侧只需要认这个虚拟 Key而真实厂商 Key 可以集中保存在服务端。这样即使某个前端项目 Key 泄露也能单独吊销不影响其他服务。预算控制。可以给虚拟 Key 设置总预算或月度预算超过预算自动拒绝请求。配合日志每次请求的模型、token 数、费用都会落到数据库里月底对账开会我就直接拉日志表格不用再靠估算。3. 实操配置与部署步骤从零搭好一个接入点3.1 安装与运行环境选择litellm 的安装方式非常简单推荐两种# 方式一pip 安装 SDK 和代理组件 pip install litellm[proxy] # 方式二Docker 部署代理 docker pull ghcr.io/berriai/litellm:main-latest如果只是想在本地脚本里调模型pip install litellm就够了。如果要把代理跑起来给团队用建议直接走 Docker。原因是 litellm 代理模式的依赖比较多包含数据库驱动、Redis 客户端等在纯净虚拟环境里装容易出现小问题Docker 镜像已经打好了这些依赖省心很多。这里有个小建议无论用哪种方式都建议把版本号固定住不要直接拉latest。litellm 迭代速度很快有时候新版本会改配置格式导致旧配置启动失败。我在生产环境就用固定的 tag升级前先在一个测试端口试跑一遍确认没问题再切换。3.2 快速开始SDK 模式直连先做一次最小验证确认 litellm 已经能正确调用上游模型。import litellm import os os.environ[OPENAI_API_KEY] sk-... os.environ[ANTHROPIC_API_KEY] sk-ant-... resp litellm.completion( modelopenai/gpt-4o-mini, messages[{role: user, content: 用一句话介绍你自己}], ) print(resp.choices[0].message.content)跑通这一步说明 SDK 模式没问题。需要注意这里如果用anthropic/claude-...一定记得在环境变量或代码里设置ANTHROPIC_API_KEY。所有上游 API Key 都是通过环境变量传参进去的这个做法和常规 SDK 不一样刚开始容易漏。另外补充一句litellm 支持通过api_key参数显式传入 Key比如resp litellm.completion( modelopenai/gpt-4o-mini, messages[{role: user, content: 你好}], api_keysk-... )但我不建议在代码里写死 Key建议统一用环境变量或后续代理模式的虚拟 Key 来管理避免 Key 被提交到仓库里。3.3 推荐做法代理模式 config.yamlSDK 模式适合快速验证和单机脚本但一旦涉及多人协作、多模型调度我更推荐代理模式。代理模式的核心是启动一个本地 HTTP 服务对外提供 OpenAI 兼容接口所有模型路由、密钥管理、日志都在这个服务上完成。先创建一个配置文件config.yamlmodel_list: - model_name: gpt-4o litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY - model_name: claude-prod litellm_params: model: anthropic/claude-3-5-sonnet-20241022 api_key: os.environ/ANTHROPIC_API_KEY - model_name: local-llama litellm_params: model: ollama/llama3.1:8b api_base: http://localhost:11434 custom_llm_provider: ollama router_settings: routing_strategy: least-busy num_retries: 2 request_timeout: 60 litellm_settings: drop_params: true set_verbose: true启动代理litellm --config config.yaml --port 4000启动后业务代码可以这样接入from openai import OpenAI # 重点是修改 base_url 和 api_key client OpenAI( base_urlhttp://localhost:4000, api_keysk-any-non-empty-value, # 代理模式会校验虚拟 Key ) resp client.chat.completions.create( modelclaude-prod, messages[{role: user, content: 你好}], ) print(resp.choices[0].message.content)这段代码看起来很“OpenAI”但它真正的模型是 Claude。这就是代理模式的价值业务代码只依赖 OpenAI SDK底层换成什么模型完全由网关控制。config.yaml 里几个容易混淆的点我展开说一下。model_list是一个模型组model_name是暴露给请求方的模型名litellm_params.model才是真正调用的上游模型名。也就是说请求方传modelclaude-prodlitellm 会把它翻译成anthropic/claude-3-5-sonnet-20241022。这样的好处很明显——假设你想把生产流量切到另一个版本只需要修改litellm_params.model业务侧完全无感知。litellm_settings.drop_params: true这个配置很关键。不同模型支持的参数不完全一致比如有的模型不支持frequency_penalty如果没有 drop_params请求会直接报错开启后litellm 会自动丢弃掉上游不支持的参数提高兼容性。3.4 本地模型接入本地模型也是我很常用的场景主要是数据安全和成本考虑。litellm 对本地模型支持得很不错特别是 Ollama 和 vLLM。Ollama 接入非常简单只要 model 用ollama/前缀再配上 api_base 指向 Ollama 服务地址就行。vLLM 部署的模型通常用openai/前缀再加custom_llm_provider: vllm来标识因为 vLLM 本身暴露的就是 OpenAI 兼容接口。接入本地模型有一个最容易踩的坑api_base 地址不能写错。如果你是在 Docker 容器里跑 litellm要连宿主机的 Ollama地址不能写localhost或127.0.0.1因为容器里的 localhost 指向的是容器自己。我之前就因为这个原因调试半天发现 litellm 一直连不上 Ollama。后来改成http://host.docker.internal:11434Docker Desktop 支持或者直接用宿主机内网 IP问题马上解决。4. 真实场景实践搭一个多模型聚合层4.1 场景与需求拆解用一个实际模拟项目 X 来说明完整落地过程。这个项目是一个内容生成平台需要支持三个业务场景日常聊天助手要求响应快、成本低用 GPT-4o mini长文档总结要求上下文长、总结质量高用 Claude内部隐私数据处理不能出内网用本地 vLLM 部署的开源模型平台的目标是同一套后端代码根据业务需求选择不同模型同时要能看到每个请求花了多少钱并且防止单个用户滥用导致预算失控。这个场景其实就是 litellm 代理模式的经典用法。我按三步走先配置模型列表再设置路由和预算最后做业务接入验证。4.2 完整配置示例与启动验证配置文件我这样写model_list: - model_name: chat-fast litellm_params: model: openai/gpt-4o-mini api_key: os.environ/OPENAI_API_KEY - model_name: doc-summary litellm_params: model: anthropic/claude-3-5-sonnet-20241022 api_key: os.environ/ANTHROPIC_API_KEY - model_name: internal-llama litellm_params: model: openai/llama3.1-70b api_base: http://10.0.0.8:8000/v1 custom_llm_provider: vllm api_key: dummy router_settings: routing_strategy: least-busy num_retries: 2 request_timeout: 90 litellm_settings: drop_params: true set_verbose: false general_settings: master_key: sk-master-key-12345 database_url: postgresql://user:passlocalhost:5432/litellm启动后先做几个基础验证。验证健康检查接口curl http://localhost:4000/health/liveliness正常会返回一个表示存活的状态。接着试一个真实对话请求curl http://localhost:4000/v1/chat/completions \ -H Authorization: Bearer sk-master-key-12345 \ -H Content-Type: application/json \ -d { model: chat-fast, messages: [{role: user, content: 你好}] }这里要注意代理模式下请求都必须带Authorization头默认用 master_key 或者虚拟 Key。请求里 model 字段传的是chat-fast而不是真实的gpt-4o-minilitellm 会根据 model_name 去映射。然后我用同样的方式请求doc-summary和internal-llama确认三个模型都能正常返回。全部跑通后再验证切换成本我在业务代码里只是改了 model 字段其他代码完全没动。4.3 成本与日志验证litellm 代理模式默认会把请求日志写到数据库里。启动后在项目管理页面或/spend/logs接口能看到每次请求的详细信息包括模型名、token 数、估计费用、响应时间。我实际统计过GPT-4o mini 和 Claude 的成本差异确实很明显。如果业务对成本敏感可以多推广使用频率比较高的轻量模型把重模型留给复杂任务。配合路由策略团队还能做“成本优先”的模型调度流量自动倾斜到更便宜的模型上。4.4 生产部署需要注意的事在本地验证通过后生产部署有几个点值得专门提一下第一数据库不要用默认的 SQLite。默认是本地文件存储数据量上来之后写入性能会变差。生产环境建议直接配 Postgres我用下来完全够用。第二多个 litellm 实例时必须配 Redis。默认的内存缓存和限流计数器无法跨实例共享如果后面部署了多个副本会出现限流失效或者缓存命中率低的问题。Redis 一配上这两个问题就都解决了。第三代理服务的健康检查。litellm 提供了/health和/health/liveliness两个端点接入负载均衡或者容器编排系统时探针直接指向这两个路径就行。5. 常见问题与排查技巧实录5.1 高频报错速查表在实际使用过程中我整理了一些高频问题。下面这个表格基本覆盖了早期接入最常见的坑现象根本原因解决方案401 Authentication Error上游 API Key 缺失或传错检查对应环境变量Claude 用 ANTHROPIC_API_KEYOpenAI 用 OPENAI_API_KEYNo model available / Model not foundmodel 名拼写错误或前缀缺失确认带 provider 前缀比如 anthropic/不能直接写 claude 开头请求超时 TimeoutError上游模型响应慢或者网络链路时间长调大request_timeout同时检查上游服务是否能直连上游报参数不支持部分参数不被目标模型支持开启drop_params: true或手动去掉该参数本地模型连接失败api_base 指向错误地址区分宿主机和容器环境不要用 localhost 连外部服务并发稍高就被限流默认 RPM/TPM 限制太严格在router_settings显式调大限制或关闭限流日志查不到历史请求用了 SQLite 且实例重启导致数据丢失换 Postgres并配置持久化存储5.2 我踩过的几个“非典型”坑除了上面表格里的常规问题有几个比较隐蔽的坑我专门记一下。第一个坑是 model 名前缀缺失。一次配置好 litellm 后我用claude-3-5-sonnet-20241022直接请求结果 litellm 一直去 OpenAI 找这个模型自然找不到报 404。后来我才意识到必须写成anthropic/claude-3-5-sonnet-20241022。这是一个很低级但非常常见的错误核对配置时第一个看 model 前缀格式。第二个坑是 Docker 容器里连接宿主机 Ollama。容器内的网络隔离导致localhost根本不可达这个问题前面已经说过。排查思路很简单先确认宿主机上 Ollama 的端口能通再用host.docker.internal替代 localhost或者直接用宿主机 IP。第三个坑是语义缓存导致的脏数据问题。有一次我把cache打开发现同样的 prompt 在用户改了另一处配置后返回结果还是旧的。原因是语义缓存命中后直接返回了相似会话的内容。后期对实时性要求高的场景我把语义缓存关掉了只在数据不敏感且内容更新频率低的场景下才开启。第四个坑是流式返回与传统接口的兼容问题。一开始我在代理模式下用streamTrue结果前端拿到的是完整内容而不是增量数据反而不符合预期。排查后确认是 litellm 代理把上游流式响应处理后先缓存、再一次性返回了。解决办法是在代理配置里明确开启流式透传同时请求侧也传streamTrue两者保持一致。5.3 调试与日志排查技巧litellm 的调试日志比想象中好用。启动时加上--detailed_debug控制台会打印每次请求的完整调用链路包括上游请求参数、返回状态、耗时。这个在排查“为什么模型返回结果和预期不一致”时特别有用。litellm --config config.yaml --port 4000 --detailed_debug如果需要查看详细的调用链路还可以在 config 里开启litellm_settings: set_verbose: true开启后日志会显示每次请求走的模型组、路由策略选择结果、上游实际命中的具体 model 名。我看到过一次很典型的情况自己配置了 fallback结果流量并没有切换到备用模型日志里把原因打印得一清二楚是 fallback 条件没有匹配上。还有一个技巧不要在刚接完所有模型后就立刻关掉日志。建议先留一个星期的 detailed_debug观察每个模型的调用情况确认稳定后再关。这个阶段积累的数据往往能发现很多不容易注意到的隐患比如某个模型偶尔超时、数据库写入波动等。6. 跑了一段时间后的真实体会把 litellm 用进生产环境之后我对这类“模型网关”工具的认知清晰了很多。以前总以为多模型接入是写几个 SDK 封装就行实际只有自己搭过一遍才发现模型切换带来的问题不在“调用”本身而在“管理”。密钥散落各处、成本没有统计、某个模型偶尔不可用导致线上报错、没有统一的限流机制这些问题在模型数量少时都不起眼规模一上来全是事故。litellm 最大的收益是让我把“接口适配”从业务代码里彻底剥离了。现在团队里任何一个服务需要接大模型只需要告诉我模型需求我在配置文件里加一组 model_list然后把网关地址和虚拟 Key 给出去就完事了。业务侧甚至不需要知道它调的是哪家模型这大大降低了认知负担。当然它也不是没有学习成本。配置项多文档虽然全但经常需要自己翻例子遇到不熟悉的 provider还是得结合日志一点点试。但比起每个模型接一遍 SDK、维护一堆适配代码所浪费的时间这些成本完全可以接受。我的建议是先从一个最小的场景开始跑挑一个高频模型配好代理业务侧只改 base_url。跑通之后再把其他模型慢慢加进来同时把日志、预算、限流能力一起打开。这样比一开始就想做“全模型万能网关”要稳妥得多。litellm 的设计思路本身也是适合渐进式演进的这一点我在实际部署中体会尤其深。