阅读时间 22 分钟

Loop Engineering:别再提示 Agent,去设计提示 Agent 的系统

通读《Loop Engineering: The Anthropic Playbook for Designing Systems That Prompt Your Agents》后,我看到的不是又一个提示词技巧,而是一层新的工程分工:人不再逐句提示 Agent,而是设计一个能发现任务、交给 Agent、独立校验、沉淀状态、再次调度的循环系统。
Loop Engineering:别再提示 Agent,去设计提示 Agent 的系统

我通读了这份 11 页的 IEEE 风格工作笔记:《Loop Engineering: The Anthropic Playbook for Designing Systems That Prompt Your Agents》。它的标题看起来像又一个 AI 黑话,但读完我反而觉得,这个词值得被认真对待。

因为它说的不是“怎么把 prompt 写得更漂亮”。它说的是:别再把自己放在每一次 Agent 调用的键盘前,去设计那个会自动提示 Agent 的系统。

这一步变化很大。Prompt engineering 关心一句话怎么写;context engineering 关心这轮窗口里该塞什么;harness engineering 关心一次 Agent 运行该配哪些工具、权限和完成条件;Loop engineering 再往上一层,关心的是:怎样让一次运行变成可重复、可校验、可记忆、会自己转起来的工作系统。

换句话说,人从“内循环里的提示者”,变成了“外循环上的系统设计者”。这篇文章我不打算翻译论文,而是把它拆成一个工程师真正能带走的模型:四层栈、五步循环、六个部件、一个最关键的生成器/评估器分离,以及四笔迟早会来收账的隐性债务。

先给结论:Loop Engineering 是第四层,不是 Prompt Engineering 的升级包

这份 PDF 最有价值的地方,是把几个容易混在一起的概念摆成了一层一层的栈。

架构图

每上一层,关注的单位都变大一圈:

层级 关心什么 核心问题
Prompt Engineering 一句好提示词 我要怎么告诉模型?
Context Engineering 当前窗口 这一轮该放进哪些上下文?
Harness Engineering 一次运行 工具、动作、权限、done 条件是什么?
Loop Engineering 反复运行的系统 怎么让它自己一轮又一轮地跑?

这个栈解释了为什么很多人刚听到 Loop Engineering 会误解。它不是“我写一个 while true 让 Agent 重跑”。那只是重复执行。真正的 loop 不是空转,而是每一轮都在做一组完整动作:发现这轮该做什么,隔离地交出去,独立验证,把状态写到对话之外,再决定下一次什么时候继续。

这也是它和自动化脚本的分水岭。脚本通常执行固定指令;loop 会把上一轮的输出变成下一轮的输入。脚本跑完就结束;loop 会携带状态,明天接着今天的未完成项继续转。

一轮 Loop 有五个动作:少一个,就会变形

PDF 把一次 loop 拆成五个动作:Discovery、Handoff、Verification、Persistence、Scheduling。我更愿意把它翻成五句话:

  1. 发现:系统自己找出这轮值得处理的工作。
  2. 交接:把工作交给合适的 Agent,并隔离执行现场。
  3. 校验:换一个独立视角判断结果能不能过。
  4. 沉淀:把状态写到对话窗口之外。
  5. 调度:让下一轮在正确时间自己发生。

架构图

这里面最容易被低估的是 Discovery。很多团队说自己做了 loop,其实只是把“执行”自动化了,早上还是人来决定“今天修哪三个问题”。这当然有价值,但它还没有把人从内循环里拿出去。真正的 loop 至少要能自己读失败的 CI、打开的新 issue、昨天合并的 commit、上一次留下的 state file,然后给出今天值得处理的候选项。

第二个关键是 Handoff。只要你允许多个 Agent 并行,隔离就不再是洁癖,而是生存条件。两个 Agent 在同一个工作目录里改同一个文件,和两个工程师同时在同一行上手搓冲突没有本质区别。PDF 里推荐的做法很朴素:一项工作一个 git worktree。不要让并行变成互相踩脚。

