ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

模型路由零代码配置:优化LLM调用成本与分发策略

模型路由零代码配置:优化LLM调用成本与分发策略

最近在搞一个内部工具,我把原本写死在业务代码里的模型调用全部收到一个统一入口后面,用一套配置文件把 Coding Plan、Kimi、GLM 按请求场景分发到不同模型上。整个过程业务代码一行没改,只是改配置、reload,路由就生效了。有朋友问这样值不值,我的结论是:非常值,尤其当产品已经过了 demo 阶段,开始认真算 token 成本的时候,模型路由几乎是成本优化的第一课。

所谓模型路由,就是让每个请求先经过一个“分诊台”,按规则判断这段请求适合哪个模型处理,再转给对应模型。它解决的是单模型打天下的两个痛点:一是贵,二是“不专”。适合谁呢?正在用单一模型做线上业务、被账单吓到过、又不想每次切换模型都要改代码发版的人。这篇我就把零代码配置的思路、规则设计、实操配置和踩过的坑完整捋一遍。

1. 为什么需要模型路由:单模型看似省事,实际很费钱

1.1 单一模型的隐性成本

很多人一开始都图省事:所有请求打在同一个模型上,逻辑最简单,出问题也好排查。但当请求量上来之后,问题就藏不住了。

我做过一个模拟项目X(某客服助手),每天大概 10 万次请求。最开始用的同一个强模型,功能没问题,但账单很疼。后来我拉了一次请求日志,发现 60% 以上是“退款流程怎么走”“营业时间是什么”“发票抬头怎么填”这类固定问答,它们根本不需要很强的推理能力。真正需要硬核推理的可能不到 15%。

把高配模型用在低难度任务上,就像让大厨天天负责切葱花,不是不行,是太浪费。token 费用是实打实的:输入量越大,浪费越多。而且单一模型还有连锁问题:

  • 模型限流或故障时,你没有任何备胎,业务直接断。
  • 所有请求都走同一个上下文策略,遇到超长文档只能硬着头皮截断。
  • 不同模型各有擅长领域,强迫一个模型什么都干,结果就是什么都“一般”。

模型路由不是炫技,不是为了混而混,而是让每类请求都有对应的最优解。低成本只是结果,真正的收益是:该花的钱花,不该花的一分不花。

1.2 不同模型/套餐的“性格”差异

在配置路由之前,得先搞清楚手头的模型各擅长什么。我这里用到的三款是 Coding Plan、Kimi、GLM,它们并不完全重合。

模型/套餐定位适合路由到的场景需要注意的点
Coding Plan编程增强套餐代码生成、Bug 分析、重构建议、SQL 编写对上下文里的代码格式敏感,输出可能偏“话痨”,要控制 max_tokens
Kimi长上下文文本理解长文档总结、PDF 分析、长篇阅读、多轮长对话上下文窗口长,但超长输入会拉高延迟,超时要给足
GLM综合问答与工具调用日常对话、结构化输出、工具调用、通用业务逻辑兼容性比较好,适合当默认模型兜底

这个表是我实际使用的经验判断,不是官方定位。而且模型版本更新很快,今天适合 Router 的明天未必适合,所以路由规则要保持可调整。这也是为什么我特别强调“零代码配置”:规则不写死在代码里,哪天发现某个模型变强了,改配置就能切。

2. 零代码不等于没有规则,而是把规则从代码里挪到配置层

2.1 模型路由的几种玩法

做模型路由,大体有四条路:

  • 在业务代码里写一个 Router 类,每次请求前判断走哪个模型。问题很明显:改规则要发版,每个接入方都要重复造轮子。
  • 在框架中间件里做拦截。可以,但绑定语言,换语言全重来。
  • 独立网关统一出口。业务侧只调一个固定 API,路由规则全部在网关侧维护,改规则就是改配置文件。
  • 利用模型厂商的多模型接口。部分平台自带路由能力,但规则通常比较粗。

