12 分钟阅读

Loop Engineering:从亲自 prompt Agent,到设计驱动 Agent 的循环

Loop Engineering 原文发布于

Loop engineering(循环工程)就是把“亲自给 Agent 写 prompt 的那个人”替换掉。你转而去设计一个替你做这件事的系统。 这里的循环(loop)可以理解为一个递归式的目标:你定义一个目的,AI 不断迭代直到完成。我认为这可能就是我们未来与 coding agent 协作的方式。不过现在还很早,我持怀疑态度,而且你绝对必须对 token 成本保持谨慎(用量模式会因为你是 token 富裕还是 token 拮据而天差地别),所以我想拆解一下它是什么、意味着什么。


Peter Steinberger 最近说:“你不应该再给 coding agent 写 prompt 了。你应该去设计那些给你的 Agent 写 prompt 的循环。”类似地,Anthropic 的 Claude Code 负责人 Boris Cherny 也说:“我不再给 Claude 写 prompt 了。我有一些循环在运行,它们负责 prompt Claude 并想清楚该做什么。我的工作是写循环。”

好,那这些话到底是什么意思?

大约两年来,从 coding agent 那里拿到成果的方式是:写一个好 prompt,分享足够的上下文。你敲一段话,读返回的内容,再敲下一段。Agent 是一件工具,而你全程握着它,一轮接一轮。这部分差不多结束了,或者至少有人认为它将要结束。

现在,你搭一个小系统:它找出要做的工作,把工作分派出去,检查结果,记下已完成的事,然后决定下一件事。你让这个系统去戳 Agent,而不是你自己去戳。我之前写过它的表亲:agent harness engineering,也就是打造单个 Agent 运行于其中的环境;还有 factory model,即构建软件的那套系统。Loop engineering 比 harness 高一层:它是 harness,但按计时器运行,会 spawn 一些小帮手,并且自己喂养自己。

让我意外的是,这已经不再真正是一个工具层面的事了。一年前,如果你想要一个循环,你得写一堆 bash,然后永远维护那堆东西,它是你的,也只属于你。现在这些组件直接随产品一起发布。Steinberger 列的清单几乎一一对应到 Codex app 上,然后又几乎同样地对应到 Claude Code 上。一旦你注意到形状是一样的,你就不再争论该用哪个工具,而是直接设计一个无论你恰好坐在哪个工具里都照样能跑的循环。

五个组件,然后是备忘

一个循环需要五样东西,再加一个记事的地方。我先列出来,再逐一对应。

  1. Automations(自动化):按计划自动触发,自己完成发现与分诊。
  2. Worktrees:让两个并行工作的 Agent 不会互相踩脚。
  3. Skills:把 Agent 原本只能靠猜的项目知识写下来。
  4. Plugins 与 connectors:把 Agent 接入你已经在用的工具。
  5. Sub-agents:让一个出主意,另一个来检查。

然后是第六样,记忆。一个 markdown 文件,或一块 Linear 看板,任何活在单次对话之外、记录着什么已完成、什么是下一步的东西。听起来蠢到不值一提。但每一个长时间运行的 Agent 都依赖同一个把戏,我在 long-running agents 里深入讲过:模型在两次运行之间会忘掉一切,所以记忆必须在磁盘上,而不是在上下文里。Agent 会忘,仓库不会。

两个产品现在都齐了这五样。

原语在循环中的职责Codex appClaude Code
Automations按计划做发现 + 分诊Automations 标签页:选项目、prompt、频率、环境;结果进入 Triage 收件箱;/goal 用于跑到完成为止定时任务与 cron、/loop、/goal、hooks、GitHub Actions
Worktrees隔离并行的 feature每个 thread 内置 worktreegit worktree、--worktree、subagent 上的 isolation: worktree
Skills固化项目知识Agent Skills(SKILL.md),用 $name 调用或隐式触发Agent Skills(SKILL.md)
Plugins / connectors连接你的工具Connectors(MCP)加上用于分发的 pluginsMCP server 加上 plugins
Sub-agents出主意与验证Subagents,以 TOML 定义在 .codex/agents/.claude/agents/ 里的 Task subagent、agent teams
State跟踪已完成的事Markdown,或通过 connector 用 LinearMarkdown(AGENTS.md、进度文件)或通过 MCP 用 Linear

名字这里那里有点不同,但能力是同一回事。让我逐个来讲,因为说实话,细节才决定一个循环是能撑住,还是到处悄悄漏水。

Automations,这是心跳

