ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

LiteLLM实战:统一多模型API网关的配置、路由与生产落地

LiteLLM实战:统一多模型API网关的配置、路由与生产落地

litellm这个项目,我第一次接触是在处理一个需要同时对接好几家大模型API的内部系统的时候。业务方今天要GPT-4,明天要Claude,后天又想把某个开源模型也接进来,每个接口的鉴权方式、请求格式、返回结构都不一样,代码里if-else写得像蜘蛛网。litellm做的就是把这堆脏活集中收编——所有模型统一成一个格式,你只对着一个本地网关说话,剩下的路由、重试、限流、计费,都是它的事。这篇文章我按自己的实操经历来写,不铺概念,只讲怎么把它用起来,适合正在做AI应用开发、或者团队内部要统一管理模型接入的人参考。

1. 为什么需要LiteLLM:一个网关解决多模型调用的“脏活累活”

1.1 多模型接入的原始痛苦

先说场景。大部分团队第一次遇到多模型问题,都是从“第二家模型供应商”开始的。第一家用得挺好,SDK一装,几行代码就调通了。然后第二家来了,请求格式不一样、鉴权方式不一样、token计算方式不一样、流式返回的字段名也不一样。你要在业务代码里为每家厂商写一套适配层,还要处理超时、重试、限流、成本统计,那可不是简单的if-else能解决的。

我见过最典型的案例是这个样子的:一个应用同时调用三家大模型,代码里充斥着if provider == "openai"、elif provider == "anthropic"这种分支,每个分支里各写一套请求构造和响应解析逻辑。每当有新的模型接入,就要把所有调用点都翻一遍。出问题时更头疼——某个上游接口超时了,是重试还是直接报错?降级到哪个备用模型?配多少预算?全靠人肉判断。

这种情况的根源在于,模型提供方的接口差异是客观存在的,但你的业务逻辑不应该被这些差异绑架。litellm的核心思路就是在这两者之间加一个中间层:业务代码只认一套格式,差异全部收敛到网关这一层处理。

1.2 代理网关方案的选型逻辑

我见过一些团队试图通过封装SDK来解决这个问题,在项目里写一个统一调用工具类,内部再做分发。这条路走了一段之后通常会发现,它只是把混乱从一个地方搬到了另一个地方——模型多了之后,这个工具类本身就成了一个新的“巨型屎山”,而且每个服务都要依赖这个工具类,改一次要全链路重新发版。

litellm选择的是独立部署一个代理网关,这个选择我觉得是比较聪明的。网关跑在独立进程里,业务服务通过HTTP请求访问它,这样一来:

  • 模型接入逻辑集中在一处,新增模型只改网关配置,不碰业务代码;
  • 鉴权、预算、限流这些横切关注的逻辑也从业务服务里剥离出去;
  • 多个服务可以共享同一个网关,不需要每个服务都维护一套模型配置。

对于大部分中大型项目来说,这个模式要比重写SDK干净得多。唯一要考虑的额外成本就是多部署一个服务,但通常也就是一套Docker配置的事。

提示:如果你的项目只有一两个模型调用点、团队小于三个人,那确实没必要上网关,直接SDK直连更省事。litellm的价值是随着模型数量、调用方数量变多才逐渐放大的,别为了用而用。

2. 核心功能逐项拆解:路由、重试、预算、密钥管理

2.1 统一请求格式:把各家接口都变成OpenAI协议

litellm对外提供的接口完全兼容OpenAI的协议格式,也就是说,你发请求时用的路径是/chat/completions、/embeddings这些,请求体也是messages、model、temperature那一套。但这只是“表面统一”,网关内部会自动完成两件事:把OpenAI格式的请求翻译成目标模型的原生格式,再把目标模型的返回结果翻译回OpenAI的响应结构。