我推荐第三种思路,也是标题里“零代码配置”的核心:业务代码只对接一个入口,路由决策完全下沉到网关层。业务方的工作量从“写一段切换逻辑”变成“改一行 base_url 配置”。

2.2 为什么先从规则路由入手

模型路由再往下分,有三类实现:

  • 固定规则路由:根据关键词、正则、请求来源、上下文长度、用户标签等条件做分发。
  • 语义路由:把请求文本做向量化,算相似度,把相近意图归到指定模型。
  • 分类器路由:用小模型或标注数据判断请求属于哪个场景,再决定目标模型。

零代码起步阶段,我的建议是:先做固定规则路由。原因很直白:规则路由命中原因可解释,排查故障时能直接说出“为什么走这个模型”;而语义路由是个黑盒,它觉得像就像,出了问题很难给老板讲清楚。语义路由固然能处理长尾意图,但你需要先有足够的日志和数据,否则就是盲调。

用生活类比来说,规则路由就像前台按你手里的单据分流,办存款的去柜台A,办贷款的去柜台B。语义路由则是让前台看一眼你的表情,判断你情绪不好,估计是来投诉的。后者更智能,但前台培训成本高,而且会误判。先做简单的,数据多了再升级,是成本最优的路径。

2.3 配置文件里到底放什么

零代码配置,本质上是把路由逻辑抽象成三类数据:

  1. 模型池:每个模型的基础信息,比如 provider、模型名、超时时间、最大输出 token 数。
  2. 路由规则:按优先级排列的匹配条件与目标模型。
  3. 治理策略:fallback、重试、熔断、成本预算、审计日志。

这三块全都写在 YAML 配置文件里,业务代码不感知。后续所有调整都只动配置,读完配置就生效。接下来的章节我会逐个拆解。

3. 路由规则设计:怎么判断一段请求该走哪个模型

3.1 先定义你的路由维度

路由规则不是随便拍脑袋写的,而是先想清楚用什么维度来切流量。我常用的维度有五个:

  • 请求来源:哪个业务方、哪个应用调进来的。
  • Prompt 关键词 / 正则:标题、内容里是否包含某个主题词。
  • 估算 token 数:输入长度超过多少走长文本模型。
  • 用户标签 / 付费等级:高级用户走更强模型,内部测试走便宜的。
  • 需求意图:借助分类器打上“代码”“文档”“闲聊”等标签。

实际配置时,我很少只用一个维度,通常是组合判断。比如“请求来源是代码助手应用 + 关键词包含报错信息”走 Coding Plan;“输入超过 8000 token + 关键词包含总结/摘要”走 Kimi;“其他所有情况”走 GLM。组合维度更稳,不会因为某一个词出现在奇怪的上下文里就误判。

3.2 配置示例:一个最基础的三模型路由

下面这段是我在模拟项目X里用过的简化版路由规则,完全可以作为起点模板:

route: default: glm_chat fallback: glm_chat rules: - name: coding_task match: prompt_contains: - "写代码" - "debug" - "bug" - "重构" - "SQL" - "正则表达式" target: coding_plan - name: long_doc_task match: est_tokens: gt: 8000 prompt_contains: - "总结" - "摘要" - "长文档" - "合同" - "论文" target: kimi_long - name: default_chat match: pass: true target: glm_chat

逐行解释一下:

  • default是兜底目标。所有规则都没命中时,走它。我把它设成 GLM,因为 GLM 综合能力强、价格适中,适合当默认值。
  • fallback是异常降级目标。目标模型报错、超时或限流时,请求会转给我们指定的模型,避免业务死掉。
  • rules是规则数组,按顺序从上到下匹配;命中即停,不再往下执行。所以顺序很重要,越具体的规则越要放前面。
  • prompt_contains做的是子串匹配,简单粗暴但有效。实际生产环境建议再加一层正则,例如把 “修复一段 JS 的报错” 中的 “JS” 也识别出来。
  • est_tokens是网关侧对输入文本做的粗略 token 估算。8000 这个阈值不是拍脑袋,我是按业务平均请求长度和长文本模型的性价比交点算出来的。如果你的业务以短问答为主,阈值可以降到 4000;如果到处都是长文档,再往上调。

