循环工程(Loop engineering)就是把“给代理写提示词的人”这个角色替换掉。你设计的是让它替你完成这件事的系统。 这里的循环可以理解为一种递归式目标:你定义一个目的,AI 反复迭代,直到完成。我认为,这可能就是我们未来与编码代理协作的方式。不过,现在还很早,我对此仍持怀疑态度,而且你绝对必须 谨慎 处理 token 成本(如果你的 token 很富余或很紧张,使用模式可能会有天壤之别),所以我想把它拆开讲清楚:它是什么,以及它意味着什么。
Peter Steinberger 最近 说:“你不应该再亲自给编码代理写提示词了。你应该设计能给代理下发提示词的循环。” 同样,Anthropic 旗下 Claude Code 的负责人 Boris Cherny 说:“我已经不再亲自给 Claude 写提示词了。我现在运行的是一些循环,这些循环会给 Claude 下发提示词并决定该做什么。我的工作就是写循环。”
好吧,那这些话到底是什么意思?
在过去大约两年里,你想从编码代理那里得到结果,做法就是写一个好提示词,并提供足够的上下文。你输入一条内容,读它返回的结果,再输入下一条。代理是一种工具,而你一直在手把手地带着它,一轮接一轮。这个阶段差不多已经结束了,至少有人这么认为。
现在,你要构建一个小系统:它负责发现工作、分派工作、检查工作、记录完成情况,然后决定下一步;你让这个系统去“戳”代理,而不是你亲自去。之前我写过它的近亲——代理运行环境工程(agent harness engineering),也就是为单个代理构建它运行的环境,以及 工厂模式——构建软件的系统。循环工程比运行环境再高一层。它像运行环境,但它会按时间表运行、派生小帮手,并且还能自我供给。
让我感到惊讶的是,这件事如今已经不再只是一个“工具”问题了。大约一年前,如果你想做一个循环,你得写一大堆 bash 脚本,然后永远维护那堆脚本,那是你自己的东西,而且只属于你自己。现在,这些组件已经直接内置进产品里了。Steinberger 的那份清单几乎可以一一对应到 Codex 应用,再几乎同样地对应到 Claude Code。等你发现它们的形状其实一样之后,你就不会再争论到底用哪个工具了;你只会开始设计一个无论你坐在哪个工具里都依然有效的循环。
一个 循环 需要五样东西,再加一个存放状态的地方。先列出来,再逐一对应。
然后是第六样东西:记忆。一个 Markdown 文件,或者一个 Linear 看板,只要是存在于单次对话之外、记录已完成内容和下一步内容的东西都行。听起来太简单,似乎没什么大不了。但这正是每个长期运行代理都依赖的技巧——我在 长期运行代理 里详细讲过:模型在两次运行之间会忘掉一切,所以记忆必须放在磁盘上,而不能放在上下文里。代理会忘,仓库不会。
现在,这两个产品都已经具备了这五样东西。
| 原语 | 在循环中的职责 | Codex 应用 | Claude Code |
|---|---|---|---|
| 自动化 | 按计划进行发现和分流 | Automations 选项卡:选择项目、提示词、频率和环境;结果进入 Triage 收件箱;/goal 用于持续运行直到完成 | 定时任务和 cron,/loop,/goal,hooks,GitHub Actions |
| 工作树 | 隔离并行特性 | 每个线程内置工作树 | git worktree,--worktree,isolation: worktree 用于子代理 |
| 技能 | 将项目知识制度化 | Agent Skills(SKILL.md),可通过 $name 调用,或在匹配时自动触发 | Agent Skills(SKILL.md) |
| 插件 / 连接器 | 连接你的工具 | 连接器(MCP)以及用于分发的插件 | MCP 服务器以及插件 |
| 子代理 | 构思与验证分离 | 在 .codex/agents/ 中以 TOML 定义子代理 | .claude/agents/ 中的任务子代理,代理团队 |
| 状态 | 跟踪完成了什么 | 通过连接器使用 Markdown 或 Linear | Markdown(AGENTS.md、进度文件)或通过 MCP 使用 Linear |
名称上有些地方不太一样,但能力是同一种东西。让我逐个说,因为说实话,细节才是一个循环能否稳住、还是悄悄漏得一塌糊涂的关键。
自动化让循环真正变成“循环”,而不是你偶尔手动跑一次的单次操作。在 Codex 应用里,你可以在 Automations 选项卡里创建一个自动化,选择项目、它要运行的提示词、运行频率,以及是在本地检出目录上运行,还是在后台工作树中运行。发现了问题的运行结果会进入 Triage 收件箱,而没有发现问题的运行则会自动归档,这一点很不错。OpenAI 内部也用它们处理一些很琐碎但很重要的事情,比如每日问题分流、总结 CI 失败、撰写提交简报、排查上周有人引入的 bug。自动化还可以调用技能,这样你就能让重复性工作保持可维护性:你调用 $skill-name,而不是把一大坨指令粘贴进一个没人会更新的定时任务里。
Claude Code 也能通过调度和 hooks 达到同样的效果。你可以用 /loop 按间隔运行提示词或命令,可以设置 cron 任务,可以通过 hooks 在代理生命周期的特定节点触发 shell 命令;或者如果你希望在合上电脑之后它还能继续跑,就把整套流程交给 GitHub Actions。思路完全一样:你定义一个自主任务,给它一个执行频率,得到的发现会主动推送给你,而不是你自己不断去检查。
还有一个值得知道的会话内原语,它更接近这篇文章的核心。/loop 会按固定节奏重新运行。/goal 则会一直执行,直到你写下的条件真正成立;每一轮结束后,都会由另一个小模型检查是否已经完成,所以写代码的代理并不是给自己打分的人。你可以给它类似“所有 test/auth 测试通过且 lint 干净”这样的条件,然后离开。Codex 里也有同样的东西,也叫 /goal,它会跨轮次持续工作,直到可验证的停止条件成立,并支持暂停、恢复和清晰的状态管理。两个工具,同一种原语,这几乎就是整篇文章的模式。
所以,这一层负责把工作浮现出来。循环的其余部分则负责对这些工作采取行动。
只要你同时运行多个代理,文件就会开始互相冲突,这就是最常见的失败模式。两个代理同时改同一个文件,本质上就跟两个工程师改同几行代码、却没先沟通一样,麻烦立刻就来了。git worktree 能解决这个问题:它是在同一个仓库历史上共享提交记录、但拥有独立工作目录的分支副本,所以一个代理的修改根本碰不到另一个代理的检出目录。
Codex 把工作树支持直接内置进去了,因此多个线程可以同时处理同一个仓库,而不会互相踩踏。Claude Code 则通过 git worktree 提供同样的隔离能力,提供 --worktree 标志来在独立检出目录中打开会话,还能在子代理上设置 isolation: worktree,让每个助手都拿到一个新的检出目录,并在结束后自动清理。我在 编排税(the orchestration tax) 里写过这类事情的人类侧代价:工作树消除了机械层面的冲突,但你的审查带宽仍然是上限;你能实际并行跑多少个,取决于你能看过多少,而不是工具本身。
技能的作用,是让你不用每次会话都像金鱼一样重复解释同一个项目上下文。两个工具都使用相同的格式:一个包含 SKILL.md 的文件夹,里面写着指令和元数据,再加上可选的脚本、参考资料和资源。Codex 可以在你用 $ 或 /skills 调用时运行某个技能,也可以在任务与技能描述匹配时自动运行,这也是为什么简洁、朴素的描述比花哨的描述更有用。Claude Code 也是同样的做法,我在 代理技能 里把这个模式写过了。
技能也是让你的意图不再反复消耗你的地方。我在 意图债务(the intent debt) 里论证过:代理每次会话都是冷启动,它会用自信的猜测来填补你意图中的任何空白。技能就是把这些意图写在外面:约定、构建步骤、“我们不用这种方式做,因为那次出过事”,一次写好,让代理每次运行都能读到。没有技能,循环每一轮都得从零重新推导你的整个项目;有了技能,它就能不断累积。
有一点要分清:技能是创作格式,插件则是你如何分发它。当你想跨仓库共享一个技能,或者把几个技能打包在一起时,你会把它们封装成插件。Codex 如此,Claude Code 也是如此。
一个只能看见文件系统的循环,还是太小了。基于 MCP 的连接器让代理可以读取你的问题跟踪系统、查询数据库、访问预发布 API、往 Slack 里发消息。Codex 和 Claude Code 都支持 MCP,所以你为其中一个写的连接器,通常可以直接在另一个里工作。插件则把连接器和技能打包在一起,这样你的同事安装你的配置时,只需一键完成,不必从记忆里把整套东西重新拼出来。
这就是“一个代理说‘这是修复方案’”和“一个循环自己打开 PR、关联 Linear 工单,并在 CI 变绿后自动在频道里提醒一次”之间的区别。连接器正是让循环能够真正作用于你的实际环境,而不是只告诉你“如果我能做,我会怎么做”的原因。
循环里最有用、最结构性的设计,毫无疑问,就是把“写代码的人”和“检查代码的人”分开。写代码的模型太擅长给自己打高分了。第二个代理拥有不同的指令、甚至不同的模型,能把第一个代理自说自话绕进去的漏洞揪出来。
Codex 只有在你主动要求时才会生成子代理,它们会并行运行,然后把结果折叠回一个答案里。你可以在 .codex/agents/ 里把自己的代理定义为 TOML 文件,每个文件包含名称、描述、指令,以及可选的模型和推理强度;因此,你的安全审查员可以是一个高强度的大模型,而你的探索者可以是某种快速、只读的代理。Claude Code 通过 .claude/agents/ 里的子代理和代理团队做同样的事,它们会在彼此之间传递工作。两者里常见的分工都是:一个代理负责探索,一个负责实现,一个负责按规格验证。
我已经分别在 代码代理管弦乐团(the code agent orchestra) 和 对抗式代码审查(adversarial code review) 里讨论过这个问题。它之所以在循环里特别重要,是因为循环是在你看不见的时候运行的;如果没有一个你真正信任的验证者,你根本没法放心离开。子代理确实会消耗更多 token,因为每个子代理都要走自己的模型和工具调用,所以只有当第二意见值得这笔成本时,才应该为它买单。这本质上也就是 Claude Code 的 /goal 在底层做的事情:由一个新的模型来判断循环是否结束,而不是由完成工作的那个模型自己来判断,把“执行者”和“检查者”的分离应用到了停止条件本身。
把这些组件拼起来,一个线程就变成了一个小型控制面板。下面是我一直在用的一种形态。
一个自动化每天早上在仓库上运行。它的提示词会调用一个分流技能,读取昨天的 CI 失败、打开中的 issue、最近的提交记录,并把发现写进一个 Markdown 文件或一个 Linear 看板。对于每一项值得处理的发现,这个线程会打开一个隔离的工作树,并派一个子代理去起草修复方案,再派第二个子代理根据项目技能和现有测试来审查这个草案。
连接器让循环可以打开 PR 并更新工单。循环处理不了的任何东西,都会进入我的 Triage 收件箱。状态文件是整个系统的脊柱:它记录哪些尝试过了、哪些通过了、哪些还未关闭;于是第二天早上运行时,就能从前一天停下的地方继续。
而且你要看清楚,你实际做的事情是什么:你只是设计了一次。你并没有逐步给这些步骤写提示词。这正是 Steinberger 想表达的核心,而在 Codex 或 Claude Code 里,它都是同一个循环,因为这些组件本来就是同一套组件。
循环会改变工作方式,但不会把你从工作中删除。事实上,有三个问题会随着循环变得更好而变得更尖锐,而不是更轻松。
验证仍然得靠你。一个无人值守的循环,也同样是一个无人值守地犯错的循环。之所以要把验证子代理从执行者那里拆出来,就是为了让循环里“已经完成”的说法有意义;即便如此,“完成”也只是一个主张,而不是证明。我在 AI 时代的代码审查(code review in the age of AI) 里一直强调同一句话:你的工作,是交付你已经确认可用的代码。
如果你放任不管,你对系统的理解也会腐烂。循环交付你没写过的代码越快,你所理解的现实和实际存在的系统之间的鸿沟就越大。这就是 理解债务(comprehension debt);如果你不去阅读循环生成的内容,一个顺畅的循环只会让它增长得更快。
而舒适的姿态才是最危险的。循环一旦开始自己跑,就很容易让你停止思考,直接接受它给回来的任何东西。我把这叫做 认知放弃(cognitive surrender)。如果你带着判断力去设计循环,它就是解药;如果你只是借它来逃避思考,它就是助推器。同样的动作,完全相反的结果。
我认为,这预示着我们的工作方式会如何演进。话虽如此,如果我不是亲自审查代码,或者我完全依赖自动化循环来修复它,我的产品质量就会受损。我很可能会陷入一个恶性螺旋,不断把自己挖得更深。
不过,尽管去搭建你的循环吧,但别忘了,直接给代理写提示词同样有效。关键在于找到合适的平衡。
循环带来的结果也会因人而异。两个人可以搭建出完全相同的循环,却得到截然相反的结果。一个用它来更快推进自己真正理解的工作;另一个用它来逃避理解工作本身。循环并不知道区别。你知道。
这也是为什么循环设计比提示词工程更难,而不是更容易。Cherny 想表达的不是工作变简单了,而是杠杆点移动了。
构建循环吧。但要像一个打算继续做工程师的人那样去构建它,而不是只做那个按下启动键的人。