← 返回资讯

LiteLLM 路由怎么配:多模型路由的第一目标是解耦,不是负载均衡

2026-08-07

我接手过一个项目,代码库里 grep 一遍模型名,出现了四十多处硬编码。有写在配置文件里的,有写在环境变量里的,还有直接写在业务函数参数默认值里的。产品说要把摘要功能换成便宜一点的模型试试效果,这个改动本身是十分钟的事,但因为改完要重新走一遍发版流程、要挨个确认没漏改,最后拖了两周才上。

这件事跟负载均衡没有半点关系。可如果你去搜「LiteLLM 路由怎么配」,绝大多数内容都在讲怎么把流量分摊到多个 key、多个上游上去。对流量真的大到需要摊的团队来说那当然重要,但对更多的小团队来说,网关路由的第一收益压根不是均衡,而是解耦:让业务代码只认一个语义化的名字,具体打给谁、用哪家的 key、走哪个 base_url,全都收进配置层。换模型从此变成改一行 YAML 加一次重载,而不是一次发版。

这篇讲的就是怎么把这层解耦搭起来。所有字段名和示例都逐字取自 LiteLLM 官方文档(核对于 2026-08-07),版本会变,落地时以官方文档当次为准。

一、别名是路由的地基

aliases 是 LiteLLM 里做模型名映射的字段。官方在虚拟 key 的文档里把它列为一个对象类型的参数,作用就是把一个名字映射到另一个名字。

它看起来只是个改名功能,但改名这件事在架构上的分量比大多数人以为的重。业务代码里不应该出现任何一个具体的模型 ID,应该出现的是这样一批名字:

别名语义典型用途
fast-cheap快、便宜、质量够用分类、抽取、格式化、批量清洗
strong-reasoning慢、贵、推理强复杂分析、多步任务、疑难 case
long-context窗口大长文档摘要、整库检索后的合成
vision-default带视觉能力图片理解

这批名字描述的是业务对能力的要求,而不是某家厂商某个版本的产品名。前者几年都不会变,后者一年能变好几次。你的代码应该只依赖前者。

这样组织之后,会顺出来三件事:

换模型不改代码。 上游发新版、旧版本要下线、或者你自己评测出来另一个模型在你的场景上更划算——改 aliases 的映射目标,业务代码一行不动。这是最直接的收益,也是唯一一个你能立刻感觉到的收益。

A/B 切换只改配置。 想验证「摘要换成便宜模型会不会掉质量」,不需要在代码里加分支、加开关、加特性标志。做法是再起一个别名指向候选模型,让一部分流量走过去,跑一段时间比数据。切回来也只是改配置。这里要注意的是别名本身不做流量比例分配,比例得靠你在调用侧或上游的路由能力去实现,具体的路由策略字段取值以官方文档为准。

不同环境指向不同实际模型。 开发和测试环境里的 strong-reasoning 完全可以指向一个便宜模型——开发阶段验证的是链路通不通、prompt 结构对不对,不是最终质量。生产环境再指向真正贵的那个。同一份业务代码在三个环境跑,配置不同而已。我见过团队在测试环境烧掉的钱比生产还多,原因就是测试环境跟生产用了同一套硬编码模型名。

有个容易忽略的点:别名一旦定下来就是对内的公共契约,改名的成本跟改代码差不多。所以起名的时候按能力维度起,别按厂商起。叫 fast-cheap 是对的,叫 vendor-a-small 就等于把厂商又焊回代码里了,绕了一圈回到原点。模型 ID 本身的命名规律和版本陷阱,可以看模型 ID 命名与版本那篇。

二、key 级别的模型白名单

models 是生成虚拟 key 时的一个数组参数,官方说明是该 key 允许调用的模型白名单。官方给的 curl 示例长这样:

curl 'http://0.0.0.0:4000/key/generate' \
  --header 'Authorization: Bearer <master-key>' \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "models": ["gpt-3.5-turbo", "gpt-4"],
    "metadata": {"user": "email@example.com"}
  }'

这一个字段同时是两条边界。

它是安全边界。 一把 key 泄漏了,泄漏的不是「你所有上游的调用能力」,而是「白名单里那几个模型的调用能力」。范围小一个数量级,善后的难度也小一个数量级。

它更是成本边界。 这一点在实践里救过的钱比前一点多。