Automations 让一个循环成为真正的循环,而不是你跑过一次的单次运行。在 Codex app 里,你在 Automations 标签页里新建一个,选择项目、它要运行的 prompt、多久一次,以及它是在你本地 checkout 上跑还是在后台 worktree 上跑。有发现的运行会进入 Triage 收件箱,一无所获的运行则自动归档,这很贴心。OpenAI 内部用它们处理那些无聊的事:每日 issue 分诊、总结 CI 失败、写提交简报、追查某人上周引入的 bug。而且一个 automation 可以调用一个 skill,这样你就能让重复任务保持可维护:触发 $skill-name,而不是把一大面指令墙粘进一个永远没人会更新的计划任务里。

Claude Code 通过调度和 hooks 到达同一个地方。你可以用 /loop 按间隔重复运行一个 prompt 或命令,可以安排一个 cron 任务,可以用 hooks 在 Agent 生命周期的特定节点触发 shell 命令,或者把整套东西推到 GitHub Actions 上,如果你想让它在你合上笔记本后继续跑的话。完全是同一个思路:你定义一个自主任务,给它一个节奏,然后发现会送到你面前,而不是你到处去检查。

还有第二个会话内的原语值得了解,它更贴近这整篇文章的主题。/loop 按节奏重复运行。/goal 则一直干下去,直到你写的某个条件真正成立为止,而且每一轮之后都会由一个独立的小模型检查你是否已经完成,所以写代码的 Agent 不是给它打分的那个。你给它类似“test/auth 下的所有测试通过且 lint 干净”这样的目标,然后走开。Codex 也有同样的东西,也叫 /goal,它跨轮次持续工作,直到一个可验证的停止条件成立,支持暂停、恢复和清除。同一个原语,两个工具都有,这差不多就是整篇文章的套路。

所以这是把工作浮现出来的部分。循环的其余部分负责对它采取行动。

Worktrees,让并行不至于变成混乱

一旦你运行的 Agent 超过一个,文件就开始冲突,这就成了故障点。两个 Agent 写同一个文件,和两个工程师往同几行代码提交、事先谁也没跟谁说,是一模一样的头疼事。git worktree 解决了它:它是一个独立的工作目录,在自己的分支上,共享同一份仓库历史,所以一个 Agent 的编辑根本碰不到另一个的 checkout。

Codex 把 worktree 支持直接内置,多个 thread 同时命中同一个仓库也不会相撞。Claude Code 给你同样的隔离:git worktree,一个 --worktree 标志让会话在自己的 checkout 里打开,还有一个 isolation: worktree 设置可以贴在 subagent 上,让每个帮手都拿到一个用完自动清理的全新 checkout。我在 the orchestration tax 里写过这一切里属于人的那一面:worktree 消除了机械层面的冲突,但你仍然是天花板,决定你实际能跑多少个的是你的审查带宽,而不是工具。

Skills,让你不用每次都重新解释你的项目

Skill 是让你不再像金鱼一样每个会话都重新解释同一份项目上下文的方法。两个工具用的是同一种格式:一个文件夹,里面有一个装着指令和元数据的 SKILL.md,然后是可选的脚本、参考资料和资源。Codex 会在你用 $ 或 /skills 调用时运行一个 skill,或者在你的任务匹配 skill 描述时自行运行,这就是为什么一段紧凑、朴素的描述胜过一段机灵的描述。Claude Code 的做法相同,我在 agent skills 里把这个模式写过一遍。

Skill 也是让意图不再一次次消耗你的地方。我在 the intent debt 里论证过,Agent 每个会话都是冷启动,它会用一个自信的猜测填补你意图中的任何空洞。Skill 就是把那份意图写在外面:约定、构建步骤、“我们不这么做是因为某次事故”,写一次,Agent 每次运行都读。没有 skill,循环每个周期都从零重新推导你的整个项目;有了 skill,它多少会复利累积。

有一点要分清:skill 是编写格式,plugin 是分发方式。当你想跨仓库共享一个 skill,或者把几个打包在一起时,你把它们打包成一个 plugin。Codex 如此,Claude Code 也如此。

Plugins 与 connectors,循环触达你真实的工具

一个只能看见文件系统的循环是个小循环。Connectors 建立在 MCP 之上,让 Agent 能读你的 issue 跟踪器、查询数据库、调用 staging API、往 Slack 里丢一条消息。Codex 和 Claude Code 都讲 MCP,所以你为一个写的 connector 通常在另一个里直接就能用。而 plugins 把 connectors 和 skills 捆在一起,你的队友一次就能装好你的整套配置,而不是凭记忆重建一遍。

