Wynn5a 的技术博客
研发流程约 10 分钟读完

笼子加固记:Agent 工作流的四版升级

上一篇《把 AI Agent 关进流程的笼子里》讲的是这套流程的设计原理,发布时框架停在 v6。四个版本过去,骨架没变,执行层几乎重写了一遍,这篇不按版本号流水账,只挑真正有价值的几条改动,讲清楚它们是被什么推着走的。

先纠正老文章里已经过时的部分#

流程的骨架还是老样子:六阶段、三份文档、三次确认、两道 hook、文档随代码入库。变的全部集中在执行层,其中有四处直接推翻了上一篇的描述。

执行层不再是一个包打天下的 task-executor,而是拆成了四种子代理,写测试、实现、审测试、审实现各司其职,每一步都是全新实例;每个任务的步数也不再固定,现在按任务风险分三条道走,派发次数从零到四不等。文档审查子代理跑在 sonnet 而不是 haiku 上,这是使用中调上去的。最后,diff 范围比对、提交证据核查、任务勾选检查这类机械活已经从模型手里拿走,交给了一个 shell 脚本,零 token 消耗

这四版在解决的是同一个问题#

v1 到 v6 解决的是能不能用:怎么把流程编译成 Agent 绕不开的结构,结构到 v6 基本成型,问题就换成了另一类:慢,贵,而且我们说不清贵在哪儿。当时那句成本翻两到三倍完全是估出来的,手上一个数字都没有。

后面四版的主线因此可以概括成一句话,从设计推演转向数据驱动。v7 的依据来自一篇外部实测报告,v8 是凭诊断做的一批优化但仍缺实测,v9 拿到了第一个真实项目的完整记录,v10 拿到了三份可以横向比较的成本数据。改动的原则始终没变:只砍读取与机械开销,不动质量防线。hook 拦截、RED 阶段测试定稿、编写者与审查者分离、双检查点、三次确认,这五样从头到尾一次都没松过。

下面六条是这四版里真正值得说的。


成本的大头是读取,所以文档要写成轨迹#

Stencil 团队的一篇实测报告(stencil.so/blog/prewalk)给出了一个反直觉的结论:Agent 的 token 消耗里编辑只占约 9%,其余绝大部分是读取,成本约等于 O(reads)。

这一刀正好砍在我们架构的软肋上:每任务一个全新上下文是刻意的设计,换来上下文卫生和审查独立性,代价则是每个子代理都要从零重建认知,而重建的唯一方式就是读。报告里有个比喻很贴切,计划文档写成明信片,执行者就得自己重走一遍探索路径。如果设计文档只写决定在导出服务里加一层筛选,执行者拿到手仍然不知道那个服务在哪个文件、现有筛选逻辑长什么样、哪些地方碰不得,于是它把设计阶段已经付过一次的探索成本又付了一遍。

应对的思路是让文档承载轨迹而不只是结论。design.md 里每个变更点必须带文件加行号的锚点、关键代码引用,以及明确的排除项,也就是哪些东西看着相关但这次不动、为什么不动;tasks.md 每个任务附一段执行简报,把该任务需要的上下文预先写进去。与之配套的是按角色收窄读取范围,编排器成为流水线里唯一通读三份规格的角色,派发时把任务行全文、design.md 相关小节和对应需求编号的原文内联进提示,子代理以摘录为依据定点读取,摘录不够才去读对应小节。

还有一条容易被忽略但很关键:执行过程中扩大探索得到的新发现,必须回写 design.md 的勘误与补充区。轨迹持续增值,散落在一次性会话里的东西才不会白探。这条顺带还压住了文档腐化,因为发现文档与代码对不上时,修正是流程动作而不是靠自觉。

机械活不该由模型来做#

诊断成本时发现一处很尴尬的浪费:opus 在干清单核对的活。diff 有没有超出任务声明的文件、提交信息里有没有任务编号、tasks.md 勾没勾上、工作区干不干净,这些全是确定性判断,模型做不但贵,还不如脚本可靠。