这个翻译能力是litellm的核心护城河,也是它最省心的地方。比如你对上游的“结构化输出”“工具调用”这类特性,不需要自己处理各家差异,网关已经把这些差异消化掉了。我用过之后最直观的感受是:业务侧根本不关心你连的是哪一家模型,只要知道模型名字叫啥、请求长什么样就够了。

为什么统一成OpenAI协议而不是其他格式?因为现在整个AI应用生态里,OpenAI兼容接口已经成了事实标准。大量的开源项目、内部工具、IDE插件,默认就能连OpenAI格式的服务。你多部署一个litellm,这些工具几乎不用改代码就能切换到你的统一入口。

2.2 模型路由与负载均衡:让请求自动找到最合适的模型

路由是litellm另一个关键能力。你可以在配置里定义一组模型,每个模型都有一个别名和对应的真实上游模型。调用方请求时传的是别名,网关负责把这个别名解析成具体的模型实例。

举个我实际用过的例子。比如你把gpt-4o配成了别名,底下挂了两把上游key,网关会根据策略把请求轮流分发到两把key上,避免一把key被限流限死。这叫负载均衡。如果你希望某个模型优先、备胎在它故障时顶上,那就配fallback列表。这些都是配置项的事,不用写代码。

litellm里路由策略有几种:简单轮询、最少请求数优先、基于使用量的路由等。我看到路由策略的配置项时会比较推荐先用最简单的版本跑通,再多想优化的事。因为大多数团队的实际瓶颈根本不在路由策略,而在于上游配额和成本控制,这两件事后面再细说。

路由对调用方是透明的。也就是说,业务侧发来一个带别名的请求,网关自己决定发给哪家、怎么处理失败。这种透明性带来的好处是,你可以在网关层做模型升级、下线的灰度,而不需要业务方配合——这在真实环境里特别重要。

2.3 失败自动转移与重试策略:别把故障抛给上层

做过线上AI服务的都知道,模型上游接口的稳定性参差不齐。有的模型高峰期响应特别慢,有的单个请求动不动就超时,有的配额一紧张就返回429。如果这些异常不加处理地抛给业务层,你的整个应用体验都会跟着崩。

litellm提供了多层保护机制。首先是请求超时控制,你能给每个模型单独设置超时时间,避免一个慢请求占着线程不释放。其次是重试机制,网关在遇到某些类型的错误(网络错误、5xx、429等)时,会自动重试指定的次数。第三是fallback机制,当一个模型彻底不可用时,自动把请求转到另一个模型上。

我拿fallback举个例子。假设你同时接了一家主流大模型和一家开源模型,主流模型偶尔抽风,你可以配置成“主流模型失败时自动降到开源模型”。这个过程中业务代码无感知,最多就是响应慢了一点。对用户来说,只是感觉自己用的应用偶发变慢了,而不是直接看到报错。

注意:fallback虽然好用,但别把两个能力差距过大的模型配成互相兜底。比如一个擅长数学推理的模型和一个普通的对话模型,在复杂任务上做fallback,结果质量可能完全没法看。fallback适合“能力接近”的模型之间做冗余。

2.4 预算控制与多租户密钥体系

这部分是我觉得litellm被低估的功能。很多团队只把它当成一个“格式转换器”用,其实它的预算管理和密钥体系才是生产环境里的救星。

litellm支持给虚拟密钥设定预算上限,比如一把key一个月最多花100美元,超过就拒绝服务。你可以为一个项目、一个团队、甚至一个用户开一把独立的key,每把key都能单独设限额、设速率限制(每分钟请求数、每分钟token数)。这样成本分摊、滥用防护、配额管理全都集中在一处,不用在每个业务服务里各自实现。

我实际用下来最大的体会是,密钥体系让“谁在花钱、花了多少、从哪里花的”这些问题变得非常清晰。你可以按key去查用量、查消费金额,这对于多团队共用一套模型资源的场景几乎是刚需。

3. 环境搭建与服务端配置:从零跑通一个可用网关

3.1 安装与启动:pip和docker两种方式