这就是“这里是修复方案”的 Agent,和会自己开 PR、关联 Linear 工单、等 CI 变绿后 ping 频道的循环之间的区别。Connectors 是循环能在你真实环境里行动、而不只是告诉你“如果它能的话会怎么做”的原因。

Sub-agents,让做的人远离检查的人

一个循环里最有用的结构性做法,遥遥领先的那个,是把写的人和检查的人分开。写代码的那个模型给自己的作业打分时太宽容了。第二个 Agent 带着不同的指令、有时还是不同的模型,能抓住第一个说服自己接受的那些东西。

Codex 只在你要求时才 spawn subagent,同时运行它们,然后把结果折叠回一个答案。你在 .codex/agents/ 里用 TOML 文件定义自己的 Agent,每个有名称、描述、指令,以及可选的模型和推理强度,所以你的安全审查员可以是高强度的强模型,而你的探索者可以是某个只读的快速模型。Claude Code 用 .claude/agents/ 里的 subagent 和在彼此之间传递工作的 agent teams 做同样的事。两者常见的分工都是:一个探索,一个实现,一个对照 spec 验证。

这个观点我已经讲过两次,一次是 the code agent orchestra,一次是 agentic code review。它在循环里之所以格外重要,是因为循环在你不看着的时候运行,所以一个你真正信任的验证者,是你能走开的唯一理由。Subagent 确实更烧 token,因为每一个都做自己的模型和工具工作,所以把它们花在第二意见值得付费的地方。这基本上也是 Claude Code 的 /goal 在底层做的事:由一个全新的模型而不是干活的那个来判断循环是否完成,把“做与查分离”应用到了停止条件本身。

一个循环长什么样

把这些拼在一起,一个 thread 就变成了一个小小的控制面板。下面是我一直在用的一种形状。

一个 automation 每天早上在仓库上运行。它的 prompt 调用一个分诊 skill,读取昨天的 CI 失败、开放的 issue、最近的提交,把发现写进一个 markdown 文件或一块 Linear 看板。对每一个值得做的发现,thread 打开一个隔离的 worktree,派一个 sub-agent 起草修复,再由第二个 sub-agent 对照项目 skill 和现有测试审查那份草稿。

Connectors 让循环开 PR、更新工单。循环处理不了的任何东西都落到分诊收件箱里等我。状态文件是整件事的脊梁,它记得试过什么、通过了什么、还有什么开着,这样明天早上的运行就从今天停下的地方接着来。

再看看你在这里实际做了什么。你只设计了一次。那些步骤你一个都没 prompt。这就是 Steinberger 的全部观点变成了现实,而且在 Codex 或 Claude Code 里是同一个循环,因为组件就是同样的组件。

循环仍然不会替你做的事

循环改变了工作,但没有把你从工作里删掉。而且有三个问题会随着循环变好而变得更尖锐,而不是更轻松。

验证仍然在你身上。一个无人值守运行的循环,也是一个无人值守犯错的循环。把验证 sub-agent 和做的人分开的全部理由,就是让循环说的“做完了”有点分量,即便如此,“做完了”也是一个主张,不是一个证明。我一直在重复 code review in the age of AI 里的那句话:你的工作是交付你确认能用的代码。

如果你放任不管,你的理解仍然会腐烂。循环交付你没写过的代码越快,“存在什么”和“你真正掌握什么”之间的鸿沟就越大。那就是 comprehension debt,一个顺滑的循环只会让它长得更快,除非你读循环做出来的东西。

而舒服的姿势正是危险的那个。当循环自己运行时,很容易就不再有自己的看法,只是照单全收它给回来的东西。我把那叫做 cognitive surrender。带着判断力去设计循环,它是解药;为了逃避思考去设计循环,它是助燃剂。同一个动作,相反的结果。

搭建循环,但留在工程师的位置上

我认为这是我们的工作将如何演进的一次预览。话虽如此,如果我不亲自审查代码,或者完全依赖自动化循环去修复它,我的产品质量会受损。我很可能会陷入一个向下的螺旋,不断把自己挖进更深的坑里。

所以,尽管去搭你的循环,但别忘了直接给 Agent 写 prompt 同样有效。关键在于找到合适的平衡。

循环也会因人而异地带来不同的结果。两个人可以搭出一模一样的循环,得到截然相反的结果。一个用它在自己深刻理解的工作上跑得更快。另一个用它彻底逃避理解工作。循环分不出区别。你分得出。

这正是循环设计比 prompt engineering 更难、而不是更容易的原因。Cherny 的观点不是工作变容易了,而是杠杆点移动了。

搭建循环。但要像一个打算继续当工程师的人那样去搭,而不只是那个按下“开始”的人。