第三个也是整篇里最重要的:Verification。一个 loop 有没有工程价值,不看它能跑多久,而看它有没有一个东西能对它说“不”。如果每一轮都是写代码的 Agent 自己宣布“我做得不错”,这不是闭环,这是自我肯定机。

Persistence 则解决另一个常见幻觉:不要把记忆放在聊天窗口里。上下文窗口会清空,会被截断,会被下一轮污染。真正能让 loop 明天接着今天干的,是 state.md、Linear board、PR、inbox、commit 这些在对话之外存在的东西。论文里有一句判断很准:Agent 会忘,repo 不会。

最后才是 Scheduling。没有调度,前面四步做得再漂亮,也只是“人手动运行的一次流程”。调度可以是本地循环命令,可以是桌面自动化,可以是 cloud routine,也可以是 GitHub Actions cron。选哪种不重要,重要的是你要知道代价:本地调度能看到本机状态,但机器关了就停;云端调度能在人睡觉时继续跑,但通常拿不到本地开发服务器和本机文件。

六个部件:把五个动作落到工程系统里

五个动作回答的是“这一轮发生什么”。六个部件回答的是“系统要长什么器官,才能让这一轮发生”。

部件 它是什么 对应动作
Automations schedule 或 event trigger Scheduling
Worktrees 给并行 Agent 的隔离目录 Handoff
Skills 项目知识的持久化文件 Discovery
Connectors MCP / 外部系统接口 Discovery / Persistence
Sub-agents 写的人和审的人分开 Verification
Memory 磁盘上的持久状态 Persistence

我最喜欢 PDF 里对 Skill 的定义:它是在偿还 intent debt。所谓 intent debt,就是你每次开一个新对话,都要重新解释“这个项目是什么、规矩是什么、坑在哪里、哪些地方不能碰”的成本。Prompt 是一次性叮嘱,Skill 是可维护的项目知识。Loop 如果靠一大坨 cron prompt 活着,迟早会腐烂;靠 Skill 活着,才有机会持续演化。

Connectors 决定 loop 的视野半径。一个只能读本地文件系统的 loop 很小;能读 issue tracker、CI、Slack、数据库、staging API 的 loop 才开始像一个团队成员。MCP 的意义也在这里:它不是炫技接口,而是把 loop 从“我能在 repo 里跑”扩展到“我能看见外部世界发生了什么”。

Memory 则决定 loop 是否能积累。没有 memory 的 loop,每天早上都像刚入职一样重新理解世界;有 memory 的 loop,才会知道昨天发现了什么、哪些已经处理、哪些进了 inbox、哪些 PR 等人看。

全文最硬的一点:生成器和评估器必须分开

PDF 里最值得抄进工程规范的观点,是 generator/evaluator separation。

写代码的 Agent,不应该负责判断自己写得好不好。

这不是因为 Agent 不够聪明,而是因为结构上有偏差。写代码的上下文里,已经塞满了它为什么这样写的理由。它回头看自己的 diff,看见的不是冷冰冰的结果,而是一路自我说服的过程。让它给自己打分,就像让作者给自己的稿子做终审。

架构图

一个好的 evaluator 有四个特征。

第一,它默认怀疑。不是“帮我看看有没有问题”,而是“假设这段代码是坏的,直到证明它不是”。语气差异背后是验收姿态的差异。

第二,它用动作验证,而不只是读代码。前端任务不能只看 JSX 看起来对不对,要打开页面、点按钮、截屏、检查 DOM、跑测试。后端任务也一样,能执行就执行,能打真实接口就打真实接口。判断行为,不判断意图。

第三,它最好和 generator 不是同一个模型或同一组指令。换一个模型不总是必要,但换一个视角非常必要。否则你只是让同一个盲点换了件衣服回来。

