973 个真实线程的 A/B 测试里,模型调用成本从每个任务 2.61 美元降到 0.94 美元,少了 64%。更有意思的是,PR 合并率没有跟着塌,从 27.3% 变成了 29.2%。

这组数据来自 LangChain 给开源编码 Agent Open SWE 做的模型路由实验。它最容易被转述成一句轻飘飘的话,给 Agent 加一个 router,就能省掉三分之二成本。

我对这种结论有点警惕。

把一个昂贵模型替换为便宜模型,当然容易。难的是让系统知道何时能换,换完如何确认没把一次真正棘手的排障误判成普通改文案,又该怎样从失败里把规则改回来。前者是价格表,后者才是工程。

对于正在把 Agent 接进 Java 服务、LangGraph 工作流或内部研发平台的人,这件事尤其绕不过去。模型越来越多,团队给出的预算却不会同比变多。你很快就会面对一个别扭的问题,所有步骤都用最强模型,账单难看。全都压到轻量模型,失败会以更隐蔽的方式回到人工 review、返工和事故里。

路由器不该只是省钱开关。它应该是一套能够对任务风险负责的决策系统。

这组 64% 数据,到底说明了什么

先把已知信息摆在桌上。LangChain 的文章介绍,Open SWE 在 973 个线程上做了 A/B 测试。引入模型路由后,中位成本由 2.61 美元降至 0.94 美元,PR 合并率从 27.3% 到 29.2%,质量没有可测得的变化。

这个实验有价值,不在于它给出了一个可复制的 64%,而在于它没有只盯着 token 单价。它至少同时看了成本和下游结果。

很多团队的第一版路由会停在这里。

1
2
3
4
if 任务看起来简单:
用小模型
else:
用大模型

这段逻辑运行起来很快,也很容易在周报里讲清楚。但它对真实工程几乎没有解释力。

同样叫修复一个 Bug,有的只是一行 null 判断,有的是跨 Spring MVC、缓存和异步消息的时序问题。有的需求描述短,但包含一条绝不能越过的权限边界。有的描述长,实际是把一段注释整理清楚。用输入长度、文件数量或关键词做判断,可以当信号,却不能当判决。

更麻烦的是,编码 Agent 往往不是一次模型调用。它会读仓库、搜索符号、跑测试、编辑文件、理解失败日志,再决定是否继续。真正的成本不是某次 completion 的价格,而是一个错误路线被持续执行时,所有后续调用叠出来的成本。

所以我会把那组 A/B 数据读成一句更克制的话,模型路由有机会把成本压下来,但前提是你为它补齐了任务识别、升级、校验和复盘。

少一个环节,省下来的钱很可能只是延后结算。

先别问哪个模型更强,先把任务拆开

一个常见误区,是按照模型能力给任务贴标签。比如把 Claude 放在复杂、把小模型放在简单。这样做很顺手,却把问题推给了一个模糊词,复杂。

对 Agent 来说,更有用的拆法是看任务的失败代价和信息不确定性。

我会把一次 Agent 执行拆成四类动作。

低风险的机械动作

读取目录、归纳 issue、提取日志时间线、生成测试用例骨架、改格式、给已有函数补注释。这些动作有一个共同点,输出可以很快被机器检查,出错后回滚也便宜。

它们适合默认交给较便宜的模型,甚至不该让模型参与。比如 JSON 转换、AST 扫描、代码格式化、依赖图收集,用确定性程序比让模型猜更稳。

这里有一个很朴素的原则。

能被工具精确完成的,不要包装成智能。

需要局部推理的动作

定位一个模块的调用链、根据堆栈缩小排查范围、在已有模式中补一段 WebFlux handler、解释一组测试为何失败。这类任务要理解上下文,但通常有足够的护栏。

便宜模型可以先做。前提是给它窄一点的工作区,只提供相关文件、已有规范和可运行的测试命令。很多人把小模型丢进整个 monorepo,再抱怨它不可靠。问题未必在模型,可能是你让它在没有地图的城市里找一扇门。

高不确定性的决策动作

跨模块重构、数据库迁移、权限模型调整、并发一致性问题、线上事故归因、外部 API 行为发生变化时的兼容策略。这些事情不是多给几千 token 就能安全解决,因为它们涉及假设。

此时要么直接选择强模型,要么让一个较便宜的模型先收集证据,再把压缩过的证据包交给强模型。后一种方式更像工程协作,调研员不做最终架构决策,架构师也不该从头翻每一份日志。

