Addy Osmani 指出程序员应掌握「让 AI 反复迭代」的循环框架而非优化单条提示,Codex 和 Claude Code 已具备五大必要构件。
Loop engineering,指的是不再由你亲自给 Agent 写 prompt,而是设计一个替你完成这件事的系统。这里的 loop,可以理解为一个递归目标:你定义目标,AI 持续迭代,直到任务完成。我相信,这可能就是未来我们与 coding agent 协作的方式。不过,这一切仍处于早期阶段,我对此也持怀疑态度,而且你必须格外留意 token 成本——token 充裕与否,会让使用模式产生巨大差异。因此,我想具体拆解一下 Loop engineering 究竟是什么,又意味着什么。
Peter Steinberger 最近说过:“你不应该再亲自给 coding agent 写 prompt,而应该设计出能够给 Agent 写 prompt 的 loop。”同样,Anthropic 的 Claude Code 负责人 Boris Cherny 也说:“我已经不再亲自 prompt Claude 了。我运行着一些 loop,由它们 prompt Claude,并判断接下来该做什么。我的工作就是编写 loop。”
好吧,所以这些话到底是什么意思?
过去大约两年里,想让 coding agent 产出结果,通常需要写好 prompt,并提供足够的上下文。你输入一段内容,阅读它返回的结果,再输入下一段内容。Agent 是一件工具,而你必须从头到尾握着它,一轮接一轮地操作。这个阶段差不多已经结束了,或者至少有些人认为,它即将结束。
现在,你会构建一个小型系统:它自己发现任务、分派工作、检查结果、记录已经完成的内容,然后决定下一步做什么。之后,你让这个系统去驱动 Agent,而不是由你亲自操作。我以前写过一个与此相关的概念:Agent harness engineering,也就是打造单个 Agent 的运行环境;还有 factory model,也就是构建软件的整个系统。Loop engineering 位于 harness 的上一层:它像一个 harness,但会定时运行、生成小型辅助 Agent,并把自己的输出重新作为输入。
令我意外的是,这其实已经不再是某个具体工具的问题。一年前,如果你想构建一个 loop,就得写一大堆 bash 脚本,永远维护着这堆脚本,而且它只属于你,也只有你能用。如今,这些组件已经直接内置在产品里。Steinberger 列出的能力几乎可以逐项映射到 Codex app,也几乎同样可以映射到 Claude Code。一旦你意识到它们的整体形态完全相同,就不会再争论该选哪个工具,而是会开始设计一种 loop:无论碰巧使用哪个工具,它都能继续运行。
一个 loop 需要五样东西,外加一个用来保存记忆的地方。先把它们列出来,然后再逐一对应。
定时触发的 Automations,能够自行完成任务发现和分流。
Worktrees,让两个并行工作的 Agent 不会互相踩到对方的修改。
Skills,把项目知识记录下来,避免 Agent 只能自行猜测。
Plugins 和 connectors,把 Agent 接入你已经在使用的工具。
Sub-agents,让一个 Agent 提出方案,再由另一个 Agent 检查。
然后是第六样东西:记忆。它可以是一个 Markdown 文件,也可以是一块 Linear 看板,只要它存在于单次对话之外,能够保存哪些事情已经完成、下一步要做什么即可。这听起来简单得似乎无关紧要,但所有长期运行的 Agent 都依赖同一种技巧。我在讨论 long-running agents 时详细讲过:模型会忘记每次运行之间发生的一切,所以记忆必须保存在磁盘上,而不能只放在上下文里。Agent 会忘记,但 repo 不会。
现在,这两款产品都已经具备了前面五种能力。
它们使用的名称略有不同,但能力本质上是一样的。下面逐项来看,因为说实话,正是这些细节决定了一个 loop 能否稳固运行,还是会在不知不觉中处处泄漏。
Automations 让 loop 真正成为一个 loop,而不只是你偶尔执行过一次的单次任务。在 Codex app 中,你可以在 Automations 标签页创建一个 Automation,选择项目、要运行的 prompt、运行频率,以及是在本地 checkout 中运行,还是在后台 worktree 中运行。发现问题的运行结果会进入 Triage inbox;什么都没发现的运行则会自动归档,这一点很贴心。OpenAI 内部也用它们处理一些枯燥的工作,比如每天分流 issue、汇总 CI 失败、编写 commit 简报,以及排查上周某个人引入的 bug。Automation 还可以调用 skill,因此你能够让重复任务保持可维护:触发 $skill-name,而不是把一大段永远没人更新的说明粘贴到定时任务里。
Claude Code 通过调度和 hooks 实现了同样的效果。你可以使用 /loop 按固定间隔运行 prompt 或命令,可以安排 cron 任务,可以使用 hooks 在 Agent 生命周期的特定阶段触发 shell 命令;如果希望合上笔记本电脑后任务仍能继续运行,也可以把整套流程放到 GitHub Actions 中。理念完全相同:定义一个自主任务,为它设定运行节奏,让发现的结果主动来到你面前,而不是由你四处检查。
还有一种值得了解的会话内原语,它与整篇文章讨论的主题更加接近。/loop 会按照设定的节奏重复运行。/goal 则会持续运行,直到你写下的条件真正成立;而且每轮结束后,都会由另一个独立的小模型检查任务是否完成,因此编写代码的 Agent 不会同时负责给自己的工作打分。你可以给它一个类似“test/auth 中的所有测试都通过,并且 lint 无报错”的条件,然后直接离开。Codex 也有同样的功能,同样叫作 /goal:它会跨越多个轮次持续工作,直到满足一个可验证的停止条件,并支持 pause、resume 和 clear。两个工具拥有同一种原语,这几乎也是整篇文章反复出现的模式。
所以,这是负责发现并呈现任务的部分。loop 的其余部分,则负责对这些任务采取行动。
只要你同时运行不止一个 Agent,文件就会开始发生冲突,而这会成为主要的失败原因。两个 Agent 同时修改同一个文件,与两名工程师在事先没有沟通的情况下向同一组代码行提交修改,是一模一样的麻烦。git worktree 可以解决这个问题:它是一个位于独立分支上的单独工作目录,与其他 worktree 共享同一份 repo 历史。因此,一个 Agent 的修改实际上无法触碰另一个 Agent 的 checkout。
Codex 直接内置了 worktree 支持,因此多个 thread 可以同时处理同一个 repo,而不会彼此冲突。Claude Code 也通过 git worktree 提供了相同的隔离能力:可以使用 --worktree flag,在独立 checkout 中打开 session;还可以给 subagent 配置 isolation: worktree,让每个辅助 Agent 都获得一个全新的 checkout,并在完成后自动清理。我在讨论 orchestration tax 时写过这一机制对人的影响:worktrees 消除了机械层面的冲突,但你依然是整个系统的上限。真正决定你能同时运行多少个 Agent 的,是你的 review bandwidth,而不是工具本身。
Skill 可以让你不再像一条金鱼一样,每个 session 都重新解释相同的项目上下文。两款工具采用相同的格式:一个文件夹,其中包含保存说明和 metadata 的 SKILL.md,还可以选择性地加入 scripts、references 和 assets。在 Codex 中,你可以通过 $ 或 /skills 调用 skill;当任务与 skill description 匹配时,Codex 也会自行运行它。这正是为什么严谨、朴素的 description,要胜过看似聪明的描述。Claude Code 的做法也一样,我在 agent skills 一文中介绍过这种模式。
Skills 还能让你不必反复为 intent 支付成本。我在讨论 intent debt 时提出,Agent 每次开始 session 时都是从零开始的,它会用自信的猜测填补你意图中的任何空白。Skill 就是写在外部的 intent:项目约定、构建步骤,以及“因为曾经发生过那次事故,所以我们不这样做”之类的经验,都只需要记录一次,Agent 每次运行时都会读取。没有 skills,loop 每个周期都要从头推导整个项目;有了 skills,这些知识便会逐渐产生复利。
有一点需要区分清楚:skill 是内容的编写格式,plugin 则是分发它的方式。如果你想跨 repo 共享某个 skill,或者把多个 skill 捆绑在一起,就可以把它们打包成 plugin。Codex 如此,Claude Code 也是如此。
一个只能访问文件系统的 loop,只是一个非常小的 loop。Connectors 基于 MCP 构建,可以让 Agent 读取 issue tracker、查询数据库、调用 staging API,或者在 Slack 中发送消息。Codex 和 Claude Code 都支持 MCP,因此为其中一个编写的 connector,通常也能直接在另一个中使用。Plugins 还能把 connectors 和 skills 捆绑到一起,让队友一次安装完整配置,而不必凭记忆重新搭建所有东西。
这正是只会说“这是修复方案”的 Agent,与能够自行打开 PR、关联 Linear ticket,并在 CI 变绿后通知频道的 loop 之间的区别。Connectors 使 loop 能够在你的真实环境中采取行动,而不只是告诉你:如果它具备相应能力,它本来会怎么做。
到目前为止,loop 中最实用的结构设计,就是把负责编写的人与负责检查的人分开。编写代码的模型,在批改自己的作业时实在太宽容了。使用另一个拥有不同指令、有时甚至使用不同模型的 Agent,才能发现第一个 Agent 自我说服后忽略的问题。
Codex 只会在你提出要求时生成 subagents,让它们同时运行,再把结果整合回一份回答。你可以在 .codex/agents/ 中用 TOML 文件定义自己的 Agent;每个 Agent 都可以拥有 name、description、instructions,以及可选的 model 和 reasoning effort。因此,你的 security reviewer 可以使用高 reasoning effort 的强模型,而 explorer 则可以是某个快速、只读的模型。Claude Code 也通过 .claude/agents/ 中的 subagents,以及能够在彼此之间传递任务的 agent teams,实现了同样的模式。在两款工具中,常见的分工都是:一个 Agent 负责探索,一个负责实现,另一个依据 spec 进行验证。
我已经两次论证过这一点,一次是在 code agent orchestra 中,另一次是在 adversarial code review 中。它在 loop 中格外重要,是因为 loop 会在你没有旁观的情况下运行。只有一个你真正信任的 verifier,才能让你放心离开。Subagents 确实会消耗更多 token,因为每个 Agent 都要单独执行模型推理和工具操作,所以应该把它们用在值得为第二意见付费的地方。这基本也是 Claude Code 的 /goal 在底层采用的做法:由一个全新的模型判断 loop 是否已经完成,而不是让执行任务的模型自行判断。也就是说,它把执行者与检查者分离的原则,应用到了停止条件本身。
把这些部分组合起来,一个 thread 就会变成一块小型控制面板。下面是我经常使用的一种结构。
每天早晨,一个 Automation 都会在 repo 上运行。它的 prompt 会调用 triage skill,读取昨天的 CI 失败、尚未关闭的 issues 和最近的 commits,再把发现写入 Markdown 文件或 Linear 看板。对于每个值得处理的问题,thread 都会打开一个隔离的 worktree,并派出一个 sub-agent 起草修复方案,再让第二个 sub-agent 根据项目 skills 和现有测试审查这份草案。
Connectors 让 loop 可以创建 PR 并更新 ticket。loop 无法处理的任何事情,都会进入我的 triage inbox。状态文件是整个系统的脊柱:它会记住尝试过什么、哪些内容已经通过、哪些问题仍未解决,因此明天早晨运行时,可以从今天停止的位置继续。
再看看你在这里实际做了什么。你只设计了一次这个系统,却没有亲自 prompt 其中任何一个步骤。这就是 Steinberger 观点的完整落地。而且无论使用 Codex 还是 Claude Code,这都是同一个 loop,因为它们拥有相同的组成部分。
loop 改变了工作方式,但不会把你从工作中删除。而且随着 loop 变得越来越好,有三个问题实际上会变得更加尖锐,而不是更容易解决。
验证仍然是你的责任。无人看管的 loop,也会在无人看管的情况下犯错。之所以要把 verifier sub-agent 与执行者分开,就是为了让 loop 所说的“已经完成”具有实际意义。即便如此,“完成”仍然只是一项声明,而不是证明。正如我在 code review in the age of AI 中反复强调的那句话:你的职责,是交付经过你确认、确实能够运行的代码。
如果你放任不管,你的理解能力仍然会退化。loop 交付那些并非由你编写的代码越快,实际存在的系统与你真正理解的系统之间,差距就越大。这就是 comprehension debt。除非你阅读 loop 产出的内容,否则一个顺畅的 loop 只会让这种债务增长得更快。
而最舒服的姿态,恰恰也是最危险的姿态。当 loop 能够自行运行时,你很容易不再形成自己的判断,只是照单全收它返回的一切。我把这种现象称作 cognitive surrender。如果你带着判断力设计 loop,它就是治疗这种问题的方法;如果你只是想借此逃避思考,它就会成为加速问题的催化剂。同一个动作,可能产生完全相反的结果。
构建 loop,但要继续做工程师。
我认为,这让我们提前看到了未来工作方式的演变方向。话虽如此,如果我不亲自 review 代码,或者完全依赖自动化 loop 来修复代码,产品质量就会下降。我很可能会陷入恶性循环,不断把自己挖进一个越来越深的坑里。
所以,你当然可以开始搭建自己的 loop,但不要忘记,直接 prompt Agent 同样有效。关键在于找到正确的平衡。
根据使用者的不同,loop 也可能产生完全不同的结果。两个人可以构建完全相同的 loop,却得到截然相反的结果。一个人用它加速自己已经深刻理解的工作,另一个人则用它彻底逃避理解工作本身。loop 不知道两者之间的区别,但你知道。
正因为如此,loop design 比 prompt engineering 更难,而不是更简单。Cherny 的意思并不是工作变容易了,而是产生杠杆效应的位置发生了变化。
构建 loop。但要像一个决心继续做工程师的人那样构建它,而不是只做那个按下启动按钮的人。