第四,停止条件要清楚。比如“test/auth 全部通过,lint 干净,登录按钮在浏览器里点击后能进入 dashboard”。不要把 stop condition 写成“修好这个问题”。太抽象的完成条件,会让 loop 把“看起来差不多”当成“已经完成”。

这里其实是一个老原则的新包装:maker-checker。银行里录入大额转账的人和审批的人不能是同一个;loop 里写代码的人和放行的人也不该是同一个。区别只是,现在 maker 和 checker 都可以是 Agent,但它们必须结构性分离。

五种坏 Loop:每一种都是少了一个动作

PDF 很好的一点,是没有把 loop 写成万能机器。它列了一组反模式,刚好对应前面的五个动作。

少掉的动作 坏 loop 症状 修法
Verification 点头 loop 跑了几百轮,从没拒绝过自己 独立 evaluator
Persistence 失忆 loop 每天重复发现同一件事 写 state file / board
Scheduling 手动 loop demo 那天很惊艳,后来没人记得跑 真实 timer / trigger
Discovery 盲 loop 人每天早上仍在喂任务清单 把发现逻辑写成 Skill
Handoff 缠线 loop 并行后冲突一团乱 一事一 worktree

这张表的残酷之处在于:最容易产出可见 demo 的,恰恰是 Discovery 和 Handoff;最容易被省掉的,恰恰是 Verification、Persistence 和 Scheduling。也就是说,一个粗糙 loop 最容易保留“看起来会干活”的部分,省掉“让它长期安全”的部分。

如果你看到一个 loop 跑了很久,但从来没有主动说过“不行,这个结果不能过”,那它大概率没有真正的校验。真实工作里不可能几百次都全对。一个从不拒绝自己的系统,不是在稳定,是在失真。

Stripe 的 Minions:可靠性来自约束,不是模型更大

PDF 提到的现实案例里,最有冲击力的是 Stripe 的 Minions:每周合并超过 1300 个机器写的 PR。这个数字很容易让人兴奋,但真正值得学的不是规模,而是它怎么把不确定性关进笼子。

它不是把所有事情都丢给 LLM。相反,LLM 醒来之前,有一段确定性 orchestrator 先做事:扫链接、拉 Jira、找文档、通过 Sourcegraph 和 MCP 定位代码。能用规则解决的,就不要交给概率模型。LLM 负责创造性产出,硬规则负责门禁。

它的管线大概是这样:

架构图

这给我的启发很直接:一个强 loop 的核心,不是“模型多强”,而是“哪些事情绝不交给模型自由发挥”。

代码搜索可以确定化,lint 可以确定化,测试可以确定化,commit 步骤可以确定化,预算上限可以确定化,PR 必须人审也可以确定化。LLM 应该被放在最需要弹性的地方,而不是被要求同时当产品经理、开发、测试、预算管理员和最终审批人。

这也解释了为什么很多小团队做 loop 会失败:他们把所有判断都塞进一个 Agent 里,然后惊讶于它越跑越飘。不是 Agent 背叛了你,是你没有给它边界。

四笔隐性债务:Loop 越顺,越容易欠

Loop engineering 最吸引人的地方,是一个人可以做出一个小团队的产出。最危险的地方,也是同一句话。

PDF 把成本拆成四类:verification debt、comprehension rot、cognitive surrender、token blowout。我翻成四笔债:

架构图

第一笔是校验债。每一个“测试绿了但其实没覆盖到”的 PR,都会把未验证产出暂存在未来。它不会马上报错,通常会在某个发布日一起爆出来。

第二笔是理解腐烂。Loop 写得越快,你脑子里的代码地图越容易落后。你没有亲手写这些代码,也没认真读它们,代码库却每天在长。几周之后,你还是 owner,但已经不是理解者。

第三笔是判断投降。系统越顺,人越容易停止拥有观点。刚开始你会认真看每个 PR;后来你只看标题;再后来你相信“反正它一直都行”。这不是节省时间,这是把判断权拱手交给一个会自信犯错的系统。

