循环工程
循环工程是一种新的编码代理工作方式,将人工提示替换为设计自动循环系统。它包含自动化、工作树、技能、插件/连接器和子代理五个核心组件,外加外部记忆存储。工具如Codex和Claude Code正在整合类似的原语,子代理将构思与验证分离,提高了可靠性。
以下文章最初出现在 Addy Osmani 的博客上,经作者许可在此转载。
循环工程是指将你自己从提示代理的角色中替换出来,转而设计系统来执行提示。这里的“循环”可以理解为一个递归目标:你定义一个目的,然后 AI 迭代直到完成。我相信这可能是我们未来与编码代理协作的方式。不过,这仍处于早期阶段;我持怀疑态度,而且你必须小心令牌成本(如果你令牌充足或匮乏,使用模式可能大相径庭)。因此,我想剖析它到底是什么以及意味着什么。
Peter Steinberger 最近说:“你不应该再提示编码代理了。你应该设计循环来提示你的代理。” 同样,Anthropic 的 Claude Code 负责人 Boris Cherny 说:“我不再提示 Claude 了。我有循环在运行,提示 Claude 并决定做什么。我的工作是编写循环。”
好吧,这到底意味着什么?
大约两年以来,从编码代理获取结果的方式是编写一个良好的提示并分享足够的上下文。你输入内容,阅读回复,然后继续输入下一个指令。代理是一个工具,你全程掌控,一个回合接一个回合。这部分已经结束了,或者至少有些人认为它将成为过去。
现在,你构建一个小型系统,它负责发现任务、分配任务、检查结果、记录完成情况,然后决定下一步。你让这个系统去操控代理,而不是你自己。我之前写过它的表亲——代理框架工程,即打造单个代理运行的环境,以及工厂模式——构建软件的系统。循环工程位于框架之上:框架加上定时器、生成子帮手,并自我反馈。
让我惊讶的是,这已经不再是工具层面的问题。一年前,如果你想实现一个循环,你需要编写一堆 bash 脚本,并永久维护它,而且它只属于你。现在,这些组件直接内置于产品中。Steinberger 的列表几乎完全映射到 Codex 应用,然后同样映射到 Claude Code。一旦你注意到形状相同,你就不会再争论哪个工具更好。你只需设计一个循环,无论你使用哪个工具,它都能正常工作。
五个组件及说明
一个循环需要五个东西,加上一个记录状态的地方。我先列出,然后映射到具体工具。
按计划自动运行并自行发现和分类的自动化 工作树(Worktrees),使并行运行的两个代理不会相互干扰 技能(Skills),记录项目知识,代理否则只能猜测 插件和连接器(Plugins and Connectors),将代理连接到你已经使用的工具 子代理(Subagents),让一个代理产生想法,另一个代理进行检查
然后是第六个东西:记忆。一个 Markdown 文件,或一个 Linear 看板,任何存在于单个对话之外并能记录已完成和下一步的内容。听起来太简单以至于无关紧要,但这是每个长期运行的代理所依赖的相同技巧,我在“长期运行代理”中详细讨论过:模型在运行之间会忘记一切,因此记忆必须存储在磁盘上,而不是在上下文中。代理会忘记,但仓库不会。
现在两种产品都具备这五个组件。
PrimitiveJob in the loop Codex app Claude Code Automations 按计划发现和分类 自动化标签:选择项目、提示、节奏、环境;结果进入分类收件箱;/goal 用于运行直到完成 定时任务和 cron,/loop,/goal,钩子,GitHub Actions Worktrees 隔离并行功能 内置每个线程的工作树 git worktree,--worktree,隔离:子代理上的 worktree Skills 编码项目知识 代理技能(SKILL.md),通过 $name 或隐式调用 代理技能(SKILL.md) Plugins and connectors 连接你的工具 连接器(MCP)加插件用于分发 MCP 服务器加插件 Subagents 构思与验证 在 .codex/agents/ 中定义为 TOML 的子代理 在 .claude/agents/ 中的任务子代理,代理团队 State 跟踪完成情况 通过连接器实现的 Markdown 或 Linear Markdown(AGENTS.md,进度文件)或通过 MCP 的 Linear
名称略有不同,但功能相同。让我们逐一介绍,因为细节决定了循环是稳固运行还是四处泄漏。
自动化是心跳
自动化是使循环成为真正循环的关键,而不仅仅是一次性运行。在 Codex 应用中,你在自动化标签页创建一个自动化,选择项目、提示、运行频率以及是在本地检出还是后台工作树上运行。发现问题的运行结果进入分类收件箱,没有发现问题的运行则自行归档,这很不错。OpenAI 内部将其用于无聊的任务,如每日问题分类、总结 CI 失败、编写提交简报以及追踪上周添加的 bug。而且自动化可以调用技能,因此你可以保持重复性任务的可维护性;你调用 $skill-name,而不是将一长串指令粘贴到永远无人更新的计划中。
Claude Code 通过调度和钩子达到同样的目的。你可以使用 /loop 按间隔运行提示或命令,可以调度 cron 任务,可以在代理生命周期中的特定点通过钩子触发 shell 命令,或者如果你希望关闭笔记本电脑后继续运行,可以将整个任务推送到 GitHub Actions。完全相同的想法:你定义一个自主任务,指定节奏,然后结果会自动呈现给你,这样你就不必亲自四处检查了。
还有一个会话内的原语值得了解,它与本文的主题更接近。/loop 按节奏重复运行。/goal 继续运行直到你编写的条件为真,并且每个回合后有一个单独的小模型检查是否完成,这样编写代码的代理就不是评估者。你给出类似“test/auth 中的所有测试通过且 lint 干净”的条件,然后走开。Codex 也有同样的功能,也称为 /goal:它跨回合继续工作,直到可验证的停止条件成立,并支持暂停、恢复和清除。相同的原语,两种工具,这几乎是本文的整个模式。
所以,这部分是浮现任务的部分。循环的其余部分是对任务采取行动。
工作树,让并行不变成混乱
一旦你运行多个代理,文件开始冲突;这就成了失败。两个代理写入同一个文件就像两个工程师在没有事先沟通的情况下提交到同一行代码一样令人头疼。Git 工作树解决了这个问题。它是一个独立的工作目录,拥有自己的分支,共享相同的仓库历史,因此一个代理的编辑完全不会影响另一个的检出。
Codex 内置了工作树支持,因此多个线程可以同时访问同一个仓库而不互相碰撞。Claude Code 通过 git worktree、--worktree 标志(在独立检出中打开会话)以及 isolation: worktree 设置(应用于子代理,使每个助手获得一个自动清理的新检出)提供了相同的隔离功能。(我在“编排税”中写过这一切的人类层面。)工作树消除了机械冲突,但你仍然是瓶颈。你审查的带宽决定了你能实际运行多少个代理,而不是工具。
技能,让你不再每次都解释项目
技能让你不再像金鱼一样每次会话都重新解释相同的项目上下文。两种工具使用相同的格式:一个包含 SKILL.md 的文件夹,里面存放指令和元数据,以及可选的脚本、参考文件和资源。Codex 在你通过 $ 或 /skills 调用技能,或者当任务与技能描述匹配时自动运行技能。因此,一个紧凑、无聊的描述胜过聪明的描述。Claude Code 以相同的方式处理,我在“代理技能”中写过这个模式。
技能也是意图不再重复花费的地方。我在“意图债务”中指出,代理每次会话从零开始,它会用自信的猜测填补你意图中的任何漏洞。技能是将意图写在外部,包括约定、构建步骤、“我们因为那次事故不这样做”等,只写一次,而代理每次运行都会读取。没有技能,循环每个周期都会从零重新推导整个项目;有了技能,它会不断积累。
有一点要分清:技能是创作格式,插件是发布方式。当你想跨仓库共享技能或将多个技能打包时,你将它们打包为插件。Codex 和 Claude Code 都是如此。
插件和连接器,循环接触你的真实工具
只能看到文件系统的循环是个微小的循环。连接器基于 MCP,允许代理读取你的问题跟踪器、查询数据库、访问暂存 API 或在 Slack 上发送消息。Codex 和 Claude Code 都支持 MCP,因此你为其中一个编写的连接器通常也可以用于另一个。而插件将连接器和技能打包在一起,使你的队友可以一次性安装你的设置,而不用凭记忆重建整个环境。
这就是代理说“这是修复方法”与循环自动创建 PR、关联 Linear 工单、并在 CI 通过后自动通知频道的区别。连接器使得循环能够在你实际环境中行动,而不仅仅是告诉你如果它能做到会做什么。
子代理,让编写者与检查者分离
循环中最有用的结构无疑是分离编写者和检查者。编写代码的模型在评估自己的作业时过于宽容。另一个具有不同指令(有时是不同模型)的代理会捕捉到第一个代理自己说服自己相信的错误。
Codex 只在请求时生成子代理,同时运行它们,然后将结果合并为一个答案。你在 .codex/agents/ 中定义自己的代理为 TOML 文件,每个代理包含名称、描述、指令以及可选的模型和推理努力度,这样你的安全审查者可以使用高努力度的强模型,而你的探索者可以使用快速只读的模型。Claude Code 通过 .claude/agents/ 中的子代理和在它们之间传递工作的代理团队实现同样的功能。两者的常见分工是一个代理探索,一个代理实现,一个代理根据规格验证。
我已经两次提出这个观点,一次是“代码代理管弦乐队”,一次是“对抗性代码审查”。它在循环中特别重要的原因是循环在你不在场时运行,因此一个你真正信任的验证者是你能够走开的唯一理由。子代理确实会消耗更多令牌,因为每个代理都执行自己的模型和工具工作,因此将令牌用在值得付出第二次意见的地方。这基本上也是 Claude Code 的 /goal 在底层所做的:一个全新的模型决定循环是否完成,而不是执行工作的那个——编写者与检查者的分离应用于停止条件本身。
一个循环的样子
将所有组件组合在一起,一个单线程就变成了一个小控制面板。这是我经常使用的一个形状。
一个自动化每天早上在仓库上运行。它的提示调用一个分类技能,读取昨天的 CI 失败、未解决的问题和最近的提交,并将结果写入 Markdown 文件或 Linear 看板。对于每个值得做的发现,线程打开一个隔离的工作树,并发送一个子代理来起草修复,第二个子代理根据项目技能和现有测试审查该草案。
连接器允许循环创建 PR 并更新工单。循环无法处理的任何问题都会进入我的分类收件箱。状态文件是整个循环的脊柱;它记住尝试了什么、通过了什么以及还有什么待办。