Loop Engineering 实战:把 AI 任务改造成可验证、可停止的自动循环。

这不是一篇概念介绍,而是一套可以直接照着做的 Loop Engineering 教程。

我们会把一句普通指令:

修复登录接口的并发问题。

逐步改造成一个真正可运行的 Loop:AI 自己读取代码、修改实现、运行测试、根据失败继续修复;验证通过后停止,连续无进展时交还给人。

最终要得到的不是“让 AI 一直跑”,而是下面这套受控闭环:

目标 → 执行 → 获取证据 → 独立验证 → 继续 / 停止 / 人工接管
        ▲                         │
        └──────── 反馈与状态 ──────┘

一、先判断任务是否适合做成 Loop

并不是所有任务都应该循环执行。开始前先问四个问题:

  1. 目标是否明确?
  2. 过程是否需要根据反馈动态调整?
  3. 结果是否可以被独立验证?
  4. 失败是否可回滚,或者能在隔离环境中执行?

适合 Loop 的任务:

  • 修复 bug,直到指定测试通过;
  • 补充测试,直到覆盖率达到要求;
  • 批量迁移代码,并逐批运行回归测试;
  • 调研多个来源,直到关键结论完成交叉验证;
  • 生成结构化数据,直到 schema 校验通过;
  • 反复优化页面,直到视觉、性能和可访问性检查达标。

不适合 Loop 的任务:

  • 解释一段代码、改写一段文案等单轮任务;
  • 完全依赖个人审美、没有验收标准的任务;
  • 路径固定、普通脚本就能稳定完成的重复操作;
  • 无法隔离且错误代价很高的生产操作。

一个简单判断方法是:

目标明确、路径不确定、结果可验证,适合 Loop。
路径完全确定,优先写普通脚本。


二、第一步:把需求改写成“任务契约”

普通 Prompt 往往只描述“做什么”,Loop 还必须明确“怎样算完成”和“什么时候必须停”。

不要这样写:

修复登录接口的并发问题,修好为止。

改成下面这种任务契约:

目标:
修复登录接口在并发刷新令牌时偶发创建两个有效会话的问题。

允许范围:
- 可以修改 src/auth/ 和 tests/auth/。
- 可以运行测试、类型检查和 lint。
- 可以创建临时文件,但结束前必须清理。

禁止范围:
- 不修改公开 API 的请求和响应结构。
- 不修改数据库表结构。
- 不连接生产环境。
- 不提交代码、不推送远程仓库。

完成标准:
1. 新增一个能够稳定复现该问题的并发测试。
2. 修复前该测试失败,修复后通过。
3. auth 模块现有测试全部通过。
4. 类型检查和 lint 通过。
5. 最终 diff 只包含与该问题直接相关的改动。

停止规则:
- 最多执行 8 轮。
- 连续 3 轮没有新增有效证据或测试结果改善时停止。
- 需要修改数据库结构或公开 API 时停止并询问。
- 发现疑似凭据、生产数据或高风险操作时立即停止。

任务契约是整个 Loop 的控制面。后面的 Agent、验证器和停止逻辑都围绕它运行。

可复制的任务契约模板

【目标】
要解决什么问题,最终交付什么。

【已知上下文】
现象、相关文件、错误信息、已有结论。

【允许操作】
可以读取、修改、执行和调用哪些工具。

【禁止操作】
不能触碰的目录、接口、环境和高风险动作。

【完成标准】
列出可被测试、检查或复核的客观条件。

【每轮要求】
读取状态 → 选择一个最小动作 → 执行 → 验证 → 更新状态。

【停止规则】
最大轮数、预算、无进展阈值、高风险边界、人工接管条件。

【最终输出】
修改内容、验证结果、遗留问题、证据位置。

三、第二步:为 Loop 准备外部状态

长任务不能只依赖聊天上下文。上下文会变长、被压缩,也可能把早期错误带进后续每一轮。

最小实现只需要一份状态记录:

