详解 Weave Router 滞回机制:为什么模型切换必须跨过 WMI 分数差才放行
2026/9/16 20:42:58 网站建设 项目流程

详解 Weave Router 滞回机制:为什么模型切换必须跨过 WMI 分数差才放行

【免费下载链接】routerModel router for agentic systems. Routes every prompt to the right model in <50ms. Cut costs 40-70% with just an endpoint change.项目地址: https://gitcode.com/GitHub_Trending/router101/router

Weave Router是面向 AI Agent 系统的模型路由器:它把 Claude Code、Codex 等工具发来的每一个 API 请求,在 50ms 内路由到最合适的模型,仅靠改一个端点地址就能降低 40%–70% 的成本。而**滞回(hysteresis)**是它防止模型频繁切换的核心机制——新模型必须与在用的"现任"拉开足够的WMI 分数差,切换请求才会被放行。这篇文章带你从零看懂这套设计的来龙去脉。

从一次"差点被换掉"的模型说起

假设你在一个长会话里用 Claude Code 写代码。第一句"帮我写个 Python 脚本"很简单,Weave Router 给这次请求配了claude-opus-5:low(低推理强度档)。十几轮之后你开始让它设计复杂架构,打分器认为high档更好,于是"挑战者"出现了:claude-opus-5:high

问题是:下一句你又问了一个小问题,打分器立刻又觉得low更好。如果每一轮都听从打分器的最新意见,你的会话就会在low ↔ high之间反复横跳——

  • 前缀缓存全部作废:每次切换,之前累积的上下文缓存前缀就失效了,下一轮要重新按全价重新计费;
  • 回答质量忽高忽低:同一会话里风格来回变,体验很割裂;
  • 成本不降反升:切换本身就是浪费。

这就是 Weave Router 引入滞回要解决的核心问题 🛡️。

什么是"滞回":给模型切换加一道缓冲带

滞回是物理和工程里的经典概念:继电器不会因为电压在临界值附近波动而疯狂开合,而是设一个"迟滞区间",让状态变化必须"明确跨过阈值"才发生。

Weave Router 把它搬进了模型路由。判定逻辑就在这一小段代码里:

核心判定:effortHysteresisHold

它做的判断只有一句话能讲清楚:

挑战者的 WMI 分数 − 现任者的 WMI 分数 ≥ 1.0,切换才放行;否则继续用现任。

这个 1.0 的分数差阈值是写死的常量:

阈值定义:effortHysteresisThreshold = 1.0

也就是说:high档必须比low至少优 1.0 分,Router 才会真的把会话切过去。

WMI 分数是什么?

WMI是 Weave Router 为每个"候选臂(arm)"算出的综合评分。这里的"臂"指"模型 + 推理强度"的组合,例如claude-opus-5:high

它由 Go 侧的确定性打分器产生,是两条归一化轴的加权混合:

  • WII:质量轴,衡量模型干活的能力;
  • WPI:评测工作负载轴(注意:它不是计费价格,预览金额永远来自模型目录价)。

官方文档对此有明确说明(权重按组织的"质量/价格偏好"调节):

docs/HMM_GO_SELECTION.md:v7 rosters score each arm asalpha*WII - (1-alpha)*WPI… WPI is a normalized evaluation-workload axis, not a billing rate

滞回比较时,直接读取每次决策附带的逐臂分数表ArmScores,按"臂 ID"(如anthropic/claude-opus-5:xhigh)取值:

分数表定义:ArmScores:ArmScores holds per-arm WMI scores for hysteresis

三种典型场景:何时放行、何时按住?

滞回检查只在同一基础模型内、仅推理强度变化时生效(跨模型切换是规划器的职权,直接放行)。用测试用例里的数字看最直观:

场景WMI 分数(low → high)分数差结果
挑战者只略优low=4.0, high=4.50.5 < 1.0🛑 按住,继续用 low
挑战者明显更优low=4.0, high=6.02.0 ≥ 1.0✅ 放行,切到 high
挑战者更差low=5.0, high=1.0−4.0 < 1.0🛑 按住,继续用 low
跨模型(opus → fable)不参与计算✅ 直接放行

对应的边界条件测试覆盖了"无分数表""现任臂无分数""强度没变"等一切直通情形,可以完整对照:

完整测试:hysteresis_internal_test.go

当切换被按住时,Router 会在路由原因上追加effort_hysteresis标签(appendEffortHysteresisReason),保留规划器原本的判断同时留下"被滞回拦下"的痕迹——离线分析就能把"被拦下的轮次"和"干净留守的轮次"分开统计。

不止一层:分数差 + 连续票数,双闸门防抖

上面讲的 WMI 分数差管的是"同一模型内换强度"。跨模型场景(比如从旗舰模型降到便宜模型)还有第二道闸门:连续降级投票数

在 turnloop.go 的注释里写得很直白:

Order matters — an unconfident cheaper vote is discarded before hysteresis sees it, so noise cannot accumulate into a switch.(低置信度的降级票在滞回看到它之前就被丢弃,噪声无法累积成一次切换。)

流程分三步:

  1. 置信度门槛:打分器的置信度低于阈值,这张"降级票"直接作废;
  2. 连续计票:置信够格的降级建议累加票数(ConsecutiveDowngradeVotes),票数没达到hmm_downgrade_hysteresis_turns设定的轮数,继续留在原模型上;
  3. 达标放行:票数够了才真正降级,同时记录"影子遥测"——用一套影子阈值统计"如果启用更严格的滞回,本来能拦住几次",让运营团队先量化再上线。

注意一个有趣的不对称:升级不受滞回限制(升级通常只多花钱、不掉体验),受保护的主要是降级和强度切换。

这套设计给用户带来了什么?

📌更稳的会话体验:模型不会在会话中途反复"变脸",上下文、风格和质量保持一致。

💰更低的账单:被拦下的每一次低性价比切换,都意味着前缀缓存继续生效、不产生重复的全价计费。

📈可量化的调优:所有滞回决策都带标签写入遥测(observation.go),配合影子计数器,阈值是"用数据调出来的",而不是拍脑袋。

关键配置与源码速查

项目位置
强度切换分数差阈值(1.0)internal/proxy/hysteresis.go#L7-L9
滞回判定主逻辑internal/proxy/hysteresis.go#L19-L48
降级连续计票闸门internal/proxy/turnloop.go#L1646-L1698
滞回相关特性开关定义internal/flags/flags.go#L63-L66
WMI 分数与打分器文档docs/HMM_GO_SELECTION.md
部署配置参考docs/CONFIGURATION.md

可调开关包括hmm_downgrade_hysteresis_turns(降级计票阈值,设为 0 可禁用)和hmm_downgrade_hysteresis_shadow_turns(影子计数阈值,默认 2,只做遥测、不改变路由行为),均可按组织维度覆盖。

小结

Weave Router 的滞回机制,本质是给"模型切换"装了一个防抖器:WMI 分数差管同模型内的强度升降(必须 ≥ 1.0 才放行),连续降级票数管跨模型降级,低置信度噪声在入口就被丢弃。三道防线共同保证了:只有"明确更好的选择"才能改变你的会话——换得少,但换得准。这既守住了体验的连贯性,也守住了 40%–70% 的成本优化空间 🎯。

【免费下载链接】routerModel router for agentic systems. Routes every prompt to the right model in <50ms. Cut costs 40-70% with just an endpoint change.项目地址: https://gitcode.com/GitHub_Trending/router101/router

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询