1. 先聊清楚:LiteLLM 到底是解决什么的
我最早接触 LiteLLM 是因为一个特别常见的痛点:项目里要接的大模型服务越来越多,每一家的请求格式、鉴权方式、计费口径都不一样。业务代码里塞满了 if-else,一个模型厂商的字段叫max_tokens,另一个叫max_new_tokens,还有一个用temperature范围 0 到 2,结果我把 [0,1] 的值传过去,对方直接报参数非法。这种事情多了之后,接口层就变成一坨谁都不想碰的意大利面。
LiteLLM 这个开源项目,做的事情简单说就是:在应用和模型服务商之间加一层网关(或叫代理),把所有服务商统一成一种请求格式来对接。你只需要向 LiteLLM 发起标准请求,它内部再帮你翻译成各家服务商真正认识的请求。它有两副面孔:一个是纯 Python SDK,适合在代码里直接调用;另一个是代理服务(Proxy Server),适合部署成一个内网服务,多人共用,统一管理密钥、预算、路由和日志。
如果你属于下面这几类人,这个项目非常值得了解:要接多家模型、经常切换模型、带着团队一起开发、或者想把模型调用成本统计清楚。反过来,如果你只接一家服务商,而且永远不会换,那这篇文章对你意义不大,后面我会专门说边界。
有一点先说清楚,避免产生错误期待:LiteLLM 统一的是「调用过程」,不是「模型输出质量」。同一个问题,GPT 答不好,Claude 答不好,LiteLLM 不会帮你变聪明,它只负责把请求送对、把失败重试、把成本记下来。
2. 最短路径跑通:本地起一个统一代理
我会按最小可用配置来演示。目标很简单:你装好 LiteLLM 之后,用 curl 就能请求一个模型服务,同时服务商的真实密钥不会暴露给调用方。
2.1 安装与前置确认
建议用 Python 虚拟环境安装,能避免不少依赖冲突。LiteLLM 对 Python 版本要求不苛刻,我常用 3.10 和 3.11 都稳定。
python -m venv .venv source .venv/bin/activate pip install "litellm[proxy]"这里注意,[proxy]这个 extra 很重要。如果只写pip install litellm,装的是纯 SDK,不会有代理服务所需的服务端依赖。想跑网关,一定要带[proxy]。
装完可以先看看版本:
litellm --version能输出版本号就说明装好了。接着准备配置文件,这是所有后续动作的核心。
2.2 写第一份最小配置
假设你手上有一家模型服务商的 API Key,这家服务商提供的是 OpenAI 兼容接口,这是目前最容易接通的类型。登录信息放在环境变量里,而不是直接写进配置文件。
model_list: - model_name: demo-chat litellm_params: model: provider_a/demo-chat-1 api_key: os.environ/PROVIDER_A_API_KEY配置里需要理解两个字段:
model_name:这是对外暴露的逻辑名。客户端调用时只需要认识这个名字。litellm_params.model:这是「真实模型」的完整标识,由两部分组成,斜杠前面是服务商前缀,斜杠后面是实际模型 ID。
比如provider_a/demo-chat-1,provider_a在 LiteLLM 生态里通常对应某个具体服务商的接入器(provider 适配器),demo-chat-1是该服务商平台上的真实模型名。你完全可以把provider_a换成你正在用的那家的简写,比如某个国内服务商简称、某个本地部署框架。
设置环境变量:
export PROVIDER_A_API_KEY=your_real_key_here然后启动代理:
litellm --config config.yaml --port 4000启动日志里会有一个 master key 信息。在正式环境里,建议显式设置一个强密钥:
export MASTER_KEY=sk-admin-123456 litellm --config config.yaml --port 4000这个 master key 是管理员身份用的,后面创建虚拟密钥、查看日志、管预算都要靠它。
2.3 用 curl 验证第一次调用
代理已经在 4000 端口跑起来了,此时用 curl 发一次请求:
curl http://localhost:4000/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer sk-1234" \ -d '{ "model": "demo-chat", "messages": [{"role": "user", "content": "你好,简单介绍一下自己"}] }'Authorization: Bearer这里可以先随便填一个值,因为默认配置下 LiteLLM 会接受。等到你配置了真实管理密钥和虚拟密钥之后,这里就必须填有效密钥了。
如果你在代码里调用,可以直接用原生 SDK,只需要把 base_url 指向代理:
from openai import OpenAI client = OpenAI( base_url="http://localhost:4000/v1", api_key="sk-1234" ) resp = client.chat.completions.create( model="demo-chat", messages=[{"role": "user", "content": "你好,简单介绍一下自己"}] ) print(resp.choices[0].message.content)这里有个容易被忽略的关键点:代理对外暴露的是 OpenAI 兼容格式。不管底层是什么服务商,代理都会把响应转换成 OpenAI 风格的 JSON 结构返回。这样带来的最大好处是,你不需要在客户端装一堆不同 SDK,统一的调用方式就够了。
2.4 为什么 OpenAI 兼容格式值得保留
经常有人问,LiteLLM 内部支持一百多家服务商,为什么对外偏要展示 OpenAI 兼容协议?原因很实际:生态工具最多、文档最全、客户端 SDK 支持最广。你要把请求转发到上游服务商,翻译成它们的格式;要在下游对外暴露,就需要选择一个「事实标准」,这样团队内所有人、所有工具都能围绕同一个协议开发。
当然,兼容格式也意味着妥协。有些服务商的独特参数,比如某些厂商支持的扩展字段,传进来时未必能原样透传。不过对绝大多数应用场景,这种统一带来的收益远大于损失。
3. 入口统一之后:模型别名、路由权重、回退与重试
第一遍跑通基本流程后,就该考虑真实业务里的复杂情况了。核心概念是「模型别名」:业务端只认识逻辑名,真实模型可以随时在代理层换掉。
3.1 用 model_name 做别名,避免业务代码跟着模型服务商变动
假设你做了一个客服机器人,最开始用的模型 A,后来发现模型 B 在某类任务上效果更好。如果没有代理层,你得改业务代码、重新发布。有了 LiteLLM,只需要改配置文件,连客户端都不用动。
更实用的做法是让同一个model_name对应多个真实模型,实现流量灰度。比如把 20% 的请求给新模型,80% 给旧模型:
model_list: - model_name: dialogue-pro litellm_params: model: provider_a/turbo-1 api_key: os.environ/PROVIDER_A_KEY model_info: weight: 80 - model_name: dialogue-pro litellm_params: model: provider_b/turbo-2 api_key: os.environ/PROVIDER_B_KEY model_info: weight: 20这份配置文件里,客户端看到的始终只有dialogue-pro,代理按权重把请求分到真实模型。搭配权重路由策略使用:
router_settings: routing_strategy: "weighted"权重路由的价值不在「换模型」,而在「灰度验证」。先放 5% 流量到新模型,观察几天错误率和成本,没问题再慢慢调高。这个过程不用业务团队参与,完全是基础设施层的灵活度。
3.2 回退链路:主服务商挂了,请求自动换路
多模型接入最爽的一点,是故障时可以自动回退。某个服务商临时过载,返回 503,LiteLLM 会尝试下一个同model_name的模型。为了避免来回试探,可以配置冷却时间:
litellm_settings: cooldown_time: 60 allowed_fails: 3意思是某个服务商连续失败 3 次后,进入 60 秒冷却。冷却期内不再把新请求发给它,其他模型继续承担流量。这个机制看着简单,实际价值非常大——以前遇到上游故障,要么疯狂重试把故障放大,要么人工切换模型,现在代理自动做了。
我在实际使用中还习惯把不同服务商的模型配置成相同的model_name,形成「主备模式」。比如主力模型配上备用模型,平时备用模型权重设为 0 或一个很小的值,主模型出问题时权重自动由路由策略调节。好处是应用层完全无感知。
3.3 运行时动态注册新服务
配置文件重启的方式适合低频变更,但实际业务里经常会遇到临时加一个模型、或者要给某个团队单独开一条新链路。LiteLLM 代理提供了运行时的模型管理接口,比如:
curl -X POST "http://localhost:4000/model/new" \ -H "Authorization: Bearer sk-master-key" \ -H "Content-Type: application/json" \ -d '{ "model_name": "dialogue-fast", "litellm_params": { "model": "provider_c/fast-model", "api_key": "os.environ/PROVIDER_C_KEY" } }'这样就不用重启代理进程了。多人协作时特别有用:同事要测试一个新模型,不用等你改配置文件,自己通过管理接口注册就能试。
4. 网关必修课:密钥管理、预算配额、用量成本审计
把代理给团队用之后,第一个要解决的就是密钥问题。你不能把真实服务商的 Key 直接发给每个人,风险太大,而且无法审计谁花了多少钱。LiteLLM 的虚拟密钥机制就是干这个的。
4.1 虚拟密钥:把真实 Key 隔离在网关层
所谓虚拟密钥,其实就是代理维护的一套自己的 Key。调用方拿着虚拟 Key 来请求,代理识别身份后,用真实 Key 去模型服务商那里取结果。真实 Key 永远不会出现在客户端环境,也不会被成员泄露。
创建虚拟 Key 的接口设计得很直观:
curl -X POST "http://localhost:4000/key/generate" \ -H "Authorization: Bearer sk-master-key" \ -H "Content-Type: application/json" \ -d '{ "models": ["dialogue-pro"], "max_budget": 10, "budget_duration": "30d", "user_id": "team-zhangsan", "metadata": {"department": "icustomer-service-agent"} }'返回值里会有一个sk-开头的 Key,把它发给对应的调用方即可。注意,虚拟 Key 只在创建时完整返回一次,之后在代理端再查只能看到部分掩码,所以要提醒调用方自己保存好。
这样做的好处不止安全:你可以针对不同团队、不同项目、不同应用分别创建 Key,后续所有调用记录都能按 Key 追溯到源头。谁调用了什么模型、花了多少钱、哪个应用触发了异常,一目了然。
4.2 预算控制:防止一个异常请求跑掉一个月的成本
大模型应用的账单很容易失控。某个测试脚本写错了循环,或者某条数据异常导致批量任务疯狂调用,成本涨起来非常吓人。LiteLLM 支持在虚拟 Key 上直接设置预算上限:
max_budget:预算总额,比如 10 美元budget_duration:预算周期,比如30d、1drpm_limit:每分钟最大请求数tpm_limit:每分钟最大 token 数
实际操作中我通常会先给测试环境的小 Key 设一个很小的日预算,比如 5 美元,工作日再调高。宁可因为预算不足被拒绝几次,也不愿意月底收到一大笔意外账单。
还有一个容易忽略的细节:成本计算依赖模型价格的正确配置。内置的常用模型价格表很全,但如果你接的是小众服务商,或者是私有化部署的模型,代理不知道单价,成本统计会是零。这时需要在配置里手动补价格:
model_info: input_cost_per_million_tokens: 12 output_cost_per_million_tokens: 60这样代理才能算出真实费用。我在接某家国内服务商时就吃过亏,成本一直显示 0,后来才发现没有手动配置价格。
4.3 用量与成本审计:日志是排障第一线索
代理会把每次请求的关键信息记录到数据库里,包括调用时间、模型名、虚拟 Key 所属用户、请求 token、响应 token、延迟、是否成功等。查询接口也很直接:
curl -X GET "http://localhost:4000/spend/logs" \ -H "Authorization: Bearer sk-master-key"可以用参数过滤,比如按user_id、model_name、start_date等维度查询。排障或者领导突然问「最近一个月大模型费用为什么涨了」,这套日志体系能让你很快给出答案,而不是靠猜。
5. 流量优化:缓存、限流、超时调度,别让每个请求都打到上游
模型调用一次要花钱,还要付出时间成本。有些场景里,同样的 prompt 会在短时间被重复请求多次,比如某个热门的问答模板、定时任务重复刷某些接口。这时候缓存的价值就体现出来了。
5.1 三种缓存:内存缓存、Redis 缓存、语义缓存
LiteLLM 支持开启响应缓存,说法很直接,请求命中缓存就直接返回上次的结果,不再往上跑。
本地单实例,开内存缓存最简单:
litellm_settings: cache: true多实例部署,或者想缓存持久化,配置 Redis:
litellm_settings: cache: type: redis host: localhost port: 6379默认缓存的是字面文本完全一致的请求。如果 prompt 稍微改一个字,缓存就不会命中了。想进一步压缩成本,可以研究下语义缓存,按 embedding 相似度判断「问题是否基本等价」。不过语义缓存在业务上有风险,返回的答案如果不够严谨,同一问题的不同措辞可能被错误匹配。我的建议是:语义缓存适合闲聊或客服场景,不适合医疗、法律等对准确性要求高的场景。
5.2 限流设置:别让一个任务塞满上游配额
每家服务商都有速率限制,有的是每分钟请求数上限,有的是每分钟 token 上限。团队里如果有人写了段脚本疯狂循环调用,很容易把一个共享 Key 的配额打爆,结果连累了其他正常使用的成员。
LiteLLM 在模型配置里直接支持配额字段:
model_list: - model_name: dialogue-pro litellm_params: model: provider_a/turbo-1 api_key: os.environ/PROVIDER_A_KEY model_info: rpm: 60 tpm: 100000rpm表示每分钟最多 60 次请求,tpm表示每分钟最多 10 万 token。超过部分代理会等待或者报错,而不是把压力直接打到上游。这相当于给共享资源加了一把锁,比写在业务代码里靠谱得多——业务代码里每个人写的限流策略都不一致,代理层统一限流才是正经做法。
5.3 流式响应和超时的处理方式
很多对话场景要求流式输出,用户体验完全不一样。LLM 生成一个字、蹦一个字,也适合在流式场景展示思考过程。LiteLLM 代理侧对流式的支持是透明的:你在请求里带"stream": true,代理就把上游的流式事件转发给客户端。我在笔记本上做 Demo 时,不需要单独处理流式转换逻辑,体验和直连上游几乎没有差别。
超时配置我建议按模型类别分,不要一套参数打天下。文本快模型可以设短超时,比如 30 秒;但某些多模态模型、或者长文档总结任务,响应可能要一两分钟,设置 30 秒会频繁误杀。超时参数在litellm_params里设置,按需调整即可。
6. 从本机 Demo 走上生产:数据库、可观测性、排错经验
只有一个人用、在本地起个 Demo,LiteLLM 默认的 SQLite 完全够用。但一上生产,多实例部署、多人并发调用,SQLite 的锁竞争问题很快就会冒出来。这时应该换成 PostgreSQL 这类真正的数据库。
6.1 数据层升级:从 SQLite 到 PostgreSQL
默认情况下,LiteLLM 会把使用数据存在本地 SQLite 文件里。单实例没问题,多实例或者频繁读写时,SQLite 会出现各种奇怪的 lock 报错,甚至导致请求处理卡顿。
只要配置一个环境变量,就能切换到 PostgreSQL:
export DATABASE_URL=postgresql://user:password@host:5432/litellm第一次启动时代理会自动建表,耐心等一会儿就好。切换后,多个实例共享同一份数据,虚拟 Key、预算、调用日志都是全局一致的。如果团队规模小、实例数量不多,SQLite 也能对付,但别等出问题再换,那会儿迁移成本更高。
6.2 可观测性:让每一次调用都有迹可循
生产环境里,出了问题找不到日志,比出问题本身更让人崩溃。LiteLLM 的回调机制提供了接入主流可观测系统的渠道,我至少会配置成功回调和失败回调两类:
litellm_settings: success_callback: ["prometheus"] failure_callback: ["prometheus"]配置后,代理实例会暴露一个/metrics端点,Prometheus 直接来抓即可。指标里重点关注这几项:
| 指标维度 | 关注什么 | 常见异常判断 |
|---|---|---|
| 请求成功率 | 整体健康度 | 掉到 95% 以下就要查上游 |
| p95 延迟 | 用户体验 | 单服务商持续变慢可能是上游排长队 |
| 上游冷却次数 | 服务商稳定性 | 冷却次数飙升说明某家服务商不稳定 |
| 成本环比 | 预算健康度 | 突增大多来自异常调用或 prompt 变长 |
实际使用里我还经常开 webhook 回调,比如把失败事件推送到群机器人、或者把超长延迟事件写进专门的告警通道。每次失败都有结构化信息,处理问题的效率高很多。
6.3 一次 401 报错排查全过程复盘
讲一个我印象很深的排错经历,值得完整复盘一次排查思路。
现象:接了一个新模型,客户端调用代理返回 401 Unauthorized,但代理本身跑得正常。
第一反应是查虚拟 Key 有没有权限访问这个模型。我用 master key 查了这个虚拟 Key 的配置,确认models列表里包含了新加的模型名,排除了权限配置问题。
第二步,绕过代理,直接拿服务商的真实 Key 调用上游接口,结果正常返回。这说明上游 Key 本身没问题,问题一定出在代理层的鉴权逻辑上。
第三步,重新检查代理配置。发现新模型配置里写的是api_key: os.environ/PROVIDER_NEW_KEY,但环境变量名写错了,实际是PROVIDER_NEW_KEY_2。代理发现拿不到环境变量,就用了空字符串,上游自然返回 401。
修复方式很简单,改环境变量名后重启代理进程。但这个排查过程值得记住:任何「代理不可用」都可以拆成三段看——客户端到代理、代理内部配置、代理到上游。每次只排查一段,定位问题更快,而且不用瞎猜。
6.4 多实例部署时的负载均衡与共享状态
生产环境往往需要跑多个代理实例来保证可用性。这里有个坑:如果只是简单起几个进程,虚拟 Key、预算配置在各个实例上是不同步的。正确的做法是给所有实例配上同一个DATABASE_URL,并让它们共用同一个 Redis 实例,这样缓存和状态才能全局一致。
实例前面再挂一层负载均衡,收敛成单入口。我自己在容器环境里跑的时候,就按照这样的拓扑:负载均衡器统一接流量,后边挂两个代理实例,数据层共用一个 PostgreSQL,Redis 做缓存。重启实例对业务没有感知,因为状态都在下层。
7. 适合用与不适合用的边界,以及我的真实体会
很多人看到这种统一网关,第一反应是「是不是所有项目都要上」。我自己的判断没那么冲动,先认真看项目边界再说。
值得引入 LiteLLM 的信号很明确:
- 模型切换频繁:团队每季度都要评测新模型,想低成本灰度验证
- 多家服务商并存:业务里同时跑了不同厂家的模型,或者一个厂商的多个版本
- 多人共同开发:不同人、不同服务要共享模型资源,需要管权限和预算
- 成本敏感:每个月要出大模型费用报表,要能按项目、按人追溯
不建议引入 LiteLLM 的信号同样明确:
- 只接入一家服务商,且未来半年都没有切换计划:多加一层网关,就是多一个故障点,多一份运维负担,收益不明显
- 团队只有一个后端程序,密钥只在一个环境变量里:虚拟密钥、预算审计这些功能用不上,纯粹增加复杂度
- 对极致低延迟有强制要求:每多一跳代理,延迟多少会有增加。虽然通常只有个位数毫秒的差距,但如果你追求的是分钟级的请求、且网络环境非常紧张,就要自己权衡
最后分享一个我个人的判断:如果你已经确定要接多家模型,LiteLLM 这类统一网关迟早会出现在你的架构里。晚点上不如早上,先用最小配置把流程跑通,再慢慢加预算、路由、缓存这些特性,比等到业务量上来之后再做迁移要平滑得多。我每次接到新模型,现在都是先改配置测一轮,再决定要不要动业务代码,这种基础设施层面的稳定感,是在实际里用过之后才真正体会到的。