这里有一个非常容易踩的坑:default一定要写,而且最好写“够用且便宜”的模型,而不是“最强”的模型。很多人觉得反正都兜底了,那就兜到最强模型上,结果所有未命中的未知请求全打到高配,月底账单教你做人。未知请求优先级最低,花最少的钱才是合理的。

3.3 组合判断:避免误判的细节

只用关键词最大的问题是误判。比如一句话 “我想总结一下代码里的 bug”,同时包含“总结”和“bug”,按上面的顺序会命中 coding_task,走到 Coding Plan,但如果用户真正想要的是“代码 bug 的总结报告”,这个路由又没错。怕就怕用户说的是“帮我总结这份代码的功能”,结果因为带了一个“代码”就进了编程模型。

解决思路是:为每条规则增加必要的上下文条件。比如 coding_task 可以要求prompt_contains里的关键词至少命中 2 个,或者配合请求来源维度。YAML 里可以加一个match_any或min_hits的配置,不同网关叫法不同,思路是一样的:

- name: coding_task_strict match: min_hits: 2 prompt_contains: - "写代码" - "debug" - "bug" - "重构"

这个min_hits: 2的意思是:下面关键词至少命中两个,才认为它是代码任务。可以显著降低“单关键词误触发”的情况。配置成多关键词命中之后,路由精度会提高不少,代价是一部分弱相关的代码请求会漏到默认模型。所以 min_hits 到底设 1 还是 2,要结合你的业务容错能力来定,不能照搬。

4. 实操:配置一套低成本混合路由并验证效果

4.1 模型池与全局参数配置

路由规则只是骨架,真正撑起稳定性的是模型池和治理策略。我放出整套配置的骨架,这个可以直接抄到自己的环境里改成实际值:

server: port: 8080 reload_interval: 30s providers: glm_provider: base_url: ${GLM_BASE_URL} api_key: ${GLM_API_KEY} kimi_provider: base_url: ${KIMI_BASE_URL} api_key: ${KIMI_API_KEY} coding_provider: base_url: ${CODING_BASE_URL} api_key: ${CODING_API_KEY} models: glm_chat: provider: glm_provider model_name: glm-chat timeout: 30s max_tokens: 2048 temperature: 0.7 kimi_long: provider: kimi_provider model_name: kimi-long timeout: 60s max_tokens: 8192 temperature: 0.3 coding_plan: provider: coding_provider model_name: coding-plan-v1 timeout: 60s max_tokens: 4096 temperature: 0.2

有一点要提醒:timeout一定要按模型差异分开设。长文本模型处理十几万 token 的输入,30 秒内根本没可能返回;而普通问答模型如果你给它 60 秒超时,一遇到模型异常,请求就要傻等一分钟,用户早跑了。我自己的习惯是普通模型 20 到 30 秒,长文本和编程模型 60 秒起步,同时配合重试策略。

另外,api_key一定要用环境变量引用,千万别把 key 写到配置里提交到仓库。这是个很低级但很多人犯过的错误。配置里的模型名和参数只是示意,接入时以对应平台实际提供的模型标识为准。

4.2 预算、限额与审计:钱要花得明明白白

零代码路由不只是把流量分出去就完事,还得有能力控制成本。我在配置里加了三项,你可以直接参考:

cost_limit: monthly_budget_usd: 300 daily_limit_usd: 15 model_limits: coding_plan: max_daily_usd: 5 kimi_long: max_daily_usd: 5 glm_chat: max_daily_usd: 8 audit: enabled: true log_field: ["route_reason", "target_model", "latency_ms", "prompt_tokens", "completion_tokens", "estimated_cost"] request_tag: true

先说monthly_budget和daily_limit。这是保命项,防止某天模型异常把预算打穿。模型级限额更有针对性:比如 Coding Plan 套餐按 token 计价偏高,我给它单独设一个每日 5 美元的封顶,超了就自动降级到 GLM,而不是直接报错。