需要承担后果的动作

执行生产变更、合并 PR、删除数据、发起付款、修改权限、向客户发送内容。这一层与模型大小无关。无论路由器给谁,系统都应要求人类确认,或至少经过独立的策略检查。

不要让「高置信度」成为绕过审批的理由。

模型知道的,是自己生成文字时的内部偏好。它并不知道你的生产环境值多少钱。

一套能落地的路由,至少要有三道门

把任务分层,只是开始。路由器真正要解决的是三次不同的判断。

第一道门判断任务能不能被轻量路径处理。这里可以组合规则和一个低成本分类器。规则负责硬边界,比如出现 production、权限、迁移、支付、删除、客户数据等标记时,不进入低成本路径。分类器负责较软的判断,比如这个 issue 是局部修复还是架构调整。

第二道门判断执行过程中要不要升级。很多任务开头看着很简单,跑到第三轮工具调用才露出复杂度。一个好的系统不应执着于第一次选对,而要尽早承认选错。

可观测信号包括这些。

  • 连续两轮测试失败,且失败集合在扩大
  • Agent 多次打开相同文件,却没有形成可验证的修改
  • 工具调用次数超过该类任务的历史分位数
  • 需要访问的模块数超过预期
  • 生成补丁触及鉴权、数据库 schema 或公共 API
  • 模型自己的计划和实际工具结果出现明显矛盾

第三道门判断结果能不能离开沙盒。这里不该由发起执行的同一个模型给自己打分。最少也要跑测试、lint、依赖安全检查和 diff policy。对于高风险改动,再增加独立模型 review 或人工审批。

这三道门连起来,路由就不再是一张静态的模型价目表,而像一个有止损线的执行系统。

1
2
3
4
5
6
7
8
任务进入
-> 硬规则筛掉高风险任务
-> 分类器选择初始模型和上下文预算
-> 在受限沙盒执行
-> 观察测试、循环次数、触及范围
-> 触发阈值时升级模型或转人工
-> 独立验证器决定是否可提交
-> 记录结果,更新路由策略

这个流程看起来比一句 if else 麻烦得多。确实更麻烦。

但 Agent 进入研发流程以后,复杂度并没有消失,只是会在失败时以更高的价格回来。

不要只路由模型,还要路由上下文

很多成本优化文章只谈 model selection。我觉得这是漏掉了一半。

同一个强模型,带着 20 万 token 杂乱上下文去解决一个局部问题,未必比一个中等模型拿着干净证据包更好。上下文越长,模型越容易把注意力分散到历史噪声里,账单也跟着膨胀。

更实用的做法,是先把上下文当成一个独立资源来治理。

对 Java 和 Spring 项目,可以先由代码索引收集 controller、service、repository 的调用路径,补上相关配置、最近的失败日志和对应测试。再把这些材料压缩成任务包。模型看到的是一个带边界的问题,而不是整个仓库的倾倒。

例如一个支付回调重复消费的问题,低成本路径可以完成这些工作。

  • 从日志里抽取同一个 eventId 的时间线
  • 找到 consumer、幂等表和事务边界
  • 列出涉及的 topic、重试策略和测试文件
  • 把不确定的问题明确标出

之后再把任务包交给更强的模型做判断。强模型不必花大量 token 学习目录结构,人的 review 也更容易追踪它依据了什么。

这类分工有一个额外好处,失败会变得可诊断。是分类错了,是检索漏了,是模型推理错了,还是验证器没拦住。没有这层拆分,所有问题最后都会被归结为模型不够聪明,团队也就失去了修的入口。

Spring 服务里,路由器不要藏在 Prompt 里

如果 Channing 正在做一个面向内部研发的 Agent 平台,我不建议把路由逻辑写进一个巨大的 system prompt。Prompt 可以表达偏好,却不适合承载不可突破的策略。

更合理的结构,是把它拆成能测试、能审计的组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public record RouteDecision(
String model,
String tier,
int maxToolCalls,
boolean requireHumanApproval,
List<String> reasons
) {}

public interface TaskRouter {
RouteDecision decide(TaskProfile task, RiskSignals signals);
}

public interface EscalationPolicy {
Optional<RouteDecision> evaluate(ExecutionTrace trace);
}

