作者用纯Markdown配置文件实现任务路由策略,根据任务复杂度分配不同层级模型,避免低优任务消耗高价模型配额,探讨了「简单vs困难」二元判断的局限性。
每次 Claude Code 会话默认使用打开时选定的模型。无论是重命名一个变量还是设计一个子系统,不加干预的话,它都会用同一档模型。这没问题——直到配额开始告警。Token 和时间是同一笔预算,而顶级模型消耗两者最快。把它们浪费在变量重命名上,系统设计就没得用了。显而易见的修复是出一条经验法则:简单任务用低成本模型,困难任务用昂贵模型。听起来很对,但它错了——而弄清楚它为什么错了,才是这个项目真正要做的大部分工作。
orchestrator 是一个路由层,不是又一个 agent 框架。它以 markdown 的形式存在:一份契约文件、若干 dispatch 模板、一份只追加的账本,所有内容在会话将任何任务委托出去之前读取。没有安装依赖,没有外部调度器。第一天就下了一个赌注:Claude Code 自己的原语已经足够,缺的是一套策略而非一个平台。
策略的核心动作是拒绝将"困难还是简单"作为路由变量。真正重要的是任务形态:工作需要多少轮修正,以及某一轮能否被客观验证。
向下路由——一次性、客观可验证:测试已存在,或输出能对照 schema 校验。把它推下成本阶梯,先经过脚本和本地模型,再到达低成本托管层。
留在顶级——迭代密集、判断主导:设计、调试、架构、review。留在顶级模型,在会话内完成。
这两个分类之下,藏着"简单任务用低档模型"这个直觉从未算进去的三种成本:
冷启动税——每个被委托的会话都要从零重新推导上下文,所以移交一个五分钟的编辑任务会直接丢失。
Review 税——分发任务的人仍然要付出代价来阅读返回结果,而低档模型的冗长回复可能抵消它自己省下的费用。
重试税(主导项)——低档模型在需要判断力的任务上失败率更高,而每次失败都意味着重来。
让三条税都存活下来的规则不是"这个任务难不难",而是预期节省能否超过预期重做成本。迭代密集的工作,无论看起来多简单,都会最大化不等式的错误一边。
四个类别永久锁定在顶级,无论单独看任务有多简单。
品味敏感型输出是第一个:设计、文案、命名、品牌调性。一个组件名称计算起来微不足道,但仍然想要有品味的模型来完成。"简单"和"低风险"是不同的坐标轴——这个想法是从项目外部引入的,但它值得占有一席之地。
终端 review 是第二个。按任务维度的 review 可以在中间层运行,但上线前的全分支通过始终走顶级,因为它能捕捉到窄切面 passes 遗漏的东西。有一次,低档模型的一次 passes 漏过了一个真实的安全漏洞,后来被更强的模型捕获了。那条规则就是从那天起确立的。
第三个,低档模型的绿灯只能为一次 dispatch 开闸,但永远不能替代验证。低档模型有一种结构性习惯:报告它们并未真正获得的成功。
第四个,orchestration 层本身。曾试过用一个低档模型充当会话和 worker 之间的中层管理。六十分钟和大约五十次 tool calls 后,它什么都没交付,还静默运行了一个没人要求的全局 install。委托始终是会话直接发出的命令。
› echo "nobody gets a middle manager"
每次委托运行都以相同方式结束:聊天中的状态块,以及账本文件中追加的一行。"完成"需要有工具输出、测试结果或截图放在旁边作为凭证,而不是空口声称。
账本不是装饰。每个行记录了路由到哪里、是否通过,以及最重要的字段:任何教训。一个填充了 lesson 的条目是候选,可以晋升进路由规则本身。策略从自身的田野证据中收紧,而不是只从设计阶段。
最艰难的教训不是来自超支。它来自一个本该正确运行、却在错误地方执行的决策。
一个"同时在所有地方执行"的条款,被写进了一个项目自己的决策日志,它本会让该项目的会话对兄弟项目的仓库执行 git rm --cached——那是一个它根本没有理由触碰的仓库。用户在它执行之前捕获了它。没有损失。
但它暴露了这台机器上每个项目的真实默认行为:任何会话都可以提出编辑相邻项目的文件,而且这个提议通常看起来是合理的。
修复不是更聪明的检查,而是反转了默认行为。
会话现在用三个层级对待每个其他项目的树:
读取(始终允许)——读取兄弟项目的文件始终允许,因为不可能产生去同步。
提议(唯一获得批准的跨项目写操作)——在兄弟项目根目录写一张便条,请它自己的下一次会话来决定。
变更(默认拒绝)——编辑兄弟项目的事实或在其仓库中运行命令。有两个狭窄的例外:其全部用途就是接收写入的文件(如路由账本),以及对真正共享配置的 gated edit——后者始终标明其爆炸半径并等待确认。
其他一切都通过提议便条路由。它建议;它从不断言。接收项目的下一次会话将其折叠进自己的文件,然后删除它。没有队列,没有索引,没有守护进程。三个临时用例验证了这个模式,之后才有人把它写成规则。
每个会话都以相同的四个文件收尾(notes、memory、active constraints、rejected approaches),这样下一次会话冷启动时仍然知道过去决策落地的原因。
第 1 天——建立在原生原语之上。外部 orchestration 框架被拒绝,取而代之的是 Claude Code 自己的 agent 文件、skills 和 hooks。第一次试运行有意在一个一次性项目中进行,设计未经证实时,零回归风险。
第 2 天——阶梯真实测试。一个 wiki 迁移作为第一个真实试验运行,脚本、本地模型和两个托管层同时在一个管道中做真实工作。分层 review 规则在同一周落地,当时低档通过漏过了那个安全漏洞。
约同一周——第一个交付的消费者。一个养老金计算器的功能成为第一个端到端全程路由的真实工作。它暴露了第一个已知缺口:本地层只会说 Python,而这个任务是 JavaScript。不是路由错误,是资质不符,但这是一个值得记录的缺口。
第 4 天——外部验证。一套无关的公开路由框架,在独立审查后,从完全不同的工作负载出发 converge 到相同的核心形态。我们采纳了它唯一真正的新想法:将品味作为独立于难度的路由坐标轴。我们拒绝了它"从不运行高推理努力"的规则,因为这与这个项目有意保持昂贵的那些阶段相冲突。
第 6 天——跨项目消息正式化。三个临时成功案例被泛化为提议便条约定。
第 7 天——近距失误,以及它产生的契约。跨项目变更的默认拒绝成为了一项常设的、机器级别的规则。
第 10 天——全车队 rollout。执行线——让项目的会话真正 consult 策略而不是静默默认的那一条——在大约十八个兄弟项目中推广。每个项目 opt-in,单独从文件系统追踪。
第 11 天——账本自检。一个陈旧性审计发现大多数已采纳项目已静默停止日志记录,所以一个 hook 现在在会话结束时 nudge emission。同一天,一个低档模型的 reviewer 将一个无关的、早已存在的文件变更归咎于它正在检查的任务。对比时间戳捕获了它,它成为了一项常设规则:低档层可以扫描,但将发现的变更归因于某个原因需要先做时间戳检查,而不是靠猜。
本地层只路由 Python 形态的工作,所以 JavaScript 任务默认走顶级,直到有第二个真实 JS 消费者来证明构建一个 driver 的价值。推理努力(reasoning effort)从未进入模型的考量:策略路由的是哪一层运行任务,而不是那层被指示多努力地思考,而且同一个模型在不同的 effort 设置下是不同的成本,arguably 也是不同的能力。那个维度未被审视,不是已被否定。而且中间层代码生成路由有一个踏脚留在阶梯上未经验证:有名字、有定价、准备好了,但没人碰过,因为还没有任务走过它。
没人用过的踏脚不等于能用的踏脚。
以上都不是理论。每条规则都来自实际运行过的工作。整个 apparatus 是一个赌注:真正的问题从来不是"哪个模型最聪明",而是逐任务地知道——什么时候为"最聪明"付钱是错误的。
配套文章:Convergent evidence。构建过程中有九个 orchestration 形状的工具经过桌面,没有一个成为依赖。
策略本身,提取并去标识化后:the routing-policy repo。
Originally published at thekilted.dev/route-dont-guess.