LOOP_STATE
├── goal              当前目标
├── acceptance        完成标准
├── current_status    当前处于哪一步
├── confirmed_facts   已由工具或测试确认的事实
├── attempts          已尝试方案及结果
├── blockers          当前阻塞
├── next_action       下一轮建议动作
├── budget            已用轮数 / 最大轮数
└── last_evidence     最近一次验证证据

在代码项目中,可以把它放进任务系统、Agent 内置 todo,或者临时状态文件。重点不是文件名,而是每轮都必须遵守同一套读写顺序:

每轮开始:读取目标、完成标准、上轮证据和剩余预算
每轮结束:记录做了什么、得到什么证据、下一步是什么

推荐的状态内容:

当前轮次:3 / 8
当前假设:会话创建缺少同一 refresh token 维度的互斥控制
本轮动作:新增 20 个并发请求的回归测试
工具结果:修复前 10 次运行中有 7 次产生两个有效会话
已确认事实:问题可以在本地测试环境稳定复现
失败尝试:仅增加事务隔离级别,问题仍存在
下一步:检查会话创建前的 token 消费是否为原子操作
是否有进展:是

不要把模型的猜测写成“已确认事实”。只有测试输出、代码、日志、接口响应等外部证据才能进入 confirmed facts。


四、第三步:把执行者和验证者分开

Loop 最常见的错误,是让同一个 Agent 一边产出,一边宣布自己已经完成。

正确做法是把职责拆成 Maker 和 Checker:

                 ┌─────────────────────────────┐
                 │         任务契约             │
                 └──────────────┬──────────────┘
                                ▼
┌──────────────┐        ┌──────────────┐        ┌──────────────┐
│ Maker        │ ─────► │ 客观验证工具  │ ─────► │ Checker      │
│ 分析与修改    │        │ 测试/lint/type│        │ 独立验收      │
└──────┬───────┘        └──────────────┘        └──────┬───────┘
       ▲                                                │
       └────────── 不通过:返回证据和差距 ───────────────┘
                                                        │
                                      通过:输出结果并停止

Maker 的职责

  • 阅读目标和当前状态;
  • 每轮选择一个最小、可验证的动作;
  • 调用工具获取真实反馈;
  • 根据反馈修改方案;
  • 不自行宣布最终完成。

Checker 的职责

  • 只依据任务契约和验证证据验收;
  • 不因为 Maker 说“已完成”就通过;
  • 明确指出未满足的标准;
  • 输出 PASSRETRYESCALATE

验证优先级

验证器不一定是另一个大模型。优先级应该是:

确定性程序验证 > 结构化规则 > 独立模型评审 > Maker 自我评价

代码任务优先使用:

  • 单元测试、集成测试、端到端测试;
  • 类型检查;
  • lint 和格式检查;
  • schema 校验;
  • 安全扫描;
  • diff 范围检查;
  • 覆盖率报告。

只有“可读性是否足够”“解释是否清晰”这类难以程序化的部分,再交给独立 Checker 模型。

可复制的 Checker 模板

你是独立验证者,不参与修改。

请只根据任务契约、当前 diff 和工具输出判断结果。
逐项检查每一条完成标准,不接受执行者的自我陈述作为证据。

输出只能是以下三种状态之一:
- PASS:所有完成标准都有充分证据。
- RETRY:仍可继续修复;列出未通过项、证据和建议验证方式。
- ESCALATE:需要越过权限边界、目标存在歧义或继续执行风险过高。

禁止为了让任务结束而降低标准。

五、第四步:设置每轮只做一个“最小可验证动作”

Loop 不是让 Agent 一口气做完全部工作。每轮动作越大,失败后越难判断是哪一步出了问题。

推荐的单轮结构:

Observe → Choose → Act → Verify → Record
观察现状 → 选择动作 → 执行 → 验证 → 记录

在登录并发问题中,合理的轮次可能是:

  1. 找到会话创建和 refresh token 消费路径;
  2. 编写稳定复现问题的并发测试;
  3. 运行测试,确认测试在修复前会失败;
  4. 修改原子性控制;
  5. 运行定向测试;
  6. 运行 auth 模块回归测试;
  7. 执行类型检查和 lint;
  8. Checker 对照任务契约审查最终 diff。

