☰
litellm 实战:统一接口调用多模型与 Router/Proxy 网关
2026/10/14 9:37:18 网站建设 项目流程

1. 从一个“模型调用地狱”说起:litellm 到底解决了什么问题

如果你最近半年在折腾大模型应用,大概率会遇到这样一个场景:项目一开始只用了某一家厂商的接口,代码写得挺顺,结果产品经理突然说“我们再加一个模型做对比吧”,或者“客户要求必须支持私有化部署的模型”。这时候你打开代码一看,发现每个模型的调用方式都不一样——参数名不同、返回结构不同、流式输出的格式不同、错误码更是五花八门。于是你被迫写了一堆 if-else,代码越来越臃肿,维护成本直线上升。

litellm就是冲着这个痛点来的。它是一个用 Python 写的开源库,核心目标只有一个:用一套统一的 OpenAI 格式接口,去调用市面上几乎所有主流的大模型服务。你不需要为每个厂商单独写适配层,只需要把base_url、api_key、model这三个东西配置好,剩下的交给 litellm 处理。它支持的服务包括各类商业 API、开源模型本地部署、以及各种兼容 OpenAI 协议的推理服务。

这个项目适合谁?三类人最应该关注:第一类是做 AI 应用开发的工程师,尤其是需要多模型切换、A/B 测试、成本优化的团队;第二类是做模型评测和研究的同学,需要批量调用不同模型跑同一套 prompt;第三类是想快速搭原型的独立开发者,不想在接口适配上浪费时间。哪怕你只是刚入门,只要写过几行 Python 调用大模型的代码,litellm 都能让你少踩很多坑。

我最初接触 litellm 是因为一个多模型对比的需求:同一个问题要同时问三个不同厂商的模型,然后把结果汇总展示。如果按传统写法,我得维护三套 SDK、三套鉴权逻辑、三套异常处理。用 litellm 之后,核心代码从两百多行压缩到了不到三十行。这篇文章就把我这段时间的使用经验、踩过的坑、以及一些官方文档里没写清楚的细节,完整地梳理一遍。

2. 核心设计思路拆解:为什么是“统一接口”而不是“再包一层”

2.1 统一到 OpenAI 格式的底层逻辑

litellm 最核心的设计决策,就是把所有模型的输入输出都映射到 OpenAI 的 Chat Completions 格式上。这个选择不是随便拍的,背后有几个很实际的考量。

首先,OpenAI 的接口格式在事实上已经成了行业“普通话”。绝大多数厂商在推出自己的 API 时,都会提供一个“兼容 OpenAI”的端点,因为这样能降低开发者的迁移成本。litellm 顺势而为,把 OpenAI 格式作为中间层,相当于找到了一个最大公约数。你写一次messages数组,传一次model参数,litellm 在内部帮你翻译成目标厂商需要的格式。

其次,这样做让切换模型的成本几乎为零。你只需要改一个字符串,比如把model="gpt-4o"改成model="claude-3-5-sonnet",其他代码一行不用动。这对于做模型对比、灰度发布、故障降级来说,价值非常大。我之前做过一个实验:主模型超时的时候自动降级到备用模型,用 litellm 实现只需要捕获异常再换一个 model 名重新调用,逻辑非常干净。

第三,统一格式意味着工具链可以复用。你为 OpenAI 写的日志记录、token 计数、重试逻辑、缓存层,全都能直接用在其他模型上。litellm 本身也内置了这些能力,比如completion函数支持num_retries参数自动重试,支持cache参数做结果缓存,这些都是跨模型通用的。

2.2 架构分层:Router、Proxy 与 SDK 三种用法

很多人第一次看 litellm 文档会有点懵,因为它其实提供了三种不同层次的用法,适用场景完全不同。我把它拆成三层来理解会更清楚。

最底层是 SDK 模式,也就是直接import litellm然后调用litellm.completion()。这是最轻量的用法,适合脚本、Notebook、小型项目。你不需要启动任何服务,装完包配好 key 就能用。缺点是它只管单次调用,不负责负载均衡、密钥管理这些事。

中间层是 Router 模式,通过litellm.Router类来管理多个模型部署。你可以给同一个模型配多个 key(比如多个账号轮询),或者配多个不同模型做 fallback。Router 会自动处理重试、负载均衡、超时降级。这个模式适合有一定规模的应用,但还是在你的 Python 进程内运行。

最上层是 Proxy 模式,也就是把 litellm 当成一个独立的网关服务跑起来,对外暴露标准的 OpenAI 接口。你的应用只需要把base_url指向这个代理,就能像调 OpenAI 一样调用所有后端模型。这个模式适合团队协作、多语言项目(比如前端直接调)、以及需要统一管控密钥和配额的场景。

