做AI应用开发的朋友,大概率遇到过这种场景:同一个需求,想在不同大模型之间横向对比效果,结果一查文档,各家接口风格完全不一样,有的用messages,有的用prompt,有的鉴权要放Header,有的要放Query参数。更头疼的是切换模型要改代码、改依赖、改配置,换个厂商就要重新联调一遍。后来我在某个开源项目里翻到了“litellm”,试了一圈发现它就是专门解决这个问题的。简单说,litellm是一个统一的大模型访问网关,可以理解成“一套接口接所有模型”,你在客户端只写一份OpenAI风格的代码,剩下的路由、鉴权、限流、日志、预算控制全交给它。
这篇文章不是给你念官方文档,而是从一个踩过坑的开发者视角,讲讲litellm到底是什么、能解决哪些问题、怎么最快跑起来,以及我实测中遇到的各种怪问题。不管你是个人开发者、小团队负责人,还是要在公司内部搭一套统一AI接入层,这篇文章应该都能帮你少走不少弯路。
1. 为什么需要litellm:多模型接入的真实痛点
先讲一个我自己的经历。之前做一个基于大模型的内容分析工具,最初接了某国外厂商的接口,后来老板说想对比另一家的效果,同时国内某云厂商的模型也要测一下。一开始我想得挺简单,不就是换个请求地址嘛。结果一动手就发现事情没这么简单。
1.1 各家模型接口的割裂程度远超想象
不同模型服务商的API设计割裂到什么程度?请求体格式不同,响应体结构不同,鉴权方式不同,错误码体系不同,限流策略不同,甚至“模型名”这种基础概念都各叫各的。有的接口叫model,有的叫engine;有的传max_tokens,有的传max_new_tokens;有的输出里带choices,有的给的字段完全不一样。
我记得最夸张的一次,某个平台的流式响应用的是自定义的SSE格式,字段名和我平时用的完全不同,为了把流式结果统一成上游需要的结构,我硬是在业务代码里写了一堆兼容层,这个兼容层里塞满了if provider == "xxx"这种判断。项目上线后,每加一个新模型,就要在兼容层里补一堆分支,代码越来越臃肿,还特别容易出bug。
1.2 密钥管理、限流和成本控制也是一团乱麻
除了接口格式,密钥管理也是个大问题。团队里几个人共用同一个模型账号,key传来传去,一不小心就被提交到代码仓库里。而且不同厂商的限流策略差异很大,有的按每分钟请求数限,有的按每分钟token数限,还有的同时限制并发数和每分钟调用次数。没有一个统一出口,我们就没办法集中做限流、监控和预算控制。月底看到账单的时候更是一头雾水,根本不知道每个应用各花了多少钱。
1.3 网关模式才是正解
后来我意识到,业务代码根本不该直接面对这些五花八门的模型接口。正确的思路是在业务系统和模型厂商之间加一层网关,就像不需要每个电子设备都自带一个特定形状的插头,而是用一个万能转换插头来统一解决。litellm就是这个“万能转换插头”,它把所有模型提供商的API统一成一套OpenAI兼容的接口,上层业务代码只需要认识这一种格式。
同时,网关还能集中管理密钥、控制访问权限、记录日志、做负载均衡和成本核算。你的业务代码只关心“我发了什么请求、拿到什么结果”,不用关心这个请求最后去了哪家厂商、以什么身份调用、花了多少钱。
2. litellm的核心能力拆解
litellm的能力不只是一个“API转发器”。它更像一个面向大模型的API网关,自带路由、重试、缓存、限流、预算控制、日志追踪等能力。下面我把最核心的几个功能一个个拆开讲。
2.1 统一的OpenAI兼容接口
这是litellm最直观的价值。它对所有上游模型做了一层适配,全部转换成OpenAI风格的请求和响应。
客户端调用的时候,只需要按照OpenAI的规范设置接口地址:
- 本地启动litellm后,监听在
8000端口,/chat/completions路径对应聊天补全 - 客户端把
base_url指向http://localhost:8000,api_key填自己在litellm里配置的虚拟key - 请求体使用标准的
model、messages、temperature等字段
举个例子,假设我在litellm里配置了一个名为gpt-x的上游模型,一个名为claude-y的另一个上游模型,对于客户端来说,用法完全一样:
from openai import OpenAI client = OpenAI( base_url="http://localhost:8000", api_key="sk-litellm-virtual-key" ) # 请求任何一种模型,代码完全一致 resp = client.chat.completions.create( model="gpt-x", messages=[{"role": "user", "content": "你好"}], temperature=0.7, ) print(resp.choices[0].message.content)换模型只需要改model字段的值,比如把model="gpt-x"改成model="claude-y",不需要改任何其他代码。这个特性极大地方便了模型对比实验,也降低了业务切换模型的风险。
2.2 灵活的路由与故障转移
litellm支持为一个逻辑名称配置多个上游模型,比如我声明一个别名my-assistant,它背后实际指向三个不同供应商的模型。litellm会根据策略选择一个上游来响应请求。
常用的策略包括:
- 优先级路由:按照你配置的顺序尝试,第一个可用就返回,失败就自动切换到下一个
- 加权轮询:把流量按照权重分配到不同上游,适合做灰度对比和负载均衡
- 健康检查:litellm会持续探测上游模型接口的可用性,自动摘除不健康的节点
这在生产环境非常实用。比如我们自己搭建了一个开源模型服务,在高峰期响应不稳定,于是配置了外部商业模型作为兜底。当开源服务返回错误或超时,litellm自动把请求转发到兜底服务,业务侧几乎无感知。
2.3 预算控制与访问治理
litellm内置了预算管理能力,可以给每个虚拟API key设置最大消费额度、每分钟请求数限制、每分钟token数限制。一旦超过限制,litellm直接返回429或403,避免失控产生巨额账单。
它的“虚拟key”机制我很喜欢。你可以把litellm理解成一个“钥匙中转站”,上游厂商的真实key只保存在litellm的环境变量或配置文件中,客户端拿到的都是litellm生成的虚拟key。每个虚拟key可以绑定不同的模型权限、不同的预算上限、不同的失效时间。就算某个虚拟key泄漏了,也只是泄漏了网关的访问权,不会暴露上游厂商的真实key,而且随时可以单独吊销。
这一点在团队协作中尤其好用。给前端项目分一个key,给后台批处理任务分另一个key,给测试环境分一个低配额key,谁调用异常超量了,一眼就能在日志里看出来。
2.4 日志、缓存与可观测性
litellm默认会记录所有请求的元数据,包括调用时间、模型名、请求模型、消耗token数、响应状态、延迟等。这些日志可以输出到控制台、本地文件,或者接入第三方日志服务。配合它提供的数据统计接口,你还能按天、按key、按模型汇总出token消耗量和费用估算。
缓存功能也可以直接启用,对相同请求做结果缓存。比如做客服问答时,很多常见问题的答案是固定的,命中缓存后直接返回,既省了上游调用费,又降低了响应延迟。
3. 从零部署一套litellm网关
看到这里,你应该对litellm能做什么有了基本概念。下面我们把一个可用的litellm网关跑起来。这里以本机部署为例,生产环境建议用Docker部署到服务器,但原理是一样的。
3.1 环境准备与安装
litellm是基于Python的,推荐使用3.9及以上版本。安装命令非常简单:
pip install 'litellm[proxy]'这会安装核心库和代理服务所需的依赖。如果你只想在脚本里用它的翻译能力而不启动网关服务,直接pip install litellm就行。
安装完成后,先验证一下版本:
litellm --version如果能正常输出版本号,说明安装成功。
3.2 编写config.yaml配置文件
litellm网关的运行依赖一个配置文件,用来声明上游模型、路由规则、预算策略等。我先写一份最小可用的配置:
model_list: - model_name: my-llm-1 litellm_params: model: provider-a/model-a api_key: os.environ/PROVIDER_A_KEY - model_name: my-llm-2 litellm_params: model: provider-b/model-b api_key: os.environ/PROVIDER_B_KEY general_settings: master_key: sk-master-123 database_url: sqlite:///litellm.db说明一下:
model_list:声明可用的模型列表。model_name是客户端看到的逻辑名,litellm_params.model字段的格式是供应商名/模型名,litellm根据前缀识别应该走哪个上游适配器。api_key:通过环境变量引用真实上游key,避免明文写在配置文件里。master_key:litellm管理端和API访问的主密钥,客户端调用时使用这个key或者用它创建的子key。database_url:持久化存储配置和日志,本地测试用SQLite足够,生产可以用PostgreSQL。
3.3 启动服务并验证
在终端启动服务:
export PROVIDER_A_KEY="sk-真实上游key" export PROVIDER_B_KEY="sk-真实另一个上游key" litellm --config config.yaml --port 8000看到类似Uvicorn running on http://0.0.0.0:8000的日志就说明网关起来了。
用一个简单的curl验证:
curl http://localhost:8000/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer sk-master-123" \ -d '{ "model": "my-llm-1", "messages": [{"role": "user", "content": "你好,介绍一下你自己"}], "stream": false }'如果配置无误,你会收到和OpenAI格式一致的JSON响应。到这里,一套最小化的litellm网关就跑通了。
4. 实战中的关键配置与脚本示例
最小配置只是开胃菜,真正到了生产环境,你会需要更丰富的功能。我把自己实际用过的几个重点场景和配置整理出来,可以直接参考。
4.1 同时接入多个供应商并配置fallback
一个很常见的需求是:主用某个模型,如果它挂了或者超时,自动切换到另一个备用模型。在litellm里,这可以通过配置模型别名和fallback实现。
先在model_list里定义两个具体模型,再定义一个逻辑别名指向它们:
model_list: - model_name: primary-model litellm_params: model: provider-a/model-a api_key: os.environ/PROVIDER_A_KEY - model_name: fallback-model litellm_params: model: provider-b/model-b api_key: os.environ/PROVIDER_B_KEY router_settings: model_group_alias: my-app-model: routing_strategy: priority-fallback models: - primary-model - fallback-modelrouter_settings是litellm较新版本中的路由配置区域,model_group_alias用来定义一组可路由的模型集合。客户端请求时,model填my-app-model,litellm会优先尝试primary-model,如果该上游返回错误或超时,则自动调用fallback-model。
这里有一个容易踩的坑:如果上游是网络超时导致不可用,litellm默认可能等待较长时间才切换。我建议在配置里显式调整超时和重试参数:
litellm_settings: request_timeout: 10 num_retries: 2 fallbacks: my-app-model: - fallback-modelrequest_timeout控制每个请求的最大等待秒数,num_retries控制重试次数,fallbacks是另一种更直观的fallback写法。两者配合使用,能显著缩短故障感知时间。
4.2 为不同的内部项目创建受限的虚拟key
在团队里,我不建议所有人直接使用master_key。更好的做法是给每个项目、每个环境生成独立的虚拟key。
通过utilities接口创建一个虚拟key:
curl http://localhost:8000/key/generate \ -H "Content-Type: application/json" \ -H "Authorization: Bearer sk-master-123" \ -d '{ "models": ["my-app-model"], "max_budget": 20.0, "budget_duration": "30d", "max_parallel_requests": 5 }'返回的JSON里会有一个key字段,这就是给对应项目使用的虚拟key。这个key只能访问my-app-model,30天内最多消耗20美元,同一时刻最多5个并发请求。过期后自动失效,非常省心。
虚拟key的预算和限流是实时在网关层执行的,所以哪怕某个项目出现了死循环疯狂调用,最坏的结果只是它自己的key被限流,不会拖垮整个网关,也不会产生意外的高额账单。
4.3 启用缓存减轻上游压力
对于重复性高的请求,比如FAQ问答、关键词抽取、摘要模板等,缓存能帮你节省大量成本。配置很简单:
litellm_settings: cache: true cache_params: type: redis host: localhost port: 6379启用后,litellm会对相同请求(包括相同的model、messages、参数)返回缓存结果,不再去上游重新计算。生产环境建议用Redis,本地测试也可以把type设为memory。缓存命中时,响应中会带有相关标记,方便你区分是缓存还是真实调用。
需要注意,如果业务对结果实时性要求很高,比如需要反映数据库的最新状态,就不要轻易开启缓存,或者给请求体加上一个随机字段来绕过缓存。
4.4 用Python SDK完成一次带路由的调用
litellm不仅提供了网关服务,还提供了Python SDK,让你在普通Python脚本里也能直接使用它统一后的接口。安装litellm库后,可以这样写:
from litellm import completion response = completion( model="my-app-model", messages=[{"role": "user", "content": "写一首关于代码的诗"}], temperature=0.8, ) print(response.choices[0].message.content)如果配置了环境变量LITELLM_MASTER_KEY和LITELLM_PROXY_BASE_URL,这个脚本就会自动通过你的litellm网关转发请求,而不是直连上游。这意味着你可以在本地开发、CI、生产环境共用同一套代码逻辑,只需要切换环境变量指向不同的网关地址。
5. 常见问题与排查技巧实录
litellm用起来虽然方便,但毕竟是自建服务,各种问题少不了。我把自己踩过以及帮别人排查过的典型问题整理成一张速查表。
| 现象 | 可能原因 | 排查与解决 |
|---|---|---|
| 返回401 Unauthorized | 客户端没带key或key无效 | 确认请求头携带了master_key或有效的虚拟key |
| 返回404 Model Not Found | 配置的模型名和请求的模型名不一致 | 检查model_list中的model_name与请求体里的model字段 |
| 请求超时 | 上游模型响应慢或网络不通 | 调大request_timeout;检查上游服务状态 |
| 返回429 Too Many Requests | 触发了litellm限流或上游限流 | 查看虚拟key的配额设置;检查上游账户余额 |
| 上游返回错误但litellm日志正常 | 上游服务商自身故障 | 在litellm中临时绕过,直接调用上游服务对比 |
| 日志中显示KeyError某个字段 | 上游响应格式有变 | 升级litellm版本,或检查供应商API变更 |
5.1 排查流程:从客户端到上游层层定位
我自己的排查习惯是:先复现请求,再分三层检查。
第一层看客户端。用curl直接请求litellm网关,排除业务代码的问题。如果curl能正常返回,而业务代码不行,大概率是SDK版本、代理设置或HTTP连接池的问题。
第二层看litellm日志。litellm在控制台输出的日志里会记录完整的请求和响应轨迹。重点看provider字段、status字段、error字段。日志里如果显示上游调用成功但返回异常,问题就在上游侧。
第三层看上游服务。直接使用上游服务商的原生SDK或用curl调用一次上游接口,确认是否是上游配置问题,比如key过期、模型名不存在、账户欠费。这一步能快速区分是litellm的问题还是上游服务的问题。
5.2 安全防护注意事项
使用litellm时,有几个安全细节值得特别注意。
- 不要用master_key作为所有客户端的共享key,应该为每个客户端生成独立虚拟key,方便回收和审计。
- 配置文件中不要明文写入真实上游key,使用
os.environ/变量名的方式引用,同时确保环境变量文件不被提交到代码仓库。 - 网关服务不要直接暴露在公网,至少要加一层IP白名单或HTTP Basic Auth。如果条件允许,放在内网并配合反向代理使用。
- 虚拟key泄漏后,不需要重新部署服务,直接调用
/key/delete接口删除即可。
有一次我把网关部署在云服务器上,安全组不小心把8000端口对公网开放了,当晚就收到了一堆来自扫描工具的请求。幸好我用的虚拟key且限制了模型权限,没有造成实际损失,但从那以后我严格限制了端口访问,并且给网关加了一层额外的防火墙规则。
5.3 版本升级踩坑记录
litellm迭代速度很快,我遇到过几个版本之间配置字段不兼容的情况。最典型的是早期版本使用router_config,后来改成了router_settings;缓存参数也从cache_config变成了litellm_settings.cache_params。
升级前一定先看官方的迁移说明,不要直接升级生产环境。比较好的做法是在本地起一个新版本实例,把旧的配置文件导入测试一遍,确认所有模型名和路由策略都能正常工作,再切换流量。
我现在都会把litellm的版本号固定在需求文件里,比如litellm[proxy]==某个固定版本,避免自动升级带来的意外。新版本需要升级时,先读变更日志,再执行测试流程。
写在最后的小经验
litellm让我最大的感受是:它把一个看起来很“脏乱差”的多模型接入问题,收敛成了一个干净、标准的接口。过去我写业务代码时要考虑厂商差异、密钥安全、限流重试、成本控制,现在这些逻辑都沉淀到网关层,业务代码反而变得很纯粹。
如果你只是想快速试玩,装个库、写个十几行的配置就能跑起来。如果你想在生产环境认真用,建议先想清楚自己的路由策略、预算策略和日志方案,不要一上来就把所有模型全塞进去。我在实际使用中体会到,litellm的价值不在于“功能多”,而在于它让你能把更多精力放在真正的业务逻辑上。
最后分享一个小技巧:接新模型供应商时,不要直接改生产配置,先在本地用alias的方式临时加一个新模型,实测效果稳定后再更新到正式配置文件里。这样即使新模型接入失败,也不会影响正在运行的线上请求。这个习惯救了我很多次。