不合理的单轮动作是:

阅读全部代码、重构认证架构、修复所有问题、补齐测试并发布。

这会扩大修改范围,也会让验证结果失去定位能力。


六、第五步:配置停止条件和人工接管

一个 Loop 至少要有四类出口:

                           ┌─► PASS:全部标准达成
                           │
本轮执行 ─► 验证结果 ──────┼─► RETRY:未达标但仍有进展
                           │
                           ├─► ESCALATE:需要人做决策或授权
                           │
                           └─► ABORT:超出轮数、预算或安全边界

必须设置的停止条件

  • 最大轮数:防止无限循环;
  • 无进展阈值:连续多轮没有新证据或指标改善;
  • 成本预算:限制 Token、工具调用或外部 API 成本;
  • 权限边界:触碰生产环境、删除、发布、支付等动作时停止;
  • 范围漂移:需要修改任务契约之外的模块时停止;
  • 目标冲突:完成标准互相矛盾时停止;
  • 不可验证:缺少环境、依赖或数据,无法证明结果时停止。

“无进展”不能只看 Agent 有没有输出新文字。更可靠的判断是:

  • 测试失败数是否减少;
  • 是否出现新的、可验证的事实;
  • 是否排除了一个假设;
  • 是否完成一个任务契约中的检查项;
  • 是否只是重复相同工具调用和相同修改。

七、第六步:限制工具和运行环境

Loop 会放大能力,也会放大错误。运行前要先缩小它能够造成的影响。

代码任务建议使用:

代码仓库
  └── 独立分支或 Git Worktree
       └── Agent 可写目录
            ├── 允许:读取代码、修改指定模块、运行本地测试
            ├── 需确认:安装依赖、修改配置、访问外部服务
            └── 禁止:推送主分支、生产部署、读取真实凭据

最低限度的安全配置:

  • 在独立分支、Worktree、容器或沙箱中执行;
  • 工具使用白名单,而不是默认开放全部权限;
  • 凭据只通过受控引用使用,不写入上下文和日志;
  • 删除、发送、发布、合并、部署等操作保留人工确认;
  • 每次工具调用都记录参数、结果和退出状态;
  • 支持中断和回滚。

如果任务不能安全地失败,就不要让它无人值守地循环。


八、在积木中启动一个 Loop

在积木中,可以直接用“启动循环”“深度任务”“持续执行”等表达触发 Loop 工作流。不要只说“持续做直到完成”,而要把前面的任务契约一起提供。

可直接复制下面这段,再替换其中内容:

启动循环完成以下任务。

目标:
修复登录接口并发刷新令牌时偶发创建两个有效会话的问题。

完成标准:
1. 新增可稳定复现问题的并发回归测试。
2. 修复前测试失败,修复后通过。
3. auth 模块全部测试通过。
4. 类型检查和 lint 通过。
5. 最终修改不超出 src/auth/ 和 tests/auth/。

循环规则:
1. 每轮开始先读取任务状态和上一轮证据。
2. 每轮只选择一个最小可验证动作。
3. 执行后必须运行对应验证,不能依据自我描述判断成功。
4. 每轮结束更新:已确认事实、失败尝试、验证结果、下一步。
5. 由独立 Checker 对照完成标准验收。

权限:
- 允许读取和修改指定目录、运行本地测试和静态检查。
- 禁止提交、推送、部署、连接生产环境或修改数据库结构。

停止规则:
- 最大 8 轮。
- 连续 3 轮没有有效进展时停止并报告阻塞。
- 需要超出权限或修改公开 API 时先询问。
- 所有标准有客观证据后才允许结束。

最终输出:
- 根因;
- 修改文件;
- 每项完成标准对应的验证证据;
- 未解决问题和风险。

启动后,人不需要每轮都告诉 Agent“继续”,但仍然应该关注三个节点:

  1. 第一次计划:确认它没有误解任务;
  2. 触及边界时:决定是否扩大权限或修改契约;
  3. 最终验收时:查看 Checker 的证据,而不是只看结论。