我个人的建议是:先用 SDK 模式跑通,有多个 key 或需要降级时上 Router,需要跨团队共享时再上 Proxy。不要一上来就搞最复杂的,很多人被 Proxy 的配置文件劝退,其实根本用不上。

2.3 与直接封装 requests 的本质区别

有同学可能会想:我自己写个函数封装 requests 不也行吗,为什么要用 litellm?这个问题我认真对比过,区别主要在三个地方。

第一是参数映射的完整性。不同厂商对同一个概念的叫法不一样,比如“温度”有的叫temperature有的叫top_p配合用,“最大长度”有的叫max_tokens有的叫max_output_tokens。litellm 内部维护了一张巨大的映射表,把这些差异都抹平了。你自己写的话,每接一个新厂商就要查一遍文档,很容易漏。

第二是异常处理的统一。各家 API 的错误码和错误结构完全不同,有的返回 429 表示限流,有的返回 503,有的在 body 里塞一个自定义 code。litellm 把这些都归一化成了统一的异常类型,比如RateLimitError、ContextWindowExceededError、AuthenticationError。你只需要捕获这几个标准异常就行,不用为每个厂商写一套。

第三是流式输出的兼容。流式返回是最容易出问题的地方,不同厂商的 SSE 格式、chunk 结构、结束标志都不一样。litellm 把流式输出也统一成了 OpenAI 的 chunk 格式,你拿到的每个 chunk 结构都一致。这一点省了我大量调试时间。

3. 环境准备与基础调用:从零跑通第一个例子

3.1 安装与密钥配置的几种方式

安装本身很简单,一条命令:

pip install litellm

但密钥配置这块有几个细节值得说。litellm 读取密钥的方式遵循一个约定:每个厂商有一个对应的环境变量名。比如 OpenAI 是OPENAI_API_KEY,Anthropic 是ANTHROPIC_API_KEY,某国内厂商可能是DASHSCOPE_API_KEY之类。你只要把环境变量设好,调用时就不用显式传 key。

我习惯用.env文件管理,配合python-dotenv:

from dotenv import load_dotenv load_dotenv() import litellm response = litellm.completion( model="gpt-4o-mini", messages=[{"role": "user", "content": "用一句话解释什么是向量数据库"}] ) print(response.choices[0].message.content)

如果你不想用环境变量,也可以在调用时直接传api_key参数,或者用litellm.api_key = "..."全局设置。但我要提醒一句:千万不要把 key 硬编码在代码里提交到仓库,这个坑我见过太多次了。用环境变量或者密钥管理服务,是最基本的习惯。

提示:如果你用的是兼容 OpenAI 协议的第三方服务,需要同时指定api_base参数,否则 litellm 会默认往 OpenAI 官方地址发请求。

3.2 一次调用背后的完整流程

很多人用 litellm 就是一行completion,但不知道背后发生了什么。我把这个流程拆开讲,理解了之后排查问题会快很多。

当你调用litellm.completion(model="xxx", messages=[...])时,内部大致经历这几个步骤:首先,litellm 根据model字符串去查它内部的“模型注册表”,确定这个模型属于哪个 provider、用哪个适配器。然后,它把你的messages和参数翻译成目标 provider 需要的格式。接着,它从环境变量或参数里取出对应的 key 和 base_url,发起 HTTP 请求。收到响应后,再把结果反向翻译回 OpenAI 格式返回给你。

这个流程里最容易出问题的是第一步的模型识别。litellm 判断 provider 主要靠模型名的前缀或关键词。比如gpt-开头认为是 OpenAI,claude-开头认为是 Anthropic。如果你用的是自定义部署的模型,名字里没有这些关键词,就必须显式指定 provider,否则它会猜错。指定方式是加前缀,比如model="openai/my-custom-model"或者model="hosted_vllm/xxx"。

3.3 参数传递的通用与专属之分

litellm 的参数分两类:通用参数和provider 专属参数。通用参数就是 OpenAI 那套,比如temperature、max_tokens、top_p、stream,这些你直接传就行,litellm 会帮你转换。

provider 专属参数则需要通过一个特殊字段传递。litellm 支持在调用时加**kwargs,或者用extra_body之类的字段。比如某个厂商有一个独有的repetition_penalty参数,你可以这样传:

response = litellm.completion( model="some-provider/some-model", messages=[{"role": "user", "content": "你好"}], repetition_penalty=1.1 )

litellm 会把不认识的参数透传给底层 provider。这个设计很实用,但要注意:如果参数名和通用参数冲突,可能会被覆盖。我遇到过传了自定义的max_tokens结果被 litellm 的默认值覆盖的情况,后来改成显式传参才解决。