litellm的安装方式有两种主流路线,一种是用Python的pip直接安装,另一种是用Docker跑现成镜像。我个人的建议是:本地测试用pip,生产环境用Docker。

pip安装很简单:

pip install litellm

装完之后,你可以在命令行直接启动:

litellm --model gpt-4o --port 4000

这种启动方式适合快速验证,但它只启动了一个直连单模型的网关,还没有用到路由、预算这些核心能力。要发挥完整功能,需要配合配置文件启动。

生产环境我强烈推荐用Docker。因为litellm毕竟是个Python服务,依赖版本、系统环境都可能出问题,用容器把运行时环境固定下来,后续升级、回滚都方便很多。Docker启动大概是这样:

docker run -d --name litellm-proxy \ -p 4000:4000 \ -v $(pwd)/config.yaml:/app/config.yaml \ ghcr.io/berriai/litellm:main-latest \ --config /app/config.yaml

这里的关键点是把config.yaml挂载进容器,这样以后改配置只要改宿主机上的文件、重启容器就行,不用重新构建镜像。

3.2 写第一份config.yaml:模型列表、路由策略、费用统计

litellm的配置核心都在config.yaml里。我先给一个最小可用的示例,然后逐个字段解释。

model_list: - model_name: gpt-4o litellm_params: model: openai/gpt-4o api_key: os.environ/OPENAI_API_KEY model_info: cost_per_token: 0.00001 - model_name: claude-sonnet litellm_params: model: anthropic/claude-3-5-sonnet-20241022 api_key: os.environ/ANTHROPIC_API_KEY model_info: cost_per_token: 0.000015 litellm_settings: drop_params: true set_verbose: false router_settings: routing_strategy: simple-shuffle num_retries: 3 request_timeout: 30 general_settings: master_key: sk-master-123456

逐个拆开说。

model_list里每一条代表一个模型入口。model_name是给外部调用方看的别名,比如业务代码里传"gpt-4o",网关就知道要路由到这条配置。litellm_params里是实际要用的模型标识和密钥,openai/gpt-4o这种格式是litellm的命名规则,斜杠前是供应商,斜杠后是模型名。api_key那行的os.environ/OPENAI_API_KEY表示从环境变量读取,这是推荐做法,不要把密钥明文写在yaml里。

model_info.cost_per_token是单价配置,litellm用它来做成本统计。注意这个单价是你自己填的,填错了统计金额就不准。你可以按“美元/token”的粒度填,也可以只填一个大概值用于内部比较。我踩过坑:刚开始没填这个字段,网关跑了一星期,账单页面显示全是0,根本不知道花了多少钱。

router_settings是路由行为参数。simple-shuffle是轮询策略,适合大多数场景,它会尽量让各把key的使用量均衡。num_retries是失败重试次数。request_timeout是整体请求超时时间(秒),这个要根据你上游模型的实际响应速度来定,设太短会导致正常请求被误杀,设太长又会导致故障时资源被长时间占用。

general_settings.master_key是网关的主密钥,所有对网关的管理操作都会校验它。客户端调用时也要带上这个key或者由它派生出来的虚拟key。

3.3 验证网关:curl直接测,确认路由生效

配置写完之后启动网关,可以用一个最简单的curl请求来验证:

curl http://localhost:4000/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer sk-master-123456" \ -d '{ "model": "gpt-4o", "messages": [{"role": "user", "content": "你好,简单介绍一下你自己"}] }'

如果配置没问题,会返回一个完整的OpenAI格式响应,包含choices、usage这些字段。这时候就说明网关本身已经跑通了,客户端可以接进来了。

验证时建议做几个额外动作:连续多请求几次,确认负载均衡在多个key之间切换;故意把某个key的api_key写错,确认触发重试和fallback;看日志,确认请求被路由到了你预期的上游。这样能提前暴露大部分配置问题,而不是等业务接进来之后再炸。