再说审计日志。这是路由调优的灵魂,我强烈建议把route_reason记录到每条请求的日志里。这样出现问题,你能直接看到“这个请求为什么被路由到了 Kimi”,是关键词命中了,还是长度阈值触发了,还是走了 default。没有这些日志,你调规则就是在瞎猜。生产上不要记录 prompt 原文,涉及用户隐私,只记录必要的元数据就够了。

request_tag: true是让接入方在请求头带一个项目标识,方便月底做成本分摊。多个业务共用同一个网关的时候,没有 tag 你根本说不清这笔钱是哪个项目烧的。

4.3 成本对比:一个模拟场景的计算过程

说个小场景,还是上面那个模拟项目X客服助手。日均 10 万次请求,平均每次输入约 1500 token,输出 300 token。

先算出每天的总 token 量:

  • 输入:100000 次 × 1500 token = 150,000,000 token,也就是 150M token
  • 输出:100000 次 × 300 token = 30,000,000 token,也就是 30M token

如果全部用同一个强模型,假设输入单价 6 元/百万 token、输出单价 18 元/百万 token,那么:

  • 输入成本 = 150 × 6 = 900 元
  • 输出成本 = 30 × 18 = 540 元
  • 单日总成本 = 1440 元

切换路由后,我用的是前面那套 60/25/15 的比例假设:

  • 60% 的普通请求走 GLM,假设输入单价 1.5 元/百万 token、输出单价 4.5 元/百万 token
  • 25% 的长文本请求走 Kimi,假设输入单价 2.5 元/百万 token、输出单价 6 元/百万 token
  • 15% 的编码请求走 Coding Plan,假设输入单价 3 元/百万 token、输出单价 9 元/百万 token

单日成本变成大约:

模型输入token量输出token量输入费用输出费用
GLM90M18M135元81元
Kimi37.5M7.5M93.75元45元
Coding Plan22.5M4.5M67.5元40.5元
合计150M30M296.25元166.5元

总额约 462.75 元,相比原来的 1440 元,降幅大概在 68% 左右。这个数字看起来很夸张,但前提是我把强模型价格设得比较高、且普通场景占比够大。不同平台的定价和模型能力差异很大,你的数字可能完全不同。我真正想让你看的是这个计算思路:先统计请求分布,再对比各模型单价,就能算出路由的大致收益,再决定值不值得做。

4.4 灰度上线的四步走

配置写完别急着全量切,我的习惯是分四步:

第一步,影子模式。流量照旧打到现有模型,但网关同时记录“如果走新规则会路由到哪个模型”。跑三到五天,对比虚拟路由结果和线上真实结果,看差异多大。

第二步,切 5% 到 10% 的流量,观察错误率和 P95 延迟。这个阶段重点关注:路由命中率、目标模型是否频繁报错、有没有超时。

第三步,逐步扩大到 50%,对照前一天的延迟和成本曲线,确认没有异常。

第四步,全量切换。但保留旧配置,随时准备 rollback。零代码的好处在这里体现得很明显:rollback 就是把旧配置重新加载一下,不需要回滚代码。

5. 常见问题与排查技巧实录

5.1 路由总是“不听话”,老往默认模型跑

这是最常见的问题,明明写了代码关键词,请求却走了 GLM 默认。我排查下来基本都是这几种原因:

  • 规则写错了优先级:把default规则放在最前面,后面匹配规则还没轮到就终止了。规则匹配是从上到下、命中即停,优先级全部藏在顺序里。
  • 关键词没覆盖用户的真实表达:用户习惯说“帮我看看这段代码哪里有问题”,而不是“debug”。这种场景要么加更多同义词,要么上正则。
  • min_hits设得太严:要求命中 2 个关键词,但用户只提了一次“代码”。

排查方法就是看日志里的route_reason,它能明确告诉你这个请求是因为哪条规则或者说哪个默认兜底被选中的。只要原因可见,修复通常一分钟内能完成。

