
Harness Engineering 15:从手动提示走向可停、可验、可恢复的自主 Loop
前 14 篇解决的是怎样让一次 Agent 协作更可靠:仓库是系统记录,任务有边界,完成有证据,运行可观测,会话结束后能恢复。但它们仍然默认有一个人坐在键盘前,负责决定什么时候开始、失败后再说什么、下一步做哪件事。
自主 Loop 的变化,是把这个“下一次提示”也工程化。人不再逐轮推动 Agent,而是在开始前定义目标、约束和停止规则,在结束后审查产物。控制没有消失,只是从每一步的临场干预,上移到了运行系统的设计。
自主 Loop 不是无限 while
最危险的理解是:把 Agent 调用塞进无限循环,只要没成功就继续重试。这样的系统通常会重复同一种错误、不断扩大上下文、消耗预算,甚至在外部系统中重复写入。
一个可运行的 Loop 至少包含六个变量:
| 变量 | 要回答的问题 | 最小工件 |
|---|---|---|
| 目标 | 最终世界应变成什么样 | goal contract |
| 状态 | 当前已经完成、失败和阻塞了什么 | 外部状态文件或任务表 |
| 动作 | 本轮允许 Agent 做什么 | 工具与权限清单 |
| 验证 | 哪些证据证明本轮有效 | 测试、查询、截图、评分器 |
| 停止 | 何时成功、失败或转人工 | 终止条件与升级规则 |
| 预算 | 最多可以消耗多少时间、token 和外部操作 | budget envelope |
Loop 的本质不是“重复”,而是每轮都读取真实状态,选择一个能缩短剩余距离的动作,再由独立证据判断是否继续。
可以把收敛写成一个很朴素的关系:
下一状态 = 执行器(当前状态,当前目标,允许动作)
是否停止 = 验证器(下一状态,验收条件,风险预算)
如果状态没有持久化,下一轮不知道上一轮做了什么;如果验证器和执行器没有分开,Agent 很容易把“我认为已经完成”当成证据;如果预算没有上限,错误就会被自动放大。
三种循环解决三种问题
工程上最常混淆的是目标循环、定时循环和事件循环。
| 类型 | 触发方式 | 停止方式 | 适合任务 |
|---|---|---|---|
| 目标循环 | 人给出终态 | 达标、阻塞或预算耗尽 | 修复缺陷、完成迁移、补齐测试 |
| 定时循环 | 每隔一段时间唤醒 | 人工取消或策略到期 | 巡检、日报、状态监控 |
| 事件循环 | PR、告警、工单等事件 | 事件处理完成或进入升级队列 | CI 修复、漏洞分诊、依赖更新 |
“完成支付系统”有明确终点,应该进入目标循环;“每 15 分钟检查一次部署”没有累积终点,应该进入定时循环。把长任务塞进定时器会造成每次从头分析,把巡检塞进目标循环则会让系统找不到可证明的终态。
最小可靠闭环:Maker 和 Checker 必须分开
一条可靠 Loop 至少需要两个角色。
Maker 读取任务、修改系统、运行低成本自检;Checker 在新上下文中只读取目标、变更和可验证证据,不继承 Maker 为自己辩护的推理过程。Checker 可以是确定性脚本、另一会话、不同模型,或三者的组合。
分离不是为了制造“多 Agent”的热闹,而是消除同源偏差:写出方案的模型已经沿着某条推理路径建立了信心,同一上下文里的自检更容易补充理由,而不是推翻前提。
一个实用的判定顺序是:
- 硬门槛:构建、类型、测试、策略规则必须通过。
- 行为证据:真实用户路径或外部系统状态符合目标。
- 质量评分:可维护性、解释性、性能等软目标达到阈值。
- 风险检查:权限、数据、发布范围没有越界。
软评分不能覆盖硬失败。界面再漂亮,只要关键流程没有跑通,Checker 就必须拒绝。
外部状态是 Loop 的脊柱
长循环不能把记忆留在聊天记录里。上下文会压缩、超长、重启,模型也会把失败尝试总结得过于乐观。真正可恢复的状态至少要保存:
- 当前目标版本和验收条件;
- 已尝试动作、结果和证据位置;
- 当前活动任务及其所有者;
- 剩余预算、重试次数和截止时间;
- 阻塞原因、需要谁做什么决定;
- 本轮产物的 commit、构建或部署标识。
状态更新还必须原子化。不要先写“任务完成”,再异步上传证据;应该先产生证据,再用一次状态转移把任务从 active 改为 verified。进程在两步之间崩溃时,系统宁可重复验证,也不能把半成品当成完成。
幂等性决定 Loop 能不能安全重试
代码分析和测试通常可以重复执行,发邮件、创建订单、合并 PR、删除资源却不是天然幂等。如果执行结果已经生效,但 Agent 在收到响应前断线,下一轮可能再次执行同一动作。
对有副作用的工具调用,应至少加入四层保护:
- 使用稳定的 operation id,服务端按 id 去重;
- 执行前查询真实状态,不用对话记忆猜测;
- 把“准备”和“提交”拆成两步,高风险提交等待审批;
- 把外部返回的资源 id 写入状态,再允许下一节点继续。
能重试不等于应该无脑重试。认证失败、权限拒绝、数据契约不匹配通常需要升级;网络抖动和临时限流才适合退避后重试。
预算门控:防止自动化把小错误变成大账单
循环会同时放大产出和浪费。预算不能只设 token 上限,还要覆盖:
| 预算维度 | 建议门控 |
|---|---|
| 轮次 | 同类失败连续出现 2–3 次后必须换策略或升级 |
| 时间 | 单轮和总任务都设 wall-clock deadline |
| 模型 | 默认用执行模型,架构判断和复核再升级 |
| 工具 | 限制浏览、构建、部署和外部写入次数 |
| 变更 | 限制文件数、diff 大小、影响服务范围 |
| 并行 | 并发数受审查带宽而不是机器数量约束 |
最有价值的指标不是“Loop 跑了多少轮”,而是每个已验证结果消耗多少成本,以及失败是否为下一轮提供了新信息。
从一个日常任务开始
第一次落地不需要自动合并几百个 PR。可以从“每天检查最近失败的 CI,并为可复现问题生成修复草案”开始:
- 触发器每天运行一次,读取最近失败列表。
- 去重器过滤已经处理过的 commit 和失败签名。
- Maker 在隔离工作区复现失败,只修改一个问题。
- Checker 在干净环境重新运行失败测试和相关回归。
- 通过后创建草案 PR;涉及依赖大升级、权限或数据迁移时转人工。
- 将失败签名、修复、证据和 PR 写回状态。
运行一周后再看:误报率、一次通过率、人工接管率、平均成本、重复失败比例。只有这些指标稳定,才值得增加并发和外部写权限。
Loop 成熟度不是自动化程度,而是失控时的可解释程度
一个成熟 Loop 并不追求完全没人管,而是任何时刻都能回答:它为什么醒来、正在追什么目标、基于什么状态做出动作、谁验证过、还剩多少预算、下一次失败会去哪里。
Harness 让单轮可靠,Loop 让可靠的单轮持续发生。真正的跃迁不是 Agent 更勤奋,而是系统第一次拥有了外部状态、独立判断和明确刹车。