1. 为什么需要litellm:多模型接入的“接口爆炸”问题
这两年只要在做AI应用,几乎都会撞上同一个尴尬:模型选型的时候很兴奋,真正接API的时候就开始头大。不同厂商的调用方式差异非常大,请求字段不一样、鉴权方式不一样、流式返回格式不一样、工具调用的结构也不一样。今天你想换一家模型试试,就得改一坨调用代码;明天那家模型厂商发了个新版本,原本写好的解析逻辑又得跟着动。更别说生产环境后面还压着一堆事:模型服务挂了怎么切换、多个调用方怎么分配额度、月底成本怎么核算。litellm就是被这个问题逼出来的一个方案,它相当于在多模型和你的业务代码之间加了一层统一网关,对外用一套接口接收请求,对内自动翻译成各个模型服务商的格式。适合正在做多模型接入的开发者、要搭内部AI网关的技术负责人,以及所有被接口差异反复折磨过的人。
1.1 大模型接口的差异到底有多烦
我用一个很直白的例子来解释。假如你接了三家不同厂商的模型,它们的请求结构可能是这样的:
A家的请求重点在messages数组,角色分system、user、assistant,参数叫max_tokens。B家可能更习惯用prompt字符串,参数叫max_tokens_to_sample,角色标记用的是完全另一套写法。C家又可能是把输入放在input字段里,参数结构又是另外一套。这还只是最基础的文本对话。再加上流式输出,A家走SSE事件,B家自定义了事件类型,C家干脆是另一种增量数据格式。更麻烦的是工具调用,有的叫function_call,有的叫tool_use,有的叫tools,里面的参数结构完全是各写各的。
面对这种差异,最笨的办法就是在业务代码里写满if model == "A": ... elif model == "B": ...。我见过一个内部问答项目,为了同时测评三家模型,第一版代码里全是这种分支,后面每次加一个新模型,都要把调用层、解析层、错误处理层全部过一遍。几百行代码堆下来,改一个字段要小心翼翼地排查半天。而且厂商更新API版本是常态,你今天跑得好好的,明天它通知你某个字段废弃了,你又要跟着改一遍。这种维护成本在模型数量少的时候还能忍,一旦到了三五个以上,就纯粹是消耗团队精力的无底洞。
1.2 litellm的定位:一个网关解决一类问题
litellm这个名字你可以拆开看,lite加LLM,轻量级的语言模型接入层。它有两种用法,第一种是作为Python SDK直接嵌在你的代码里,调用litellm.completion()就能发请求,底层自动选择对应厂商的客户端;第二种是作为独立代理服务跑起来,变成一个网关,所有调用方只需要按OpenAI的接口规范往这个网关发请求。第二种形态才是生产环境的重头戏。
它的思路本质上和传统API网关一样:客户端不直接面对各种上游服务,而是面对一个统一入口。入口帮你做协议翻译、路由分发、重试降级、限流缓存和成本统计。好处很直接——你的业务代码这辈子只需要维护一种调用方式,以后不管接入多少新模型,改的是网关配置,而不是业务逻辑。至于为什么是OpenAI格式而不是别的格式,后面我会细说,但简而言之,OpenAI的接口风格已经成为这个领域事实上的通用方言,litellm顺势承接下来,学习成本一下就低了很多。
2. 核心原理拆解:一次接入、到处调用的背后
很多人在用litellm之前会有一个疑问:它凭什么能号称对接几百家模型服务商?其实原理不神秘,核心就是适配器模式。它维护了一张很大的映射表,把每个厂商的API特征都记录在案,包括请求格式、鉴权方式、模型名称规则、返回结构等。当统一格式的请求进来,它识别目标厂商,然后按映射规则把请求改写成对方能理解的样子;上游返回后,它又把响应解析、规整成统一格式吐给调用方。整个过程的关键在于它对“OpenAI兼容格式”的坚持。
2.1 OpenAI兼容格式为什么成了事实标准
现在的主流模型服务商,几乎无一例外地提供了兼容OpenAI接口的接入方式,或者至少文档里会专门标出“OpenAI兼容模式”。为什么?因为OpenAI的生态文档、SDK、社区教程是最多的,各家为了降低开发者迁移门槛,都主动往这个格式上靠。litellm敏锐地抓住了这一点,把OpenAI格式当作自己对外收发的“通用语”,对上游保持翻译能力,对下游保持格式统一。客户端不管后面连的是哪家模型,只要会用OpenAI的Python SDK、Node SDK,或者直接发HTTP请求,就能立即对接litellm。这带来的一个直接好处是,你团队里的新成员不需要再学每家厂商的调用姿势,会一套就够了。
协议转换这件事听起来复杂,但litellm做得很务实。比如A厂商要求在URL里带某个版本号,B厂商要求在请求头里放API Key,这些都属于标准映射;再比如A厂商的参数叫max_tokens,B厂商叫max_tokens_to_sample,litellm会在内部做字段名转换。上游响应里字段结构不同,它也会在返回前帮你整理成统一结构。不过也要承认,这种翻译不是100%无损的,总有一些厂商独有特性映射不到位。所以在配置里有一个drop_params开关,开启后如果遇到当前目标厂商不支持的参数,litellm会自动丢弃而不是报错。这在大规模多模型路由时非常有用,它保证了一个通用请求模板能尽可能兼容更多上游。
2.2 路由、重试与限流是怎么协同工作的
网关要比SDK多干一件事,就是路由。你在配置里可以定义一组模型,比如一个主模型加一个备用模型,再给它们设置权重。网关收到请求后,会按照策略挑选一个上游实例转发。路由策略大概是这么几类:简单轮询、加权分配、基于延迟优先、基于健康状态。生产环境里我一般不会用纯随机轮询,而是优先用“健康状态感知”的方式——如果某个上游最近一直报错,网关会自动把它拉进冷却窗口,后续流量自动转向健康实例,直到它恢复并重新通过健康检查。
重试和限流是网关里的另外两板斧。模型服务商的接口是不稳定的,经常看到429限流、5xx服务错误,或者连接超时。litellm允许你设置重试次数,并且会对特定错误码进行重试,例如限流和超时。重试策略默认带指数退避,也就是第一次失败等一小会儿,再失败等更长时间,避免一窝蜂地重试把上游打挂。限流则是为了防止某一个业务方把整个网关的配额打爆,你可以针对每个调用方的Key设置每分钟请求数、每分钟Token数限制。没有这一层的话,一个测试脚本写个死循环,就能把你花了大半个月配好的模型配额全部烧光,这一点我后面会详细讲。
2.3 成本统计是怎样算出来的
绝大多数模型是按Token收费的,而且输入价格和输出价格还不一样,不同模型的价格天差地别。litellm做成本统计的思路也很直接:它在代理层拿到了每次请求的usage数据,也就是输入Token数和输出Token数,再对照每个模型维护的价格表,算出本次请求的估计费用,然后写到数据库里。这样你就可以看到一个Key、一个模型、甚至一个团队在一天或一个月内的实际花费。
预算限制也是基于这套数据做的。你可以设一个每日预算或每月预算,一旦某个Key的花费超过阈值,网关可以直接拒绝后续请求。这个功能在内部业务中特别实用,很多部门做AI试点的时候,老板最怕的就是月底账单爆掉。提前按团队或项目设定预算,大家各自控制用量,比事后看账单后悔要强得多。
2.4 一次完整请求的数据流转
我在最开始使用litellm的时候,特意做了个请求追踪,想搞清楚它内部到底走了哪些步骤。简单来说,一次请求的流程是这样的:客户端把请求发到代理,代理先做用户身份校验,再根据请求里的model字段找到对应的模型组;接着是一套路由决策,选出一个具体上游实例;然后代理改写请求格式,加上对应的密钥信息,发往上游;上游响应回来后,代理校验响应的完整性、解析Token用量,再把响应结构转换回统一格式返回给客户端;最后异步地把用量、延迟和费用写入存储。
整条链路看下来,你可能会担心多了一层转发会拖慢速度。实际测试里,litellm本身是纯逻辑转换,没有二次调用模型,所以增加的时间也就是十几毫秒到几十毫秒的量级。对于大多数对话类应用来说,这个开销完全可接受。如果真对延迟极其敏感,你还可以把代理和服务部署在同一机房,走内网调用,进一步压缩这部分损耗。
3. 实操:从零搭建litellm代理服务
前面讲了半天理念,接下来进入正题。我现在假设你手上已经有一个模型服务商账号的API Key,拿它来做第一步的跑通验证。我用的环境是Linux服务器,Python版本3.10,下面所有操作都在虚拟环境里进行。要是你本机环境比较乱,建议直接用后面的Docker方案,能省掉很多依赖冲突的麻烦。
3.1 安装与最小启动
安装litellm代理版很简单,一条pip命令:
pip install 'litellm[proxy]'这个写法会把你跑代理服务所需的额外依赖一起装上。装完可以先验证一下:
python -c "import litellm; print(litellm.__version__)"能输出版本号就说明基础环境没问题。接下来试一把最小启动。假设你用的是某个OpenAI兼容的模型,先把密钥放进环境变量:
export OPENAI_API_KEY=sk-xxxxxx然后直接启动:
litellm --model gpt-4o-mini --port 8000这个命令会在8000端口起一个代理,默认暴露/v1/chat/completions接口。这时候你用任何OpenAI SDK,把base_url指向http://localhost:8000,就能直接发起对话请求。这种模式适合快速验证,但不适合生产,原因很简单:没有鉴权、没有配置管理、没有用量统计、没有数据库,纯粹是裸奔。
3.2 用config.yaml管理多模型配置
生产级用法是写配置文件。litellm支持YAML格式的配置,在里面注册模型列表、设置路由策略、打开鉴权和数据记录。我拿一套典型的配置来拆解:
model_list: - model_name: gpt-4o-chat litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY - model_name: claude-chat litellm_params: model: anthropic/claude-3-5-sonnet api_key: os.environ/ANTHROPIC_API_KEY litellm_settings: drop_params: true request_timeout: 30 set_verbose: true router_settings: routing_strategy: usage-based-routing num_retries: 2 request_timeout: 30 general_settings: master_key: sk-your-master-key database_url: sqlite:///litellm.db这里逐个解释。model_list是你对外暴露的模型清单,model_name是用户调用时填的名字,litellm_params.model旁边那个前缀很关键,openai/、anthropic/是告诉litellm该用哪套适配协议,不能随便乱写。api_key可以放在配置里,也可以像这样用os.environ/变量名从环境变量读取,我强烈建议用后者,密钥不进仓库。
litellm_settings是通用行为配置,drop_params刚才说过,request_timeout是整体请求超时,set_verbose开启后会打印详细日志,排查问题很有用,但生产环境开它会有大量日志输出,我一般只在调试阶段开启。router_settings管的是路由和重试,这里我配置了失败重试2次、请求超时30秒。general_settings里最核心的是master_key,它相当于代理的管理员密钥,调用接口时必须在Header里带着,否则会被拒绝;database_url是用来记录请求日志和成本数据的,默认SQLite,生产建议换Postgres。
3.3 启动代理并验证调用
配置写好后,启动命令变成:
litellm --config ./config.yaml --port 4000先别急着接业务,用一条curl验证一下:
curl http://localhost:4000/v1/chat/completions \ -H "Authorization: Bearer sk-your-master-key" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o-chat", "messages": [{"role": "user", "content": "你好,帮我介绍一下自己"}] }'正常情况下你会收到一个结构完整的响应,里面包含id、choices、usage这些字段。能走到这一步,说明代理的基本链路已经通了。
接下来用Python的OpenAI SDK测一下流程:
from openai import OpenAI client = OpenAI( api_key="sk-your-master-key", base_url="http://localhost:4000" ) resp = client.chat.completions.create( model="claude-chat", messages=[{"role": "user", "content": "一句话介绍你自己"}] ) print(resp.choices[0].message.content)注意这里我故意把model换成了claude-chat,意思是从OpenAI兼容格式转去调用Anthropic的模型,整个调用方式没有任何变化。你能明显感觉到,对业务代码来说,模型提供方是透明的。如果启动了set_verbose,日志里会详细显示路由决策、上游地址、耗时这些信息,第一次接触的人建议开着看一遍,能快速建立直觉。
3.4 用Docker部署网关服务
服务器上跑代理,我还是推荐Docker方式,一是环境干净,二是迁移部署方便。官方维护了镜像,一条命令就能起:
docker run -d \ --name litellm-proxy \ -p 4000:4000 \ -v $(pwd)/config.yaml:/app/config.yaml \ -e OPENAI_API_KEY=sk-xxxxxx \ ghcr.io/berriai/litellm:main-latest \ --config /app/config.yaml这里做了三件事:映射端口、把本地配置文件挂载进容器、把密钥通过环境变量传进去。如果你用docker-compose,还可以顺带定义网络、日志轮转和健康检查。要特别提醒的是,镜像tag不建议直接用latest,生产环境应该锁定到某个具体版本号,避免自动升级带来不兼容变化。
3.5 生产环境落地的几个关键设置
从测试环境切到生产环境,有几件事必须做。第一,master_key一定不要写在配置里明文提交,改成从环境变量读。第二,数据库从SQLite切到Postgres,因为SQLite在多个网关实例并发写入时容易出问题,Postgres才能支持集群化部署。第三,网关尽量部署在内网,前端再挡一层公司的API网关负责外部认证,不要直接把litellm端口暴露到公网。第四,日志接上现有的监控系统,至少要做到请求量、错误率、延迟这三个指标有告警。这些听起来琐碎,但少了任何一件,后续出问题的时候都会很难受。
4. 核心功能实战:路由、重试、缓存与成本控制
网关的价值不仅在统一接口,更在它提供的生产级能力。这节我展开讲讲几个高频功能的具体用法和适用场景。
4.1 路由策略:权重、优先级与健康检查怎么配
假设你有两个模型,一个主力模型,一个便宜但质量稍弱的备用模型,想按“大部分走主力、小部分走备用”的方式分配流量。路由配置可以这样写:
model_list: - model_name: main-chat litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY model_info: priority: 0 weight: 80 - model_name: main-chat litellm_params: model: anthropic/claude-3-5-sonnet api_key: os.environ/ANTHROPIC_API_KEY model_info: priority: 1 weight: 20注意这里两个模型的model_name都叫main-chat,这在litellm里是允许的,表示它俩属于同一个模型组。请求打到main-chat时,网关会按权重和优先级做决策。weight控制流量比例,priority决定业务优先级,一般priority数值越小优先级越高。我实际用下来,路由决策对延迟的影响很小,但配置时要理解一个原则:如果某个上游开始报错,它的权重会被临时拉低,流量自动转向健康实例,这对AI服务的稳定性提升非常明显。
4.2 重试与降级:让模型故障不再中断业务
重试不是随便设置的。如果你还算比较保守,可以让网关对429限流和5xx服务端错误各重试一次,超过次数就返回错误。配置方式很简单:
router_settings: num_retries: 2 retry_policy: timeout: 1 rate_limit_error: 1 internal_server_error: 1意思是超时、限流、服务端错误各重试一次,加上原始请求,最多两次。这里的关键是控制重试总量,否则上游一抖动,网关会疯狂重试,反而加剧问题。降级则是更高级的玩法:当一组模型整体失败时,把请求切到另一组模型。比如你的主力是某个高精度模型,但它偶尔不稳定,你可以配置fallback到另一个模型,保证用户的请求永远有响应。我在一个客服问答场景里就这么干过,主模型挂了以后自动切到备用模型,用户无感,只是答案质量略有下降。
4.3 缓存加速与成本节省
缓存是节省成本最直接的手段。litellm支持把请求和响应缓存到Redis里,命中缓存的请求不再调用上游模型,自然也就不产生费用。配置Redis缓存大概是这样的:
litellm_settings: cache: true cache_params: type: redis host: localhost port: 6379 ttl: 3600除了普通的按请求内容精准匹配的缓存,它还有一种语义缓存,会把用户输入做向量化,语义相近的问题也能命中缓存。比如“帮我总结一下这份文档”和“请总结这份资料”,在语义上相近,语义缓存可以复用同一个结果。这个功能用于FAQ、产品帮助中心这类高频且问题重复率高的场景非常合适。但我要提醒一点:涉及用户隐私或者个性化内容的请求,不要开缓存。比如医疗咨询、财务问答,缓存会带来数据泄露风险,即使设置了TTL也要谨慎。我一般只对通用知识类、开放问答类请求开缓存。
4.4 成本统计与预算限制
litellm会把每次请求的Token数、费用、模型名、调用方Key都记进数据库。有了这套数据,你就可以做几件事。第一,按调用方Key看不同团队的月度花费,谁用了多少一目了然。第二,按模型维度对比成本,很多团队最后会发现,原本作为备选的某款便宜模型,实际效果和主力模型差不多,完全可以把流量权重调整一下,大幅降低账单。第三,设置预算上限,比如:
general_settings: max_daily_budget: 100 max_monthly_budget: 2000超过上限后,网关会拒绝新请求并返回明确错误信息。我们内部一个AI试点的项目,就是用这种方式把月度预算锁死的,业务团队自己配了一个Key,消耗了多少看后台数据,超出就等次日自动恢复。这种方式比月底再对账舒服太多了。
5. 常见问题排查与避坑实录
用了这么久,我也踩过不少坑。下面这些问题基本都是新手最容易碰到的,按现象、原因、解法的方式整理出来,你可以当作速查表用。
5.1 请求返回401或403
现象:调用网关返回鉴权失败,但明明加了Authorization头。这个问题的原因通常是三类。第一,master_key配错了,或者请求Header里写成了其他密钥。第二,上游模型厂商的Key有问题,例如环境变量里没正确设置,litellm转发时没带对密钥。第三,配置里api_key字段写成了字面字符串而不是环境变量引用。排查看三点:先确认网关日志里记录的master_key校验有没有通过;再看日志里转发给上游时带的Key是什么;最后确认环境变量在启动进程里有没有正确生效。我试过最坑的一次就是配置文件里os.environ/API_KEY写错了变量名,导致网关一直拿空Key去调上游。
5.2 上游返回正常但litellm报格式转换错误
现象:直接用上游服务商的SDK请求没问题,但走litellm就报类似“response not in expected format”的错误。这是最典型的版本兼容问题。模型服务商会调整响应结构,特别是工具调用和流式输出的字段,而litellm内部的解析逻辑没跟上时,就会报这种错。排查方法是在配置里打开set_verbose,看日志中打印的原始上游响应,对比litellm当前版本期望的结构。解决方式通常是升级litellm版本,如果还不行,可以到它的GitHub仓库看看近期issue里有没有人报同样的坑。这里我要特别强调:生产环境不要频繁升级,但也不能长期不升,我一般会每两个星期进去看一眼新版本变更,挑一个稳定版本固定下来。
5.3 流式请求输出到一半断开
现象:非流式请求正常,换成流式响应后,客户端收到一半内容就断了。这个坑的原因往往不在litellm本身。如果你前面挂了Nginx反代,默认的proxy_read_timeout通常是60秒,大模型的流式回复一长就会触发超时断开。另一个常见原因是客户端没有正确设置流式请求的超时,导致连接被客户端自己掐断。解法分两层:网关层把请求超时调大,例如request_timeout: 300;Nginx层把proxy_read_timeout也调到300秒以上,并确保没有开启会缓冲完整响应后一次性返回的配置。我遇到过好几次“为什么流式反而比非流式更容易断”的疑问,最后查出来都是反代超时设置的问题。
5.4 成本统计出现0元或异常数据
现象:数据库里能看到请求日志,但部分请求的花费是0。原因多数是上游模型没有返回usage字段,或litellm没有内置这个模型的价格表。本地部署的开源模型特别容易出现这种问题,很多本地模型服务不返回标准Token用量。排查时先看原始请求日志里有没有usage数据,如果没有,说明litellm拿不到计费依据;如果返回了但still是0,那就是价格表缺数据。解决办法是检查配置里是否可以补上模型的价格信息,或者对这类请求标记为不计费。这个坑告诉我们,成本统计的准确性依赖上游配合,不要完全盲信数字,要定期抽检几个请求对账。
5.5 配置了多个模型但路由没生效
现象:你在model_list里配了三个上游,但实际请求永远只打到第一个,或者一直打到同一个。最常见的原因是调用方传入的model名字和配置里的model_name不匹配。比如你配的是main-chat,但客户端填的是gpt-4o-chat,网关找不到这个模型组,就会用默认逻辑选第一个能匹配上的模型。另外,如果你开了缓存,相同请求会直接命中缓存返回,根本不会走到路由那一步,看起来就像路由不生效。排查顺序是:先确认为什么模型名对不上,再关掉缓存重测一次,最后看路由日志确认决策结果。
5.6 Docker重启后配置或数据丢失
现象:容器重启后,配置文件改动没了,数据库里的请求统计也全空了。这是典型的volume挂载遗漏。配置文件如果没挂载进容器,容器重建时就使用镜像里的旧文件;SQLite数据库文件如果写在容器内而不是挂载卷,容器一删就全没了。解决方法是docker run时把配置目录和数据目录都映射到宿主机,或者直接改用Postgres作为外部数据库。另外,容器镜像tag用latest也会导致重启后版本跳变,带来一些莫名其妙的兼容问题,前面提到过,生产环境务必固定版本tag。
最后再分享一点实操体会
litellm这个项目我用下来,最大的感受是它把“多模型接入”这件本来很琐碎的事情,压缩成了一个配置问题。以前每接一个模型要写一整套适配代码,现在改几行YAML就能上线。但我也要实话实说,它不是银弹。多模型场景的复杂度只是被收敛到了网关层,并没有消失,你还是得花时间去理解各家模型的特性和参数差异。我的建议是:入门先跑通最小配置,选两个风格差异大的模型(比如一家OpenAI兼容、一家非OpenAI兼容)做对照,跑通了再上路由、缓存、成本控制这些功能,不要一开始就配七八个模型,那样出了问题你都分不清是哪里错了。
还有一个小技巧:给每个调用方单独分配一个Key,不要所有人共用一个master_key。这样出了问题既能精确定位是谁在刷接口,也能在预算控制和日志统计上做到按团队拆分。网关这种基础设施,早期花点心思把结构搭好,后面能省下大量的协调成本。