让编程智能体把每次失败变成回归测试
文章提出让智能体在失败后追查根因并留下测试,避免仅靠重试、重启等操作恢复运行。核心是将故障经验持久化,减少新会话因缺少上下文而重复犯错。
完整中文译文
当 AI 编程 Agent 遇到错误时,最省事的做法就是先绕过去:重试、重置、重新处理。这样能让今天的运行顺利通过,却也让同样的故障下周再次发生。自退火循环(self-annealing loop)是一条简短的规则,它改变了 Agent 在出错后的处理方式,让系统每经历一次故障,就更难被同样的问题击倒。
为什么 Agent 默认会优先恢复
让编程 Agent 完成一项工作,等于在暗示它:完成工作就是目标。某个步骤失败后,回到“已完成”状态的最短路径就是恢复:再运行一次、清理卡住的队列、重启服务,或者加一次重试。单独看,每种做法都有道理。但放在一起,它们会掩盖真正的原因。而下一次会话又会从全新的上下文开始,于是除了绕过问题的临时办法,关于这次故障的其他信息都没有留下来。
工程师在压力下也会这么做。区别在于,人通常会记住教训。Agent 不会,除非你给它一个记录教训的地方,再加上一条必须记录的规则。
退火是冶金学术语,指通过加热和冷却金属,释放其内部应力,让它变得更坚韧。在由 Agent 运行的系统里,故障就是那次“加热”。这个循环有五个步骤,顺序正是关键:
-
诊断根因。 不断追问“为什么”,直到找到一个只要改变就能防止故障重演的因素。“请求超时了”是症状。“健康检查把任何超时都当成商店故障”才是原因。
-
在代码、配置或基础设施中修复问题,而不是修改数据。
-
补上本该捕获这个问题的测试,避免修复悄悄退化失效。
-
把规范写下来。 在指令文档的
Edge cases & learnings部分补充一条;如果能通过机械化检查识别这种模式,再加一条 lint 规则。 -
最后再恢复:重新处理卡住的条目、重启、重新运行。
恢复仍然要做,只是要放在最后,等根因无法再引发同样的故障之后再做。还有一条值得加上的规则:如果 Agent 无法在本次会话中完成前四步,就必须明确说明,并将该事项保持为未解决状态,而不是悄悄恢复运行。
把规则放在 Agent 最先读取的位置
只有把这个循环写进 Agent 的常驻指令,它才会起作用,不能靠你想起来时再输入一段 prompt。在 D.O.E. 仓库中(Directive、Orchestration、Execution,即指令、编排、执行,第一篇文章中有介绍),它放在 CLAUDE.md 里。下面是一个精简示例:
## When something fails (self-annealing)
1. Diagnose the root cause. A symptom is not a cause.
2. Fix it in code/config/infra.
3. Add the test that would have caught it.
4. Add the standard to the directive's "Edge cases & learnings"
(and a lint if it is mechanically detectable).
5. Only then recover. Never recover first and call it fixed.
Record each pass in the anneal log: date, symptom, cause, fix, test.
退火日志比看起来更重要。它能让你发现同一个地方是否反复出错,而这通常意味着第一次修复处理的只是症状。
一个真实案例:四天内的三轮退火
我们用一台小型 Linux 主机上的每日 Agent 任务来运维商店。脚本每天检查商店前台和结账端点,只要其中任意一个没有返回响应(HTTP 状态码为 000),就会发出一条 ATTENTION 提醒:Agent 必须先诊断并修复根因,才能做其他事情。
第 1 轮。 ATTENTION:商店前台无法访问。根因:主机自身的上行连接断了,网关和 DNS 都出了问题;商店本身正常。留下的改进:在发出 ATTENTION 前增加“主机离线”检查,为 curl 增加超时和重试,并修复了一个因日志文件存在未提交修改而失败的 git pull。
第 2 轮。 指标任务因名称解析错误而失败。根因:同一台主机上的 DNS 不稳定。留下的改进:仅对传输错误执行带退避的重试。
第 3 轮。 再次出现 ATTENTION,商店前台无法访问。根因:LAN 正常,但 WAN 大量丢包,而那一次离线探测恰好成功,导致第 1 轮增加的检查没有触发。留下的改进:把一次探测改为六次;只要失败两次或以上,就归类为“主机网络劣化”,而不是商店故障。另外,在商店运维指令文档中补充了一条规范,并注明这是一个需要人工处理的硬件问题。
第 1 轮确实修复了问题,但仍未覆盖全部情况,这很正常。日志提供的是历史记录:到了第 3 轮,同样的告警又出现了,原因也属于同一类,这就把排查方向指向了检查逻辑本身,而不是商店。
最后一轮还展示了这个循环面对现实的退出方式:有些根因无法通过代码解决。当问题出在不稳定的网络链路上,正确的规范是“准确分类并通知人工”,而不是再加一次重试。
宣布一轮处理完成前,要检查什么
你能否用一句不包含“有时”或“不稳定”的话说清原因?如果不能,就继续追问为什么。
是否有一个测试,在旧代码上失败、在新代码上通过?如果写不出来,就补充 lint 规则或指令文档中的规范,并说明原因。
经验是否写进了下一次会话最先读取的文件,而不是只留在 commit message 或聊天记录中?
恢复是否发生在修复之后?恢复结果是否经过验证,例如收到 HTTP OK、哈希匹配,或者核对了行数?
从一份指令文档开始
尝试这个方法不需要引入框架。把上面的内容加进 CLAUDE.md、.cursorrules 或 AGENTS.md,再为最容易出故障的任务写一份指令文档,其中包含 Edge cases & learnings 部分。一周后,读一读这一节。你会看到一份故障清单,而每个故障现在都有对应的测试、规则或 lint 检查来防范。
免费的 Agent Ops Kit Lite(MIT 许可证)包含写有这个循环的 CLAUDE.md、指令模板,以及一个 secrets linter。它还可以作为 Claude Code plugin 安装:/plugin marketplace add skibuff22/agent-ops-kit-lite。
本文最初发表于 calyx-ai.net。利益披露:我销售完整版 Agent Ops Kit(截至 11 月 2 日,首发价为 19 美元,之后为 29 美元),其中增加了自退火、缺陷修复和 Agent 治理指令、三份示例指令、七个执行脚本,以及多 Agent 模板。上面的免费 Lite 工具包可以独立使用。本文在 AI 辅助下起草;故障列表来自我们自己的运行日志。
如需采取进一步措施,可以考虑屏蔽此人和/或举报滥用行为。