5.2 超时重试配置不当,反而放大了故障

我在早期犯过一个错误:所有模型统一设 30 秒超时,重试次数设 3。结果某个模型故障时,一次请求变成四次调用,流量放大四倍,直接把模型打挂。

正确做法是:

  • 重试次数通常设 1 次,最多 2 次,并且要加上指数退避和随机抖动。没有抖动,所有超时的请求会在同一秒全部重试,等于自己给自己制造流量峰。
  • fallback 目标不要设成最强模型。模型故障时你也希望控制成本,fallback 到够用且便宜的那个更合理。
  • 有条件的可以加熔断:连续 N 次失败就短路,不再把请求发到故障模型。

5.3 模型参数不一致导致输出行为“玄幻”

同一段 prompt,从 GLM 切到 Kimi 后,输出风格忽然变了。很多人第一反应是“模型能力不行、幻觉了”,但很多时候只是参数不一致:temperature 从 0.7 变成了 0.3,top_p 不同,输出长度变了。

更隐蔽的是工具调用格式差异。不同模型的 function calling 格式不是完全兼容的,结构化的 JSON 里字段名可能不同。如果你要让模型输出严格 JSON,不要在 system prompt 里只写一句“输出JSON”,而是要给出明确的示例格式,并针对不同模型做一层适配。零代码路由解决的是“分发”问题,但模型之间的格式差异不是配置一把梭就能解决的,需要在接入层做好这层兼容。

5.4 上下文窗口估算不准,导致长文本被截断

中文场景特别容易踩这个坑。不要用字符串长度判断,一个汉字在部分 tokenizer 里等于 1 到 2 个 token,而且不同模型的 tokenizer 不完全一致。正确的做法是路由前先做一次估算,可以用目标模型对应的 tokenizer 库,也可以用网关内置的估算器,至少保证估算误差在一个量级内。

长文本走 Kimi 没问题,但你要明白“能处理超长文本”不等于“所有模型都能处理超长文本”。如果一条很长的问题被规则切到了默认模型,而默认模型窗口不够,它会在入口就拒绝或截断,导致输出质量崩掉。所以路由规则里必须有一条长度上限判断:超过安全线,要么走长文本模型,要么提示用户分段处理。

5.5 预算没设好,账单失控

模型路由上线后,如果某个规则误命中了大量高价模型,又没有模型级限额,月底账单会非常难看。我见过一个团队把“长文档”规则写得过于宽泛,结果四成请求全走了长文本模型,预算翻了三倍。

防呆手段就是我在 4.2 节写的model_limits,以及日志里的estimated_cost。建议每周看一次各模型的调用占比和成本分布,如果发现某个模型费用占比异常,说明规则要调整了。

5.6 一张避坑速查表

问题一句话避坑要点
规则总走默认模型检查优先级顺序和关键词覆盖,看 route_reason 日志
模型故障后流量放大重试次数不超过 2,加指数退避和抖动,fallback 到便宜模型
输出格式变了检查 temperature/top_p,确认 function calling 兼容性
长文本被截断用 tokenizer 估算,规则里设置长度安全线
成本失控设置模型级每日限额,按 tag 做成本归集
上线不敢收敛影子模式先行,灰度流量到 10% 再放大
关键词误触发用 min_hits 多关键词命中,减少单词判断

最后分享一点个人经验:模型路由入门不难,难的是持续调优。不要一上来就搞一堆规则,先挑两个最典型的场景写进配置,跑一两周看日志,再往里加。规则是越迭代越准确的,不是一次设计出来的。我一般从三条规则起步:编程任务、长文档任务、默认问答,跑一段时间后再根据日志拆出更细的意图。

另外,配置里永远要给default和fallback留活路。我见过很多线上事故,都是因为他漏写了 fallback,目标模型一挂,所有请求全部报错。一句话:路由可以先粗,但兜底绝对不能少。

RELATED READING

延伸阅读

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