说个具体场景。团队来了个实习生做数据清洗,任务是把几万条工单描述规整成结构化字段。这活儿用便宜模型完全够,但新人不一定知道哪个贵哪个便宜,也可能只是复制了同事的代码片段没注意里面写的是什么模型。跑一晚上批量任务,第二天早上账单出来能让人心梗。

正确的做法不是发一封「请注意使用便宜模型」的邮件,而是给他单独签一把 key,models 里只放便宜的那几个。这样他就算想调贵的也调不出来——请求会在网关这一层直接被挡回去,而不是打到上游、产生费用、然后靠人工事后发现。外包、第三方联调、演示环境、内部工具,同理。

这里的思路是:成本控制要做成机制,不要做成纪律。 靠人记住规矩,只要人一多、一换、一忙就会破。靠白名单挡住,破不了。

models 只是虚拟 key 参数里的一个,还有 user_idteam_idmetadatadurationmax_budgettpm_limitrpm_limit 这些,配额和花费追踪那一套在LiteLLM 虚拟 key那篇讲得更细,这里不重复。

三、路由和回退是两件事

这是这篇里最想说清楚的一点,也是我见过最多人配错的地方。

  • 路由回答的是:正常情况下,这个别名的请求该打给谁。判断依据是用途、成本、能力。
  • 回退回答的是:失败之后,换谁再试。判断依据是失败的类型。

两者的输入不同、触发时机不同、目标不同,本该是两套独立的配置。但很多人的做法是把它们揉成一条链:主模型写一个,后面跟几个备胎,出问题就往后顺。这条链平时看着挺美,出事那天才发现它是个陷阱。

陷阱长这样:某天上游抖了一下,触发了回退,请求切到了链上的下一个——那个更贵、能力更强的模型。抖动过去了,可如果你的熔断和恢复没配好,或者没有任何机制把流量拉回来,这批本该按成本路由到便宜模型的请求,就永久停在了贵的那一条上。等你从账单上发现异常,钱已经烧了一周。我见过的版本是抖了不到十分钟,账单多了三倍,而且持续了六天没人注意到,因为服务一直是好的、监控全绿、没有任何告警会为「变贵了」而响。

所以这两件事要分开配、分开观测:

分开配,意思是别把「日常该走谁」写成回退链的第一环。日常选择是路由的职责,跟着用途和成本走;回退是异常路径,它的目标是把这次请求救回来,不是改变以后的默认选择。回退的具体字段和熔断恢复,见LiteLLM 回退与故障转移

分开观测,意思是你的监控里必须能分别看到这两组数字:

指标回答的问题异常时说明什么
各别名的请求量分布路由按预期在分流吗某个别名突然暴涨,可能是调用方写错了
各实际模型的请求量分布钱花在哪个模型上便宜模型占比骤降=可能被回退永久带偏
回退触发次数与触发原因异常路径被走了多少长期非零=主上游有慢性问题
回退后是否恢复到主路径流量拉回来了吗一直不回=熔断恢复没生效

第四行是最容易漏的。大多数团队会监控「回退触发了没有」,但不监控「回退之后有没有回来」。而后者才是钱漏掉的地方。网关侧可观测性怎么搭,网关路由与流量分配那篇有更完整的展开。

关于路由策略本身——LiteLLM 提供了 routing_strategy 这类配置,但具体有哪些取值、各自的语义是什么,请以官方文档为准,我在这里不给取值,因为写错一个取值的代价是你照着配完发现行为跟预期完全不同,而且很难查。方法论层面可以先定下来:先明确每个别名的选择依据是成本还是能力还是延迟,再去官方文档里找对应的策略;反过来先挑一个策略再倒推用途,多半会拧巴。

四、按「别名 × 上游」二维组织配置

配置文件怎么排,长期看比配了什么更影响维护成本。

常见的错误组织方式是按上游分散写:A 家的配置写一段,B 家的写一段,C 家的写一段。刚开始只有两三家的时候没问题,等上游到了五六家、别名到了七八个,你想回答「fast-cheap 现在到底会打给谁」这个问题,得在文件里上下翻好几处,而且很容易漏掉一处。

正确的组织方式是按「别名 × 上游」二维来排:以别名为第一层,每个别名下面列出它可能落到的所有上游条目。这样任何时候你想知道一个别名的全貌,只看一段就够了。

