不知道你们有没有这种经历:项目对接了好几个模型服务商的API,每个服务商的鉴权方式不一样、返回结构不一样、限流策略也不一样。最难受的是,方案评审时说的是“随时可以切换模型”,真正切的时候才发现,切换一次要改几十处代码。我盯上litellm这个统一网关,就是因为团队在那个月被模型切换折腾得够呛。这篇文章把我从第一次接触litellm到把它放进生产环境的完整过程,包括配置、原理、踩坑,全部整理出来,希望能给同样在跟大模型API打交道的你一个可以照着做的参考。
1. 先聊我为什么会盯上这个大模型统一网关
事情要从一个模拟项目X说起。
当时团队接了一个智能客服的需求,产品经理拍板说“先用A厂商的模型,后续根据效果换B厂商的”。我听的时候没太当回事,觉得换模型不就是在代码里改一下base_url吗。真正动工之后才发现,完全不是那么回事。
A厂商的SDK有自己的请求签名方式,B厂商的SDK用的是另一种鉴权头;A厂商返回的流式事件字段叫这个,B厂商的返回字段叫那个;还有超时时间、重试机制、限流报错格式,各家都有自己的脾气。最麻烦的是,我们的业务代码里到处散落着对上游SDK的依赖,今天在这个服务里调一下对话接口,明天在那个任务里调一下Embedding接口。真要切换模型,等于把所有调用点全部捞出来重写一遍。
当时我脑子里冒出来的第一个想法是:能不能做个中间层,把上游的差异性全部挡在外面,业务代码只面向一种接口格式?这样切换模型就只是改配置的事了。
然后我就找到了litellm。
严格来说,litellm做了两件事。第一,它是一个Python SDK,可以让你用统一的代码去调用各种主流大模型服务商,不管是对话模型、Embedding模型还是其他类型,调用方式基本一致。第二,它内置了一个轻量级的网关服务,也就是常说的Proxy模式,你把服务商API密钥配置好、启动一个服务,团队所有人只需要把请求发到这个服务上,由它再去转发给真正的模型服务商。
这个概念听起来很简单,但真正用下来你会发现,中间层能承载的东西比想象中多得多。模型路由、密钥管理、预算控制、限流、重试、日志追踪,这些在大模型工程化里很麻烦的事,都能在这个统一网关里一起解决。
这也是我这篇文章想讲清楚的核心:不只告诉你litellm怎么用,更想让你知道,为什么中间层方案能解决大模型API接入的绝大多数痛点。
2. 拆开看:litellm 到底把哪些事接了过去
2.1 把各家API差异收敛成一种请求格式
不同模型服务商对外提供的接口风格千差万别,但有一个趋势是明显的:越来越多的服务商在兼容业界常见的/chat/completions这种对话补全接口格式。
litellm也选择了这种格式作为它的统一入口。你在业务代码里写的请求,基本就是固定的结构:模型名、消息列表、温度、最大token数等参数。至于上游服务商到底需要什么样的鉴权方式、什么样的请求体、什么样的URL拼接,这些全部由litellm在内部翻译处理。
这就意味着,你的业务代码里不再需要引入某个服务商专门的SDK,只需要记住一套请求写法就行。
2.2 模型路由与别名
litellm里有一个很重要的设计:请求里的model参数,并不一定等于上游真实的模型标识,而是你配置出来的一个“别名”。
比如你在配置里定义model_name: customer-service-v1,把它映射到A厂商的某个旗舰对话模型。业务方调用时只需要传model: customer-service-v1。等你想换成B厂商的模型了,只需要在网关配置里把这个别名指向新模型,业务代码一行都不用动。
这个设计在生产环境里太有用了。产品、算法、业务可以在同一个网关下各自管理自己的模型映射,后端同学不用天天跟着模型版本升级改代码。
2.3 多密钥负载均衡
很多模型服务商是按API密钥来限流的。一个密钥每分钟只能发那么多请求,超了就报429。
litellm支持为同一个模型配置多个API密钥,网关会自动做轮询或根据策略分配请求。我们当时用这个功能,把一个密钥的限流压力拆到三个密钥上,线上429报错立刻少了很多。配置也简单,给同一个上游模型多挂几个api_key就行。
2.4 重试与降级
大模型API的故障率并不低,尤其是高峰期,上游偶尔会返回超时或5xx错误。
litellm可以配置重试次数和备用模型。比如主模型超时后,自动重试两次;两次还不行,就把请求转到备用模型。这个降级能力非常实用,因为对用户来说,一次智能客服请求背后到底是哪个模型在回答,根本没人在意,只要别一直报错就行。
2.5 成本追踪与配额
大模型的成本是按token算的,一个不小心,某个团队一个月能烧掉不少钱。
litellm会记录每个请求的输入token数、输出token数以及估算费用,并且支持按API密钥、按团队设置预算上限。额度用完了,后续请求直接拒绝。这对于需要把成本分摊到不同业务线的团队来说,几乎是刚需。
3. 快速跑通:从 pip install 到第一个代理请求
理论说再多,不如亲手跑一遍。下面是我第一次跑通litellm的完整过程。
3.1 安装
我建议先用Python 3.9以上的虚拟环境,避免和系统环境冲突。
pip install 'litellm[proxy]'这里装的是带代理网关能力的完整版本。如果只是想在本地代码里用统一SDK,直接pip install litellm也够。
3.2 写一个最小配置文件
在项目目录下新建config.yaml,内容大概是这样的:
model_list: - model_name: demo-chat litellm_params: model: provider-a/chat-model-1 api_key: os.environ/PROVIDER_A_API_KEY - model_name: demo-embedding litellm_params: model: provider-b/embedding-model-1 api_key: os.environ/PROVIDER_B_API_KEY这里我用provider-a/chat-model-1这样的占位符来代表真实模型标识。实际使用时,你把model字段替换成对应模型服务商支持的标识格式就行。
注意环境变量的用法。litellm支持在YAML里写os.environ/变量名,这样密钥不会直接暴露在配置文件里,这个习惯建议从一开始就保持。
3.3 启动网关
export PROVIDER_A_API_KEY=你的密钥 export PROVIDER_B_API_KEY=你的密钥 litellm --config config.yaml --port 4000启动成功后,终端会显示网关地址http://localhost:4000。此时它就相当于一个本地的模型API服务了。
3.4 发第一个请求
用curl测试最直接:
curl http://localhost:4000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "demo-chat", "messages": [{"role": "user", "content": "你好"}], "temperature": 0.7 }'如果你之前用过业界常见的对话补全接口,会发现这个请求长得很眼熟。litellm网关就是把自己伪装成了一个标准API服务,内部再转换成上游服务商真正需要的格式。
3.5 在Python业务代码里调用
我们的业务代码基本都是Python,统一通过openai风格的客户端去连网关,但base_url指向本地服务:
from openai import OpenAI client = OpenAI( api_key="sk-any-key", base_url="http://localhost:4000/v1" ) resp = client.chat.completions.create( model="demo-chat", messages=[{"role": "user", "content": "你好"}] ) print(resp.choices[0].message.content)这里有个细节:网关默认有虚拟密钥机制,请求时必须带一个key,但本地测试时可以先用任意字符串顶替。后面讲到团队协作时,我再展开说虚拟密钥怎么配置。
4. 配置文件的几个关键字段,决定了生产环境稳不稳
如果你只是本地玩一下,上面那个最小配置就够了。但要上生产,配置文件里的几个细节必须认真对待。
4.1 model_list 里的模型标识要写完整
很多人在litellm_params.model字段里漏写了前缀。不同服务商对模型标识的格式要求不一样,有的直接是模型名,有的要求服务商前缀/模型名。
litellm的通用约定是服务商名称/具体模型标识。配置前先确认一下你要映射的那个服务商到底认什么格式,不要想当然。
4.2 超时和重试参数
大模型接口响应慢是常态,尤其遇到长文本生成。如果网关层不设超时,一个慢请求可能会拖垮整个服务的连接池。
我一般会在litellm_params里加超时控制:
model_list: - model_name: demo-chat litellm_params: model: provider-a/chat-model-1 api_key: os.environ/PROVIDER_A_API_KEY timeout: 60这个超时是网关等待上游响应的上限。超过60秒还没返回,网关会中断这次请求。设置多少取决于你的业务场景,如果是聊天类应用,30到60秒比较常见;如果是离线批量处理,可以放宽到120秒。
重试方面,litellm不是简单地在每次失败后盲目重发,它也会考虑错误类型。比如上游返回限流错误,重试的间隔会适当拉开;返回4xx参数错误,重试也没意义。配置重试次数时,我建议从2次开始,不要一上来就配5次。重试越多,对上游的压力越大,也越容易造成重复请求。
4.3 备用模型与降级策略
有条件的团队一定配置备用模型。我们的做法是给核心对话模型配一个备用池:
model_list: - model_name: customer-service litellm_params: model: provider-a/chat-model-1 api_key: os.environ/PROVIDER_A_API_KEY model_info: mode: completion - model_name: customer-service litellm_params: model: provider-b/chat-model-2 api_key: os.environ/PROVIDER_B_API_KEY当同一个model_name对应多个配置时,网关可以在主模型异常时自动使用备用。这种降级对业务方完全透明,用户只知道请求成功了,不会去关心到底用的是哪个模型。
4.4 日志和审计
litellm默认会把请求日志打印在终端,但生产环境一定要接入外部日志系统。
我们在配置里把日志输出到标准输出,再由日志收集器采集到统一平台。这样每次请求的模型、token数、耗时、费用都能查到。排查线上问题的时候,这些日志比什么都管用。
提示:不要把网关日志只留在本地进程里,进程一重启就什么都没了。日志持久化要提前规划。
5. 一次请求在网关内部的完整旅行
理解了配置,再来看请求从发出来到拿到结果,在litellm网关内部到底经历了什么。这能帮你推断很多线上问题。
5.1 第一步:鉴权和准入检查
请求到达网关后,第一件事是检查这个Key能不能用。这个Key可能是你配置的虚拟密钥,也可能是团队某个成员申请的子密钥。
通过鉴权后,网关还会检查这个Key对应的额度、速率限制。如果预算超了,直接返回错误,根本不会帮你转发给上游。
这个前置检查很重要,它把很多成本风险挡在了真正调用之前。
5.2 第二步:根据模型名找路由
接着,网关根据请求里的model字段去配置表里查。你会看到,同一个model_name下可能有多个上游配置,网关要决定这次走哪个。
这里涉及负载均衡逻辑。如果所有上游配置都健康,网关会按配置好的策略分配流量。如果某个上游最近返回了大量错误,网关会动态降低它的权重,把更多请求分给健康的上游。
5.3 第三步:翻译请求并调用上游
确定上游后,网关把业务方传来的标准格式请求翻译成上游服务商需要的样子,包括URL、鉴权头、请求体结构,然后发起真正的网络调用。
这一步也是超时、重试、熔断发生的地方。上游如果迟迟不响应,网关会在预设超时时间后中断,然后根据配置决定是否重试或切换到备用模型。
5.4 第四步:响应组装与记录
上游返回后,网关解析回包,把它统一成标准响应格式,再返回给业务方。
如果是流式请求,网关会把上游的流式数据逐段转发,同时统计token用量。最后,请求日志、费用数据、token统计都会被记录下来。
了解了这个链路,你就能理解为什么很多问题其实不出在业务代码,而是出在网关配置。比如某个请求特别慢,你先看是不是鉴权/限流阶段被卡了,再看是不是网关在无休止地重试,最后再看上游本身的状态。排查路径清晰很多。
6. 预算、限流、虚拟密钥:团队协作时绕不开的控制面
litellm对个人开发来说是便利工具,对团队来说更像一个控制面板。下面这几个能力,是我们当时决定把它作为团队统一入口的关键。
6.1 虚拟密钥
网关不会要求你把上游服务商的真实密钥发给每个开发。你可以在网关上为每个团队成员或每个项目生成独立的虚拟密钥。
虚拟密钥相对于真实密钥有几个好处:可以单独吊销、单独设额度、单独限流。比如某人离职了,只需要把他的虚拟Key删掉,不影响其他同事;某个项目预算收紧,直接把那个项目的Key额度调低就行。
生成密钥的管理界面或API一般会返回类似sk-开头的字符串。注意,虚拟密钥和上游的真实密钥要严格分开管理,不要混用。我们之前就吃过混用的亏,后面详细说。
6.2 预算限制
我们可以为每个虚拟Key设置预算上限,单位可以是美元或其他币种,也可以按token数量限制。
举个例子,给A项目设置的月度预算是500美元,当所有通过这个Key发出的请求累计费用达到500美元时,后续请求会被拒绝。这样财务上就不会出现“月底一查账单吓一跳”的情况。
预算设置需要考虑的是:估算费用和实际账单可能有细微差异,因为网关是根据token数乘单价估算的,而服务商账单可能有其他税费或折扣。我一般会把网关预算设为实际预算的90%,留一点缓冲。
6.3 速率限制
大模型API经常有限流,常见单位是RPM(每分钟请求数)和TPM(每分钟token数)。
在团队场景里,你可以按Key为不同人配置不同速率。比如测试环境的Key限制每分钟10次请求,生产环境的Key限制每分钟1000次请求。这样测试代码不会把生产额度打爆,生产流量也不会被测试拖垮。
6.4 多项目隔离
如果团队同时维护多个产品,最好让每个产品线使用独立的虚拟Key、独立的模型别名空间。
比如A项目和B项目都调用同一个大模型,但A项目的预算和B项目完全隔离。网关会在日志里按Key区分请求归属,月底出成本报表时,直接按Key分组汇总就行。这个做法让财务核算简单了很多。
7. 实测中的血泪经验:重试、超时、缓存、流式
最后这部分是我真正愿意称之为“经验”的内容。很多东西文档上写得简单,但实际用起来,坑都在细节里。
7.1 重试会放大重复请求
最开始我们天真地给所有请求配了3次重试。结果有一次上游服务商出现半故障状态,网关这边疯狂重试,上游那边收到大量重复请求,我们的费用也翻了好几倍。
后来我们改了策略:只有明确的连接超时、5xx错误才重试,某些4xx错误直接放弃。另外,对于非幂等的操作,尽量少重试。大模型对话请求本身是幂等的,但如果你的业务里有联网搜索或工具调用,重复请求可能带来副作用,这一点要评估清楚。
7.2 流式请求要关注连接断开
流式输出是聊天场景的标配。网关转发流式数据时,如果客户端中途断开,网关需要感知并停止上游请求,否则上游还在继续生成token,费用一直累积,而我们根本没人消费这些数据。
我们在网关层做了客户端断连检测,一旦发现连接关闭,立即取消上游请求。这个细节不处理,流式场景下钱会烧得特别快。
7.3 缓存不是越多越好
litellm支持响应缓存,尤其是Embedding这类结果相对固定的请求,缓存命中率很高,可以显著降低成本。
但是对话类请求的缓存要谨慎。相同的问题在一段时间内可能确实适合复用同一个答案,但过度缓存会导致用户得不到实时反馈。我推荐只缓存那些有明确标识的、结果稳定的请求,比如文本Embedding、分类任务、关键词提取。
还有一种语义缓存,简单理解就是对输入做语义相似度匹配,相似的问题直接返回缓存答案。这个功能效果很好,但要额外消耗Embedding模型的调用,也需要评估成本和收益。
7.4 真实密钥和虚拟密钥混用的坑
我们曾经在一个内部工具里图省事,直接把上游真实密钥配给了某个测试脚本,绕过了网关。结果那个脚本在跑批量任务时,消耗了大量token,而网关的成本报表里完全看不到这笔费用,因为请求根本没走网关。
后来我们做了个硬性规定:所有模型调用必须走网关,真实密钥只保存在网关配置里,任何人不得在业务代码或脚本里使用上游真实密钥。这样一来,成本报表才真正变得可信。
7.5 网关本身的异地部署和网络延迟
网关所在服务器到上游服务商之间的网络质量,会直接影响请求耗时。我们一开始把网关部署在本地办公室,结果每次调用都要经过漫长的公网链路,响应时间多了几百毫秒。
后来我们把网关挪到了离上游服务商较近的云服务器上,业务服务再通过内网或专线访问网关,整体延迟降了下来。如果你对响应时间敏感,一定不要忽略这个网络链路问题。
8. 如果你也要上 litellm,我的建议
回头再看,litellm帮我们解决的问题,本质上不是“如何调用大模型”,而是“如何把大模型接入这件事变得可管理、可治理、可切换”。对于还在早期阶段、模型选型经常变的团队来说,这个价值尤其大。
我个人实际操作用下来的体会是:不要把它当成一个简单的API转发工具,而要当成你团队大模型基础设施的一部分来规划。从安装、配置、虚拟Key、预算、日志、监控,每一步都按生产标准来做,后面会少踩很多坑。
有一个小技巧可以分享一下:初次安装后,别急着配一堆模型,先用一个模型、一个虚拟Key、一台本地服务,把请求链路完整跑通。确认你能看到日志、能统计到token和费用,再逐步加模型、加团队成员。这样循序渐进,出了问题也容易定位。
litellm这个方向还会继续演化,模型服务商会越来越多,接口标准也可能变化。但只要统一网关这个中间层在,我们面对未来的模型切换就能从容很多。希望这篇实战记录能帮你少走点弯路。