提示:启动后一定要看一眼日志。litellm的启动日志会打印它加载了哪些模型、哪些配置项被解析了,如果配置写错,很多问题在启动阶段就能发现。

4. 客户端接入实战:OpenAI SDK一键切换

4.1 不改业务代码,只改base_url和api_key

litellm对外接口兼容OpenAI,这意味着所有用OpenAI SDK写的代码,接入litellm几乎只需要改两个环境变量:OPENAI_BASE_URL和OPENAI_API_KEY。

以Python的openai库为例:

from openai import OpenAI client = OpenAI( base_url="http://localhost:4000/v1", api_key="sk-master-123456", ) resp = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "你好"}], ) print(resp.choices[0].message.content)

对比原来直连上游的代码,差异仅仅是把base_url指向了网关、api_key换成了litellm的key。模型名传的是你在config里定义的别名。这对已有代码的迁移非常友好,很多时候只是配置替换,不用动业务逻辑。

对于非Python语言的调用也一样,只要是支持OpenAI接口的SDK,比如TypeScript、Java、Go的对应库,同理把base_url和api_key换掉即可。

这里要额外提一下知识库场景。很多团队用向量数据库做RAG,embedding模型也是通过类似接口调用的。litellm支持/embeddings端点,你可以把文本嵌入模型也统一走网关。好处是成本统计、限流管理可以一并覆盖embedding请求,而不用单独维护一套逻辑。

4.2 流式输出、工具调用、多模态输入的处理

很多人担心用网关会影响一些高级功能,比如流式输出、函数调用,还有图片输入。我实际验证下来,litellm对这几个场景的支持是完整的,前提是上游模型本身支持这些能力。

流式输出就是传stream=True,网关会以SSE格式一段一段返回内容。这要求你在配置里不要把超时设得太短,因为流式请求是长连接,整体耗时会比普通请求长很多。

工具调用(function calling)也正常。你按OpenAI格式构造tools参数,网关会透传给上游模型。如果配置了fallback,而备用模型不支持工具调用,那需要注意一下——fallback之后功能可能降级,这一点在配置时要看清楚模型能力差异。

多模态输入(比如图片)用的是image_url格式,网关同样支持。我建议在生产环境里实际测一遍,因为每家对图片编码的宽容度不一样,有时候上游要base64、你要传URL,网关的转换有没有bug,得实测才知道。

4.3 参数覆盖与自定义模型名

litellm的配置里有个drop_params参数,它决定了一个比较实用的行为:当传入的参数在上游模型不支持时,是直接报错还是自动丢弃。

举个例子,某个模型不支持temperature参数,但你从统一入口过来的请求里带了它。如果drop_params设为true,网关会把这个参数丢掉再转发给上游,而不是报错。这个特性在接多家模型时特别有用,因为不同模型支持的参数集合差异很大。我遇到过这种情况:一个给GPT-4写的请求模板,参数里带了好几个高级项,换到一个开源模型上根本认不出来,设了drop_params之后直接就能跑通,省了大量参数适配工作。

自定义模型名也很有用。你可以在config里给某个上游模型起一个业务语义化的名字,比如把某个开源模型叫成“internal-llm-v1”,业务方只需要记住这个名字,底层换成哪个模型不影响他们的代码。

注意:drop_params是有副作用的。如果业务方传了一个对结果质量影响很大的参数,被网关默默丢掉,生成结果可能跟预期不一致。调试的时候如果发现结果不对,先查一下日志里参数有没有被丢弃。

5. 生产环境落地经验与常见问题排查

5.1 吞吐与高可用:多实例部署、数据库持久化

单机部署litellm跑个小团队的业务没问题,但如果调用量上去了,或者需要高可用,就得考虑多实例部署。

litellm支持把配置和用量数据存到PostgreSQL数据库里(general_settings.database_url),这样多个网关实例共享同一份配置和统计数据。部署方式可以是多容器、多机器,前面再挂一个负载均衡器分发请求。这样的话某个实例挂了,其他实例还能继续服务,对业务无感知。