第四笔是 token 失控。Loop 会派生 helper,会重试,会在一个坏假设上转一整夜。没有 per-run budget、daily budget、max retries,它的 bug 不只是 bug,还是一张账单。

四笔债不是独立的。未校验产出越多,人越不理解;越不理解,越不想看;越不想看,loop 越长时间无人监管;无人监管越久,成本和错误越滚越大。最可怕的是,四笔债在积累时都很安静。

怎么做第一个 Loop:小,但五脏俱全

PDF 最后给了一个很务实的建议:第一个 loop 不要学 Stripe。Stripe 是终点,不是起点。第一版应该小到能被你完整理解,但必须把该有的安全部件都装上。

真正的第一版 checklist 可以很简单:

元素 你要问自己的问题
Discovery 它定时读什么?CI、issue、commit、inbox?
State file 跨轮记忆写在哪里?
Evaluator 有没有独立检查能说 no?
Isolation 并行时是不是每个 Agent 一个 worktree?
Token cap 每轮、每天、最大重试有没有上限?
Human review 哪一步会停下来给人看?

注意最后一项。Human review 不是临时脚手架,不是“等 loop 成熟后就拆掉”。恰恰相反,它是让 loop 长期可信的永久入口。你不一定每次都介入,但你必须随时能介入。

下面这个 morning triage 示例,就是按这张 checklist 搭出来的。

完整示例:用 Codex 做一个每天早上自己巡检 CI 的 Loop

只讲原则还是太飘。下面我把第一版 loop 写成一个基于 Codex CLI 的完整示例:每天早上 6 点,它读取失败的 CI、新增 issue、昨天合并的 commit,挑出值得处理的 finding;每个 finding 开一个隔离 worktree;Codex 起草修复;再用一次独立的 Codex review 做验收;最后只开 PR,不自动合并。

这不是 Stripe Minions 那种企业级管线,而是一个个人或小团队今天就能照着改造的最小闭环。下面的代码是 Codex-first 骨架,不是即插即用脚本;真实落地时,你还要补上 Codex CLI 的安装和认证、仓库权限、测试命令和通知方式。

架构图

1. 调度:先让它在固定时间醒来

第一步不是写一个巨大 prompt,而是给 loop 一个真实触发器。比如 GitHub Actions:

# .github/workflows/morning-triage.yml
name: morning-triage

on:
  schedule:
    - cron: "0 22 * * *" # UTC 22:00 = 北京时间 06:00
  workflow_dispatch:

jobs:
  triage:
    runs-on: ubuntu-latest
    timeout-minutes: 30
    permissions:
      contents: write
      issues: read
      pull-requests: write
      actions: read
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Run Codex morning triage loop
        run: ./scripts/loop/morning-triage.sh
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          MAX_READY_FINDINGS: "3"

这段 YAML 只负责两件事:到点启动,并给这次运行套上外层上限。timeout-minutes 防止整夜空转,MAX_READY_FINDINGS 防止第一版 loop 一口气开太多工作面。

这里我故意没有展开 Codex 的安装和登录。GitHub-hosted runner 默认不会凭空拥有你的 Codex 登录态;生产里更合理的做法,是在受控 runner 上预装 Codex CLI,或者在 CI bootstrap 阶段用团队批准的 secret 注入认证。

它不应该承载大量业务判断。调度器里塞满 prompt,是 loop 腐烂的开始,因为没人会认真维护 cron 里的那坨自然语言。

2. Discovery:把“怎么找事”写成 Skill

真正的发现逻辑,应该放进 Skill。Skill 是可读、可改、可 review 的项目知识。

<!-- .codex/skills/morning-triage/SKILL.md -->
---
name: morning-triage
description: 每天早上读取 CI、issue、commit,找出值得处理的工程 finding。
---

## Read

- 过去 24 小时失败的 CI run
- 过去 24 小时新开的 issue
- 昨天合并到 main 的 commit
- 上一次留下的 `state/triage.md`

## Judge

保留满足任一条件的 finding:

