如果你维护过哪怕一个需要对接多个大模型API的项目,你对下面这种“割裂感”一定不陌生:每个厂商都有自己的SDK、各自的鉴权方式、互不兼容的响应结构,甚至连token计费口径都不一样。项目里光写适配代码就占了小半本需求文档,换一个模型就要动一处业务代码。LiteLLM就是来拆这个墙的。
LiteLLM是一个开源的LLM接入层工具,核心很朴素:把所有模型提供方统一成一套OpenAI兼容的接口格式。它有两种用法——当Python SDK嵌入项目直接调用,或者部署成一个独立的代理网关,让团队所有人的请求都走一个统一入口。不管你是个人开发者做SaaS应用,还是团队要统一管理多个模型的使用配额、成本、密钥,它都值得认真看看。
这篇文章我会从它的核心设计讲起,再带你从零搭一个代理网关,最后把我实操里踩过的坑和排查经验一并整理出来。
1. 先搞懂LiteLLM解决的核心问题:API割据
1.1 为什么说多模型接入是一个真实的工程负担
很多项目早期的模型接入都很“单纯”:用一家模型,调官方网站的示例代码,跑通了就算完事。但应用一旦进入真实环境,情况马上就不一样了。
模型要留退路。某家模型的接口不够稳定、某个版本的模型效果不达预期、或者单纯因为价格策略调整导致成本压力变大,你都会想换掉当前的模型。如果你的代码里到处散落着某个模型的SDK调用,那换模型就变成了一次代码改造工程,而不是一次配置切换。
更麻烦的是,同一个业务里可能需要不同的模型来处理不同任务:一个轻量的模型做意图识别,一个参数更大的模型做内容生成,一个本地部署的模型跑敏感数据。这个时候你面临的问题已经从“接一家”变成了“管一群”,而这群模型的语言、格式、鉴权方式还互不通用。
这种割裂带来的隐性成本其实很高。新增一个模型要开发联调、出问题要分头排查、账单要手动汇总。团队稍微大一点,每个人接模型的方式都可能不一样,代码库里就会出现各种名称乱七八糟的封装函数。
1.2 LiteLLM的设计思路:让所有接口说一套“通用方言”
LiteLLM解决这个问题的思路非常直白——定一个“通用方言”,然后去兼容所有“地方话”。它选择的是把各家模型统一成OpenAI格式。这么选不是因为它站长,而是因为OpenAI的接口格式已经成为事实上的行业通用语言,很多模型厂商在设计自己接口时本来就在模仿它,生态里大量的开源工具也天然支持这个格式。
在LiteLLM里,每个模型有一个带提供方前缀的标识,格式是provider/model-name。比如openai/gpt-4o表示OpenAI家的模型,anthropic/claude-3-5-sonnet表示Anthropic家的模型。你传给LiteLLM一个统一结构的请求,它在内部完成协议转换、鉴权处理、超时控制,再把各家返回的数据翻译成统一格式给你。
这一点用起来很像一个“接头暗号翻译器”。调用方永远只需要说标准普通话,至于对面讲的是英语、日语还是法语,翻译器帮你搞定了。
1.3 网关形态和SDK形态,分别适合什么场景
LiteLLM的两种形态对应了两种完全不同的需求。
SDK形态适合“你自己项目内部”用。你在项目里直接import litellm,调用litellm.completion(),传入模型名、消息内容。SDK会处理路由、鉴权、重试这些事情。这种形态侵入性小,适合个人项目或者你说了算的单体应用。
网关形态则是一个独立的代理服务。你在服务器上跑一个LiteLLM进程,团队所有人把请求发到这个服务上,再由它转发到真实的模型厂商。调用方甚至不需要知道LiteLLM这个库的存在——它只需要知道网关的地址和密钥。
网关形态是团队协作场景下的核心优势。模型密钥不用分发到每个人手里,统一由网关保管;每个人的访问权限、预算、调用量都能在网关层面控制;模型切换对客户端完全透明。再加上网关还能挂数据库实现密钥持久化、做成本追踪,这已经不是一个SDK的功能范围了,而是变成了一个企业内部的模型基础设施。
2. LiteLLM核心功能拆解:它到底强在哪里
2.1 统一接口:一行代码调用不同模型
LiteLLM最基本的能力,就是让你用同样的调用结构访问不同提供方的模型。看这段Python代码:
from litellm import completion resp = completion( model="openai/gpt-4o-mini", messages=[{"role": "user", "content": "你好"}] ) resp2 = completion( model="anthropic/claude-3-5-sonnet-20240620", messages=[{"role": "user", "content": "你好"}] )注意两个调用的结构完全一致,只有model参数不同。LiteLLM内部会把请求转换成各家API需要的格式,再把结果统一回传给调用方。
这里的关键设计是model前面的提供方前缀。它决定了LiteLLM用哪套适配逻辑去解析请求、用什么方式鉴权、怎么处理响应。前缀后面跟着的是厂商侧的真实模型名。所以你在任何地方看到一个openai/前缀的模型字符串,就知道这个请求最终是由OpenAI兼容接口处理的;看到anthropic/就知道走向的是另一套协议。
这也意味着,如果你的项目里现在用的是官方SDK,迁移到LiteLLM并不需要重写业务逻辑,只需要把调用入口换一下。这是它上手成本低的重要原因。
2.2 代理网关:统一出入口和虚拟密钥
把LiteLLM以代理模式跑起来之后,它就变成了一个完全独立的服务。客户端像调用OpenAI那样调用这个服务,剩下的转发、鉴权、负载均衡都由网关处理。
部署好之后,你的客户端只需要这样做:
curl http://localhost:4000/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer sk-你的密钥" \ -d '{ "model": "gpt-4o-mini", "messages": [{"role": "user", "content": "你好"}] }'客户端发送的model参数,是你在网关配置里定义的“别名”,比如gpt-4o-mini、claude-sonnet。网关根据配置映射,决定实际转发到哪个真实模型。
代理模式还有一个很实用的功能:虚拟密钥。你可以通过网关的管理接口,生成一串只存在于网关内部的密钥,比如sk-abcdef1234567890。这串密钥对接着你自己的权限范围、可用模型列表和预算上限。把虚拟密钥分发给团队成员,比直接把模型厂商原始密钥发给每个人安全得多。万一某个人泄露了密钥,你只需要在网关里吊销这一把虚拟密钥即可,不需要去厂商侧重置根密钥。
2.3 负载均衡、重试和故障转移
真实生产环境里,单一模型渠道随时可能因为限流、过载、区域网络问题而失败。LiteLLM的网关本身内置负载均衡和重试机制,而且可以针对同一个模型配置多个供应商实例。
配置大约长这样:
model_list: - model_name: gpt-4o-mini litellm_params: model: openai/gpt-4o-mini api_key: os.environ/OPENAI_API_KEY - model_name: gpt-4o-mini litellm_params: model: azure/openai/gpt-4o-mini api_key: os.environ/AZURE_API_KEY api_base: os.environ/AZURE_API_BASE上面这个配置里,model_name都是gpt-4o-mini,但对应了两个不同的后端。网关在收到请求后,默认会在两个后端之间轮询,如果一个渠道失败,还能自动转到另一个渠道。这个过程对客户端完全透明。
路由策略也是可配置的。LiteLLM提供多种策略,比如按顺序轮询的simple-shuffle、考虑延迟的usage-based-routing等。你还可以配置兜底链路,假设gpt-4o-mini的渠道全部不可用,就把流量降级到gpt-3.5-turbo,保证核心功能不被单一厂商挂掉拖死。
2.4 预算控制与成本可见性
说到成本,LiteLLM的跟踪能力可以精确到每一次请求。你可以把成功的回调接进数据库、日志或监控系统,从而统计每个模型、每个密钥、每个用户(请求里的user字段)的花费。
数据库接入成本追踪的配置:
litellm_settings: success_callback: ["postgres"] general_settings: database_url: postgresql://user:password@localhost:5432/litellm接入数据库之后,网关会自动维护一张张成本明细表。配合虚拟密钥功能,你还能对每个密钥设置max_budget,当某个密钥的消费累计超过预算,网关会直接拒绝该密钥的新请求。这就实现了团队场景下的费用治理——每个人有每个人的额度,不会出现某个人把一个月的预算一个下午跑完的情况。
成本追踪的价值还要再加一层:跨厂商的账目汇总。不同厂商的计费口径不统一,有的按字符、有的按token,有的按不同模型区间计费。LiteLLM内部根据调用情况估算token数和费用,把不同厂商的数据做成一张统一的表。虽然它给出的金额不完全等于各厂商账单上的实际金额,但对比较模型性价比、追踪异常消耗来说,参考价值非常高。
2.5 动态管理与灵活配置
LiteLLM的配置主要靠YAML文件,但这些配置不一定要通过重启服务来变更。网关提供了管理API,可以动态添加、更新、删除模型。
动态添加模型的接口:
curl -X POST http://localhost:4000/model/new \ -H "Authorization: Bearer sk-主密钥" \ -H "Content-Type: application/json" \ -d '{ "model_name": "gpt-4o", "litellm_params": { "model": "openai/gpt-4o" } }'这让LiteLLM在很多场景里变得像一个“模型商店”。日常维护时,新模型上线不再需要改代码、发版本,调一下接口就行。配合鉴权管理,不同团队看到的模型列表可以不一样。这种动态性在模型快速迭代的阶段尤其有用——这个月你还在测一个新模型,下个月可能就回落了,通过配置开关来管理流量会比改代码高效得多。
3. 从零搭一个LiteLLM代理网关:完整实操记录
3.1 安装与环境准备
LiteLLM要求Python 3.8以上,建议独立建一个虚拟环境,避免污染其他项目的依赖。
python -m venv .venv source .venv/bin/activate pip install 'litellm[proxy]'如果你用Docker,官方镜像也提供代理模式,这里先用本地Python来演示,便于看清日志。安装完成后可以确认版本:
litellm --version注意:
[proxy]这个安装标签会连同代理服务所需的依赖(比如数据库客户端)一并装上。如果你只用SDK模式,直接pip install litellm即可,不需要多余依赖。
3.2 编写config.yaml
网关的核心配置都写在YAML里。先写一个最小可用的配置:
model_list: - model_name: gpt-4o-mini litellm_params: model: openai/gpt-4o-mini api_key: os.environ/OPENAI_API_KEY - model_name: claude-sonnet litellm_params: model: anthropic/claude-3-5-sonnet-20240620 api_key: os.environ/ANTHROPIC_API_KEY - model_name: llama3-local litellm_params: model: ollama/llama3 api_base: http://localhost:11434 litellm_settings: drop_params: true general_settings: master_key: sk-local-master这里解释几个字段:
model_name是网关对外暴露的模型别名。客户端调用时写这个名字。litellm_params.model是真正要请求的模型,带提供方前缀。os.environ/OPENAI_API_KEY表示从环境变量读取密钥。实际使用时,把密钥放在配置文件里显然不合适,环境变量或密钥管理服务更稳妥。drop_params设为true,表示当某个提供方不支持传入的某个参数时,自动丢弃而不是报错。这个开关我建议在接入混合厂商时打开,不同厂商对参数的宽容度差异很大。master_key是管理整个网关用的主密钥,所有管理接口、生成虚拟密钥的操作都要用它做鉴权。
3.3 启动代理服务
用配置文件启动:
export OPENAI_API_KEY='你的key' export ANTHROPIC_API_KEY='你的key' litellm --config ./config.yaml --port 4000看到日志输出表示代理已在4000端口监听。现在验证模型列表:
curl http://localhost:4000/v1/models \ -H "Authorization: Bearer sk-local-master"如果配置正确,返回的模型列表里能看到我们配置的gpt-4o-mini、claude-sonnet、llama3-local。
3.4 发送第一条请求
用虚拟密钥之前,先用主密钥直接测试转发链路:
curl http://localhost:4000/v1/chat/completions \ -H "Authorization: Bearer sk-local-master" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o-mini", "messages": [{"role": "user", "content": "你好,请简短介绍一下你自己"}] }'正常返回时,你会看到响应结构跟OpenAI官方的chat.completions格式一致。到这里,网关的核心转发能力已经就位。
3.5 用虚拟密钥和数据库实现权限分配
上面的网关还很“原始”,因为所有人都在用同一个主密钥,无法区分调用来源。接下来接上数据库,启用虚拟密钥能力。
LiteLLM支持SQLite和PostgreSQL。为了验证方便,先用SQLite:
general_settings: master_key: sk-local-master database_url: sqlite:///litellm.db重启网关后,生成一把虚拟密钥:
curl -X POST http://localhost:4000/key/generate \ -H "Authorization: Bearer sk-local-master" \ -H "Content-Type: application/json" \ -d '{ "models": ["gpt-4o-mini"], "budget": 50 }'返回内容中带一个sk-xxxxxx的密钥。这把密钥只能用在我们指定的gpt-4o-mini这一个模型上,且最高消耗50美元额度。把密钥分发给前端或测试同学,就能做到权限和预算的有效隔离。
提醒:SQLite适合开发和单机验证,生产环境建议换PostgreSQL。多实例部署时如果共用SQLite会带来并发写锁问题,而PostgreSQL能支撑更稳定的并发。
3.6 客户端接入:几乎零改造
如果你的客户端原本就是使用OpenAI的SDK,那么迁移到LiteLLM网关只需要改两个参数:base_url和api_key。
from openai import OpenAI client = OpenAI( api_key="sk-虚拟密钥", base_url="http://localhost:4000/v1" ) resp = client.chat.completions.create( model="claude-sonnet", messages=[{"role": "user", "content": "你好"}] ) print(resp.choices[0].message.content)因为网关提供的就是OpenAI兼容接口,所以原本用OpenAI SDK写的代码几乎不用动,只是把base_url指向了网关。对于那些基于OpenAI SDK封装的组件,比如一些Agent框架、函数调用框架,也能原样接入。这是LiteLLM生态成熟度的一个优势。
4. 实操中的踩坑记录与排查方法
4.1 模型映射不生效,请求打到错误的厂商
我刚开始用LiteLLM时遇到过一个挺迷惑的问题:明明在配置文件里把model_name设成了gpt-4o-mini,调用时却提示模型的提供方不被支持。
排查到最后发现,问题出在客户端的请求参数。我的客户端代码把model写成了openai/gpt-4o-mini,而网关配置里定义的model_name是gpt-4o-mini。网关收到请求后,把openai/gpt-4o-mini当成一个别名去查找,结果在模型列表里没找到这个别名,于是走了它自己内置的模型解析逻辑,尝试把openai/gpt-4o-mini当真实模型直接转发,这才报了错。
这里要说清楚:在代理模式下,客户端的model字段只能填model_name这个别名,不能带提供方前缀。这个容易搞混的点,恰恰是把LiteLLM同时当作SDK和网关使用时的典型误区——SDK里需要前缀,网关里不需要。
4.2 不同厂商对参数的要求差异导致报错
接入多家模型后,最先暴露问题的往往不是请求失败,而是同样的参数在不同厂商那里得到的待遇完全不同。比如response_format、logprobs这类参数,有的模型支持,有的直接报错。
我在一次接入某个新模型时,请求总是返回一个关于未知参数的报错。查了很久,最后发现是我在代码里写了一个OpenAI支持的参数,而那个目标模型的API并没有这个能力。
解决办法有两个层面:
- 配置文件里打开
drop_params: true,让LiteLLM自动丢弃不支持的参数。 - 在代码里通过
extra_body传递厂商特有参数,或者用litellm_params传参来做到模型级别的细粒度控制。
遇到厂商特有参数时,不要试图找一套“万能参数”覆盖所有模型,兼容层能解决的边界是有限的。
4.3 流式响应超时和连接池问题
代理模式下,如果客户端请求的是流式响应,网关和上游之间需要长时间保持连接。我遇到过一次奇怪的现象:小请求一切正常,但长文本生成的请求在服务端处理了几十秒后,客户端就报连接中断。
排查后发现是网关与客户端之间那一层连接的超时阈值设得太小了。LiteLLM本身支持在请求中设置timeout和stream_timeout参数。对长流式请求,这两个值需要放宽。另一个容易被忽略的是网络环境的TCP连接超时,很多内网环境会把空闲连接快速回收,导致流式中断。这种问题看LiteLLM日志不一定能直接看到,需要结合网关所在机器的TCP配置一起排查。
4.4 数据库重启后密钥丢失
这个问题一开始也坑了我一次。我用SQLite配置好了虚拟密钥,分发给同事后一切正常。后来某天我重启了网关容器,结果所有密钥全部失效,同事那边全部报鉴权失败。
原因很简单:数据库文件没有做持久化挂载。容器重启后SQLite文件被重置,密钥自然不存在了。换成Docker部署时,务必把SQLite文件或PostgreSQL连接保持住。类似的道理也适用于所有需要持久化的配置——不要想当然认为重启后就什么都没发生过。
4.5 成本追踪不准,误差方向要心里有数
LiteLLM的成本统计是一个估算值,这一点在接入计费系统时要特别注意。不同厂商的计费规则差异很大,比如有的厂商对缓存命中的请求单独计费、有的厂商对输出token和输入token单价不一样。LiteLLM虽然内置了不少主流厂商的定价表,但碰到特价、折扣、新模型等场景,估算误差是难免的。
我的经验是:把它当“量级参考”而不是“账单依据”。用来做趋势监控、团队配额控制、模型性价比对比,足够了。但如果你要拿这个数字直接去跟厂商账单对账,那建议你仔细核对定价模型,必要的话自己写一个回调来修正。不要指望一个通用工具能自动适配所有厂商的复杂计费逻辑。
5. 我的实际使用心得和几条建议
用了很长一段时间LiteLLM之后,我有几个比较真实的感受。
第一,它最适合的团队规模是“有人需要管模型、有人需要接口”的场景。一个项目的后端就一两个人,模型只有一家,那直接SDK调一下就行,引入网关纯属给自己加运维负担。一旦团队里超过三个人都要接模型,或者同时使用多家厂商、本地模型、需要做权限控制和预算管理,网关形态的价值才会真正体现出来。
第二,配置文件的整洁程度决定了你后期的维护心情。模型别名的命名规范、环境变量的统一前缀、每个模型的model_info注释,这些细节如果在第一天就做规范,后面所有排查和时间成本都会小很多。别小看这件事,模型数量增长到二十个以上时,混乱的配置会让你想掀桌子。
第三,动态模型管理接口非常值得用起来。哪怕你只是个小项目,也建议把/model/new、/model/delete整理成一份简单的维护脚本。新模型上线这件事,越快越有好处——尤其是当你需要临时切换某个模型做灰度验证时,改一个配置远比重发代码快得多。
第四,日志和监控一定要在最开始就接好。LiteLLM支持把日志和指标输出到多种后端,常见的如Prometheus、Postgres、自定义回调。别等着模型多了、问题多了才补监控,那是给自己挖坑。成本数据从第一天就开始积累,后面想分析模型性价比时,这些数据就是你最宝贵的资产。
我个人现在的做法是:团队的每一条模型请求都通过LiteLLM网关转发,网关后面再接一层自定义回调,把请求ID、模型名、token数、延迟这些信息写进自己的统计表。配合一个简单的看板,整个团队的模型使用情况一目了然。偶尔厂商出问题,团队内的影响也被隔离在网关层,不至于让底层API的波动直接冲垮业务。
如果你的项目也正陷入“接模型一时爽,管模型火葬场”的困境,LiteLLM值得花半天时间试一下。从最小的SDK接入开始,感受一下它的统一接口设计,再决定要不要把它升级成你们团队的模型网关。很多工具是“用了才知道香”,LiteLLM就是其中之一。