TaskRouter 处理初始分流。它拿到的不是一段原始用户输入,而是结构化的 TaskProfile,包括任务类型、目标仓库、允许工具、数据等级、预期变更范围。EscalationPolicy 只看执行轨迹,例如测试结果、重试次数、变更文件、工具错误和 token 消耗。

策略最好用配置表达,而不是散在业务代码里。

1
2
3
4
5
6
7
8
9
routes:
- when: risk in [critical, high]
model: frontier
approval: required
- when: taskType == documentation and risk == low
model: economical
maxToolCalls: 8
- when: filesTouched > 12 or testFailures >= 2
action: escalate

这里的模型名故意不写死。模型更新会很快,风险策略不该跟着供应商命名一起改得面目全非。你的业务层只关心 economical、standard、frontier 这类能力档位,Provider Adapter 再去完成具体映射。

从架构上看,这也让 LangGraph 很顺手。把路由、执行、校验、升级建成独立 node,状态里保留 routeDecision、evidence 和 executionTrace。当执行失败时回到升级 node,而不是重新跑整个图。这样既能控制预算,也不会把已经收集到的证据丢掉。

A/B 测试不是上线仪式,它才是路由器的训练数据

LangChain 那组 973 线程的数据告诉我们,不能只靠直觉宣布路由有效。但我也不建议直接照抄它的阈值。不同团队的代码库、测试覆盖率、问题类型和人类 review 标准都不一样。

上线前,先准备一组脱敏的历史任务。每个任务记录原始描述、最终修改、测试结果、人工是否接受、实际耗时和成本。然后让原策略与候选路由在相同任务上回放。

评估指标至少要四类一起看。

  • 成本,中位数和 P95 都要看,别被少量极便宜任务遮住尾部
  • 成功,测试通过率、任务完成率和人工接受率要分开记
  • 安全,越权工具调用、敏感文件触及、策略拒绝是否正确
  • 体验,首次可用结果的时间、升级次数、人类接管比例

尤其是 P95。平均成本降了,不代表体验更好。如果 5% 的高价值任务因为路由错误多绕了十分钟,研发同学对系统的评价不会是「真省钱」。

还要给路由器留一部分探索流量。所有任务都按历史最优策略走,系统就很难发现轻量模型在新类型任务上已经够用,或者某个强模型在特定代码库里反而更容易犯错。小比例、受控、可回滚的探索,能让策略有机会更新。

别把它做成模型之间的选秀。

你在训练的,其实是团队对任务风险的共同理解。

这件事最容易踩的四个坑

第一个坑,是只按 token 单价排序。单价低不等于总成本低。一次失败后再启动三轮排障,往往比第一轮用强模型更贵。

第二个坑,是把路由器当作万能分类器。它永远会误判。真正的目标不是消灭误判,而是把误判限制在可升级、可回滚、可审计的范围里。

第三个坑,是把质量指标做得太晚。只在 PR 合并后统计失败,反馈已经太慢。测试失败、循环次数异常、修改范围膨胀,都应该是在线信号。

第四个坑,是让同一个 Agent 既执行又验收。模型很擅长为自己的输出寻找看似合理的解释。独立验证器不必更聪明,但它要有不同的目标和更硬的约束。

这话听着有点保守,但研发平台最怕的不是一次聪明的 demo。最怕的是大家在 demo 里看见了 64% 的节省,随后把没有护栏的自动化推进生产。

从明天就能开始的一小步

如果你的团队还没有模型路由,不要急着做一个无所不包的 Agent 控制平面。挑一类低风险、可验证、量又足够大的任务开始,比如测试失败摘要、代码评审建议或文档变更。

给它定义一个明确的成功标准。用两档模型跑两周。记录任务类型、成本、耗时、测试结果和人工接受情况。把需要升级的原因写进日志,而不是只写一个 failed。

然后你会得到第一批真正属于自己团队的数据。它也许不会给你 64% 这个好看的数字,但能告诉你哪些任务可以放心下放,哪些地方必须保留强模型和人工判断。

这比一份漂亮的模型选型表有用得多。

LangChain 这次实验给出的启发,并不是「小模型终于能替代大模型」。它更像提醒我们,Agent 工程的成熟标志,不是接入了多少模型,而是系统能否在每一次选择里讲清楚,为什么此刻该用它,又在什么时候必须承认自己选错了。

那 0.94 美元并不是故事的结尾。

它只是一个团队开始认真对待决策成本的起点。

参考资料