- 阻塞 main 分支
- 影响发布
- issue 有明确复现步骤
- commit 引入了可疑失败

跳过这些噪音:

- 没有复现信息的抱怨
- 已经有 PR 处理的重复问题
- flaky 且三次重跑后恢复的 CI

## Write

把结果写入 `state/triage.md`,每条 finding 必须包含:

- id
- source
- priority
- hypothesis
- next_action
- status

## Stop

不要自动合并。不要删除代码。信心不足的 finding 写入 `state/inbox.md`。

这里的 Stop 很重要。很多自动化失败,不是因为它不会做事,而是因为它不知道哪里不该做。这个边界不能指望 Agent 自己猜,必须由工程师写进系统。

如果你的 Codex 环境不会自动发现 repo-local skills,就把这个 Skill 安装到 $CODEX_HOME/skills,或者在脚本里显式读取这份文件并塞进 prompt。示例里用 repo 路径,是为了让团队能把 Skill 和代码一起 review。

3. Persistence:让状态活在对话之外

Skill 的输出不是聊天回复,而是磁盘上的状态文件:

<!-- state/triage.md -->
| id | source | priority | hypothesis | next_action | status |
|---|---|---:|---|---|---|
| auth-ci-4821 | CI #4821 | P0 | 登录测试在 token refresh 后失败 | 修复 refresh mock | ready |
| issue-913 | issue #913 | P1 | 空项目打开 dashboard 报错 | 加空态保护 | ready |
| dep-audit | commit a3f91c | P2 | 依赖升级引入 warning | 先观察 | inbox |

这就是 loop 的记忆。明天早上它会先读这个文件,知道哪些还没处理,哪些已经开了 PR,哪些被人类放进了 inbox。没有这个文件,loop 每天都在重新入职。

4. Handoff:每个 finding 一个 worktree

现在才轮到执行。一个简单的脚本可以把 ready 的 finding 拆出去:

#!/usr/bin/env bash
# scripts/loop/morning-triage.sh
set -euo pipefail

codex --ask-for-approval never exec \
  -C "$PWD" \
  -s workspace-write \
  -o codex-triage-summary.md \
  - <<'PROMPT'
Use the morning-triage skill at .codex/skills/morning-triage/SKILL.md.
Read CI, issues, commits, and state/triage.md.
Update state/triage.md and state/inbox.md.
Do not modify application code in this discovery step.
PROMPT

这一步只做 Discovery 和 Persistence:让 Codex 读外部信号,更新 state/triage.md,但不碰业务代码。把“找事”和“做事”拆开,后面才容易验证。

接下来,每条 ready finding 都交给一个独立 worktree:

python scripts/loop/list-ready-findings.py state/triage.md |
head -n "${MAX_READY_FINDINGS:-3}" |
while read -r finding; do
  branch="loop/${finding}"
  worktree="../worktrees/${finding}"

  if [ -d "$worktree" ]; then
    echo "skip existing worktree: $worktree"
    continue
  fi

  git worktree add "$worktree" -b "$branch"

  (
    cd "$worktree"
    codex --ask-for-approval never exec \
      -C "$PWD" \
      -s workspace-write \
      -o codex-generator-summary.md \
      - <<PROMPT
Fix finding ${finding}.
Keep the change minimal.
Run the relevant tests.
Do not commit.
PROMPT
  )
done

这里最朴素、也最关键的设计是一行:git worktree add。它让每个 Codex run 有自己的工作台。没有隔离,并行就是灾难预演。

5. Verification:让 Codex review 专门挑错

执行用的是 Codex,验收也可以用 Codex,但必须是另一次独立调用。不要让刚写完代码的那次上下文顺手宣布胜利。

给 review 单独一份指令:

<!-- .codex/evaluator-review.md -->
# Role

你是 adversarial evaluator。默认假设代码是坏的,直到证据证明它是好的。

# Check