九、完整 Loop 的控制逻辑

下面的伪代码展示了一个工具无关的最小实现:

state = load_state()

for step in range(MAX_STEPS):
    if budget_exceeded(state):
        return abort("预算已耗尽", state)

    action = maker.choose_minimal_action(
        goal=state.goal,
        acceptance=state.acceptance,
        evidence=state.confirmed_evidence,
        failed_attempts=state.failed_attempts,
        allowed_tools=state.allowed_tools,
    )

    observation = sandbox.execute(action)
    deterministic_checks = run_required_checks()

    verdict = checker.verify(
        acceptance=state.acceptance,
        observation=observation,
        checks=deterministic_checks,
        diff=current_diff(),
    )

    state.record(action, observation, deterministic_checks, verdict)
    save_state(state)

    if verdict.status == "PASS":
        return build_final_report(state)

    if verdict.status == "ESCALATE":
        return ask_human(verdict.reason, state)

    if state.no_progress_rounds >= 3:
        return ask_human("连续三轮无进展", state)

return abort("达到最大轮数", state)

实现时不必照抄代码,但下面几个结构不能缺:

  • 循环开始前读取状态;
  • 工具在受控环境中执行;
  • 验证与生成分离;
  • 每轮保存证据;
  • 成功、升级、超限都有明确出口。

十、怎样判断 Loop 真的在工作

不要用“Agent 看起来很忙”作为判断标准。观察下面这些信号。

健康信号

  • 每一轮都有新的工具证据;
  • 失败范围逐步缩小;
  • 已确认事实与模型假设明确分开;
  • 下一步动作与上轮结果直接相关;
  • 修改范围保持在任务契约内;
  • Checker 能明确指出通过或不通过的依据;
  • 达到停止条件时系统真的会停。

失控信号

  • 反复读取同一批文件却没有新结论;
  • 多轮执行相同命令并得到相同失败;
  • 没有测试结果,却声称问题已经修复;
  • 为绕过失败而删除测试、降低阈值或扩大范围;
  • 状态中充满推测,缺少工具输出;
  • 子 Agent 越开越多,但没有清晰分工;
  • 已超出预算或权限,仍然继续运行。

发现失控信号后,不要简单追加一句“请认真完成”。应该中止当前循环,检查任务契约、验证器和权限设计哪里出了问题。


十一、从最小 Loop 开始,不要一上来堆多 Agent

第一次实践建议只保留四个组件:

1 个 Maker
1 组确定性检查
1 个 Checker
1 份外部状态

先让这个最小闭环稳定运行,再考虑增加:

  • 定时器或事件触发;
  • 多个并行 Worker;
  • 专项安全审查 Agent;
  • 跨任务长期记忆;
  • 根据历史 Trace 自动优化 Prompt 和工具。

复杂度应该来自真实需求,而不是为了让系统看起来更“Agentic”。


十二、开始前的最终检查清单

  • 任务满足“目标明确、路径不确定、结果可验证”;
  • 已写出任务契约,而不只是普通 Prompt;
  • 每一条完成标准都有对应证据来源;
  • Maker 不负责最终判定完成;
  • 优先使用测试和规则进行确定性验证;
  • 每轮只执行一个最小可验证动作;
  • 状态保存在聊天上下文之外;
  • 设置了最大轮数、预算和无进展阈值;
  • 运行环境隔离,工具遵循最小权限;
  • 高风险和不可逆操作保留人工确认;
  • 有明确的 PASS、RETRY、ESCALATE、ABORT 出口;
  • 最终报告能把每项结论对应到真实证据。

Loop Engineering 的关键不是让 AI “永远继续”,而是把下面这句话真正落到系统里:

有证据就推进,未达标就带着反馈重试,触及边界就停下,无法可靠判断就把控制权交还给人。

当任务契约、外部状态、独立验证、权限边界和停止条件都被明确设计出来时,Agent 才从“需要人不断催促的聊天助手”,变成一个可以持续工作、可以验收、也可以安全停止的工程系统。

1 个赞