于是新增了 scripts/verify-task.sh,在流水线上占两个关卡:实现完成后做范围预检,超范围直接打回,不浪费一次审查派发;提交完成后做五项终验,一次核完提交编号、勾选状态、全量测试、工作区状态和提交范围。

同一思路还解决了一处重复开销,提交拦截 hook 跑完全量测试通过后,会把 git write-tree 的结果写成一个绿树标记;脚本终验发现 HEAD 的树哈希与标记一致,就直接复用结果不重跑,复用只凭内容哈希,树一致就是内容一致,不存在过期问题,哈希对不上就老实重跑,安全兜底。

把钱花在拦截率高的地方#

v9 的依据是 tracer-v1,一个 15 个任务的真实需求。第一个可靠结论有点难看,全流程耗时约为 v5 的两倍,但分项数据非常值钱:测试审查判 FAIL 的比例是 36%,实现审查一次 FAIL 都没有、范围拦截也是零次,产出基本都是建议项。

这组数字把钱该往哪儿花说得很清楚。原来统一由 opus 承担的代码审查因此拆成两个角色,test-reviewer 审测试留在 opus,code-reviewer 审实现降到 sonnet,范围合规反正有脚本兜底。测试是一切质量的地基,三分之一以上的拦截率证明这道检查配得上它的价格;实现审查则更像是提意见,sonnet 完全够用。

另一个衍生动作是分道。任务在拆解阶段标注风险级,标准道走完整四步,低风险道把测试审查和实现审查合并成一次,派发从四次降到三次。这里有条红线不能破:只有明确判定为低风险的任务才准走这条道,因为合并审查是在实现完成之后才看测试,存在锚定风险,分级存疑一律走标准道。

后来还加了第三条道。有些任务本质上是跑一条命令核对输出,压根没有测试可写,以前它们仍然走完整流水线,派发全在空转。现在这类任务标记为验收型,编排器亲自执行验收命令、核对输出、记录证据后直接收尾,零派发,这个模式在两个需求上验证过才正式写进流程。

意见要就地闭环,不要攒到最后#

tracer-v1 里有个任务的实质缺口混在建议项里,一路拖到重构阶段才被处理。原因是审查结论只有三级,必须修复和可以考虑之间没有严格区分,重要的东西被稀释了。

现在测试类意见一律在 RED 阶段闭环,审查给出建议后立即回派 test-writer 处理一轮,测试随后定稿,不再流向后面的环节;重构阶段改名 FOLLOWUP 并明确禁止碰测试文件,hook 的锁定范围也跟着扩到这个阶段。实现子代理则在转绿后于同一上下文内顺手整理它碰过的文件,超出范围的坏味道上交,由需求末尾自动追加的结构重构任务统一收拾。这样一来 FOLLOWUP 退化为条件派发,只在真有实现类意见时才启动。

条件派发上线后又冒出新问题:三份成本记录显示 FOLLOWUP 的触发率从 1/4、1/4 退化到了 2/2,而看下来大多数触发是注释措辞、文档补充这类不碰代码逻辑的意见,为它们启动一次子代理派发明显不划算,于是审查建议再分一级,触碰代码逻辑的标为建议项,才转交 FOLLOWUP;注释、文档、文案级的标为琐碎项,由编排器在机械收尾时顺手改掉,提交拦截的全量测试兜底。风险在于分级误用,实现类问题被错标成琐碎就绕过了 FOLLOWUP,所以红线写死了触碰代码逻辑的意见不得标琐碎,避免遗漏一些必要的实现修改。

复审居然比首审还贵#

三份成本记录里最反直觉的一条:文档质量门的复审用了 128k token,首审只有 81k;测试审查的重审也达到首审的 73%。

原因不难理解。复审时子代理是全新实例,它把整份文档重新读了一遍,还额外读了修订后的 diff,而修订通常只动几行,代价却接近重来一次。这是冷启动架构必然要付的一笔账,之前没意识到它在复审环节被放大了。

处理办法是给审查者加增量复审模式。复审派发时附上一轮的必须修复项清单和修订 diff,审查者只做两件事,核对这些阻塞项是否闭环,扫描修订本身有没有引入新问题,不做全量重审。