第二条纪律是:每个上游条目要绑成一个整体。base_url、key、模型 ID 这三样必须写在一起、一起改、一起 review。它们是一个不可分割的三元组——base_url 和 key 不配套,认证就过不去;模型 ID 和 base_url 不配套,会得到一个模型不存在的错误。我见过把 key 集中放在文件顶部一个大 map 里、base_url 散在下面各处的写法,换一家上游的时候漏改了 map 里的一项,排查花了半天,因为报错信息只说认证失败,看不出是哪一半配错了。

第三条:给每个上游条目留一行注释,写清楚它为什么在这儿。是主力?是容量补充?是某个别名专用的大窗口后备?半年后你自己都会忘,而配置里的一个孤儿条目没人敢删,因为不知道删了会不会出事。这类条目会一直堆着,直到某次事故顺带把它扫出来。

第四条:别名和实际模型 ID 之间要留一层可追溯的记录。至少让 git log 能回答「三个月前 fast-cheap 指的是谁」。做成本归因、做质量回溯的时候,这个问题一定会被问到。

五、必须演练一次

配置写完之后不要相信它,去验一次。

验的方法很直白:把主上游指向一个必然失败的地址——一个不存在的 base_url,或者一把已经作废的 key——然后跑完整的业务流程。不是发一条 curl 看看有没有返回,是走真实的业务链路:用户从界面提交请求,一路到结果展示。

必须这样验,是因为回退是一条平时不走的路径。平时不走的路径永远处于「大概能用」的状态,直到出事那天才第一次被真正执行,然后暴露出它的问题——回退目标的 key 早就过期了、回退目标不在这把虚拟 key 的 models 白名单里所以直接被自己的网关挡了、回退目标的模型 ID 名字写错了一个字符、或者回退成功了但返回格式跟主模型不同、业务侧的解析代码直接崩了。这些问题在配置文件里都看不出来,只有跑一遍才会掉出来。

演练要记的东西有三样:

  1. 业务流程是不是真的完成了,而不只是网关返回了 200。这两件事差别很大,回退到一个输出格式不兼容的模型时,网关是成功的,业务是失败的。
  2. 切换花了多久。这个时间会叠在用户感知的延迟上。如果单次超时设得比较宽松、重试次数又给得多,最坏情况的总耗时可能远超用户的忍耐阈值。num_retries(每个模型的重试次数)和 request_timeout(单次调用最长时间)控的是单次行为,allowed_fails(触发冷却的失败次数阈值)和 cooldown_time(冷却时长,之后该模型重新启用)合起来是熔断语义。官方文档里出现的示例值是 num_retries: 3request_timeout: 10allowed_fails: 3cooldown_time: 30——这些是官方示例值,不是默认值,也不是推荐值,你的取值要按自己的 SLA 和上游实际表现来定,别照抄。
  3. 主上游恢复之后,流量有没有自动回到主路径。这就是第三节说的那个钱漏掉的地方,只有演练时把主上游恢复回去、再观察一段,才能确认。

另外 enable_pre_call_checks(发送前校验请求)这个字段值得单独提一句:它的作用是在请求发出去之前先做校验,把注定失败的请求挡在发送前。注定失败的请求打出去,除了浪费一个往返和可能产生的费用之外,还会污染你的失败率指标——本该被算作「配置错误」的东西被算进了「上游不稳定」,然后你去排查一个根本不存在的上游问题。具体的校验范围以官方文档为准。

六、上线前自查清单

  1. grep 一遍代码库,确认业务代码里已经没有任何硬编码的具体模型 ID,只剩语义化别名。
  2. 别名是按能力维度命名的(fast-cheap / long-context 这类),不是按厂商或版本命名的。
  3. 每一把虚拟 key 都设了 models 白名单,尤其是给实习生、外包、演示环境的那几把。
  4. 路由配置和回退配置在文件里是分开的两块,没有把日常选择写成回退链的第一环。
  5. 监控里能分别看到「各别名请求量」和「各实际模型请求量」,且有一条告警盯着便宜模型占比骤降。
  6. 配置按「别名 × 上游」二维组织,每个上游条目的 base_url / key / 模型 ID 绑在一起,且带一行说明它为什么存在。
  7. 已经把主上游指向失败地址跑过一次完整业务流,确认回退真的生效、业务真的完成、恢复后流量真的回来了。
  8. num_retries / request_timeout / allowed_fails / cooldown_time 是按自己的 SLA 定的,不是抄的官方示例值;routing_strategy 的取值查过官方文档当次说明。

想从头搭一套自部署网关的,可以先看LiteLLM Proxy 部署与使用

相关阅读