1. 运行相关测试,贴出真实输出。
2. 运行 lint 或 typecheck,贴出真实输出。
3. 检查 finding 是否真的被解决,而不是只改了测试。
4. 检查边界条件:空数据、重复请求、权限不足、网络失败。
5. 如果是前端问题,用浏览器打开页面,点击复现路径,截图或描述 DOM 状态。

# Verdict

只能输出 `PASS` 或 `REJECT`。

`REJECT` 必须列出原因和下一步建议。

然后对每个 worktree 调 codex exec review

(
  cd "$worktree"
  codex --ask-for-approval never exec review \
    --uncommitted \
    -o evaluator-report.md \
    - < ../../.codex/evaluator-review.md
)

codex exec review --uncommitted 会针对当前 worktree 里的未提交改动做 review。它和 generator 是两次独立 Codex 调用:前一次负责生成,后一次负责反对。

这里的关键不是“多跑一次 Codex”,而是“换一种姿态”。Generator 的姿态是完成任务;Evaluator 的姿态是证明它还没完成。一个负责造,一个负责拆,这个结构比任何单句 prompt 都可靠。

6. Human review:只开 PR,不自动合并

最后一步故意留给人:

if head -n 1 evaluator-report.md | rg -q '^PASS$'; then
  git add .
  git commit -m "fix: ${finding}"
  git push -u origin HEAD
  gh pr create \
    --head "$branch" \
    --title "fix: ${finding}" \
    --body-file evaluator-report.md
else
  cp evaluator-report.md "../../state/inbox-${finding}.md"
fi

注意,这里没有 gh pr merge。这不是保守,而是设计。第一版 loop 的目标不是“完全没人管”,而是“把机械劳动推到 PR 前,把最终判断留给人”。当你连续几周看到 evaluator 真能抓住错误,再讨论哪些低风险 PR 可以自动合并。

把这个例子映射回前面的五个动作,会很清楚:

动作 示例里的 Codex 落点
Discovery codex exec 调用 morning-triage Skill 读取 CI / issue / commit
Handoff 每个 finding 一个 git worktree
Verification codex exec review --uncommitted 做独立验收
Persistence state/triage.mdstate/inbox.md、PR
Scheduling GitHub Actions cron 每天早上触发

这就是一个基于 Codex 的完整 loop。它不大,也不神秘,但已经具备最重要的工程性质:会自己醒来,会自己找事,会隔离执行,会被独立校验,会留下记忆,也会在该停的地方停下来等人。

这篇 PDF 真正想提醒工程师的事

Loop engineering 表面上是在讨论 Agent 自动化,底层其实是在讨论工程师的价值迁移。

当生成变得便宜,判断就变得更贵。Loop 可以生成代码、计划、PR、修复方案,甚至一晚上生成几十个候选实现。但它不能真正替你判断哪个方向值得做,哪个看起来合理但根上是错的,哪个改动虽然测试绿了却会破坏产品语义。

这会把工程师分成两类。

一类人用 loop 放大自己已经拥有的判断。他们读代码,理解系统,知道哪里需要人工 checkpoint,知道哪些事情必须确定化,知道 evaluator 该怎么怀疑。Loop 让他们更强。

另一类人用 loop 逃离理解。他们把“不想看”包装成“自动化”,把“不知道”交给 Agent 去猜,把“过了测试”当成“我可以不关心”。Loop 也会放大他们,只是放大的是懒惰和盲区。

所以我读完这篇 PDF,最想带走的不是某个具体命令、worktree 或 MCP 连接器。真正要带走的是一句工程纪律:

设计 loop,但要像一个打算继续当工程师的人那样设计它。

把生成交给机器,把校验拆出来,把状态写到磁盘,把预算上限写死,把人工 review 的门留着。不要做那个每分钟提示 Agent 的人,也不要做那个只会按下 Go 的人。Loop engineering 最好的形态,是让人从机械劳动里退出来,但仍然站在判断的位置上。

这才是“别再提示 Agent,去设计提示 Agent 的系统”的完整含义。