4. 多模型管理与 Router 实战:让调用更稳更省

4.1 为什么需要 Router:单点调用的三个隐患

直接用completion在简单场景下没问题,但一旦上生产,三个隐患就会暴露出来。

第一个是单 key 限流。很多厂商对单个 API key 有 QPS 或 TPM 限制,你请求一多就被限流,报 429。解决办法是配多个 key 轮询,但自己写轮询逻辑很麻烦。

第二个是单模型故障。任何服务都可能临时不可用,如果只有一个模型,挂了就是挂了。你需要一个 fallback 机制,主模型失败时自动切到备用模型。

第三个是成本与延迟的权衡。有些请求用便宜的小模型就够了,有些需要大模型。你希望根据情况动态选择,而不是写死一个。

Router 就是来解决这三个问题的。它把多个“部署”(deployment)组织在一起,每个部署可以是一个模型加一个 key 的组合,然后按策略分发请求。

4.2 配置一个带降级和重试的 Router

下面是我实际项目里用的一段配置,做了简化:

import litellm from litellm import Router model_list = [ { "model_name": "primary", "litellm_params": { "model": "gpt-4o-mini", "api_key": "sk-xxx", }, }, { "model_name": "primary", "litellm_params": { "model": "gpt-4o-mini", "api_key": "sk-yyy", }, }, { "model_name": "fallback", "litellm_params": { "model": "claude-3-5-haiku", "api_key": "sk-zzz", }, }, ] router = Router( model_list=model_list, fallbacks=[{"primary": ["fallback"]}], num_retries=2, timeout=30, ) response = router.completion( model="primary", messages=[{"role": "user", "content": "写一个二分查找"}] )

这段配置做了几件事:primary这个名字下挂了两个部署(两个不同的 key),Router 会自动在它们之间做负载均衡;如果primary全部失败,会按fallbacks配置降级到fallback;num_retries=2表示失败自动重试两次;timeout=30是单次请求超时。

注意:fallbacks的写法是一个列表,里面每个元素是{"源模型名": ["目标模型名列表"]}。我第一次配的时候写成了字典,结果一直不生效,查了半天文档才发现格式问题。

4.3 负载均衡策略与权重控制

Router 默认用的是简单的轮询(round-robin),但你可以通过routing_strategy参数改成其他策略。常见的几种我列个表对比一下:

策略名称适用场景特点
simple-shuffle默认,通用随机打散,实现简单
least-busy请求耗时差异大优先选当前并发最少的部署
usage-based-routing关注成本按 token 用量或成本选最省的
latency-based-routing关注响应速度按历史延迟选最快的

如果你给同一个模型名配了多个部署,还可以用weight参数控制流量比例。比如新 key 刚接入,想先给它 10% 的流量观察稳定性,就可以设weight: 1和weight: 9。这个功能在灰度切换 key 的时候特别好用,我每次换新 key 都会先跑一周低权重,确认没问题再全量。

5. Proxy 模式:把 litellm 变成团队共用的模型网关

5.1 什么情况下该上 Proxy

SDK 和 Router 都是在你的 Python 进程里跑的,这意味着每个应用都要自己配 key、自己管配置。当团队变大、应用变多的时候,问题就来了:key 散落在各个项目里,谁用了多少不知道,换 key 要挨个改。

Proxy 模式就是把这些统一收口。你启动一个 litellm 的代理服务,所有应用都往这个代理发请求,代理再转发给真正的模型。好处很明显:key 只存在代理这一处,应用侧完全不用管;用量统计和配额控制可以在代理层做;换模型、加模型只改代理配置,应用无感知。

我判断的标准是:如果只有你一个人用,SDK 就够了;如果有两个以上的人或两个以上的项目要共用,就该考虑 Proxy 了。

5.2 配置文件结构与启动方式

Proxy 的核心是一个 YAML 配置文件。下面是一个最小可用的例子:

model_list: - model_name: gpt-4o-mini litellm_params: model: gpt-4o-mini api_key: os.environ/OPENAI_API_KEY - model_name: claude-haiku litellm_params: model: claude-3-5-haiku api_key: os.environ/ANTHROPIC_API_KEY general_settings: master_key: sk-my-master-key database_url: os.environ/DATABASE_URL

注意api_key那里写的是os.environ/XXX,这是 litellm 的语法,表示从环境变量读取,不要直接写明文。master_key是访问代理本身的鉴权 key,客户端要用这个 key 来调用代理。

启动命令:

litellm --config config.yaml --port 4000

启动后,你的应用就可以把base_url指向http://localhost:4000,api_key用master_key,然后像调 OpenAI 一样调用。比如用官方 SDK:

from openai import OpenAI client = OpenAI(base_url="http://localhost:4000", api_key="sk-my-master-key") resp = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "你好"}] )

5.3 用量追踪与虚拟密钥

Proxy 模式一个很实用的功能是虚拟密钥(virtual key)。你可以给每个团队成员或每个项目发一个虚拟 key,而不是共用 master key。这样就能追踪每个人用了多少 token、花了多少钱,还能给每个 key 设预算上限。

配置了database_url之后,litellm 会把调用记录写进数据库,然后通过管理接口创建虚拟 key。这个功能对于控制成本特别有用,我见过有团队因为没做用量控制,某个月账单直接翻了好几倍。提前把预算卡住,比事后追责有用得多。

提示:Proxy 模式依赖数据库来存储 key 和用量,生产环境建议用 PostgreSQL,SQLite 只适合本地测试。

6. 常见问题与排查技巧实录

6.1 模型识别错误:最常见的“找不到 provider”

这是新手遇到最多的报错,通常长这样:litellm.exceptions.BadRequestError: LLM Provider NOT provided。原因就是 litellm 从模型名里推断不出 provider。

解决办法有两个:一是用标准前缀,比如openai/、anthropic/、azure/;二是如果你用的是自定义部署,用对应的 provider 前缀,比如本地推理服务常用hosted_vllm/或openai/加自定义api_base。我一般建议养成显式加前缀的习惯,哪怕模型名本身能被识别,加上前缀也更清晰,不容易出歧义。

6.2 超时与重试:参数怎么设才合理

超时和重试的设置很讲究,设太短会误杀正常请求,设太长会拖垮整个链路。我的经验值是:普通对话类请求 timeout 设 30 到 60 秒,长文本生成设 120 秒以上。重试次数num_retries设 2 到 3 次就够了,再多会放大故障。

还有一个细节:litellm 的重试默认是指数退避的,也就是每次重试间隔翻倍。这个策略对限流场景很友好,但如果你需要快速失败,可以调整retry_after相关参数。另外要注意,流式请求的重试行为和普通请求不同,流已经开始返回后再失败,重试可能会导致内容重复,这个要特别小心。

6.3 流式输出中断:几个容易忽略的点

流式输出中断是排查起来最烦的问题,因为报错信息往往不明确。我总结了几种常见原因和对应处理:

现象可能原因处理方式
流到一半卡住网络超时或代理缓冲检查 timeout,关闭中间层缓冲
chunk 解析报错厂商 SSE 格式差异升级 litellm 版本,或指定 provider
内容重复重试导致重复发送流式场景降低重试次数
提前结束达到 max_tokens检查 max_tokens 设置

我踩过最深的一个坑是:某个厂商的流式返回里,最后一个 chunk 的结束标志和 OpenAI 不一样,导致 litellm 一直等不到结束信号,最后超时。解决办法是升级 litellm 到最新版,因为这类兼容问题官方修得很勤。保持 litellm 版本较新,能省掉大量兼容性调试。

6.4 成本失控:几个必须提前做的设置

成本问题往往是用量上来之后才发现的。我建议从第一天就做三件事:第一,在 Proxy 层给每个虚拟 key 设预算上限;第二,开启用量日志,定期看哪些模型、哪些调用最费钱;第三,给非关键请求配便宜模型,比如分类、摘要这类任务用 mini 模型完全够。

还有一个容易被忽略的点:失败重试也会产生费用。如果某个请求一直失败一直重试,token 是照样扣的。所以重试次数不要设太高,同时要监控失败率,失败率异常升高时及时排查。

7. 一些实操心得与扩展方向

用了一段时间 litellm,我最大的体会是:它把“多模型”这件事的边际成本降到了很低。以前接一个新模型要半天,现在改一行配置就行。这带来的直接好处是,你可以更大胆地做模型对比实验,哪个模型在哪个任务上表现好,用数据说话,而不是凭感觉。

几个我常用的扩展玩法分享一下。一是用 Router 做 A/B 测试,同一个模型名配两个不同版本的部署,按权重分流,对比效果。二是把 litellm 和评测框架结合,批量跑同一套测试集,输出对比报告。三是在 Proxy 层加自定义回调,litellm 支持success_callback和failure_callback,可以把每次调用的耗时、token 数、成本打到自己的监控系统里。

最后提醒一句:litellm 迭代很快,新版本经常加新 provider、修兼容问题,但也偶尔引入回归。我的做法是生产环境锁定版本号,测试环境跟进最新版,确认没问题再升级生产。这个习惯帮我避免了好几次因为升级导致的线上问题。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询