数据库模式还有一个好处:可以通过管理接口动态增删模型、生成虚拟密钥,而不需要重启网关。比如运营同学临时要开一把新key给某个合作方试用,直接在管理端操作就行,不用动配置文件。

多实例部署后要注意一个点:如果你用了“基于使用量的路由策略”(usage-based routing),它会依赖数据库里的请求统计,统计刷新有延迟,可能短时间内的路由不是绝对最优,但绝大多数场景可接受。

5.2 典型故障排查实录:限流、超时、密钥失效、成本暴涨

我在使用litellm过程中遇到过几类典型故障,逐个记录一下排查思路。

第一类是上游429限流。表现是日志里大量429状态码,业务方反馈部分请求失败。排查方法是先确认是上游限流还是litellm自身的速率限制。如果看日志里没有litellm主动限流的记录,那就是上游限流,解决思路是增加上游配额,或者在litellm里配置更保守的rpm(每分钟请求数)和tpm(每分钟token数)限制,让流量更平滑地打向上游。

第二类是请求超时。表现是一个模型经常报超时错误,其他模型正常。这时候先看是不是这个模型本身响应就慢,把request_timeout调大一些试试。如果一个上游偶尔会出现长时间无响应,可以配上fallback,让慢请求在超时后自动转到备用模型。

第三类是密钥失效。表现是突然开始报401错误。常见原因有两个:一是上游平台侧的密钥过期或额度用完,二是litellm里配置的密钥格式不对,比如多了一个换行符。这种问题查配置就能定位,建议在config里用环境变量引用密钥,排查的时候只要检查环境变量有没有正确注入。

第四类是成本异常上涨。这个是最吓人的。我遇到过一次,某个月账单暴增,查下来发现是有个调试脚本忘了关闭,每秒钟都在循环调用模型。litellm的预算体系就是为这种场景兜底的。给每把虚拟key设好月预算上限,超了就自动拒绝请求,能把事故损失控制在可接受范围内。

5.3 运营监控:日志、用量统计、成本分摊

litellm会记录每一次请求的模型、token用量和费用,这些数据在管理端可以看到。对于团队运营来说,比较有价值的几个维度的视图是:按key统计的消费排行、按模型统计的调用量分布、按时间段的请求量趋势。

这些数据除了自己看,还能用来做成本分摊。比如三个团队共用一套模型资源,每个月账单来了,按每个团队的key统计费用,一拉账单就能算清楚谁该承担多少。这比月末手工翻日志效率高出太多了。

我建议生产环境把日志接入统一的日志采集系统,比如ELK或者云厂商的日志服务。litellm本身的日志信息已经很全,关键是要能及时检索、告警。比如突然出现大量429或者5xx错误,能第一时间收到告警通知,而不是等到用户投诉才知道出了问题。

在日志里面,我最关注几个字段:model(模型名)、status(状态码)、response_time(响应耗时)、total_cost(单次成本)。响应耗时要重点盯——某个模型如果有异常波动,往往是上游出问题或者配置不合理的前兆。

提示:成本数据不是实时精确到每个token的,它依赖你配置的单价。如果你接了一个新模型,记得第一时间把cost_per_token配好,不然账单里会出现一堆0美元但实际花了钱的记录,对账时会很痛苦。

根据我个人经验,litellm真正省下来的时间,并不在第一次接入的时候,而是在之后每次换模型、调配额、排故障、对账单的时候。把模型接入集中到一个不受业务代码干扰的层面管理,长期来看能让团队的AI基础设施变得非常干净。如果你正被多模型调用折磨得焦头烂额,可以先从一个最小的config.yaml跑通一次对话开始,把路由、重试、预算一点点加进去,先解决有没有的问题,再追求好不好。接入模型这条路,走得稳比走得快重要得多。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进