探索成本要前置,不然它会转嫁到更贵的环节#

两个需求形成了一组有意思的对照。catalog-timeout-v1 走完整流程,在设计阶段用探针脚本实测了目标状态怎么构造,执行段 649k token,全流程墙钟 82 分钟;pool-idle-error-v1 走快车道,设计阶段没做探针,执行段 1.28M token、123 分钟,其中单次 test-writer 派发就烧掉 247k。

拆开看,pool-idle 的超支主要发生在测试编写环节:测试子代理不知道怎么稳定构造出那个状态,只能在派发里反复试。设计阶段省下的探索并没有消失,而是转嫁到了测试编写,后者的单价高得多。需要说明的是两个需求内容不同,这不是受控对照,不能把差额全算在探针头上,但方向清楚,成本结构的差异也和拆解结果吻合。

现在探针先行是 /spec-design 的硬性步骤,适用于难构造状态类的需求,快车道对这类需求强制转道完整流程,判断标准就一句话:涉及超时、时序、并发、外部进程的需求不走快车道。同时给 test-writer 加了探索熔断,同一场景实跑五次仍未得到稳定的失败测试就直接报告需要人工介入,不再硬试。阈值五是拍的,合不合适还得等数据。

顺带一提,三份记录都指向同一处,测试编写加测试审查这两步占单任务成本的 70% 到 80%,这是下一轮优化的前沿。

现在的流水线#

正在渲染图表…

图里蓝色是零 token 的脚本关卡,红色是两道 hook 物理执法,黄色是流水线上唯一还跑 opus 的检查点,其余派发全部走 sonnet。

模型分工现在有数据支撑:

环节模型依据
规格与编排(阶段 1–3、/tdd-next 主会话)Fable 5需求理解与方案取舍最吃综合判断力
测试审查 test-revieweropus实测 FAIL 率 36%,唯一还用 opus 的检查点
实现审查 code-reviewersonnet实测 FAIL 零次,产出以建议项为主
文档审查 gate-reviewersonnet清单核对是收敛任务
执行 test-writer / implementersonnet规格明确后的收敛任务

欠账清单#

按老规矩,说清楚哪些验证过、哪些是推测。

v10 的改动全部未经实测:增量复审对成本的实际压降、琐碎分级的误用率、探索熔断阈值是否合适、验收型任务的错标风险,这些目前都只有设计预期。

分道流水线的收益有天花板:低风险道能省多少取决于 low 任务的占比,而实测中标准道占绝对多数,一个需求十个任务里只有三个判为 low,指望这条道大幅降本不现实。

hook 防线仍然不是绝对的: 为了不引入 jq 依赖,hook 从输入 JSON 里提取字段用的是文本匹配,格式随版本变化就可能失效。它挡的是 Agent 在目标压力下的变通,不是蓄意的绕过。

测试质量仍然是天花板:测试出自 Agent 之手,它对边界与异常路径的想象力弱于有领域经验的工程师,36% 的拦截率说明这道检查有效,但它审的仍然是 Agent 写的测试,验收标准写得越具体,这一环越靠谱。

人工确认还是最脆弱的一环:自动化程度越高,三次确认的注意力质量就越决定最终结果,工具能约束 Agent,约束不了敷衍。

写在最后#

回看这四版,真正有效的改动没有一条是坐在桌前想出来的。v7 来自别人的一篇实测报告,v9 来自一个 15 任务的真实需求,v10 来自三份成本记录。中间的 v8 是唯一凭诊断和推演做的,方向做对了,但当时提出的三个结构性提案里,有一个在拿到数据后被证明触发率太低、根本不值得做

所以这套框架的迭代方式本身也成了一条规则:不要做设计推演层面看起来更优雅的重构,等一个真实的痛点或一份可信的数据,再动手。

配置包和 README 在团队仓库里。升级之后如果遇到莫名其妙的拦截,或者觉得某个环节纯属形式,直接提出来,这套流程本身也在复盘循环里,它该被持续修订而不是被供起来。

读完了,谢谢你读到这里

有新文章时想第一时间看到?订阅 RSS