原文:Loop Engineering — Addy Osmani 作者:Addy Osmani | 发布于 2026 年 6 月
一句话总结
循环工程(Loop Engineering),就是不再让自己做那个给 agent 发指令的人——你转而设计一套系统,让系统去干这件事。这里的”循环(loop)”可以理解成一个递归式的目标:你定义一个目的,AI 反复迭代直到完成。它大概由五个构建块组成,而 Claude Code 和 Codex 现在都把这五样凑齐了。
我相信这也许就是我们未来与编程 agent 协作的方式。不过现在还早,我对此持怀疑态度,而且你绝对要小心 token 成本(token 充裕和拮据时,用法天差地别)。所以我想把”它是什么、意味着什么”拆开讲一讲。
Peter Steinberger 最近说:
你不该再去给编程 agent 写提示词了。你应该去设计那些替你给 agent 写提示词的循环。
无独有偶,Anthropic 的 Claude Code 负责人 Boris Cherny 也说过:
我已经不再亲自提示 Claude 了。我有一堆循环在跑,它们替我提示 Claude、自己琢磨该干什么。我的活儿就是写循环。
好,那这些话到底是什么意思?
大概有两年的时间,你想从编程 agent 那里得到点东西,方式就是:写一个好的提示词,再给足上下文。你敲一句,读一句回的,再敲下一句。agent 是工具,而你全程握着它,一回合接一回合。这一部分,正在过去——至少有人这么认为。
现在你构建一个小系统:它去发现工作、分发任务、检查结果、记录完成情况,然后决定下一步;你让这个系统去”戳”那些 agent,而不是你自己。我之前写过它的表亲——agent harness engineering(agent 框架工程):即搭建单个 agent 运行的环境,以及”工厂模型”——那个真正构建软件的系统。循环工程比 harness 高一层:harness 是按计时器跑的,它能衍生出小帮手,还能自己喂自己。
让我意外的是,这已经不算是”工具”层面的事了。一年前你想要个循环,得写一大堆 bash,然后永远维护下去,而且那是你一个人的事。现在这些零件已经直接内置在产品里了。Steinberger 列出的清单,几乎严丝合缝地对应 Codex 应用,也几乎同样对应 Claude Code。一旦你注意到两者形状相同,你就不再纠结用哪个工具——你只管设计一个循环,让它无论落在哪个工具里都还能跑。
一个循环需要五样东西,外加一个用来记事的地方。先列出来,再一一对应:
- 自动化(Automations)——按计划触发,自己做发现和分拣。
- 工作树(Worktrees)——让两个并行 agent 互不踩脚。
- 技能(Skills)——把项目知识写下来,免得 agent 每次瞎猜。
- 插件与连接器(Plugins and connectors)——把 agent 接入你已经在用的工具。
- 子 agent(Sub-agents)——让一个负责想,另一个负责查。
然后是第六样:记忆(memory)。一个 markdown 文件,或者一个 Linear 看板——任何活在单次对话之外、能记住”什么做完了、下一步是什么”的东西。听起来简单得不像话。但这是每个长期运行的 agent 都依赖的老把戏——我在讲 long-running agents 时深入聊过:模型在两次运行之间会忘掉一切,所以记忆必须放在磁盘上,而不是上下文里。agent 会忘,但 repo 不会。
两个产品现在都凑齐了这五样。各处名字略有不同,但能力是同一回事。让我一个一个讲,因为说实话,细节才决定一个循环是站得住,还是悄悄到处漏水。
1. 自动化(Automations)
自动化是把一个循环变成”真正的循环”的东西,而不只是你跑过一次的某次运行。在 Codex 应用里,你在 Automations 标签页里建一个:选好项目、要跑的提示词、跑的频率,以及是跑在你本地 checkout 上还是后台 worktree 上。找到东西的运行结果会进入 Triage 收件箱,啥也没找到的就自动归档——这挺贴心的。OpenAI 内部就用它干些无聊活:每日 issue 分拣、汇总 CI 失败、写提交简报、猎杀某人上周引入的 bug。而且 automation 可以调用 skill,这样你就能让那个重复任务保持可维护——你触发 $skill-name,而不是往调度里糊一大坨再没人会去更新的指令。
Claude Code 走的是另一条路到达同一个地方:调度(scheduling)和钩子(hooks)。你可以用 /loop 按间隔重跑一个提示词或命令,可以安排 cron 任务,可以在 agent 生命周期的某些节点用 hook 触发 shell 命令,或者干脆把整件事推到 GitHub Actions 上,这样你合上笔记本后它还能继续跑。思路完全一样:你定义一个自主任务,给它一个节奏,发现结果会主动来找你,而不是你四处去检查。
还有第二个会话内原语值得知道,它更贴近这篇文章真正讲的东西。/loop 是按节奏重跑。/goal 则会一直跑,直到你写下的某个条件真的成立;而且每一回合之后,会用一个独立的小模型来检查”你是不是做完了”——所以写代码的那个 agent,不是给它打分的那个。你给它一句类似”test/auth 下所有测试通过、lint 干净”的话,然后走人。Codex 也有同名的东西,也叫 /goal,它会跨回合持续工作,直到某个可验证的停止条件成立,支持暂停、恢复、清除。同一个原语,两个工具都有——这其实也是整篇文章的套路。
所以这一部分负责”把工作浮现出来”。循环剩下的部分,负责”对它采取行动”。
2. 工作树(Worktrees)
你一旦同时跑超过一个 agent,文件就开始冲突——这会变成主要的失败模式。两个 agent 写同一个文件,和两个工程师改同一行代码却互不通气,完全是同一种头痛。git worktree 解决了它:它是一个独立分支上的独立工作目录,但共享同一份 repo 历史,所以一个 agent 的改动物理上碰不到另一个的 checkout。
Codex 把 worktree 支持直接做进去了,于是多个线程同时打同一个 repo 也不会撞车。Claude Code 给你同样的隔离能力,手段是 git worktree、一个 --worktree 标志(在一个独立 checkout 里开会话),还有一个能挂在 subagent 上的 isolation: worktree 设置,让每个小帮手都拿到一个跑完会自动清理的新 checkout。我之前在 orchestration tax 里写过这事的人性面:worktree 解决了机械碰撞,但你自己仍然是天花板——你能并行跑多少个,是由你的 review 带宽决定的,不是由工具决定的。
3. 技能(Skills)
skill 是你停止像金鱼一样、每个 session 都重新解释同一堆项目上下文的办法。两个工具用的是同一种格式:一个文件夹,里面放 SKILL.md 承载指令和元数据,再加可选的脚本、参考、资产。Codex 在你用 $ 或 /skills 调用时运行它,或者当你的任务匹配到 skill 的 description 时它自己运行——这就是为什么一个枯燥但精准的描述,胜过一个聪明却含糊的描述。Claude Code 一样,我把这套写法在 agent skills 里讲过。
技能也是意图(intent)不再反复向你收费的地方。我在 intent debt 里论证过:agent 每个 session 都从冷启动开始,它会在你意图的任何空隙里填上自信的猜测。skill 就是把这份意图写在外部——那些约定、构建步骤、”因为某次事故我们再也不这么干”——写一次,agent 每次运行都会读。没有 skill,循环每个周期都从零开始重新推导你整个项目;有了 skill,它就有点复利的味道了。
有一件事要分清楚:skill 是编写格式,plugin 是发布方式。当你想跨 repo 共享一个 skill、或者把几个打包在一起时,你就把它们封成 plugin。Codex 如此,Claude Code 亦然。
4. 连接器(Connectors / 插件)
一个只能看见文件系统的循环,是个很小的循环。连接器(基于 MCP)让 agent 能读你的 issue 跟踪系统、查数据库、打 staging API、往 Slack 丢消息。Codex 和 Claude Code 都讲 MCP,所以你为一个写的连接器,通常在另一个里直接能跑。而插件能把连接器和技能打包到一起,于是你的队友一次装好你的整套配置,而不是凭记忆从头重建一遍。
这就是”说着 这是修法“和”循环自己开 PR、关联 Linear 工单、CI 一绿就 ping 频道”之间的差别。连接器,正是循环能在你真实环境里动手、而不只是告诉你”如果我能做我会怎么做”的原因。
5. 子 agent(Sub-agents)
一个循环里最有用的结构性手段,远超其他:把”写的人”和”查的人”拆开。写代码的那个模型,给自己的作业打分太宽容了。一个带着不同指令(有时还是不同模型)的第二个 agent,能逮住第一个把自己说服了的那些东西。
Codex 只在你要求时才 spawn 子 agent,并行跑,然后把结果折回成一份答案。你在 .codex/agents/ 下用 TOML 文件定义自己的 agent,每个有名字、描述、指令,可选 model 和 reasoning effort——于是你的安全 reviewer 可以是高 effort 的强模型,而你的 explorer 是某个只读的快东西。Claude Code 用 .claude/agents/ 下的 subagent 和在彼此间传递工作的 agent 团队做同样的事。两者里常见的分工是:一个探索、一个实现、一个对照 spec 验证。
我已经为这事讲过两次:一次叫 code agent orchestra,一次叫 adversarial code review。它在循环里特别要紧的原因是:循环是在你不看着的时候跑的,所以一个你真正信得过的验证者,才是你能放心走开的唯一理由。子 agent 确实更烧 token(每个都各自做模型和工具活),所以把它们花在”第二意见值得付钱”的地方。Claude Code 的 /goal 底下干的就是这个:用一个新鲜的模型来判断循环是否完成,而不是干活的那个——把”制造者 / 检查者”的分工,应用到了停止条件本身上。
把它们拼起来
把上面拼起来,一条单线程就变成了一个小控制台。下面是我常用的一种形态:
一个 automation 每天早上在 repo 上跑。它的提示词调用一个 triage 技能,读取昨天的 CI 失败、开放的 issue、最近的 commit,把发现写进一个 markdown 文件或 Linear 看板。对每个值得做的发现,线程开一个隔离 worktree,派一个子 agent 起草修复,再派第二个子 agent 对照项目技能和现有测试审查这份草稿。
连接器让循环能开 PR、更新工单。循环处理不了的,就进我的 triage 收件箱。那个状态文件是整套东西的脊柱——它记得什么试过、什么过了、什么还开着,所以明天早上那次运行,会从今天停下的地方接着走。
然后看看你实际做了什么:你只设计了一次。你没给那些步骤里的任何一步写过提示词。 这就是 Steinberger 那番话落地的样子;它在 Codex 里和在 Claude Code 里是同一个循环,因为零件是同一套零件。
循环改变工作,但不会把你删掉
循环改变工作,它没有把你从工作里删掉。而且有三个问题会随着循环变强而变得更尖锐,不是变轻松:
-
验证仍然在你身上。 一个无人值守跑着的循环,也是一个无人值守在犯错的循环。你之所以把验证子 agent 从制造者里拆出来,正是为了让循环那句”完成了”有意义;但即便如此,“完成”是个主张,不是个证明。我一直重复 code review in the age of AI 里那句话:你的工作是发布你已确认能跑的代码。
-
你的理解照样会腐烂——只要你允许。 循环越快地发布你没写过的代码,”存在的东西”和”你真正懂的东西”之间的鸿沟就越大。这就是 comprehension debt,而顺滑的循环只会让它长得更快——除非你把循环做出来的东西读一遍。
-
舒服的姿势才是危险的姿势。 当循环自己跑起来,你会很受诱惑去不再有意见,直接照单全收它给回来的东西。我管这叫 cognitive surrender(认知缴械)。带着判断力去设计循环,它是解药;为了逃避思考而去设计循环,它是助燃剂——同一个动作,相反的结果。
我觉得这是我们工作方式演进的一个预演。话说回来,如果我不自己 review 代码、或者完全依赖自动化循环去修,我产品的质量就会下滑。我很可能会陷进一个下行螺旋,不断把自己挖进更深的坑。
所以,放手去搭你的循环吧,但别忘了直接给 agent 写提示词同样有效。关键在于找到平衡。
循环也会因为你而得出不同结果。两个人可以搭出一模一样的循环,却得到完全相反的结局。一个用它来在自己深谙的工作上跑得更快;另一个用它来逃避理解工作本身。循环不知道这二者的区别,你知道。
这正是让循环设计比提示词工程更难、而非更容易的地方。Cherny 的意思不是说活儿变简单了,而是——杠杆的支点挪了地方。
去搭循环吧。但要像一个打算继续当工程师的人那样去搭,而不只是那个按下”开始”的人。