循环工程:速成课
15 个概念 · 从 agentic coding 到能在你睡觉时工作、甚至「做梦」的自提示系统
你已经学会了如何驾驭 coding agent。你给出指令,它读取文件、完成修改,然后由你检查结果。做完一轮,再做下一轮,如此继续。工具始终握在你手中。
现在想象一下,你不再一直握着它,而是构建一个小系统。每天早上,系统会自行启动,查看夜间发生了哪些变化,判断什么值得处理,把每项工作交给 agent,检查结果,并且只在真正需要人做决定时才来找你。你只构建一次,之后它会自行发出提示。
这就是循环工程。有价值的技能,从你写出的提示词转移到你设计的循环。本课会讲清循环由什么组成,以及如何分别在 Claude Code 和 OpenCode 中构建循环。两种工具走的是截然不同的路,却会到达同一个地方。
前置要求:Claude Code 与 OpenCode:速成课。 那门课讲过计划模式、上下文管理、规则文件、技能、子 agent 和 MCP。本课默认你已经掌握这些内容。如果这些词对你还很陌生,请先完成那门课。循环工程直接建立在它之上。最好也完成过 Spec-Driven Development:循环的停止条件本质上是一份 spec,而那门课教你如何写好它。没有它,你仍然可以继续学习,但循环的质量最终取决于你能否准确写出条件。
第一次接触这些内容?用 2 分钟回顾你应该已经知道的知识
- 计划模式:agent 先读取文件并提出计划,在你批准后才修改内容
- 规则文件(
CLAUDE.md/AGENTS.md):简短、长期有效的项目说明,agent 会在每次会话开始时读取 - 技能(
SKILL.md):保存起来、可重复使用的指令,只有任务匹配时 agent 才会加载 - 子 agent:拥有独立上下文窗口的辅助 agent,完成一项工作后只把结果交回来
- 连接器 / MCP:把 agent 接入外部工具的标准方式,例如 GitHub、Slack 或数据库
- 上下文管理:保持对话精简;上下文越满,模型表现越差,成本也越高
如果其中任何一项对你来说是新的,请先完成 agentic coding 速成课。本课直接建立在它之上。
用日常语言解释关键词
这些词会贯穿整门课。现在先读一遍,之后任何术语感觉不清楚时,再回到这里。
| 术语 | 日常含义 |
|---|---|
| agent | 能使用工具并完成多个步骤的 AI 系统,而不只是回答问题。 |
| prompt | 你给 agent 的指令。 |
| loop | 启动工作、检查结果、记录并在需要时重复的系统。 |
| beat | loop 的一次完整运行。 |
| heartbeat | 启动一次 beat 的时间表、事件或条件。 |
| trigger / fire | 开始运行,例如 GitHub 事件触发 Routine。 |
| unattended | 运行时没有人在旁边看着每一步。 |
| stop condition | 告诉 loop 工作何时完成的可测试规则。 |
| maker-checker | 一个 agent 产出,另一个 agent 或命令检查。 |
| worktree | 独立工作文件夹和 branch,避免并行 agent 改同一文件。 |
| skill | agent 可重复使用的一套项目指令。 |
| connector / MCP | 让 agent 使用 GitHub、Slack 或数据库等外部系统的连接。 |
| state / memory | 保存在模型之外的信息,让以后运行知道之前发生了什么。 |
| spine | 本课对连接相邻 beat 的持久状态的称呼。 |
| human gate | 危险动作继续之前必须由人审查或批准的位置。 |
| Routine | Claude Code 的云端自动化,由保存的 prompt 和 trigger 启动新 session。 |
本课使用一个人体图景:heartbeat 启动工作,body 执行工作,spine 在多次运行之间承载记忆。每个比喻都会与技术含义并列。
2026 年,构建这些工具的人已经把话说得很直白。Claude Code 的创建者 Boris Cherny 说:「我不再亲自提示 Claude 了。我让 loop 运行,由它们去提示 Claude……我的工作是编写 loop。」OpenClaw 的创建者 Peter Steinberger 说:「你应该设计能提示 agent 的 loop。」随后,Addy Osmani 为这种模式命名,并列出了它的组成部分。他们都没有说工作变轻松了,而是说重要的技能发生了转移。整门课就建立在这个观点上。
有人把 loop 设计称为当前最重要的 agent 构建技能,也有人认为它只是 agent 工具早已在做的事情换了一个名字。两边都对了一部分:组成部件并不新,但它们终于便宜、可靠到可以日常使用。结果是,主要工作越来越偏向设计 loop,而不是亲自引导 agent 的每一个 turn。一项实践变成常见工作时,一个名字才真正有用。(主要引文、论断与技术细节,都列在结尾的 来源与延伸阅读。)
用一幅图看懂思维转变

把上面的图播放出来。左侧每一步都由你亲自启动;右侧由系统自动启动。每个被取代的位置都会在后面再次出现:第 2 部分的 schedule、概念 11 的 checker,以及第 5 部分的 human gate。
本课同时讲解两种工具。一个方法如果在两种工具中都能用,它就是可迁移的技能,而不是某个产品的窍门。两种工具提供部件的方式不同:Claude Code 把许多 loop 功能内置在产品里;OpenCode 提供 agent worker,由操作系统或 CI 启动每次运行。命令不同,但 loop 的结构相同。
Claude Code 已经为你提供了更多 loop 组件。OpenCode 提供 worker,scheduler 和其他组件由你自己连接。
截至 2026 年 7 月中旬为最新内容。两种工具变化很快,Claude Code 的多项 loop 功能仍处于研究预览阶段。每次使用前,请运行
claude update或opencode upgrade。在依赖某个限制或 flag 前,先查看实时文档(code.claude.com/docs、opencode.ai/docs)。
loop 可以在哪里运行?
真正的 loop 是 unattended 的:你离开后,它仍会自己 prompt。直到 2026 年 7 月,Web 完全做不到这一点。在 claude.ai 和 chatgpt.com 上,你只有一个聊天框,而聊天框每一轮都在等你:你就是 schedule。要运行真正的 loop,必须离开浏览器,使用能够自行触发的工具,也就是 Claude Code 或 OpenCode。
2026 年 7 月,情况改变了。两家厂商都把 agent 产品集成进相同的 Web 地址。Claude Cowork 现在运行在 claude.ai 上(见 Cowork 速成课)。它的远程 session 运行在 Anthropic 服务器上,因此即使浏览器标签早已关闭,计划 loop 仍会触发。ChatGPT Work 在 chatgpt.com 上做同样的事。因此「Web 不能运行 loop」已经不准确。准确说法是:聊天框不能运行 loop,但围绕它的 Web 页面可以。大多数 surface 都是付费功能,一些仍在 beta 或分阶段开放。你可以在任何地方学习和设计 loop,但要真正运行,就需要这些 surface 中的一种。本课展示其中两种 coding surface。 问得好,而且答案在 2026 年 7 月发生了变化,所以要分成前后两段。 2026 年 7 月之前: claude.ai 和 chatgpt.com 都只是聊天框。聊天框每一轮都等你,不能按 schedule 或 event 自行启动。你可以手动重新 prompt,但此时 你 就是 heartbeat,而 loop 正是为了移除这项工作。所以答案是否定的:你可以在 Web 上设计 loop,却必须转到 Claude Code 或 OpenCode 才能运行。 2026 年 7 月之后: 两家厂商都把 agent 产品放到了相同 Web 地址中。聊天框本身没有改变,仍然会一直等你。变化的是旁边新增的 surface: 两家厂商相隔几天把 loop 搬进浏览器,这是本课中心论点最强的证据:技能在结构,而不在工具。 因此分工变得更清楚,并没有消失。聊天框用于设计和练习 loop:起草 skill、写 reviewer prompt、设停止条件、手动运行一次 beat。旁边的 agent surface,无论 Cowork、Routine、ChatGPT Work 还是 OpenCode,负责运行。这正是 Spec-Driven Development 之后自然的下一步。 截至 2026 年 7 月中旬为最新内容。Web 版 Cowork 和 ChatGPT Work 都只发布了几周,会按套餐分阶段推出,也会计量用量。依赖某项限制或功能前,请查实时产品页。“但我在 claude.ai 上完成了规范驱动课程,也能在那里运行 loop 吗?”
claude.ai/code/routines 创建),早已为 coding 工作提供相同能力:Anthropic 服务器上的全新 cloud session,笔记本关闭也能运行。它需要付费套餐,仍属研究预览。
用聊天框设计和练习 loop。无人值守运行时,使用 Claude Code、OpenCode、Cowork 或 ChatGPT Work 这类能自行启动的 surface。自 2026 年 7 月起,这不再要求离开浏览器:Cowork 在 claude.ai 运行,ChatGPT Work 在 chatgpt.com 运行。
从这里开始,几乎每个概念都能在真实 session 中运行,而不只是阅读。请在页面旁打开终端(claude 或 opencode),用小型、可丢弃的 git repo 逐个尝试,避免 loop 伤及重要内容。
本课涵盖什么
| 部分 | 主题 | 你会学到什么 |
|---|---|---|
| 1 | 转变 | 循环是什么、它的 6 个部分,以及构建循环的两条道路 |
| 2 | 心跳 | 让系统自行运行:会话内、运行到完成、定时、事件驱动 |
| 3 | 主体 | 隔离、知识、行动,以及执行者–检查者拆分 |
| 4 | 脊柱 | 能在多次运行之间保留下来的状态,也是人们最容易忘记的部分 |
| 5 | 同一个循环,两种实现 | 一个从晨间分流到 PR 的完整循环,使用真实文件在两种工具中分别构建 |
| 6 | 继续做工程师 | token 成本、检查工作,以及随着循环变强而越来越严重的陷阱 |
| 实战 | 本书自己的 loop | 在生产中运行的两个 loop,以及每个 loop 把人放在哪里 |
| 练习 | 练习项目 | 由易到难,亲手构建 8 个循环 |
| 附录 | Routines 端到端 | 完整的 Routines 字段指南:每个表单字段、全部 3 种 trigger、secret、常见问题,加上 3 个动手 drill 与一个 capstone |
喜欢边做边学? 先阅读 第 5 部分,从头到尾看一遍完整循环,再回来理解各个组件。概念理解之后,请用 练习项目 亲手构建 8 个循环。
完整阅读约需两小时。完成第 5 部分和练习项目需要更久,这正是重点:你要构建循环,而不是走马观花。
三个今天就能运行的 loop,而不只是阅读
本课大部分内容讲想法,这 3 个项目不是。4 种 heartbeat 中有 3 种附带一个可以克隆、几分钟内触发的小项目,而且项目就放在解释对应概念的位置,让你在刚理解它时立刻动手。
| 项目 | 概念 | 会发生什么 |
|---|---|---|
| Watch the Space Station | 4,会话内 | 一句普通的话,真实的 ISS 在你做别的事时每分钟报告位置。关闭终端,监看就停止,而这正是概念本身。 |
| Build your portfolio | 5,条件式 | 放入 CV,把终点交给 /goal,然后离开。它读取 PDF、设计页面、检查成果,再次尝试,直到通过。 |
| The Doorbell | 7,事件驱动 | 打开 pull request,一份没人手动请求的 review 就会出现;它运行在不属于你的电脑上,无论你的笔记本是否开着。 |
它们按正确顺序逐渐变难:第一个 5 分钟完成,不需要任何额外条件;第二个要花一个真实的下午;第三个完全不需要你也会运行。(概念 6 的 schedule 暂时没有项目,因为它需要一整夜才能证明。)
3 个项目都在 agentfactory-labs。至少做第一个。亲眼看过一次 heartbeat 触发,比读过 3 次更有价值。
第一次阅读? 按顺序走完第 1–5 部分这条核心路径。跳过所有标有「可选技术细节,第一次阅读可以安全跳过」的 note。这些内容都是真实的,但后文不依赖它们,留到第二次阅读即可。完成项目 1–3,然后停下来。这条路径大约需要 2 小时;如果这些想法对你全新,约需 3 小时。完成后,你就能构建并运行一个安全 loop。
第二次阅读(在第一个 loop 真正运行之后):阅读那些深入 note、第 6 部分、项目 4–8,以及包含 3 项练习的 Routines 附录,为构建真正的 cloud Routine 做准备。本课特意这样设计:正因为此时你已经有一个正在运行的 loop,第二遍才会更有收获。
本课贯穿着两层知识,它们老化的速度完全不同。记住第一层,查阅第二层。
- 持久层。 Loop 的结构,包括 heartbeat、4 个工作部件与 spine;maker-checker 拆分;以及 loop 永远无法替你完成的两件事:意图,即把需求说清楚到结果可以被检查;责任,即为交付内容负责。这才是技能。即使下面每条命令都改变,它仍然成立。
- 机械层。 每个 flag、path、model 名称与 command。这些工具每周更新,多个功能仍是研究预览。因此,把每条命令视为指向实时文档的路标,而不是必须背下来的事实。如果本课与当前文档冲突,以文档为准。
记住 6 部分结构、忘掉每个按键,你学会了 loop engineering。背下按键却错过结构,你只学会了这个月的命令。
📚 教学辅助
查看完整演示:循环工程速成课
第 1 部分:转变
1. 从提示到循环
过去大约两年里,从 coding agent 那里获得工作的方式很简单:写好提示词,给它足够的上下文,阅读返回的内容,再输入下一条指令。agent 是一种工具,你每次只使用一轮。
循环用系统取代了作为操作者的你。系统发现工作、分配工作、检查工作、记录所做的事,并决定下一步。它替你向 agent 发出提示。
那么,价值去了哪里?它没有消失,而是分到了循环无法自动化的两个端点:意图,也就是准确说明你想要什么,让结果可以被检查;以及责任,也就是为最终产出负责。循环自动化的是中间的步骤,两个端点仍属于你。你获得回报,是因为你能表达意图并作出判断,而不是因为你忽略了工作是如何完成的。
区别并不是「更大的提示词」,而是一种不同的工作结构:
| 提示(你已经熟悉的方式) | 循环(本课新增的方式) |
|---|---|
| 你启动每一轮 | 计划或事件启动每一轮 |
| 你阅读输出并决定下一步 | 检查者检查输出,循环决定下一步 |
| 你停止输入时,它立刻停止 | 你睡觉时,它仍会继续运行 |
| 一项任务、一次会话、占用你的全部注意力 | 多次小型运行,大多无人值守,只在关口需要你的注意力 |
这是一段可选的技术细节,第一次阅读可以安全跳过。
「loop engineering」这个词被用来指两种不同的东西。本课讲的是大 loop,但你也会听到人们用它指小 loop。先把两者分清,就不会混淆。
小 loop(inner loop)。 每个 agent 内部都有一段很小的代码循环:把上下文发给模型;模型请求使用工具;运行工具;把结果加入上下文;重复。当模型不再请求工具时,循环结束。代码只有几行:
while True:
reply = model(context)
if not reply.tool_calls:
break # the model decided it is done
context += run_tools(reply.tool_calls)
请看带 break 的那一行,它很重要。小 loop 会在模型自己认为已经完成时停止,没有任何东西检查这个判断是否正确。
这会带来问题,因为模型在给自己的工作评分。常见失败是:agent 改了一个文件,信心十足地说「完成!全部修好了」,然后停下。但它根本没有运行测试。这个 turn 结束了,任务却没有完成。
所以本课强调外部停止条件。外部停止不依赖模型对自己的看法:
- 经过检查的条件:用真实测试证明工作完成
- 上限:限定最多尝试多少次
- 无进展检查:如果结果没有变好,就停止
- 独立 checker:让第二个过程给工作评分
它们存在的原因只有一个:小 loop 自带的唯一停止依据,是模型对自己的判断。
大 loop(outer loop,也就是本课)。 把小 loop 想成一个 worker 完成一项 task,大 loop 则是 manager。它决定给 worker 什么任务、何时开始、怎样给结果评分,以及明天要记住什么。小 loop 的一次完整运行,只是大 loop 的一次 beat。
各层怎样套在一起。 每一层都包住前一层。行业关注这些层的顺序也大致如此,彼此约相隔一年,所以每一层都曾经历一轮高度关注:
- Prompt engineering:你发送的文字
- Context engineering:模型在一个 turn 里看到的一切
- Harness engineering:模型周围的代码,负责运行工具和处理错误;小 loop 就在这里
- Loop engineering:本课讲的外层循环,也就是整个系统处理什么、何时启动、如何知道工作完成
所以,提示词没有以前那么重要了。它现在只是更大系统中的一个输入。

每一层也会阻止不同类型的失败,所以任何一层都无法替其他层承担职责。强 context 可以弥补弱 prompt,但再好的 prompt 也救不了缺失的 context、缺失的 checker,或仍由你亲自充当的 schedule。因此,构建时可以这样自检:这些层里,还有哪一层是我在手工完成?
把小 loop 做扎实,包括良好的停止条件、干净的上下文和恰当的工具,是实实在在的工程工作。但这些都发生在一次 beat 之内。凡是小 loop 与大 loop 的关系重要时,本课都会指出。参见停止条件(概念 5)与 connector 设计(概念 10)。

这不是魔法,也不是「设置好后就不用管」。 循环自行运行,也意味着它会自行犯错。本课的全部内容,都是为了构建一个你真正敢于放手运行的循环。这比提示更难,而不是更简单。回报是杠杆效应:一个设计良好的循环会反复替你工作,完成原本每次都得由你手动启动的任务。
2. 循环由什么组成
真正能自行运行的循环有 5 个部分和 1 条脊柱。其中 4 个部分已经在 agentic coding 课程中出现过,现在它们有了新的职责。

- 心跳:启动循环的计划(或事件)。没有它,你拥有的只是一次运行,不是循环
- 工作树:提供隔离,让两个同时工作的 agent 不会覆盖彼此的文件
- 技能:只写一次的项目知识,让每次运行都不必从零开始
- 子 agent:执行者–检查者拆分。编写代码的 agent 不能同时负责给代码评分
- 连接器(MCP):让循环能在真实工具中采取行动(打开 PR、更新工单),而不只是提出建议
还有第 6 个部分,初学者最容易跳过:
- 状态 / 记忆,也就是脊柱。 磁盘上的文件(或 Linear 一类看板)保存已经完成和接下来要做的工作。模型会忘记不同运行之间的一切。脊柱让今天的运行知道昨天做过什么。没有脊柱,就没有循环,只会永远重复相同的第一步。
本课后面的每一节都会讲解一个组件,最后再用一个完整示例把它们连接起来。
不是。本课使用代码作为示例,也就是 repo、测试和 PR,因为工具在这类工作上最强。但 loop 的结构并不在乎工作内容是什么。书籍、报告、课程、newsletter,都可以保存为装满文件的 repo,loop 的每个部分都能原样映射过去。例如,本书就是一个由 Markdown 文件组成的 repo。每晚 link check、style sweep、标记过时模型名称的 pass,都是本章所说的 loop。
真正发生变化的是 checker。代码拥有最诚实的 checker,也就是测试和 linter;命令能够证明「完成」。Prose 没有 test suite,因此 writing loop 要依赖两类更弱的检查:能机械验证的内容,例如 broken link、缺图、禁用词和 heading level;以及一个按照书面 rubric 审查其他内容的 reviewer agent。(机械检查这一层比内置检查范围更大。只有你的项目知道的规则,例如「memory file 中不能有相对日期」或「每张图都需要超过 40 词的 alt text」,只要写成命令,仍然属于机械检查。概念 11 后面的插曲会说明怎么做。每把一条规则从 rubric 下移到 command,就把一项主张变成一项证明。)有一个技巧可以让 rubric 成为停止条件:给它分数与及格线。「按照 rubric 给这份 draft 评分,低于 95 不得停止」会把软判断变成 loop 可以据此行动的条件。但模型给出的分数仍然只是一项主张,不是证明,也比通过测试更弱。Checker 越弱,越多工作必须经过人工关口。

还有一件事不会变化:heartbeat 菜单。领域不会决定 heartbeat,任务形状才会。「现在开始做,直到完成」是 conditional loop,会立刻启动。「每天夜里做一次」是 schedule,会在设定时间启动。「有东西到达时作出反应」是 event。无论 repo 中放的是代码还是章节,这份菜单都相同。而且当你正在主动写作或编码时,通常根本没有 loop。你自己就是 heartbeat,loop 只承担周围那些边界明确的工作块和维护任务。
如果你的工作对象是文档而不是代码,先照常阅读本课,再查看 Cowork 与 OpenWork 速成课,了解如何用非编码工具构建同样的 loop。
3. 两条道路:内置组件与更底层的一层
这是两种工具真正不同的地方,也会塑造后面的全部内容。

Claude Code 把循环组件内置在产品里。 心跳(/loop、/schedule、云端 Routines)、带内置检查者的运行到完成(/goal)、隔离(--worktree)和事件输入(Channels)现在都是内置命令。一年前,你需要编写并维护一堆 shell 脚本才能实现这些功能;如今大多只需完成配置。
最重要的是 Routines:运行在 Anthropic 服务器上的云自动化,即使笔记本已经关闭也会继续运行,可由计划、API 调用或 GitHub 事件启动。便利的代价是账号级每日运行上限;请在 claude.ai/settings/usage 查看当前上限,不要依赖固定数字。Routines 仍处于研究预览阶段,未来可能变化。
OpenCode 提供更底层的一层。 它没有内置云调度器。OpenCode 是你调用的工作者,而心跳由你从操作系统或 CI 提供。
关键命令是 opencode run "<prompt>"。它不打开聊天界面,只运行一条提示,打印结果,然后退出。这条命令就是循环的一次心跳。要把它变成循环,可以用按计时器触发的东西包住它:cron 或 launchd(macOS 和 Linux)、Task Scheduler(Windows),或带计划触发器的 GitHub Actions。这需要更多接线,但你能完全控制系统,可以使用已有机器运行,也不需要厂商云。
请注意,两个标签页描述的是相同的 5 个部分。无论心跳是托管的 Routine,还是 cron 中的一行命令,心跳就是心跳。无论由 /goal 评分,还是由第二次 opencode run 完成,执行者–检查者拆分仍是同一个想法。只需学会一次循环的结构,就可以迁移。 这正是我们同时教授两种工具的原因。
我们将要到达的地方(先看完整循环)
在拆解各个组件之前,先看终点。你将在 第 5 部分 构建的循环只有 6 个朴素步骤:
every weekday at 9am: # 1. Heartbeat
read progress.md # 6. Spine (memory)
find overnight CI failures + issues # what to work on
for each one:
draft a fix in its own checkout # 2. Worktree
using the project's triage skill # 3. Skill
have a separate reviewer grade it # 4. Subagents (maker/checker)
if PASS: open a PR via GitHub # 5. Connector (MCP)
if risky: write it to progress.md and leave it for a human
update progress.md # 6. Spine again
请记住这幅图。第 2–4 部分的每个概念,都是其中一行。
一个循环每天早上运行,但每次都从全新状态开始,完全不记得昨天做过什么。它缺少 6 个部分中的哪一个?为什么这会破坏循环? 缺少的是脊柱(状态 / 记忆)。模型会忘记不同运行之间的一切,因此如果磁盘上没有状态文件,循环只会永远重复第一步,而无法承接昨天的工作。查看答案
第 2 部分:心跳
心跳把一次运行变成循环。心跳有 4 种,从「只在当前会话中持续」到「完全不需要你也能运行」。请按顺序学习,因为多数真实循环都会使用后两种。

先用日常语言看 4 种,再进入细节。会话内 loop 像厨房定时器,只有你还在厨房里才会响。条件 loop(运行到完成)表示「一直做,直到试吃的人说可以了」。Schedule 像闹钟,不管你在不在家,到点都会响。Event 像门铃,没有人按时什么也不发生;一旦按下,就立刻响。记住这 4 幅图,下面每条命令都只是其中一种的具体名字。
这四种心跳背后有一个共同的想法:循环不是单次动作,而是「做这件事、等待、再做一次」,反复进行,因此必须有某个东西在两次搏动之间保持清醒,好触发下一次。唯一的问题是这个东西存在于哪里。
- 会话内循环把计时器放在你打开的会话里,也就是终端开着时一直运行的那个进程。关闭会话,握着计时器的东西就没了,循环也随之停止。
- 定时任务或 Routine(你会在概念 6 中构建它们)把计时器移到会话之外,交给一个永不休眠的调度器(你自己机器上的 cron,或云端 Routine 所用的 Anthropic 服务器)。每一次滴答,它都会启动一次全新的、短暂的运行,让它跑完,然后关掉,下一次再启动一个新的。同一个循环,但你这边不需要保持任何东西开着。
| 心跳 | 计时器存在于哪里 | 两次搏动之间由谁保持清醒 |
|---|---|---|
会话内 /loop | 会话内部 | 你打开的会话(你的机器、终端开着) |
| 定时任务 / Routine | 外部,在调度器中 | 调度器,它每次滴答都启动一次全新的运行 |
这幅图也请记住。它能解释后面出现的每一条限制:会话内循环会随它的会话一同停止,而必须在笔记本关闭后依然存活的循环,需要外部那种(概念 6)。
4. 会话内循环(你看着它重复)
这是最简单的 heartbeat:只要会话保持打开,就按计时器重新运行一条提示。它适合「一直盯着,直到完成」的任务,比如部署、长时间测试或 CI 作业。
只要 session 开着,它就按间隔触发一次 beat;因为你一直在旁边,它能等到部署完成。关闭 session,监看就停止,所以会话内 loop 不能在你睡觉时继续运行(概念 6)。
使用内置的 /loop 技能,提供时间间隔和提示:
/loop 5m check if the deployment finished and tell me what happened
Claude 会把时间间隔转成 schedule,给任务分配一个 ID,并在会话保持打开时每 5 分钟运行一次提示。完成后,取消它,再去做别的事。
取消 /loop。 每个 loop 都是一个带 ID 的定时任务,所以你可以像启动它一样,用自然语言停止它:
show my running loops
cancel the deploy-check loop
Claude 会查找并取消任务。如果同时运行多个 loop,它会询问你要取消哪一个;也可以直接提供任务 ID。关闭会话同样会停止普通会话 loop,但主动取消才是干净的做法:一个你本来就想停止的 loop,不该依赖会话碰巧关闭。精确子命令属于机械层。自然语言失效时,查阅 实时文档。
阅读 heartbeat 和亲眼看见它触发,不是一回事。本概念附带一个只做这一件事的小项目:监看真实的国际空间站绕地球飞行。
git clone https://github.com/panaversity/agentfactory-labs.git
cd agentfactory-labs/crash-course/loop-eng/iss-loop
claude
工具询问是否信任该文件夹时,选择 yes。这会启用项目自带的权限,避免 loop 中途停下来询问。然后输入一句普通的话:
/loop show me the location of the ISS every minute
这是你最后一次输入。之后,每分钟都会出现一个新位置,你可以同时做别的事。请注意两点,它们合起来就是这个概念的全部:
/loop把「every minute」读成 heartbeat。 你没有写 schedule、脚本或 URL;项目文件保存了其余知识,所以提示只需一句话。- 现在关闭终端。 监看也随之消失。这不是 bug,而是会话内 loop 的定义,也是概念 6 与 7 存在的原因。
完整说明和两个更难的提示,见项目的 README。
可选:会话关闭时会发生什么?
需要知道的一项限制: 普通会话中的 /loop 有意运行在你的会话内部。关闭终端或让笔记本进入睡眠,它就会停止。这是一项安全功能,不是 bug。随手启动的会话内循环,本就不该比启动它的会话活得更久。有两项近期改动放宽了这条规则:
--resume会恢复尚未过期的任务。一个周期性任务在你创建后的七天内保持有效。- 把会话移到后台会把你的
/loop任务一并带走,因此即使没有打开终端,它们也会继续触发。但要注意:机器睡眠期间错过的触发,它们不会补上。
对于无论如何都必须继续运行的工作,不要依赖 /loop,而要使用定时任务或 Routine(概念 6)。工具现在也会替你处理这一点。在云会话(运行在远程服务器上、而非你自己机器上的会话)中,近期版本已经完全不再提供 /loop,因为这种会话在你的请求一结束就会关闭,没有任何东西留下来维持循环。
一个折中选项:后台会话。
后台会话是会话内 /loop 和 Routine 之间的中间档:在你关闭终端窗口后,它会在你自己的机器上保持一次运行存活。用 claude --bg 启动。
它本身只做一件事然后停止,不会按计时器重复。它的价值在于承载循环。启动一个 /loop,把那个会话送到后台,循环就会随之运行,即使窗口已关闭也继续触发。(之后 claude agents 会列出它,/resume 会重新打开它,并标记为 bg。)
当你想关闭终端、却又想让一个循环继续盯着某件事(比如一次部署或一次长时间测试)时,就用它。只是别忘了「在你自己的机器上存活」的代价:这台电脑必须保持清醒。一旦工作必须在笔记本关闭后依然存活,你就进入了调度器的领域,正确的工具是 Routine(概念 6)。
这三个档位可以归到同一个问题上:需要多少东西保持清醒?
| 档位 | 选项 | 关闭终端后仍继续触发? | 笔记本睡眠或关机后仍继续触发? |
|---|---|---|---|
| 最低档 | 会话内 /loop | 否 | 否 |
| 中间档 | 承载 /loop 的后台会话(--bg) | 是 | 否(需要你的机器保持清醒) |
| 最高档 | 定时任务 / Routine | 是 | 是(云端 Routine 运行在 Anthropic 的服务器上) |
OpenCode 没有 /loop 命令,需要自己用 shell 构建计时器。由于 opencode run 会在运行一条提示后退出,因此包含 sleep 的 while 循环可以实现同样的效果:
while true; do
opencode run "check if the deployment finished; if it did, say DONE"
sleep 300 # 5 minutes
done
这与 /loop 是同一个想法,只是由更底层的组件搭成。Shell 是 heartbeat,opencode run 是 beat。按 Ctrl-C 可以停止前台 loop;如果它在后台运行,就终止对应进程。每次全新的 opencode run 都会先启动完整 runtime,包括配置、模型、插件和 MCP server,之后才做一件事。要避免每个 beat 都支付这份启动成本,可以先启动一次 server,再连接到它:
opencode serve --port 4096 &
# then, each beat:
opencode run --attach http://localhost:4096 "check the deploy status"
你仍在旁边看着工作时,使用会话内 loop。工作必须在你离开后继续时,使用 cloud Routine 或其他无人值守计划。
5. 运行到完成(由循环决定何时停止)
固定计时 loop(概念 4)按选定的时间间隔重复。每次运行都可以检查工作,但检查结果不会停止计时器。它会一直继续,直到你取消、会话结束,或任务过期。
条件 loop,也叫运行到完成,会在某个明确条件成立时停止。想的是「一直运行,直到测试通过」,而不是「我看着的时候每 5 分钟运行一次」。
关键区别很简单:固定计时 loop 不知道工作何时完成;条件 loop 因为工作已经完成而停止。 必须由独立命令或 checker 作出这个判断。完成工作的 agent 不应批准自己的结果。
Loop 不断尝试,由一个独立 checker判定「完成」。目标一旦得到证明,它立即停止,不靠计时器,也不靠你。
使用 /goal。给它一个 Claude 能在自己的输出中证明的停止条件,比如「test/auth 中的全部测试通过」。Claude 会持续工作,直到条件成立。
每个 turn 之后,一个独立的小模型(默认是 Haiku)会读取 transcript,并询问「完成了吗?」因此,写代码的 agent 不会给自己的工作评分。
Checker 不能运行命令,只能阅读对话。所以 worker 必须运行测试,并在输出中展示结果。没有可见证据,checker 就无法确认目标已经达成。
/goal All tests in test/auth pass and `npm run lint` is clean.
它会编辑代码、运行测试、读取失败信息、再次尝试,直到检查者确认条件确实成立,或你用 无人值守的运行指循环在无人盯着的情况下工作,可能在夜间,也可能在你离开键盘时。没有人会注意到它卡住并把它停下来。正因如此,这里的重试才需要一个上限。 重试指循环再次尝试一个失败的步骤(比如一次超时的 API 调用)。如果没有上限,卡住的循环可能会在你睡觉时无休止地重试,白白耗费时间和 token。因此 Claude Code 现在会替你设定这个上限: 这些参数名和数字属于机械层,因此在依赖它们之前,请查看实时文档。而持久的要点是:如今连厂商都默认,一次无人值守的运行需要一个由人有意选定的上限。/goal clear 手动停止。系统没有内置的「尝试 N 次后放弃」。如果需要上限,请把它写进条件(…or stop after 20 turns)。条件应当能由命令证明,例如「测试通过且 lint 干净」,而不是「认证代码写得好」。Spec-Driven Development 在这里发挥价值:验收标准本来就是命令可以证明的条件,因此一份好 spec 会直接提供停止条件。可选:Claude Code 当前的重试设置
CLAUDE_CODE_MAX_RETRIES),你可以把上限提高到 15。CLAUDE_CODE_RETRY_WATCHDOG=1。它会把临时错误(比如短暂的网络抖动)的重试时间拉长很多(撰写本文时最多 300 次,约三小时的退避),并在你自行设置 MAX_RETRIES 后解除那个 15 次的上限。
npm test 是一个整齐的停止条件,因为测试已经有人写好了。大多数工作不是这样。Portfolio 项目 提出更难的问题:一个人要看的页面,怎样才算「完成」?你能否把它写得足够精确,让 loop 自己到达终点?
把 CV 或 LinkedIn PDF 放进文件夹,然后给 /goal 一条终点线:
/goal Build my portfolio in site/ from my-cv.pdf, following spec.md. Done when `python3 check.py site` prints 20/20 and the reviewer agent replies PASS on all six judgment promises — show me both. Stop after 15 check attempts or 3 review rounds and write what is still failing to progress.md.
然后离开。它会读取 PDF、决定设计、写文案、构建页面、运行 checker、读取失败,再次尝试,直到这句话成为事实。
其中每个从句都在实际运用本概念。命令可以证明的条件: check.py 运行 20 项机器能够裁定的检查,比如 5 个 section 是否齐全、对比度是否合格,以及手机屏幕上是否有内容溢出。可见证据: show me both 存在,是因为 /goal 的 checker 只读 transcript;没有打印出来的结果无法确认。上限: 最多 15 次尝试,因为 /goal 没有内置放弃机制,追逐不可能条件的 loop 会追整晚。Checker 不能是 maker: spec 强制要求独立 reviewer agent。
最后一点让项目不再只是教程。20/20 仍不算完成。 Reviewer 还必须通过 6 个命令无法衡量的问题:文案是否忠于 CV、页面是否真的经过设计而不只是排版、它是否做到了 PDF 本身做不到的事。Spec 说得很直白:「A 部分只需一个上午,B 部分才是真正的工作。」你在概念 2 的 checker ladder 里已经见过这个想法,在这里会真正体会到。
项目有一条绝不让步的规则,也是最尖锐的一课:绝不要为了通过而编辑 check.py。 一个只追求绿色结果的 loop 很可能会这么做;注意到这种冲动,本身就是课程内容。
OpenCode 没有 /goal,所以要用 shell 和退出码构建同样的执行者–检查者停止机制。模式是:agent 完成工作,然后由一条真实命令(不是 agent)决定是否停止。
for i in $(seq 1 8); do # cap the tries — never loop forever
opencode run "Make the tests in test/auth pass and fix any lint errors."
if npm test -- test/auth && npm run lint; then
echo "Condition met on try $i"; break
fi
done
这里,测试运行器和 linter 就是检查者。这是最诚实的检查者,因为命令无法说服自己「工作已经没问题」。要做更智能的检查,可以使用专门的审查 agent 再运行一次 opencode run,让它输出 PASS 或 FAIL。一定要限制尝试次数;没有上限的重试循环,会让 token 账单失去控制。
OpenCode 也开始把上限内置进去。每个 agent 都可以在配置中设置 steps 上限;旧名称 maxSteps 已弃用。Agent 到达上限后,会被要求总结已经完成的工作和剩余事项,而不是继续尝试。两种上限防守不同的层面:shell 上限控制 loop 最多触发多少个 beat;steps 控制一个 agent 在一次 beat 内最多走多少个 turn。两者都要设置。
每个循环都需要三种停止条件。每一种都能防止一类特定的失败:
| 停止条件 | 它是什么 | 一旦缺失…… |
|---|---|---|
| 成功条件 | 循环据以判断任务已完成的依据 | 没有东西定义「完成」,循环就无法有意停下,也无法评分 |
| 上限 | 一个天花板:最大尝试次数、分钟数或支出 | 一个无法达成的目标会耗尽你的全部 token 预算 |
| 无进展检查 | 发现 agent 反复用相同参数执行相同动作(说明它卡住了,再重试也没用) | 它会把整个上限花在重复同一个错误上 |
你会在网上遇到一个名字:Ralph 循环。 它是最广为人知、最简单的运行到完成循环:反复运行同一个提示,每次运行读取并更新同一个状态文件。它只保留了三种停止条件中的两种(一个成功条件和一个时间上限),没有卡住检查、没有技能,也没有独立的检查者。正是这份朴素,让它把这个道理讲得格外清楚。条件含糊的 Ralph 循环会一直徘徊,直到时间上限烧尽;而换成一个命令可以证明的条件,同样的循环就能运行良好。
循环的好坏,取决于它的停止条件。
一个运行到完成、跑了很多轮的循环,会把自己的上下文塞满垃圾:旧的工具输出、走过的死胡同、过时的推理。随着这堆东西越积越多,模型的回答也越来越差。社区把这种结果叫作厄运循环(doom loop):混乱的上下文导致更糟的决定,更糟的决定又添了更多混乱,让下一个决定更糟。应对之道,正是 agentic coding 课程 里那些相同的上下文习惯:
- 压缩长时间运行: 每隔一段时间,用一段简短的摘要替换原始的来回对话,让上下文保持精简。
- 把大块输出移到文件里: 把大体量的结果(日志、数据、生成的文本)写入文件,在上下文里只留一个指针,而不是把整段内容粘进去。
- 把杂乱的子任务交给子 agent: 让一个助手在它自己的上下文里做嘈杂的探索,只把干净的结果返回来。
这三条背后是同一个想法:把上下文当作一份你有意花用的预算,而不是一个你不停往里倒的水桶。一个更小、更干净的上下文,才能让长时间运行的决定保持敏锐。
6. 无人值守的计划(你睡觉时也运行)
这种心跳让循环工程真正有了意义:一项任务无论你是否在电脑旁都会运行。「每个工作日上午 9 点,整理夜间的 CI 失败。」「每周一检查依赖,并为安全修复打开 PR。」
在 claude.ai 上设好 Routine,然后合上笔记本电脑;Anthropic 的服务器仍会按计划运行它。
先说说命名。**调度器(scheduler)**是一个笼统的概念:任何一直开着、能按时启动一次全新运行的时钟(cron、GitHub Actions,或某个云服务)。而 Routine 是 Claude Code 自带的、托管在云端的调度器,由 Anthropic 同时提供时钟和机器,因此你这边不需要开着任何东西。每个 Routine 都是一种调度器;但并非每个调度器都是 Routine。
根据笔记本电脑是否需要开机,分为两类。
云端 Routines(笔记本可以关机)。 这是现代默认方案,值得慢下来理解。云端 Routine 是一条常驻指令,存放在 Anthropic 的服务器上,而不是你的电脑上。指令只写一次,此后会在设定时间自行运行,无论笔记本是开着、睡眠,还是装在包里。可以把它想成雇了一位坐在 Anthropic 办公室而不是你办公室里的 worker:你交出书面工作说明,不需要自己托管,工作照样发生。
整节都用一个例子。每天早上,你花 30 分钟做同样的分流:读取夜间新开的 issue、给它们打标签、标出看起来像 crash 的内容,再把摘要发到团队 Slack。这半小时非常适合 Routine。它会重复、遵循能够写下来的规则,而且不需要你在场,只需要你的指令。
每个 Routine 的 4 个部分。 创建时要填 4 个空白,每个回答一个问题。
- Prompt:它要做什么? 这条常驻指令应像你学过的 spec 一样写:目标、规则和「完成」的样子。每次运行都是同一条提示,而且没人现场澄清,所以它必须经得住你的缺席。分流示例的提示如下:
Review all issues opened in the last 24 hours. Label each as bug,
feature-request, or question. If any issue describes a crash or data
loss, add the "urgent" label. Then post a summary to the #triage Slack
channel: total new issues, how many urgent, and one line per urgent
issue. If there are no new issues, post "No new issues overnight."
Do not close or comment on any issue.
请注意这里的 spec 结构:目标(分流并总结)、规则(按这些类别打标签,crash 或数据丢失算 urgent)、边界(不得关闭或评论),以及即使没有新 issue 也明确定义的「完成」。
-
Repos:它可以碰什么? 指定它可以工作的仓库。没有列出的内容都在触达范围之外。只授权
yourteam/product-app,就只有这一个;其他仓库,包括放计费代码的那个,对 Routine 而言都不存在。 -
Connectors:它能接触什么? Slack、邮件、日历。这些是 Routine 伸出仓库以外的手,用于读取外部世界并向你汇报。只接 Slack connector,让它可以发到 #triage,不接其他东西。Connector 是权限,不是建议:没有邮件 connector,即使 prompt 要求,它也发不了邮件。
-
Trigger:何时启动? 这就是 heartbeat,有 3 种,分别对应 3 种工作形状。Schedule:时钟在每个工作日 8:30 启动,让摘要在团队上班前就等在 Slack。API call:另一个程序启动它,例如部署脚本完成发布后,触发一次「做 smoke check 并汇报」的运行,只有真正有事要检查时才跑。GitHub event:仓库事件启动它,例如「pull request opened」触发器在安静的一天运行 0 次,在忙碌的一天运行 9 次(概念 7 会详细讲)。
做什么、可以在哪里行动、能接触什么、何时启动。每个 Routine 都是这 4 个答案。于是分流 worker 已经完整定义:上面的 prompt、一个 repo、一个 Slack connector,以及工作日 8:30。
一个功能,3 个入口。 可以在 claude.ai/code/routines、Desktop 应用或 CLI 的 /schedule 中创建 Routine。它们不是 3 种不同功能。无论从哪个入口创建,都会保存到同一个云账号,并在另外两处出现。比如,你可以在终端里用 /schedule 创建,再到浏览器里编辑。
一次运行会发生什么。 周一 8:30,Anthropic 的服务器启动全新的 Claude session,把 prompt 交给它,并只提供列出的 repo 与 connector。它读取周末的 issue、打标签、向 #triage 发送「7 个新 issue,1 个 urgent:Android 登录 crash(#412)」之类的摘要,然后关闭。周二的运行完全从新 session 开始,周一的 session 已经消失。一切都不依赖你的机器,这正是它成为真正 loop 而非需要你看守的 session 的原因。(也正因此,spine(概念 12)必须放在 repo 里。) 依赖它之前要确认两条当前产品规则。 规则 1:存在每日上限。 每个账号每天可运行固定次数:发布时 Pro 为 5、Max 为 15、Team/Enterprise 为 25。运行在别人服务器上的无人值守系统必须有预算;这是每项云服务都存在、应提前纳入设计的数字。以 Pro 为例:早晨分流 1 次、晚间 commit 摘要 1 次、某天 4 个 PR 触发 reviewer 4 次,共 6 次,比上限多 1 次。可依次考虑:把两个日报合为一个 Routine;只在繁忙日为 PR reviewer 购买额外用量;或升级套餐。应在 loop 静默停在第 5 次之前算清。3 个细节会缓和限制:这些是发布时数字,应查看 规则二:默认情况下,它只能推送到 这是好事,不是障碍。它从第一天起就保证了无人值守的工作是安全的:Routine 可以尽情完成它要做的一切,但合并什么仍由你决定。 举个例子。你设置了第二个 Routine,让它在夜间修复不稳定的测试。凌晨 3 点,它把修复推送到一个 后来,当某个仓库经过多次干净的运行赢得了你的信任,你可以只为那个仓库,用「允许不受限制的分支推送」设置关掉这条规则。有意为之,一次一个仓库,就像把一把钥匙交给别人。可选:当前 Routine 的限制与分支规则
claude.ai/settings/usage;一次性计划运行不计数;超出后可以购买额外用量。claude/ 分支。 新建的 Routine 无法写入 main。它推送的每个分支都必须以 claude/ 开头。claude/ 分支。早上你读一遍这处改动,确认看起来没问题,再自己合并。你睡觉时 Routine 干了活;你醒来后做了检查。这中间的间隔正是全部意义所在。
何时该用 Routine: 只要工作不需要你的机器。比如 issue 分流、每周五给相关人的「本周变化」摘要、监看竞争对手的 changelog、为常规支持问题起草回复。每当你想到「这件事应该每天自己发生」,就适合用它。(Routines 附录 会逐项讲解表单、环境与 secret。云端和 cron 之间还有第三种原生选择:在 Desktop 应用中创建的 Desktop scheduled tasks。它们在本地真实文件上运行,包括未保存的改动,不需要打开 session,但机器必须开着。) 你可以用平实的中文,在终端里管理一个 Routine 的整个生命周期。 创建它、列出你已有的、立即运行一个,或者修改它的运行时间: 有三点要快速了解: 如果 可选:从终端管理 Routine
/schedule every weekday at 9am, run the daily-triage skill # create it
/schedule list # see what you have
/schedule run the triage routine now # fire one run, to test it
/schedule update the triage routine to every two hours # change the timing
update 也接受一个 cron 表达式:一小段用来精确写出时间的代码。例如,0 9 * * 1-5 表示「上午 9 点,周一到周五」。/schedule tomorrow at 9am, …。一次性任务不计入你的每日运行上限,所以它是一种免费的方式:先把提示试跑一次、确认没问题,再决定是否让它每天运行。/schedule 在你的 CLI 里似乎不存在,Routines 附录 说明了该检查什么。
在自己的 cron 中运行一次提示(笔记本开机,不用 Anthropic 云)。 claude -p 运行一条提示后退出,可以直接放进电脑的 crontab:
# every weekday at 9am: sort through CI and summarize failures
0 9 * * 1-5 cd /path/to/repo && claude -p "check the CI dashboard and summarize any failures" >> ~/claude-cron.log 2>&1
当你准备好构建第一个真正的 Routine(每一个表单字段、全部三种触发器、密钥,以及常见问题)时,本课结尾的 Routines 附录 会一步步带你走完整个过程。
Schedule 是唯一不适合盯着触发的 heartbeat,因为午夜到来时你通常没在看。因此,这里有一个专门留在夜间独自运行的项目:Sky Watch。它每天早晨检查 NASA 的小行星 feed,并留下说明:今天有什么经过地球,其中是否有危险。
克隆项目后,先手动证明它能工作:
what asteroids are coming this week?
你会得到一份普通语言的观察结果,比如「没有危险,最近的也在月球距离的 23 倍外,一切正常」。然后用一行把它变成 loop:
/schedule every day at midnight, run the sky-watch skill for today and write me the forecast
合上笔记本。早上,这份观测会等着你,它由一台不属于你的机器在你睡觉时写成。接上邮件 connector,还可以把带有距离条的视觉卡片发到收件箱。请注意提示写的是 for today,不是「未来一周」:每日运行应报告触发当天,否则每天只是在重发昨天的预报。窗口要与 cadence 匹配。
它与门铃(概念 7)有两个区别。第一,它向前看:警告明天的经过,而不是汇报昨天发生的事。第二,即使什么也没发生,它也会照常发声。大多数早晨只是「一切正常」,而安静报告本身就是 watch 的意义。事件驱动 loop 在平静的一天保持沉默,schedule 则照样报告。若不想等到午夜,先用一次性任务排练(/schedule in 2 minutes, run the sky-watch skill),一次性任务不计入每日上限。这正是第 6 部分规则的实际应用:在快速、有人看守的条件下证明它,再信任缓慢、无人值守的运行。
OpenCode 的无人值守心跳始终来自操作系统或 CI,这正是 OpenCode 的路线。使用不带聊天界面的 opencode run,让调度器负责触发。可选:社区调度插件
opencode-scheduler 等社区插件可以把自然语言请求转换成操作系统计划,也可能防止运行重叠,并拒绝会等待人工回答的提示。它们属于第三方软件,依赖前请确认插件仍在积极维护。
在自己的机器上使用 cron:
# every weekday at 9am: sort through CI and summarize failures
0 9 * * 1-5 cd /path/to/repo && opencode run "check the CI dashboard and summarize any failures" >> ~/opencode-cron.log 2>&1
在云端使用 GitHub Actions,这样你的任何机器都不必保持开机。下面的 model 字符串仅作示例,请运行 opencode models 查看当前安装认识的准确 ID:
name: Scheduled OpenCode Task
on:
schedule:
- cron: "0 9 * * 1-5" # weekdays at 9am UTC
jobs:
opencode:
runs-on: ubuntu-latest
permissions: { contents: write, pull-requests: write, issues: write }
steps:
- uses: actions/checkout@v6
with: { persist-credentials: false }
- uses: anomalyco/opencode/github@latest
env: { ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} }
with:
model: anthropic/claude-sonnet-5 # confirm with `opencode models`
prompt: |
Review the codebase for TODO comments and summarize them.
If any are worth acting on, open an issue to track them.
定时事件必须提供 prompt,因为没有评论可以充当指令。如果循环要创建分支或 PR,还必须授予 contents: write / pull-requests: write 权限。
Claude Code Routine 在 Anthropic 服务器上运行;OpenCode schedule 由操作系统或 GitHub Actions 启动。两者都在指定时间开始全新工作,因此都需要保存在模型之外的 state。
当前官方文档把 OpenCode GitHub Action 写作 anomalyco/opencode/github@latest;部分旧指南仍使用 sst/opencode/github@latest。两者指向同一个项目,请使用 opencode github install 生成的版本。Claude Sonnet 5 是当前 Sonnet 层级,API ID 为 claude-sonnet-5;它直接取代 Sonnet 4.6。新的 tokenizer 会让同一文本产生更多 token,因此不要照搬旧预算。较早的 4.5 代模型,例如 Haiku 4.5,既有带日期的规范 ID(claude-haiku-4-5-20251001),也有指向最新快照的无日期别名 claude-haiku-4-5。下面的示例为了可复现性固定使用 claude-haiku-4-5-20251001。请运行 opencode models 查看当前安装认识的准确字符串。
7. 事件驱动(事件发生时立即响应)
计划问的是**「每小时检查一次」。事件问的是「X 发生时立刻响应」**。PR 打开、issue 创建或消息到达时,循环立即运行。
它没有时钟,也无人守候。PR、消息或告警到达时,loop 立即沿对应 route 响应,然后再次安静下来。
本节讲的是事件。所谓事件,就是某件事发生了。每一类事件都有一个接住它的工具,也就是对它作出反应的工具,而这些工具并不都一样。GitHub 事件和「其他所有」事件由 Routine 接住;聊天消息由 Channel 接住。所以三个接住者里有两个是 Routine,另一个(Channel)不是。Channel 唯一的弱点是需要你的机器开着,这一点在下面的表格里写得很清楚。
在 Claude Code 里,事件来自哪里决定了你用哪种工具。一共有三条路线:
1. 来自 GitHub,用 Routine。 Routine 不一定要按时钟运行,它的触发器也可以是一个 GitHub 事件。有两类可用:一个**拉取请求(pull request)发生变化(打开、更新或合并),以及一次发布(release)**被发布。例如:「当 PR 打开时,审查它并留下评论」,或「当发布版本时,起草更新日志」。
在构建之前,有三点需要知道:
- 安装。 必须在仓库上安装 Claude GitHub App。注意:
/web-setup只授予克隆访问权限,并不会安装这个应用。很多人第一次尝试时都栽在这里。 - 没有推送触发器。 不存在「推送时触发」的事件。但如果有人向一个已经打开 PR 的分支推送提交,GitHub 会把它算作对该 PR 的更新(即
synchronized事件),于是 Routine 就会触发。向没有 PR 的分支推送,则什么也不会发生。 - 过滤器。 你可以限定哪些事件会触发它:按作者、标题、标签、分支、草稿状态等等。完整的字段列表、每小时上限和那些棘手的边界情况,都在 Routines 附录(A3)中。
2. 来自聊天应用,用 Channel。 Channel 会把来自外部应用的一条消息直接送进一个已经在运行的会话。Telegram、Discord 和 iMessage 开箱即用;其他来源则需要你自行设置一个 webhook。这就是第 2 部分里的门铃:消息到达之前什么都不会发生,一旦到达,会话就立即作出反应。
例如,离开办公桌时,你可以通过 Telegram 问一句「部署完成了吗?」现有会话会检查,并带着它当前的上下文、技能和历史记录回复你。循环也可以通过同一个 Channel 把报告发回来。
有两点需要注意:
- 它需要一个活着的会话。 会话必须已经在运行(一个打开的终端,或一个后台会话),因此机器关机时 Channel 无法工作。
- 它是一扇敞开的门。 任何能向那个来源发消息的人,都可能操纵你的会话,所以只连接你自己掌控的来源。
设置方法:code.claude.com/docs/en/channels。
3. 来自其他任何东西,用带 API 触发器的 Routine。 有些事件既不来自 GitHub,也不来自聊天应用:监控工具里触发了一条告警、一次部署完成了、一个表单被提交了。给 Routine 一个 API 触发器,那么任何能发送经过身份验证的 Web 请求(一种能证明自己身份的请求)的系统,都可以在你笔记本关闭的情况下触发它。这个请求甚至可以携带事件本身的细节:一个可选的 text 字段会把与本次运行相关的上下文(告警内容、出错的日志)连同它保存的提示一起传给 Routine。端点、令牌以及一条重试警告都在 Routines 附录(A3)中。
在三者之间选择:
| 事件来自…… | 使用 | 这项工作运行在…… | 笔记本关闭? |
|---|---|---|---|
| GitHub(一个 PR、一次发布) | Routine,GitHub trigger | 每个事件一个全新的云会话 | 可以 |
| GitHub,但不使用 Routine | CI 中的 Claude Code GitHub Action | 每个事件一个全新的 CI runner | 可以 |
| 聊天消息(Telegram、Discord、iMessage) | Channel | 你已经在运行的会话 | 不可以 |
| 任何能发送 Web 请求的东西 | Routine,API trigger | 每次调用一个全新的云会话 | 可以 |
第二行比看上去更重要。Routine 不是笔记本关闭后继续工作的唯一办法。GitHub Actions workflow 中的 anthropics/claude-code-action@v1 能完成同样的工作,而且不需要研究预览权限,也没有每日运行上限。Pro 或 Max 套餐就够了:claude setup-token 会提供 runner 可用的凭据,因此也不需要 API key。这是进入无人值守工作的最低成本入口。
这也指向 4 行背后的共同规则:问题从来不是「它是不是 Routine」,而是「工作运行在谁的电脑上」。 你自己的机器会随合盖停下;Anthropic 的服务器和 GitHub runner 不会,因为它们从来不属于你。这也是那些行需要 token、而 /loop 不需要的原因:你的笔记本已经知道你是谁,租来的陌生机器不知道。需要凭据与能在合盖后继续,其实是同一事实的两个侧面。
除 Channel 外,每一行都遵循同一模式:每个事件启动一个全新 session,所以两个事件彼此一无所知。对同一个 PR 的两次 push,是两个独立 session。Spine(概念 12)是它们共享 state 的方式。
上面的描述只有亲眼看过才会真正落地。这个概念附带一个小项目:The Doorbell。它只做一件事:审查一份没有人主动要求它审查的 pull request。
把项目 kit 复制进自己的 repo,用 claude setup-token 生成 token,把它添加成一个 secret,然后打开一份带 bug 的 PR。大约一分钟后,review 会出现。你没有输入 prompt,也没有人在看守。
接着做真正让概念落地的那一步:合上笔记本,让别人打开 PR。 Review 仍然出现。这一动作就说明了它与概念 4 的差别。ISS loop 随终端关闭而停止,因为它运行在你的机器上;这个 loop 从来不在你的机器上。门铃响起时,GitHub 临时租一台电脑,在那里运行工作,完成后丢掉机器。
这也解释了 token。你的笔记本已经知道你是谁,租来的陌生机器不知道。每个无人值守 loop 都要付出这项代价。
有一件事需要提前知道,因为它曾让我们浪费一个小时:绿色勾选不等于真正工作。 漏掉一个设置,run 仍会成功、完成 review,却什么评论都不发布。项目 README 写明了具体设置与症状。(它使用 Claude Code GitHub Action,而不是 Routine:同一扇门铃,不需要预览权限,也没有每日上限。)
结尾正好证明上面的段落。第二次 push 后,新的 review 会准确引用更早的 commit hash,尽管它运行在一台从未存在过、也不记得任何事情的新机器上。它没有记忆,而是读取了 repo。那就是 spine,第 4 部分会正式讲解。
运行一次 opencode github install 安装 GitHub agent,它会添加 .github/workflows/opencode.yml。此后,OpenCode 会响应仓库事件,包括 pull_request、issues,以及 /oc 或 /opencode 评论,并在 GitHub Actions runner 中运行:
name: opencode-review
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
jobs:
review:
runs-on: ubuntu-latest
permissions: { contents: read, pull-requests: read }
steps:
- uses: actions/checkout@v6
with: { persist-credentials: false }
- uses: anomalyco/opencode/github@latest
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
model: anthropic/claude-sonnet-5
use_github_token: true
prompt: |
Review this pull request for bugs, quality issues, and security risks.
对于没有提示的 pull_request 事件,OpenCode 默认会审查 PR。
由时间开始工作时用 schedule;由外部动作开始工作时用 event,例如打开 PR 或发送消息。
你希望循环不断修复一个失败的测试,直到它通过,然后自行停止。应该使用哪种心跳?由谁判断「完成」? 使用运行到完成:在 Claude Code 中是 查看答案
/goal,在 OpenCode 中是有上限的 shell 循环。必须由一条命令(测试运行器)判断「完成」,绝不能由编写修复的 agent 自己判断;同时还要设置上限,防止无限重试。
你现在认识全部 4 种 heartbeat,但不要总去够最大的一种。每个 loop 都由两个选择定义:什么启动它、什么停止它。因此,在构建之前先问:任务会结束,还是会重复?
- 任务会结束,而且命令可以证明终点 → 条件式 loop。现在启动,让它运行到完成。
- 任务会重复 → schedule 或 event。
- 任务只发生一次 → 完全不需要 loop。普通 session 逐轮完成,仍然适合大多数工作。
还有一条规则:新的重复 loop,先观察最初几次真实运行,再信任它无人值守。第 6 部分会把它正式写成规则:先证明 loop,再交给夜间运行。
最后说明几个常见名字。网上这些词常被混用:「turn-based」就是普通 session;「goal-based」就是条件式 loop;「time-based」把本课区分开的会话内与 scheduled 两种混在一起;「proactive」则是第 5 部分那样完全组合好的 loop。标签不同,部件相同。
练习:选择 heartbeat,然后构建一个
你已经知道全部 4 种 heartbeat,现在正适合动手,因为 4 幅图还很清晰。分两步:先证明你会选择正确 heartbeat,再真正启动一个。
第 1 步:选择(2 分钟,无需安装)。 为下面每项任务说出 heartbeat,其中一个是陷阱。
- 每周五为团队起草本周已合并 PR 的摘要。
- 持续修复失败 build,直到变绿后停止。
- 有人打开 PR 时自动开始 review。
- 一项 40 分钟 migration 正在运行,希望结束时立即获知。
- 只在 repo 中重命名一个变量。
查看答案
- 计划式。 它按时钟重复运行,而且无需有人守候,可用 Routine 或一行
cron(概念 6)。 - 条件式。 命令可以证明任务结束,可用
/goal或有上限的 shell loop(概念 5)。 - 事件驱动。 外部动作启动它,所以安静的一天可以一次也不运行(概念 7)。
- 会话内。 你仍在观察,关闭会话即可停止,可用
/loop,或带sleep的whileloop(概念 4)。 - 完全不用 loop。 一次性任务仍应使用普通 session。
第 2 步:构建一个。 课程结尾为每种 heartbeat 都准备了项目。现在不要全做,其中两种已经可以开始,另外两种有意指向尚未学习的部分:
| Heartbeat | 概念 | 在哪里构建 | 现在可做? |
|---|---|---|---|
| 会话内 | 4 | 项目 1:watch loop | 可以 |
| 条件式 | 5 | 项目 2:让测试通过后停止 | 可以 |
| 计划式 | 6 | 项目 3:有 memory 的晨间简报 | 学完第 4 部分后;它需要 spine |
| 事件驱动 | 7 | 项目 6:doorbell loop | 学完第 3 部分后;它需要 connector |
最后一列值得停一下。没有 memory 的 schedule,只是每天早上重复相同的第一步;不能打开 PR 的事件驱动 loop,只能说话。所以,恰好是那两种能在你不在时运行的 heartbeat,最需要先学完课程其余部件。这不是填充内容,而是系统本身的结构。
项目 1 是成本最低的起点:大约 15 分钟,无需配置 schedule,也没有任何东西会在你离开后继续运行。如果进入第 3 部分前只做一件事,就做它。
第 3 部分:主体
心跳启动循环,下面 4 个部分则决定循环在每次搏动中做什么。在 agentic coding 课程里,它们只是方便的附加能力。在循环中,它们至关重要,因为没有人会盯着每一步。
8. 隔离:工作树
只要循环同时运行多个 agent,它们就会开始覆盖彼此的文件,就像两个人不打招呼地同时编辑相同的行。Git 工作树可以解决这个问题:它是一个独立工作文件夹,位于自己的分支上,同时共享同一份仓库历史。一个 agent 的修改无法碰到另一个 agent 的检出目录。
这项功能已经内置。使用 --worktree 参数可以在独立检出目录中打开会话;为子 agent 设置 isolation: worktree,则每个辅助 agent 都会获得全新检出目录,并在完成后自动清理。定时任务可以为每次运行启用工作树隔离,让并行运行永远不会与你手动进行的工作冲突。
没有单一参数。需要使用 Git 自己的工作树,并把每次运行指向对应目录。同样的隔离,只是明确写了出来:
git worktree add ../wt-feature-a feature-a
git worktree add ../wt-feature-b feature-b
( cd ../wt-feature-a && opencode run "implement feature A" ) &
( cd ../wt-feature-b && opencode run "implement feature B" ) &
wait
如果经常这样做,可以使用社区 runner,也就是围绕 OpenCode 构建的工作树管理器,代为处理这些事务。
9. 知识:用技能避免每次运行都是「第一天」
循环每次都从冷启动开始,它面对的是一个全新会话,对项目习惯毫无记忆。没有帮助时,它会在每次搏动中重新推断(或猜测)整个设置,浪费 token,也更容易犯错。技能就是只写一次的知识,保存在 SKILL.md 文件中,由 agent 在每次运行时读取。
这在两种工具中的工作方式相同:一个带有 SKILL.md 指令和元数据的文件夹,还可选带脚本和参考资料。在循环中,规则很简单:凡是你原本需要每次运行都重新解释的内容,都应该放进技能。 分流步骤、项目习惯,以及「因为某次事故,所以我们不这样做」的经验,都放在技能中。这样,循环会不断积累,而不是每次重启。(完整说明见 技能与连接器速成课。)
每次云端 beat 都是全新 session,因此项目知识必须由 SKILL.md 明确携带。Claude Code 与 OpenCode 使用相同的 SKILL.md 形状,避免每次运行重新猜测约定。
不要把一堵没人维护的指令墙粘进计划。定时提示可以缩成一句:「运行 daily-triage 技能」,详细逻辑由技能保存。循环提示更短,逻辑更容易更新,每次搏动的 token 成本也更低。
10. 行动:连接器(让循环行动,而不只是建议)
只能读取文件的循环,也只能说话。基于 MCP 的连接器让它能够行动:打开 PR、更新 Linear 工单、向 Slack 发消息、查询数据库、调用预发布 API。这就是「这里有一份修复」和真正打开 PR、关联工单并在 CI 变绿后向频道发消息之间的区别。
两种工具都使用 MCP,因此协议可以迁移,但封装和认证方式(本地或托管、OAuth、权限)通常需要针对工具单独接线。
在配置中添加 MCP 服务器,并把它们加入 Routine 的连接器列表,让无人值守运行可以访问。手动使用的连接器,同样可用于定时运行和云端运行。
在 opencode.json 的 mcp 小节中声明服务器。本地服务器会启动子进程,远程服务器则通过 HTTPS 端点连接,并自动处理 OAuth。对于定时的 opencode run,先启动一次 opencode serve,再用 --attach 连接,这样每次搏动都不必支付 MCP 启动成本。
Loop 会重试,而且会在无人看守时自行选择工具。这会改变一套优良工具应有的样子:
- 少量、专注的工具胜过大量彼此重叠的工具。 选择工具是模型在每一次 beat 中都会作出的决定,而且当时没有人在旁监督。给它 100 个互相重叠的工具,它就会弄不清哪个才合适。实践者发现,削减 agent 可用的工具反而会提高成功率。Anthropic 的经验法则是:如果人类工程师都无法确定该用哪个工具,agent 也无法确定。手动工作时,选错一次工具只耽误片刻;在 loop 中,它每发生一次就浪费一次 beat,而且以后每次都可能继续浪费。因此,只保留 loop 真正需要的 connector。(Routines 附录出于安全原因也会要求同一件事,这两个理由恰好一致。)
- 写操作必须能够安全重复。 Loop 重试失败步骤时,会再次调用同一个写操作。若重试「创建客户」会再创建一个客户,就会留下重复记录和重复计费。应优先使用可安全重复的操作,例如 update-or-create,或每个 branch 只建一个 PR,而不是盲目创建。
- 错误必须说明下一步怎么办。 在 loop 中,错误消息就是下一次 beat 的输入。「Permission denied: request the
reposcope」能让它在下一次尝试中自行修复;只有「Error 403」则会白白浪费一次 beat。
手动工作时,你会在不知不觉中替系统吸收这三个问题:你会选对工具、跳过重复操作,并搜索错误含义。无人值守时,没有人替它吸收这些问题。
11. 执行者–检查者:子 agent
循环中最重要的单项设计选择是:编写工作的 agent 绝不能同时负责审批。 模型检查自己的输出时往往会过分宽松。第二个 agent 使用不同的指令,也可以使用不同的模型,从而抓住第一个 agent 漏掉的问题。正是这种职责分离,让无人值守的 loop 更安全。这个模式也叫 LLM-as-judge:由独立模型给工作评分,而不是让执行者给自己评分。
在 .claude/agents/ 中定义子 agent,再把它们组成 agent 团队:一个探索,一个实施,一个根据 spec 和测试检查。「spec」就是你在 Spec-Driven Development 中学会编写的那一种;它的验收标准,正是可信检查者评分的依据。含糊的 spec 只会得到含糊的结论。/goal 内部也是同一机制:由全新模型判断循环是否完成,而不是让工作者自己评分。
OpenCode 内置 Build 和 Plan 两个主 agent,还内置 general、explore 和 scout 三个子 agent。Scout 是只读 agent,用于查询外部文档和依赖项。你也可以在 opencode.json 或 agents 文件夹中的 Markdown 文件里定义更多 agent。
为检查者配置自己的模型,它通常可以更便宜,并设为只读。执行者可以通过 @ 提及或 Task 工具调用它。常见拆分方式是:强模型负责探索和实施,专注模型负责检查结果。
Loop 中有两项设置尤其重要:为每个 agent 设置 steps 上限(概念 5),并用 permission.task 规则阻止子 agent 再启动更多子 agent。没有这些限制,agent 可能循环委派工作,消耗不必要的 token。
---
mode: subagent
model: anthropic/claude-haiku-4-5-20251001
description: Reviews a diff against the spec and tests. Replies PASS or FAIL with reasons.
---
You are a strict code reviewer. You do not make changes.
Check the diff against the spec and the test results, then reply PASS or FAIL with the reasons.
每个子 agent 都会运行自己的模型和工具,因此执行者–检查者拆分确实消耗更多 token。这是获得可信检查者的代价。请把它用在第二意见真正重要的地方,例如循环会在你离开时提交的任何内容;对于可丢弃的只读任务,可以省略。
11b. 固化主体:动态工作流
到目前为止,一次搏动的主体,包括发现工作、在独立检出目录中起草修复、让另一个 agent 评分,都是 agent 逐轮组装的。Claude Code 现在可以把整套编排固化成可重复运行的脚本,这叫作动态工作流。你描述任务,Claude 编写脚本,把工作扇出给多个子 agent;运行时在后台执行脚本,而当前会话仍然可用。它把概念 11 的执行者–检查者拆分和概念 8 的工作树拆分封装成一个可重复单元。它还可以运用真正的质量模式,例如让独立审查者彼此进行对抗式检查,再报告任何发现,而不只是运行更多 agent。
可以直接用自然语言提出要求(「用 workflow 来……」),使用 ultracode 关键词启动它(较早的 workflow 触发词已在 2026 年年中停用,直接描述需求仍然有效),或运行内置的 /deep-research。某次运行达到预期后,在 /workflows 视图中按 s,把脚本保存成 /command,以后可以在每个分支重复运行。Guardrail 会约束它:并发 agent 数有上限(约 16 个,单次运行最多 1000 个),防止失控脚本无限膨胀;持续无法通过验证的子 agent 会在尝试几次后停止,而不是永远循环。一次运行的记忆也只存在于本次运行中。你可以在同一会话里继续它,但新会话会从头开始。
它没有 /workflows 命令。你编写的脚本就是工作流:概念 5 中有上限的 for 循环,加上概念 8 中用 & / wait 实现的扇出,就是手工搭建的同一种机制。shell 保存计划,opencode run 是每个 agent,退出码则是检查者。你获得完全控制,也没有 agent 上限,代价是自行编写和维护编排。
工作流开始显得强大后,这是最容易犯的错误。动态工作流只会在你(或 ultracode 设置)启动时运行一次,结束后会忘记一切。它没有心跳,也没有脊柱。因此,它只是一次搏动的主体,不是循环。循环是这些部分的组合:由心跳(Routine、/loop 或 cron)触发搏动,由工作流执行主体,再由 agent 写入的进度文件形成脊柱,供下次触发时读取。**按字面来说:**工作流执行一次 run,trigger 启动之后的 run,progress 文件则在多次 run 之间保存信息。
可以这样记:工作流是发动机,Routine 负责转动钥匙,progress.md 把信息带到下一段行程。
你的 loop 同时运行两个 agent,并且你也希望信任它离开你视线时产生的 commit。这是两个不同的问题,body 中哪一部分分别解决它们? Worktree 解决并行 agent 的编辑冲突;maker-checker 解决无人值守提交是否可信的问题。隔离让 agent 不互相妨碍,checker 让坏成果进不了 repo;两者解决的是不同问题。查看答案
插曲:用 verification skill 固化 checker
上一节把一次 beat 的 body 固化下来,这一节固化 checker。2026 年 7 月,Anthropic 的 Claude Code 团队发布了专门讲解这一模式的指南,并把它称为 verification loop:agent 检查自己的工作,尝试修复,再次检查,直到通过。你已经在本书中见过这个 loop,只是名称不同。它就是 agentic coding 课程中的 Attempt → Check → Fix → Repeat,也是 Boris Cherny 所说的使用该工具时最重要的一条建议。
先把名称说清楚,以免「loop」一词造成混淆。Verification loop 运行在一次 beat 内部,没有自己的 heartbeat,也没有 spine;beat 结束,它也随之结束。按照本课的词汇,它属于 small-loop 机制。它与本章的关系在于下一步:一项只写一次的检查,只要由 heartbeat 触发,就会成为 big loop 的 checker。第 5 部分的 reviewer agent,正是按照这种写下来的检查来评分。因此,本节真正讲的是一个动作:把只存在于你脑中的检查交给一个文件。
哪些检查值得写下来? 判断方法很简单:凡是 agent 每次完成工作后,你都要亲手纠正的事情,都值得写成检查。 每次前端改动后的手动点击流程;检查错误日志是否移除了 request body;批准 migration 前总要重新核对的步骤。用日常语言写下流程,就像交给第一天入职的新同事。如果你还无法清楚写出来,可以先让 agent 给出标准最佳实践,再按项目实际修改。你的版本只会在少数地方与标准版本不同,而这些差异正是最值得写下来的部分。模型已经知道通用规则,项目特有的规则才最有价值。
检查也不必一定是主观判断。_「拒绝任何在没有 backfill 步骤时删除列的 migration」_是一条可以由命令证明的固定规则;通用 linter 永远不会包含它,因为它属于你的项目。这是概念 2 的 checker ladder 中很容易漏掉的一层:在工具自带的机械检查和 reviewer 按 rubric 评分之间,还有一层只有你能写出的机械检查。每把一条规则从 rubric 下移到这一层,就把一项主张变成了一项证明。
它的包装形式就是 skill。 你已经在概念 9 见过这个容器:任务匹配时,agent 会加载一个 SKILL.md。Verification skill 使用相同容器,只是里面装的是检查,而不是工作流程。一个完整示例一屏就能放下:
# .claude/skills/verify-log-hygiene/SKILL.md (or .opencode/skills/…)
---
name: verify-log-hygiene
description: Check that error logs include the request ID and never
include the request body. Use when the diff touches error handling
or logging.
allowed-tools: [Read, Edit, Grep]
---
Read the error-handling paths in the current diff.
For each log call on an error path, confirm it includes the request ID
and does not pass the request body, headers, or any user-supplied
payload.
Report each violation with file:line, then fix it: add the request ID
where it's missing and strip the payload from the log call.
注意 allowed-tools 这一行:这项检查只能读取、编辑和搜索,不能做其他事情。这就是概念 14 的 standing permission 思想在检查上的应用。(与第 5 部分 reviewer 的 tools 行一样,这里填写的是工具名称,而不是单条命令。把检查限定到_具体命令_属于强制执行层,由下一门 Harness Engineering讲解。)
检查在哪里运行。 Anthropic 的指南为本课补充了一个真正的新观点:同一项检查有四个可能的家,每个家都对应不同的 heartbeat。

- Standalone。 工作已经存在后,由你主动调用。用本课的话说,你仍然是 heartbeat。适合多类工作会用到、但不是每次改动都需要的检查,例如 commit 前安全扫描、许可证头检查、PR 前无障碍审计。代价是你必须记得多进行一次调用。
- Embedded。 把检查附在产出工作的 skill 末尾,工作流无需另行要求就会运行它。最简单的形式只需增加一句:「创建组件后,对它运行 eslint,并在报告完成前处理所有错误。」 请在一个全新任务上调用 skill,确认检查确实随输出一起运行;若没有触发,说明 skill 中更早的指令没有加载它。这里有一个硬性限制:只能在你有权编辑的 skill 中嵌入。内置 skill 和由 plugin 管理的 skill 更新时会被覆盖,追加的检查会悄悄消失;遇到这种情况应改用 chain。
- Chained。 一项 skill 在结尾调用另一项,使多次经过验证的交接端到端运行。Anthropic 的 Claude Code 团队每天都使用这条链:
/code-review查找 bug,/simplify清理 diff,/verify确认端到端行为,自定义/designskill 则按照DESIGN.md检查界面改动。Chaining 也能为你无法修改的 skill 增加验证:编写一个很薄的 wrapper skill,先调用原 skill,再调用你的检查。值得记住的是:原本的习惯(「我总会在之后运行检查」)变成了合约(skill 每次结束时一定运行检查)。 取舍也是真实的:chain 会牺牲灵活性,你不再容易只运行其中一步;每多一环,每次运行都要增加 token 成本(概念 13)。若步骤相互独立到你有时只想运行其中一项,就不要 chain。 - On every PR。 当 chain 已在你自己的改动上稳定后,同样的 skill 通过概念 7 的事件 heartbeat,也就是 Doorbell,在每个 pull request 上运行,并以你的检查作为 prompt。无论同事是否记得调用任何内容,他们的改动都会通过与你相同的 gate。这一刻,verification 不再是个人基础设施,而成为团队基础设施:原本每周替你节省 2 分钟的检查,现在会在每次改动中替所有人节省 2 分钟。
升级规则。 不要从第 4 个家开始。检查准备离开 standalone 的信号,是你发现自己每次改动后都会运行它。这时它才赢得永久住所,可以 embed 或 chain。Chain 仍在变化时,不要急着加成全 PR gate,因为一旦它开始守护团队的 PR,你对它所作的每一项修改都会被整个团队看见。这两条规则并不陌生:先在快速、有人看守的环境中证明,再交给慢速、无人值守的环境。Checker 也要爬同一座信任阶梯。
截至 2026 年 7 月下旬,有 3 条捷径:
- 先用产品自带的能力。 内置
/verifyskill 会构建、运行并观察应用改动,编写自定义版本前应先试用它。同一指南还给出一个简短的CLAUDE.md习惯:在规则文件中列出准确的构建和测试命令,让任何 run 都不必猜测。(概念 12 已解释原因:agent 猜一次的命令,loop 会永远重复猜测;写下来的命令则是它每次读取的事实。) - 让 agent 采访你。 编写 verification skill 最快的方法是 skill-creator plugin:
/skill-creator Create a skill for verifying frontend changes end-to-end. Interview me about my workflow.。它提问,你回答,文件随即生成。像概念 9 一样,直接写入.claude/skills/也完全可行。 - 托管的第 4 个家。 Code Review(research preview)是产品化的第 4 个家:它是一项托管的多 agent 服务,会在你启用的仓库中对 PR 自动执行审查。你可以修复 finding 后 push;若概念 7 的 GitHub Action 已配置,也可以在 finding 下评论
@claude回复。它与手工构建的 chain 形状相同,只是 heartbeat 和 reviewer 由 Anthropic 托管。
无需安装任何新东西,这正是重点。上面的 SKILL.md 无需修改就能从 .opencode/skills/ 加载,4 个家映射到你已经构建的部件:
- Standalone 是
opencode run "run the verify-log-hygiene skill on the current diff"。 - Embedded 是在你自己的产出 skill 主体末尾追加同一句话。
- Chained 是你的脚本,也就是概念 5 的模式;每项 skill 的退出状态决定下一个
opencode run是否触发,shell 保存这份合约。 - On every PR 是概念 7 的 GitHub Action,并把检查作为 prompt。你已经见过的默认无 prompt PR review,是用 OpenCode 部件搭建出的 Anthropic
/code-review思路;把 prompt 指向 verification skill,它执行的就不再是通用标准,而是_你的_标准。
Verification skill 就是把你已经在做的手工检查写下一次,让 agent 执行并修复问题。先手工调用;每次都要用时接入 workflow;对自己的工作稳定后,最后才放到每个 PR 上。
有两个主题被有意留给相邻课程。强制执行,也就是逐条命令限制检查只能检查、无法做任何越界操作,属于 Harness Engineering。allowed-tools 会缩小工具箱,但真正的锁在那里。评分,也就是检查依据 rubric 而不是证明时,模型分数能信到什么程度,属于 Trusting the Checker。还有一个产品说明也属于后一门课:Claude Managed Agents 中的 Rubrics(beta)把概念 2 中「带及格线的 rubric」做成托管服务。独立 grader agent 会按照 rubric 验证结果,未通过的工作会自动返回再次尝试。你手工学到的形状如今已经成为平台原语。与本课其他内容一样,这只是机械层,仍应查看实时文档。
你写好了一项 accessibility check,希望从今天起让全团队的 PR 都通过它。它目前只由你手动调用并成功运行过两次。升级规则怎么说?你会跳过哪两个家? 现在还不能升级。手动调用两次仍处在 standalone;直接跳到 on every PR 会越过 embedded 与 chained 两个家。先让它在你自己的工作中稳定,再让它成为团队 gate。原则仍是先有人看守,再无人值守;先个人使用,再团队推广。查看答案
第 4 部分:脊柱
12. 能在多次运行之间保留的状态
这是初学者最容易跳过的部分,也是让循环真正成为循环的部分。模型会忘记不同运行之间的一切。 如果每次搏动都从零开始,你拥有的不是循环,只是永远重复相同的第一步。解决办法朴素却强大:把状态保存在模型之外,也就是磁盘上。
没有存档时,agent 每次都死在同一排尖刺上。接着它学会保存,而开关在你手里:打开时,它读取文件并完成;关闭时,它忘记一切、从头开始。
这个小游戏与本概念完全一样。Agent 会忘记多次运行之间的一切,所以要在磁盘上保留一个存档文件,那个文件就是 spine。从 checkpoint 而不是起点复活,对应你的进度文件(progress.md):记录已经完成和仍然开放的事项,让下一次运行继续,而不是从零开始。游戏里保存的「前方有尖刺」对应规则文件(CLAUDE.md / AGENTS.md):一次学到的教训,避免反复犯同一错误。这就是开关重要的原因。有文件,loop 会一轮轮积累;没有文件,它永远是起点上的陌生人。每次运行都先读取这些文件,最后再更新,因为 repo 能记住模型无法记住的事。
两层状态协同工作:
- 规则文件(
CLAUDE.md/AGENTS.md):循环每次运行都会读取的稳定习惯。(保持简短;上一门课已经讲过原因。膨胀的规则文件会在每次搏动中重复计费) - 进度文件:一个普通 Markdown 文件(或通过 MCP 访问的 Linear 看板),记录尝试过什么、什么通过了、还有什么未完成。这才是真正的脊柱。明天上午 9 点的运行会打开它,从今天停止的地方继续
应养成的习惯是:每次运行开始时读取进度文件,结束时更新它。 如果循环每次都犯同一个错误,解决办法不是更聪明的提示词,而是让循环把经验写进规则文件,让修复在未来每次运行中都生效。
如果两层状态仍显得抽象,可以把它想成培训一名新实习生。你先交代一次背景:工作流程、工单看板、哪些任务可以领取、何时必须来问你。接着给他一本日记和两条长期指令。第一:每次收到反馈,都把教训写在日记前半部,并在每天早上重新阅读。 例如:「不要使用那个设计模式」「本团队会 squash commit」「展示任何内容前一定先运行 linter」。第二:下班前在后半部写下今天完成了什么、停在哪里,让明天从今天的终点继续,而不是从零开始。日记前半部就是规则文件:保存长期教训,每次运行都读取;后半部就是进度文件:保存 checkpoint,每次运行都更新。没有日记的实习生,无论多聪明,都会反复学习同样的纠正、永远重做昨天的工作。Loop 也一样,而且模型的记忆会在两次运行之间被彻底清空,实习生的记忆至少只是逐渐淡去。对两者而言,日记都不是锦上添花,而是员工与每天来报到的陌生人之间的区别。

<!-- progress.md — the loop's memory between runs -->
## Done
- 2026-06-22: fixed flaky test in test/auth (retry on token refresh)
## In progress
- Dependency audit: 3 of 7 advisories patched; lodash bump blocked by an API change
## Open / needs a human
- CVE-2026-xxxx in image lib — the fix changes the output format, escalating to a maintainer
进度文件只是仓库中的文本,因此也记录了循环在你离开时做过什么。回到人工关口时,你只需阅读脊柱,而不必阅读每次运行的完整记录。
Spine 很难相信,直到你亲眼看见 loop 忘记。所以这里有一个专门让它可见的项目:Paper Watch。它每天显示你所选主题的最新论文,但只显示尚未看过的内容。无需安装,也无需密钥,Claude 会自行从 arXiv 获取。
克隆项目,在 agent 中打开文件夹,然后询问:
show me what's new on arXiv about "LLM agents"
你会按从新到旧看到最新论文。接着,一字不改地再问一次,它会回答「nothing new since last run ✓」。Loop 记住了:它把刚刚展示的每篇论文写进 progress.md,回答前又读回了该文件。这个文件就是 spine。要证明这一点,删掉记忆,再问一次:
rm progress.md
show me what's new on arXiv about "LLM agents"
每篇论文都会重新变成「new」。你用一条命令让 loop 忘记了一切:没有 spine,就没有 loop。 要把它变成真正的 watch,给它一个每日 heartbeat(概念 6):/schedule every weekday at 9am, run the paper-watch skill and show me what's new。arXiv 大约每天更新一次,所以每日 cadence 正合适。
现在把它与概念 6 的 Sky Watch 并排看,区别就清楚了。两者都是每日 Routine,但 Sky Watch 每次重新打印今天的小行星,不需要记忆;Paper Watch 只显示新增内容,没有 spine 就根本无法工作。Heartbeat 相同,记忆需求相反。这正说明了 loop 什么时候需要 spine。
可选背景资料,首次阅读可以跳过。
本课的 spine 是磁盘上的普通 Markdown。这不是为了照顾初学者而作的简化。Anthropic 在尝试了一年其他方案后,自身的 memory 工作也落在这里;他们公开的原则是「做能奏效的最简单方案」。这条演进路径值得了解:
- 最先出现的是规则文件(
CLAUDE.md):每次 session 开始时注入的 Markdown 文件。效果远超预期,但它会膨胀,而膨胀的文件会在每次运行中重复付费。 - 接着出现的是 session 内 memory 工具:让 agent 在任务期间自行决定何时读写 memory。自主性确实有效,但工具设计过于固执己见。
- Skills 解决了增长问题:agent 只读取每项 skill 的简短 description,需要时才加载完整 body。
- 当前最佳实践反而最简单:把 memory 建模成普通文件系统,也就是文件夹中的 Markdown 文件。让 agent 使用它本来就会的
grep和 shell command 等普通工具搜索,而不是引入特殊 memory API。允许存储不断增长,只要它仍然易于搜索。
请注意最后一步是什么:它与本课讲授的 spine 完全相同,文件保存在 repo 中,开始时读取,结束时更新,并用普通工具搜索。当顶尖实验室的生产答案与初学者的第一个 loop 采用同一设计时,说明这是一种承重结构,而不是训练轮版本的临时替代品。
可选技术细节,首次阅读可以跳过。
前面的规则文件习惯比表面看起来更重要。当 loop 把一条教训写进 CLAUDE.md,让以后每次运行都表现得更好时,你正在手工完成一种有正式名称的工作:hill-climbing loop。它的产出不是工作本身,而是对执行工作的系统作出的改进。
自动化版本这样运行:每次 run 都留下 trace;另一个步骤读取 trace,寻找反复出现的错误;发现结果再修改 prompt、工具或 checker 规则。简而言之,loop 会编辑自己。
(LangChain 描述了上下叠加的 4 个 loop:agent 的工具循环、检查循环、事件驱动循环,以及位于最上层的这个改进循环。swyx 把堆叠 loop 的实践称为 loopcraft。注意,前三者只是本课的 inner loop、maker-checker 和 heartbeat 换了名称。行业不断抵达同一种形状,这有力证明真正可迁移的技能是形状,而不是工具。)
有一个区别能让这些说法保持诚实:这都不是模型在学习。 目前没有公开模型会根据你的 session 修改自己的权重。明天运行的模型与今天完全相同。真正改进的是模型周围的一切:规则文件、skill、checker rubric、prompt。有些人把前者称为 self-learning,把后者称为 self-improving。本课中的所有 loop 都属于第二类,而 spine 正是它能奏效的原因:教训无法留在模型里,所以必须在磁盘上存活。一次令人失望的运行之后,也能看到这个区别。初学者的本能是明天写一个更好的 prompt,再从零开始,因此没有任何东西留下;loop 的本能则是:运行、记录、提取教训、把它写进系统,再次运行。Memory 不断累积,系统越来越敏锐,收益随着一次次运行叠加。
无需特殊平台也能开始。阅读一周的 progress.md 条目,只问一个问题:「规则文件应该写什么,才能阻止这类错误再次发生?」这就是以人工速度运行的同一思路。仍有一个与第 6 部分完全相同的警告:如果你从不阅读改进 loop 的产出,它就在无人看守的情况下改写自己的规则。对 loop 本身的修改也必须经过 human gate。
可选技术细节,首次阅读可以跳过。
上面的 hill-climbing loop 如今已经成为一项托管功能。Anthropic 的 applied AI 团队在 2026 年的一次会议演讲中介绍了它,并称之为 dreaming。这个名称很贴切:工作 agent 休息时,该流程继续运行,整理它们学到的东西。
先了解他们的词汇。In-band memory 工作发生在 live session 内部:agent 暂停手头任务,把教训写到磁盘。这就是概念 12 教授的做法,而且确实有效。但它有两个固有限制。Agent 必须在你交给它的任务与帮助未来 run 的 memory 工作之间分散注意力;而且它只能看到自己的 session,因此无法发现同一个错误在 10 个 session 或 10 个 agent 中反复出现。
Dreaming 是 out-of-band 的答案。 它是一个独立 loop,拥有自己的 heartbeat 和 token budget。一次 beat 按照以下步骤运行:
- 收集 memory store,也就是 loop 维护的规则文件和进度文件,再收集一批近期 run transcript,其中不仅有对话,也包括工具调用。
- Orchestrator 把 transcript 分给一组子 agent,每个 agent 分析其中一部分。
- Orchestrator 寻找跨 session 重复出现的模式:同一个失败的工具调用、同一块缺失的知识、同一种风格错误。
- 它提出对 memory store 的修改,并附上证据:模式出现在哪些 transcript 中,以及出现了多少次。
- 每项修改生效前,都由人类接受或拒绝。
用本课的词汇重新阅读这份清单:批处理 schedule 是 heartbeat;memory store 是 spine;分析 transcript 的 agent 是 subagent;带证据的提案送交人工决策,是 maker-checker 上再加一层 human gate。Dreaming 并不是新形状,而是把六部分 loop 指向 loop 自身的 memory,而不是你的代码。演讲使用了学校这个比喻:学生(工作 agent)完成任务,校长(dreaming pass)阅读所有批改后的试卷,发现每个班都答错了同一道题,于是修正课程,使明天的每个学生都变得更好,而且没有任何学生因此占用课堂时间。

同一演讲中的两个警告可以直接用于你自己的 loop。Memory 会过时:6 个月前写下的教训如今可能已经错误,因此必须有东西定期清扫和修剪,这正是 dreaming 一半的用途。当多个 agent 共享一个 memory store 后,该 store 还需要生产级 guardrail;这些内容放在概念 14 的共享 memory 注释中讲解。
先检查你已经拥有的工具,因为这个功能的一个版本已经发布。 Claude Code 有一项 research preview 功能叫 Auto Dream。你工作时,Claude Code 会悄悄记录项目笔记。经过多次 session,这些笔记会变得混乱:重复内容不断堆积,旧事实与推翻它的新事实并列存在。Auto Dream 就是清理器。它在 session 之间后台运行,合并重复内容,删除已被新工作证明错误的笔记;它只能写 memory 文件,永远不能修改你的代码。要查看自己是否拥有该功能,可以在 session 中运行 /memory,寻找 Auto-dream 开关。若要手工运行一次,只需说「consolidate my memory files」。OpenClaw 提供了类似的可选 /dreaming 系统,OpenCode 用户也能找到社区实现。照例,这些都属于机械层,应查看实时文档。
依赖它之前还有一个警告:清理器会相信较新的证据而不是旧笔记,因此即使某条笔记由你亲手写入,也可能被重写或删除。请坚持这条规则:任何你永远不希望被修改的规则都应放在 CLAUDE.md 中,因为该文件只由你控制。自动管理的笔记是 Claude 的笔记本,CLAUDE.md 才是你的。
还要分清两个看起来相似的工作。Auto Dream 做的是卫生整理,让笔记保持整洁;下面的 loop 做的是改进,找出 loop 反复犯下的错误,提出能阻止这些错误的新规则。前者是清洁工,后者是教练。形状相同,工作不同。教练更值得你亲自构建,因为你已经学会了每个部件:
- Heartbeat:每周,而不是每天。 Dreaming 要寻找跨 run 的模式,所以必须积累一批 run。每周一次 cloud Routine、
cron或 GitHub Actions schedule 是合适的 cadence。每天运行太频繁,看不出模式,还会无意义地放大成本(概念 13)。 - 输入:让工作 loop 留下可读取的 transcript。 这是唯一前提。工作 loop 必须按照你已经学过的可观测性习惯记录发生了什么:在
progress.md中为每次 beat 留下一条带日期的记录,并把 run log 保存在 repo 中。OpenCode 可用opencode run --format json和opencode export获取完整记录;Claude Code 中则让每项 Routine 把结果追加到它会 commit 的日志文件。没有日志,就没有可供 dreaming 的材料。 - Body:orchestrator 与 analyst subagent。 Dreaming prompt 读取上次运行以来的一批日志。批次较大时,把不同部分分给子 agent,每个只回答一个问题:哪里失败了?同一个失败是否出现不止一次? 一次错误只是噪声,同一个错误出现 3 次才是一条缺失的教训。
- Maker-checker,而且检查两次。 Analyst 提出建议,orchestrator 只保留证据充足的模式。随后才是真正的 checker:loop 绝不直接编辑规则文件或 skill。它在
claude/branch 上起草修改,并打开 PR;PR description 附上证据,包括哪些 run 出现了该模式、出现多少次,以及为什么这一行能阻止问题。 - Human gate:由你 merge 或关闭。 修改
CLAUDE.md或 skill 是整个系统中杠杆最高的写操作,因为以后每个 loop 的每次 run 都会读取它。课程所说的 gate 恰好应该放在这里:代价高、难以逆转,因此必须由人决定。 - Dreaming loop 自己的 spine。 使用一个小型状态文件,例如
dreaming-state.md,记录上次审查批次的日期。这样下周只读取新日志,而不会重新 dream 全部历史。
这个设计还免费带来几项好处。Store 位于 git 中,因此 versioning 与 rollback 不增加成本;所有规则修改都以 PR 到达,所以权限可以简化为「除你之外无人可以 merge」。过时内容也有了自然去处:要求 dreaming prompt 同时提出_删除_,也就是删除近期 run 从未需要的规则,以及与当前日志矛盾的旧教训。一个只增不减的 memory store,最终会变成你每次 beat 都要付费、却每月更难信任的规则文件。
它可能把攻击洗白。 Dreaming pass 读取的 transcript 中包含外部人员写入的文本,例如 issue body、PR description,以及 loop 抓取的网页。安全研究者把这种风险称为 memory poisoning:某次 run 输入中植入的一条指令被写进 memory,在最初攻击早已消失后,仍然操纵之后的每次 run。Dreaming pass 恰好可能把一次性注入变成永久规则。这不是跳过 dreaming 的理由,而是必须遵守设计中已有两条规则的原因。永远保留证据:提案必须引用它来源的 run,让你在信任教训之前阅读原始内容。永远保留 human gate:绕过审查的规则修改,正是攻击者最想获得的写操作。同一 pass 也会帮助你,因为每周阅读规则文件,是发现一行从未由你批准的内容的最好机会。
它可能侵蚀自己维护的内容。 研究反复重写 memory 的人员发现了两种失败模式:brevity bias,也就是重写保留概括却丢掉细节,例如「检查响应 payload,而不是 status code」退化成「处理错误」;以及 context collapse,也就是每次完整重写都是上一版的有损副本,直到详细 playbook 退化成含糊段落。防御方法正是本 loop 已采用的做法:只提出小 diff,绝不完整重写。而且 store 位于 git 中,规则文件一旦缩水,就会在 diff 中显现,你可以拒绝它。一个小习惯还能阻止一整类衰减:memory 文件绝不能包含相对日期。「昨天我们选择了 Redis」在 6 周后毫无意义。每次 consolidation 都应把「昨天」改为实际日期,而你的 loop 从一开始就应写绝对日期。
还要承认一个限制:dream 需要足够材料。只有 3 次运行历史的 loop,或一次性项目,没有可供发现的模式,pass 只会从噪声中编造听起来合理的教训。只对真正跑出里程的 loop 做 dreaming,并用最低成本的方法检查结果:如果一条教训有效,对应失败就不应再出现在下周日志中。
最后再提醒一次,这与上面的改进 loop 注释完全相同:该 loop 会改写指导其他所有 loop 的规则。在你拥有的所有 loop 中,它最不应该取消 gate。
Anthropic 已把托管版本作为 Managed Agents memory tooling 的一部分提供。与本课其他产品细节一样,这属于机械层,依赖前应查看实时平台文档。不会过时的是它的形状,而你在前面两段已经亲手构建了这种形状。
循环应当把目前完成的工作保存在哪里?为什么不能放在对话中? 保存在磁盘上的进度文件(加上规则文件),或 Linear 一类看板中。模型的记忆会在多次运行之间清空,因此所有必须保留的信息都要放在模型外部。仓库会记住,模型不会。查看答案
第 5 部分:同一个完整循环,两种实现
在允许任何循环自行运行前,它必须具备下面 7 项。接下来要构建的循环全部具备:
- 成功条件:如何知道工作已经完成(概念 5)
- 上限:最大尝试次数、分钟数或支出,防止无限运行(概念 13)
- 隔离的分支或工作树:防止并行工作发生冲突(概念 8)
- 只读检查者:负责评分却不能编辑的独立 agent(概念 11)
- 状态文件:作为脊柱,在多次运行之间保存记忆(概念 12)
- 人工关口:高风险或失败的工作交给人处理,绝不直接进入
main(第 5 部分) - 日志或通知:让夜间失败变得可见,而不是悄无声息(第 6 部分)
缺少任何一项,循环就会不安全、健忘或不可见。
现在把这些组件连接起来。下面是同一个循环:晨间维护循环会整理夜间 CI 失败,起草安全修复,让检查者审查,为通过的修复打开 PR,并标记其余项目。我们会在两种工具中各构建一次。下面的文件都是真实可用的,可以复制到仓库中运行。
循环结构(两种实现相同):
- 心跳: 每个工作日上午 9 点
- 技能:
daily-triage技能保存详细步骤,让提示保持一行 - 脊柱: 开始时读取
progress.md,结束时更新 - 工作树: 每项修复在独立检出目录中起草
- 执行者–检查者: 实施者起草,独立审查者回答 PASS 或 FAIL
- 连接器: PASS 时打开 PR;FAIL 或存在风险时写入「needs a human」,然后停止

共享技能
下面这个文件可同时用于两种工具。Claude Code 中保存为 .claude/skills/daily-triage/SKILL.md,OpenCode 中保存为 .opencode/skills/daily-triage/SKILL.md。
---
name: daily-triage
description: >-
Runs the morning maintenance pass. Reads the progress file, gathers overnight
CI failures, open issues, and new audit advisories, drafts safe fixes (each
one checked by a separate reviewer agent), opens pull requests for what passes,
and writes anything risky to the progress file for a human. Use this for the
scheduled morning maintenance loop.
---
# Daily triage
You are the morning maintenance loop. Work through these steps in order.
Do not skip the progress file. It is your only memory between runs.
## 1. Read your memory first
- Open `progress.md`. Read the "In progress" and "Open / needs a human" sections.
- Do not redo anything already listed under "Done".
## 2. Find the work
Gather candidates in this order, and stop once you have at most 5:
1. CI runs that failed since the last entry in `progress.md`.
2. Open issues labelled `bug` or `maintenance`.
3. New advisories from `npm audit` (or this project's audit command).
## 3. Work each candidate
- Create an isolated checkout: a git worktree, or a fresh branch named
`claude/<short-slug>`.
- Draft the smallest fix that solves the one problem. Do not bundle changes.
- Send the diff to the reviewer agent. Wait for its verdict before going on.
## 4. Decide from the verdict
- PASS, and the change is low risk (no public API change, no data migration,
no file deletion): open a pull request. Title it `fix: <one short line>` and
link the issue.
- FAIL, or the change touches anything risky: do NOT open a pull request. Add a
short entry to the "Open / needs a human" section of `progress.md`. Say what
you tried and why you stopped.
## 5. Update your memory last
- Move finished items to "Done" with today's date.
- Save `progress.md`. This is the file tomorrow's run will read.
## Rules
- Never open more than 5 pull requests in one run.
- Never change `main` directly. Only `claude/*` branches.
- When in doubt, escalate. A flagged item a human checks is always safer than a
wrong fix shipped while no one was watching.
审查者(检查者)
审查者把执行者–检查者拆分真正落到实践中。你需要同时拥有下面两个文件,它们不是二选一。两种工具的格式略有不同,所以这里给出完整内容。
Claude Code:保存为 .claude/agents/reviewer.md:
---
name: reviewer
description: Reviews a diff against the spec and the test results. Replies PASS or FAIL with reasons. Makes no changes.
tools: Read, Bash
model: claude-haiku-4-5-20251001
---
You are a strict, read-only code reviewer. You never edit files.
1. Run the tests and the linter. Read the output yourself. Do not trust a claim
that they pass.
2. Check the change against the project conventions in `CLAUDE.md` and the
relevant spec.
3. Look for bugs, missing edge cases, security risks, and any change to public
behaviour.
Then reply with exactly one of:
- `PASS` — followed by one line saying what you verified.
- `FAIL` — followed by the specific reasons, one per line.
A change that only "looks fine" is not a PASS. The tests must actually pass, and
the change must do only what was asked.
关于 tools 这一行,有一点需要如实说明:它只接受工具名称(Read、Bash),因此无法把 reviewer 限制为只能运行 test、lint 和 diff 命令。目前,这一限制由编号说明来承担。下一门 Harness Engineering 课程会加入强制执行它的规则。(而且 reviewer 评分所依据的检查不必都写在 prompt 中。verification skill 插曲会说明如何把每项检查写成独立 skill,让 reviewer 和 /goal checker 按照同一个文件评分。)
OpenCode:保存为 .opencode/agents/reviewer.md:
---
mode: subagent
model: anthropic/claude-haiku-4-5-20251001
description: Reviews a diff against the spec and tests. Replies PASS or FAIL with reasons. Read-only.
permission:
edit: deny
bash:
"*": deny
"npm test*": allow
"npm run lint*": allow
"git diff*": allow
---
You are a strict, read-only code reviewer. You never edit files.
1. Run the tests and the linter. Read the output yourself. Do not trust a claim
that they pass.
2. Check the change against the project conventions in `AGENTS.md` and the
relevant spec.
3. Look for bugs, missing edge cases, security risks, and any change to public
behaviour.
Reply with exactly one of:
- PASS — followed by one line saying what you verified.
- FAIL — followed by the specific reasons, one per line.
A change that only "looks fine" is not a PASS. The tests must actually pass, and
the change must do only what was asked.
接好心跳
在 claude.ai/code/routines 创建一个 Routine,设置工作日上午 9 点运行,选择仓库,并接入 GitHub 和 Slack 连接器。让提示指向技能,这样 Routine 定义可以保持精简:
Run the daily-triage skill.
Start by reading progress.md; finish by updating it.
For each fix: draft it in an isolated worktree, have the reviewer subagent grade it,
open a PR only on PASS, and append anything risky to the "needs a human" section.
技能保存步骤。.claude/agents/reviewer.md 是检查者。isolation: worktree 会把并行修复彼此隔离。GitHub connector 负责打开 PR。因为这是 cloud Routine,无论笔记本是否开机,它都会在上午 9 点运行。它也符合套餐的每日运行上限:工作日上午 9 点的计划每周运行 5 次,即使 Pro 套餐每天最多运行 5 次,也还留有充足余量;每隔几小时触发一次的 Routine 则未必如此。第一次配置这类 Routine 时,environment、connector scope 和 secrets panel 的各个字段,可以参照 Routines 附录逐项完成。
把它构建成 GitHub Actions 工作流,这样就能在云端运行,不需要你的机器保持唤醒。Action 是心跳,opencode run 是工作者,仓库则保存技能、agent 和 progress.md。
name: morning-maintenance
on:
schedule:
- cron: "0 9 * * 1-5"
jobs:
triage:
runs-on: ubuntu-latest
permissions: { contents: write, pull-requests: write, issues: write }
steps:
- uses: actions/checkout@v6
with: { persist-credentials: false }
- uses: anomalyco/opencode/github@latest
env: { ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} }
with:
model: anthropic/claude-sonnet-5 # confirm with `opencode models`
prompt: |
Run the daily-triage skill.
Read progress.md first; update it last.
For each candidate fix: draft it on a new branch, then invoke the
@reviewer subagent to grade it. Open a PR only when the reviewer
replies PASS. Append anything risky to the "needs a human" section
of progress.md and leave it for the maintainer.
使用更便宜只读模型的 reviewer agent 是检查者。CI 中的新分支承担工作树隔离的作用。OpenCode GitHub 应用负责打开 PR。如果想在自己的机器上运行,而不是在 GitHub 上运行,同一段提示可以直接从调用 opencode run 的 cron 行启动,只有心跳会变化。
一个真实的早晨会是什么样
上面的设计只需完成一次。下面是一次运行,也就是你早上醒来可能看到的内容(这段运行只是结构示例,不是真实记录):
[09:00] daily-triage fires
→ reads progress.md: 1 item still "in progress" (lodash bump), nothing new flagged
→ finds: 2 CI failures overnight, 1 new npm-audit advisory
→ CI failure #1 (flaky auth test):
drafts fix on branch claude/fix-auth-retry
reviewer → PASS (tests green; retries on token refresh; no API change)
→ opens PR #142, links the issue
→ CI failure #2 (type error in report.ts):
drafts fix on branch claude/fix-report-types
reviewer → PASS → opens PR #143
→ advisory (image library):
the safe fix changes the output format
reviewer → FAIL (public behaviour change)
→ writes it to "Open / needs a human" in progress.md, opens no PR
→ updates progress.md, exits
[you, 09:30] two PRs to review, one flagged item to decide on. You typed nothing.
看看发生了什么。 循环发现工作、起草修复、完成检查、交付安全部分,只把真正需要人的一项决定交给你。这就是循环工程的实践。还要注意,两种工具之间唯一真正不同的是心跳和运行位置。中间的技能、脊柱、工作树、执行者–检查者和连接器,设计完全相同。
上面的 run 现在会以动画呈现:它触发后找到工作,逐项起草修复,再由独立 reviewer 对每项给出 PASS 或 FAIL;安全的两项会成为 PR,风险项则被拒绝。随后,它把人工关口交给你,由你作出唯一必须由人决定的选择。
在晨间分流循环中,什么能防止错误修复在你睡觉时被合并? 3 项机制共同发挥作用:审查者子 agent 必须返回 PASS(执行者–检查者);只有低风险修改才能打开 PR;人工关口会把任何高风险或失败项写入「needs a human」,而不是送入 查看答案
main。每次运行还有上限和日志。
第 6 部分:继续做工程师
循环改变了工作,却没有把你移出工作。随着循环变好,有 3 个问题会变得更严重,而不是更轻。本部分是整门课最重要的内容。
你的循环如何嵌套在三个循环之中
你刚刚构建了一个循环。它有六个部分,一旦启动,就会自行运转:agent 写代码、测试、修复,然后再试一次,全程不需要你。
那是一个循环,也是最快的一个,以分钟为周期转动。但它不是独自工作的。它是三个循环里最小的那个,另外两个要靠你来运转。
最容易看清它们的方式是举个例子。假设你让一个 agent 为孩子做一个小小的打字游戏。
- 编码循环以分钟为周期转动。agent 写出游戏、测试它、修复 bug,直到符合你的指示。这就是你刚刚构建的循环。
- 反馈循环以小时为周期转动。你打开游戏,试玩一下,决定要改什么:把按钮做大一些、加入孩子可以解锁的猫咪造型、加一个登录让家长能帮忙。然后你更新指示,agent 再次构建。
- 外部循环以天为周期转动。真实的人在用这个游戏。朋友试了试,孩子玩了玩。他们的举动会告诉你下一步该修什么。

这些循环不是并排摆放的,而是彼此嵌套的。你运行一个反馈循环时,里面会发生许多次编码循环。外部世界返回一轮反馈的过程中,会发生许多次反馈循环。快的循环自行运转,慢的循环需要你。
有两个词要知道。spec 是你对「要构建什么」的书面描述。evals 是一小组测试,用来检查 agent 是否做对了。两者一起位于前两个循环之间,把你的决定带进代码里。
现在是核心观点,来自 Andrew Ng。为什么 agent 不能自己运转全部三个循环?因为你知道它不知道的事情:谁会用它、他们真正需要什么,以及「好」是什么感觉。只要你还知道 agent 不知道的东西,你就留在循环里去告诉它。Ng 把这称为你的上下文优势。
这正是本课结尾要讲的道理。机器运转那个快的循环。你则握着它永远无法握住的两样东西:要构建什么,以及由谁来负责。
即使你永远不做产品,也请保留这张地图。它显示了在任何 agent 的工作中,人处在什么位置。想让连接前两个循环的 spec 更清晰,参见 Spec-Driven Development。想了解你的角色如何在外层循环中成长,参见 本书培养的角色。
13. 真正的限制是 token 成本,不是快捷键
到目前为止,这是循环最常见的失控方式。循环会反复运行,往往还会启动子 agent,而每个子 agent 都运行自己的模型和工具。成本增长速度会超出几乎所有人的预期。解决办法很简单:
- 限制每个循环:最大尝试次数、最大分钟数或最大支出,永远都要设置(概念 5)
- 让模型与工作匹配:强模型负责规划和检查,便宜模型负责执行。这是最大的一项节省,你已经在上一门课学过。Claude Code 还要让推理强度与 beat 匹配(
/effort,或 headless 运行中的CLAUDE_CODE_EFFORT_LEVEL)。默认值足以处理常规分流,较高设置只留给真正需要深入推理的 beat。每次 scheduled loop 都支付最高推理强度,与用前沿模型做机械杂务是同一种错误 - 保持循环提示和规则文件简短:它们会在每次搏动中计费。把细节放进只有使用时才加载的技能
- 降低运行频率:每小时一次通常比每 5 分钟一次更合适,而且成本约低 12 倍
快速感受一下数字(仅作示例)。假设一次 beat,也就是 maker 加 checker,大约读取 40,000 个 token,写出 6,000 个。按 Sonnet 标准价格每百万输入 token $3、每百万输出 token $15 计算,每次约 $0.20。每天 5 次、每月 20 个工作日,约为 $20。
同一个 loop 如果全天候每 5 分钟运行一次,beat 数会增加 100 多倍。即使每次做的工作完全相同,月费也很容易超过 $1,000。增长由频率驱动,而不是命令名称。
可选的当前模型细节:Sonnet 5 发布时提供持续到 2026 年 8 月的介绍价格,而且新 tokenizer 可能让同一文本产生比旧模型更多的 token。请测量实际 loop 的 token 用量,乘以当前模型价格,再乘以计划运行次数。

在 OpenCode 中,模型是第二根成本杠杆。 上面的例子按 Sonnet 级模型的标准价格计算。Claude Code 的 loop 命令使用 Claude,而 OpenCode 可以选择其他模型。低成本模型能显著降低每个 beat 的价格。
不过,较弱的 maker 可能产生更多失败尝试,抵消省下的钱。实用模式是低成本 maker、可信 checker:便宜模型处理清晰、机械的工作,测试、linter 或你信任的 reviewer 继续充当 checker。
频率仍然最重要。即使一个模型便宜 30 倍,每 5 分钟运行一次,也可能比 Sonnet 每小时运行一次更贵。模型改变每个 beat 的成本,schedule 与 retry rate 决定你要为多少个 beat 付费。
常见失败总是相同:循环自行运行,停止条件永远无法满足,于是整夜重试。启动前先设置上限,观察最初几次真实运行,之后再让它自行运行。
Spine 通常被当成正确性功能来讲:没有 memory,就没有 loop。但 Anthropic 从生产 fleet 中报告了第二个效果。维护良好的 memory store 会让 agent 第二次做同类任务时更好,因为第一次的教训已经在磁盘上。第一次尝试更好,就意味着 retry 更少、token 更少。因此 loop 会同时变得更准确、更便宜。这也是「为什么要为 dreaming pass 花 token」的诚实答案:它先花一次成本,再通过未来每个一次成功、不再重试的 beat 把成本赚回来。
14. 检查工作仍然是你的职责
循环自行运行,也意味着循环会自行犯错。执行者–检查者拆分让循环说出的「完成」具有一定意义,但「完成」仍然是一项主张,不是证明。你的工作没有消失,只是发生了移动。你不再输入每一步,却仍然要确认循环交付的代码真正有效。阅读循环打开的 diff。相信循环能完成工作,但要在工作正式生效前检查它。
可选技术细节,第一次阅读可以跳过。
当你拥有许多 loop 时,本课针对单个 loop 讲过的 3 件事会立刻变成组织级问题。企业平台领域已经开始讨论这 3 类问题。
第一,无人看守时,失败是怎样叠加的。 模型步骤可靠,但不是百分之百确定。把 5 个步骤串在一起,假设每步有 95% 的概率正确,最终只有大约四分之三的 run 能顺利完成。情况还会更糟:无人值守 loop 的错误会在 spine 内部不断累积。今晚写进 progress.md 的一行错误内容,会成为明天错误的起点。这就是执行者–检查者拆分如此重要的原因。Checker 会阻止某个错误步骤变成永久状态。
第二,不仅要审查 loop 做出的工作,还要限制它能够做什么。 Reviewer 只会给展示给它的工作评分,也就是 diff。但这项审查并不能证明 loop 没有做其他事。它可能修改了配置值、切换了某个 flag,或通过自己恰好拥有的 connector 调用了外部系统。解决方法不是更聪明的 reviewer,而是更窄的 loop。请把 loop 的每个部分都看作一项长期授权:schedule 是允许它在你睡觉时行动;connector 是对真实系统的长期访问权;subagent 则使用借来的身份行动。因此,每个 loop 只应得到完成工作真正需要的权限。(claude/ 前缀、精简的 connector list 和只读 checker,都是这一个思想的不同表现。)
第三,清点的问题。 当 5 位同事复制你的 loop 后,迟早会有人提出一组棘手问题:这个团队运行多少个 loop?每个 loop 能接触什么?它以谁的身份行动?谁批准了它?这已经不再只是 loop engineering,而是 workforce management。它正是本书 Human-Agent Teams 内容与 Digital-FTE 思想接手的地方。一个 loop 即使独自赢得信任,仍然需要再为自己在团队中的位置赢得信任。
可选技术细节,第一次阅读可以跳过。
只有一个 loop 写自己的 progress.md 时,不需要下面这些措施。但只要多个 loop,甚至整支 fleet,共同读写一个 memory store,就会出现新的失败模式:两个 agent 同时写同一个文件;一个 agent 擅自「修复」所有其他 agent 都会读取的组织级规则;以及 1 月正确、到 6 月已经错误的教训。Anthropic 的团队在生产环境中运行这样的 fleet,并给出 4 条 guardrail。每一条都是旧的软件工程实践,只是重新应用到 agent memory:
- 版本记录。 Store 的每次修改都要记录:改了什么、由哪次 run 触发,以及由谁或什么执行。这样,错误更新可以回滚,而不会悄悄污染未来每一次 run。(由 git 跟踪的 spine 天生就具备这一点,这也是 spine 要留在 repo 中的又一个理由。)
- 写入前检查冲突。 Agent 提交修改前,先检查文件是否在起草期间发生变化。如果变化了,就重新读取再尝试,而不是覆盖其他人的更新。数据库几十年来一直这样做,agent memory 也需要同样的保护。
- 按层级划分权限。 Agent 可以自由写自己的 scratch space。所有 agent 都会读取的组织级规则,则应对普通 agent 只读,任何修改都要经过 review。顶层错误的一行会扩散到整支 fleet。
- 可移植性。 精心整理的 memory 是一种资产,你会希望在不止一个产品中使用。请把它保存在开放的普通格式中,并置于清晰接口之后,让它能跟随你迁移。
共同原则是:fleet 依赖的 memory 就是生产数据,应当采用生产纪律。尤其是 memory 过时的问题,正是概念 12 注释中的 dreaming pass 要定期清扫的内容。
在 loop 里、loop 上,还是 loop 外:行业对 gate 的称呼
本课把它称为「人工关口」。更广阔的领域,包括 AI safety 论文、欧盟《人工智能法案》、银行合规团队和企业采购,使用 3 个更早的术语来表达同一种思想。只需学一次。买方或监管机构会使用这些词,而它们与刚才构建的机制完全对应。
| 术语 | 含义 | 你在本课中的实现位置 |
|---|---|---|
| Human in the loop | 每个动作生效前都必须由人批准。控制更强,但速度更慢 | 逐轮 prompting、plan mode、人工关口处的 merge,以及双 Routine gate(A4) |
| Human on the loop | 系统自行行动,人负责监看,并能随时介入。速度更快,自主性更高 | Routine 向 claude/ branch 推送,你每天早晨审查。9:30 阅读 spine |
| 人在 loop 之外 | 无人监看,也没有介入方式 | 本课刻意没有实现。这里它不是第三种选择,而是 failure mode |
这张表带来 3 个结论。
第一,概念 1 的思维转变现在有了正式名称。 Prompting 是 human in the loop:你既是 heartbeat,也是 checker 和 memory;没有你进行下一轮,就什么都不会发生。Loop engineering 把你移到 loop 上方:系统自行运行,而你的注意力守在 gate。用行业术语来说,这就是本课的全部转变。
第二,好的 loop 不是二选一,而是根据每种动作混合设置。 晨间分流 loop 对安全修复采用 on-the-loop;遇到任何风险时,则退回 in-the-loop,例如 reviewer 返回 FAIL、修改 public behavior,或真正执行 merge。claude/ branch 规则表达的正是这一点:允许无人值守工作,但进入 main 前必须有 in-the-loop 步骤。概念 2 的 checker ladder 会告诉你如何调整两者比例:checker 越弱,需要从 on-the-loop 退回 in-the-loop 的动作就越多。通过测试可以赢得自主权,rubric 分数则不能。
第三,「out of the loop」是 AI gravity 会把系统拖向的地方。 没有人会故意设计 out-of-the-loop 系统,它通常由漂移造成。停止阅读 diff、开始盲信绿色勾选、跳过每周对已交付内容的检查后,一个原本设计为 on-the-loop 的 loop,就会在设计完全没变的情况下悄悄变成 out-of-the-loop。Dogfooding 规则就是防线:把人放在自动动作一旦错误就代价高、难以逆转的位置,并持续确认人确实还在那里。概念 15 的每周习惯,就是这种确认。
当你把 Digital FTE 卖进受监管行业时,对方很可能会这样问:「人在 loop 之中,还是在 loop 之上?」 现在你可以按动作逐项准确回答,并指出 gate 在哪里。
In the loop:人批准每个动作。On the loop:系统行动,人监看并可停止。Out of the loop:无人监看,对写操作不可接受。本课默认 on-the-loop,并在高风险步骤设置 in-the-loop gate。
Dogfooding 一节中的 What's New loop 没有逐次批准。它属于 human in the loop、on the loop,还是 out of the loop?为什么在那里可以接受? 它仍是 on the loop。虽然无人批准每次运行,但产出公开、留有日志、可一键回滚,而且团队会读 transcript。只有从来没人检查它发布了什么时,它才会变成 out of the loop。控制强度取决于错误动作的代价,而一行不够好的 changelog 很容易撤回。查看答案
15. 不要停止理解自己的项目
循环交付你没有亲手编写的代码越快,项目实际包含的内容与你真正理解的内容之间,差距就会越大。这种差距是一项真实成本,而顺畅的循环会悄悄放大它。治疗方法与陷阱是同一个动作。认真设计循环会让你持续参与;为了逃避工作而设计循环,则会让你停止思考。同样的动作会产生相反结果。循环无法分辨,你可以。
两个人可以构建完全相同的循环,却得到相反结果。一个人用它加速自己深入理解的工作;另一个人用它逃避理解。请构建循环,但要像一个打算继续做工程师的人那样构建,而不是只做按下启动键的人。
这两个人都处在同一种力之下,而且这股力有名字。MIT Sloan 的 Eric So 把它称为 AI gravity:一种持续的拉力,让你越来越愿意把思考外包给 AI。循环就是带着心跳的这种拉力:它在你睡觉时也会运行,所以即使你什么都没碰,交付出来的东西与你真正理解的东西之间的差距也会继续变大。如果放任不管,这股 gravity 会挤压本课说仍然属于你的两个端点:意图会从精确、可检查的条件变薄成「只要继续能用」,责任会从阅读 diffs 变薄成相信绿色勾选。无论哪种情况,循环都会继续运行。它无法判断你是否仍然是工程师。回答概念 15 那个问题的每周习惯是:阅读本周交付了什么,并检查你的理解是否跟上了。这就是你抵住那股拉力的方式。
这也是整门课的主线。工具每年都会吸收更多循环机制。动态工作流、/goal、Routine、重试上限、每个 agent 的 steps 上限和后台会话等功能,如今已经取代了过去需要自定义脚本才能完成的工作。工具无法吸收的,是概念 1 中的两个端点:准确到可以检查的意图,以及对交付内容承担的责任。正因如此,这件事叫作工程,而不是按按钮,也是这项技能中不会腐烂的部分。可以让工具变强,也可以依赖它们,但请始终把这两个端点握在自己手中。
循环在你睡觉时失败怎么办
无人值守循环同样会无人值守地失败。在信任它过夜运行前,要先让它具备可观测性:
- 把输出发到你能看到的地方:日志文件、Slack 或 Discord 消息(Claude Code Channels),或者 Triage 收件箱,而不是已经关闭的终端
- 每次运行都写一行,即使失败也要写:每次搏动都向
progress.md(或日志)追加带时间戳的说明,包括尝试了什么、什么通过了、什么失败了。静默失败是最糟的失败 - 让运行可以重放:在 OpenCode 中,
opencode run --format json、opencode export <id>和opencode session list会提供完整记录;在 Claude Code 中,Routine 会在网页界面保留运行历史,后台 session 会和交互 session 一起出现在--resume中 - 到达上限时明确失败:循环达到上限或发生错误后,应留下清楚的「needs a human」说明,而不是直接停止
- 先证明 loop,再交给夜间运行:同时沿两个维度逐步扩大。Cadence 上,先让它每小时运行并观察几天,再改成夜间无人值守。Capability 上,先从只报告开始(loop 可以描述问题,但不能修复),再允许它在 human gate 后修复,最后才允许无人值守行动。Loop 必须在较低一级持续正确,才能赢得下一级权限。发现异常时,先读 spine,它会告诉你上次成功运行做了什么
无法调试的循环,也无法信任。
loop 之后:graph engineering
本课故意留下一条开放线索。2026 年 7 月 18 日午夜后,本课开头那位主张「设计能提示 agent 的 loop」的 Peter Steinberger 发了一个只有 12 个词的问题:「我们还在谈 loop,还是已经转向 graph 了?」人群很快把它变成口号(「loop engineering 已死,graph engineering 万岁」),噪声背后却有一个本课无法独立回答的真实问题:当你运行不止一个 loop,它们就需要接线,包括谁给谁提供输入、谁检查谁、共享 memory 放在哪里,以及哪些 measurement 是任何 loop 都无法辩解的硬标准。
这个问题会在本系列再往前两步的位置,得到一门完整课程:Graph Engineering(排在 Harness Engineering 之后)。它会讲这个短语的两半:loop 共享的 memory graph(工作所形成的 commit DAG,以及事实所形成的 knowledge graph,参考 Karpathy 的 autoresearch 与 Anthropic 的 Knowledge Graph Cookbook),以及让多个 loop 保持诚实的 governance graph(参考 Carlos E. Perez 对单 loop 的 4 种失败分析,其中也解释了为什么那句口号是错的)。现在只保留口号里诚实的版本:graph 就是组合起来的 loops。 拿掉 loops,graph 只剩空盒子。你在这里构建的每一个停止条件、checker、spine 与 gate,都是 graph engineering 假设你已经会做的基础。请照本课方法先构建第一个 loop;当你构建第二个时,graph 课程就在等你。
截至 2026 年 7 月下旬为最新内容。这个术语可能流行,也可能消失,但它指向的结构是稳定的。
在本书中运行这些 loop(dogfooding)
现在,这套结构已经很熟悉了。Loop 是这样一种系统:找到工作、完成工作、检查自己的结果、记录做过什么,再决定下一步;heartbeat 负责启动它,spine 则把各次运行连在一起。你已经在纸面上用两种工具构建过一次。接下来,它不再只停留在纸面上。
任何工具在要求你信任它之前,都应该先回答一个公平的问题:构建它的人自己是否真的在运行它?在软件领域,这叫 dogfooding:在真实生产环境中使用自己的产品,而不只是在 demo 中展示。下面直接给出答案:每天有两个 loop 维持本书运行,而且它们就是本课刚刚教你的同类 loop。本书正在对自身执行它教你为自己执行的事情。 两个 loop 运行在不同技术栈上,这让概念 3 变成了现实:loop 的结构只需学一次,就能跨工具迁移。
Loop 1:反馈 loop(让本书保持正确)。 每课底部的反馈框,包括这一课的反馈框,都是一个 loop 的入口。
- Heartbeat: 两个 cloud Routine。一个每周数次分流新反馈,另一个每周起草修复
- Spine: 一个实时数据库,保存读者留下的每条说明,以及由这些说明创建的 GitHub issue。每次 run 都会读取之前 run 已经完成的工作,因此同一条反馈不会处理两次
- 一次 beat: 读取新反馈并分类。其中大多数,包括评分、感谢、重复项和已经处理的内容,都会自动关闭,无需任何人接触。其余内容会变成可跟踪的 issue;对于其中小而安全的项目,loop 会起草包含实际修复的 pull request
- 人工关口: 只有极少数关键内容会到达人类手中,例如被阻塞的读者、主动提出贡献的人,以及真正的内容错误。每项起草好的修复也必须由人批准后才会交付。更小的内容由系统自行处理并关闭。人只在真正需要时介入,而不是为了关闭一条五星评分
- 已经完成的工作: 最初几次 run 清理了数千条无人有时间阅读的积压说明,只把真正需要决定的极少数内容升级给人
Loop 2:What's New loop(让读者及时了解变化)。 你现在就能打开的 What's New 页面,由一个 loop 编写,而不是人工撰写。
- Heartbeat: 每天运行一次的 GitHub Actions schedule。Worker 是 OpenCode,而不是 Claude Code,也就是本课讲授的另一款工具
- Spine: 一个小型 state file,记住上一次写到哪项修改,确保既不重复条目,也不会漏掉条目
- 一次 beat: 查看本书自上次以来发生的全部变化,判断读者真正关心什么,为每项写一句直白说明,检查自己的链接以确保没有失效,然后发布
- 人工关口: 没有。上线前无人逐项批准
请注意两个 loop 唯一分歧的地方,因为这是本页最有用的内容。反馈 loop 在任何内容交付前都停下来等待人;What's New loop 则不会为任何人停下。决定因素不是哪个 loop 更重要,而是错误动作的代价。对一课作出错误修改,代价高而且难以撤销;一条笨拙的 changelog 文案,只需一次 revert 就能修复。因此,可以带回自己项目的规则很短:当错误的自动动作代价高且难以逆转时,让人介入;其他地方则不必让人逐次批准。 这就是概念 1 的「意图与责任仍属于你」,被变成了可以为每个 loop 单独设置的旋钮。(用第 6 部分的行业术语来说:代价高的地方采用 in-the-loop,其他地方采用 on-the-loop。)
最后也要诚实说明,因为前几页一直在讨论如何继续做工程师:这两个 loop 都没有被彻底放任。我们会阅读 run transcript,因为绿色 run 不等于正确 run(附录 A5)。仍然由人决定哪些反馈值得修复。Loop 承担不知疲倦的中间部分,两端仍然属于我们。这不是需要道歉的限制,而是设计本身。
现在,你已经从外部看过一个运行在生产环境中的完整 loop。下面的项目将让你构建第一个自己的 loop。
🚀 项目
阅读循环不等于构建循环。下面有 8 个由易到难的项目。任选一种工具完成即可。循环结构相同,只需使用对应概念中的命令(Claude Code 使用 /loop 和 /goal;OpenCode 使用 opencode run 和 shell 计时器)。
每次开始前都要遵守两条规则:
- 使用可丢弃的 Git 仓库。 循环会自行编辑文件。第一次构建时,不要指向你真正关心的工作
- 先设置上限。 在允许任何任务自行运行前,先规定最大尝试次数、分钟数或支出(概念 13)
Project 115–30 分钟观察循环让循环监视一项长任务,并在完成时立刻告诉你。
难度:简单 · 使用:概念 4(会话内循环)。
构建。 在仓库中启动一项长任务,例如一个先等待一段时间再写入文件的脚本。建立一个会话内循环,每分钟检查一次任务是否完成,并在完成时立刻通知你。
完成标准: 循环能发现任务完成,只通知一次,而且你可以干净地停止它,全程不必盯着终端。
Project 230–45 分钟让测试通过,然后停止不断循环,直到一条命令,而不是 agent,判断工作完成。
难度:简单到中等 · 使用:概念 5(运行到完成)、概念 11(执行者–检查者)。
构建。 在仓库中放入 2–3 个小型失败测试。构建一个持续工作到测试通过的循环,但必须由一条命令(测试运行器)判断是否完成,而不是 agent。将尝试次数限制为 6 次左右。
完成标准: 循环因为测试真正通过而停止,不是因为达到上限。如果它总是撞到上限,说明停止条件或提示需要改进,这正是本项目要教你的经验。
Project 345–60 分钟带记忆的晨间简报一个定时循环,第二次运行明显建立在第一次运行之上。
难度:中等 · 使用:概念 6(无人值守计划)、概念 12(脊柱)。
构建。 创建一个定时循环。它运行一次,读取 progress.md,从仓库收集简单内容(开放的 TODO 注释或前一天的提交),写出简短摘要,并用发现内容和日期更新 progress.md。
完成标准: 连续运行两次,第二次明显建立在第一次之上,不会重复已经记录的内容。这证明脊柱有效。如果第二次从零开始,循环还没有记忆。
Project 41–2 小时带真实检查者的修复循环实施者起草,独立审查者评分,只有 PASS 才打开 PR。
难度:中等到困难 · 使用:概念 8(工作树)、概念 9(技能)、概念 11(执行者–检查者)。
构建。 做一个第 5 部分循环的缩小版。编写一个包含修复步骤的短技能,再编写一个回答 PASS 或 FAIL 的审查 agent。选取一个真实 bug,让实施者在独立检出目录(工作树或分支)中起草修复,再让审查者评分。只有 PASS 时才打开 PR。
完成标准: 两件事必须同时成立:良好修复得到 PASS 和 PR;你故意放入的错误修复得到带理由的 FAIL。如果审查者通过错误修复,说明检查者过于宽松,请收紧标准。什么都批准的检查者不算检查者。
Project 51–1.5 小时固化 body把项目 4 的 orchestration 变成一个可重复运行的单元,然后证明它还不是 loop。
难度:中等到困难 · 使用:dynamic workflows 插曲、概念 8 和 11。
构建。 把项目 4 的 fix loop body 固化下来。Claude Code 路线用普通语言描述:「用 workflow 在并行 worktree 中为这 3 个问题起草修复,并让 reviewer 分别给每项修复评分。」让 runtime 编写并运行 script;一次 run 符合预期后,在 /workflows 视图中把它保存为 /command。OpenCode 路线则把同样的工作写成 shell script:用 for 遍历候选项,以 & / wait 扇出,并用 reviewer 的 exit code 充当 checker。运行两次。
完成标准: 两件事必须成立。第一,一条命令(或一个 script)能执行整个 draft-and-review body,也就是处理多个候选项、使用隔离 checkout,并为每项给出 verdict,全程不需要你逐步提示。第二,你要在自己的机器上证明插曲中的警告:启动 fresh session(或 fresh shell),确认 workflow 完全不记得上次 run。然后说出它要成为 loop 还缺什么:触发它的 heartbeat,以及供 agent 写入的 progress file。能说出这两项,就说明你理解 engine 与 loop 的区别。(Dynamic workflows 仍是 research preview;项目与实时文档冲突时,以文档为准。)
Project 645–60 分钟门铃 loopPR 到来时自行反应,不需要你输入 prompt。
难度:中等 · 使用:概念 7(event-driven)、概念 10(connector)。
构建。 让可丢弃 repo 自动 review 自己的 pull request。OpenCode 路线运行 opencode github install 并接受生成的 workflow。Claude Code 路线创建带 GitHub pull-request trigger 的 Routine(附录会逐项讲 filter)。然后打开一个包含故意埋入 bug 的 PR,例如 off-by-one 或被删除的 null check,等待结果。
完成标准: PR 收到一条你没有请求的 review,而且指出了故意埋入的 bug。若没有发现,就收紧 prompt 后再次 push;synchronize event 会让 loop 再次触发。至此,项目 1–3 加上本项目覆盖全部 4 种 heartbeat:in-session、conditional、scheduled 和 event-driven。
Project 745–60 分钟故意把它弄坏破坏自己的 loop,然后只靠 spine 诊断。
难度:中等 · 使用:Observability、概念 13(成本)和概念 14。
构建。 使用项目 3 的 loop。先测量一个 beat:大致记录一次 run 读取和写入多少 token,再乘以 cadence,得到月度成本。然后故意破坏它:让 prompt 指向不存在的 file,或给出永远无法满足的 success condition(同时设置 limit)。让它按 schedule 触发并失败。接着只使用 loop 留下的 log line 和 progress.md 诊断,不重放完整 run。
完成标准: 仅凭 spine 就能说明什么在什么时候失败;loop 留下清晰的「needs a human」说明,而不是静默失败;你也知道当前 cadence 下的月度成本。如果它静默失败,先补上 log line。现在在你看着、代价还低时排练一次夜间故障。
Project 82–4 小时你自己的每日循环针对真实重复任务构建完整的 6 部分循环,并无人值守运行一周。
难度:综合项目 · 使用:全部 6 个部分。
构建。 从你真正参与的项目中,选择一项真实、乏味且重复的工作,例如依赖审计、文档新鲜度检查、更新日志草稿或 lint 清扫。构建完整循环:心跳、工作树、技能、执行者–检查者、连接器和脊柱。加入预算护栏,然后让它运行。
完成标准: 它无人值守运行一周后,你因为亲自阅读过交付内容而信任它,而不是因为你停止阅读。然后诚实回答概念 15 的问题:你对项目的理解跟上循环的修改了吗?如果没有,请减慢循环,直到理解跟上。(夜间失败一定会发生。责怪模型前,请先按 循环在你睡觉时失败怎么办 排查。)
附录:Routines 端到端
本附录包含产品设置、限制、认证细节和失败情形。准备配置真正的 cloud Routine 时再读。理解 loop engineering 并不要求你记住这些内容。
课程正文把 Routine 当作一种 heartbeat,随后继续讲其他内容。本附录则是一份现场指南:创建表单中的每个字段、全部 3 种 trigger、secret 应放在哪里,以及真正会让人浪费数小时的失败模式。这是全课机械性最强的一节。Routines 仍处于 research preview,因此细节一定会变化;遇到任何分歧,都以官方页面为准。请在即将构建第一个真实 Routine 时阅读,而不是提前背诵。
先用一句话定位:Routine 是一份保存好的 Claude Code 配置,也就是把 prompt、一个或多个 repository、cloud environment 和一组 connector 打包一次,随后在 Anthropic 服务器上自动运行。它把概念 6 的 heartbeat 做成了产品。你负责 loop 设计,平台负责 scheduler、机器与底层接线。
如果只想快速浏览,整份附录都在下面这张表中;后续各节会逐行解释:
| 默认行为 | 风险 | 修复 |
|---|---|---|
| New routine 里的 Local | 把 Desktop task 当成 cloud Routine | Remote 才是 cloud;Local 在你的机器上运行(A1) |
| 默认包含全部 connector | 无人值守 agent 可在所有工具中写入 | 删除任务不需要的每个 connector(A2) |
.env 被 gitignore,因此永远到不了 cloud clone | Routine 找不到 credential,随后失败或自行猜测 | secret 放 environment variables panel,并在 prompt 中明确说明(A4) |
| 每次全新 clone | loop 永远重复第一步 | 提交 context/progress file 或用外部 board(A4) |
| schedule 最短一小时 | 设计需要 15 分钟触发 | API trigger 配自己的 scheduler(A3) |
| API bearer token 只显示一次,endpoint 不去重 | token 丢失,webhook retry 造成重复 run | 立即保存 token,并让 prompt 可安全重复(A3) |
| GitHub 超额事件直接丢弃 | loop 悄悄漏工作 | nightly reconciliation sweep(A3) |
matches regex 检查整个字段 | hotfix 无法匹配「urgent hotfix for auth」 | 使用 .*hotfix.*,或直接用 contains(A3) |
| run 使用你的身份且不能中途批准 | 外部动作未经审查就发出 | 两个 Routine 之间放 human gate(A4) |
| 绿色状态只代表平台没报错 | 任务失败却看似成功 | 每次都读 transcript(A5) |
A1. Local session 不是 cloud Routine
Desktop 应用的 New routine 按钮会让你选择 Remote 或 Local,而这两个名称让网上一半的教程都混淆了。Remote 创建 cloud Routine,也就是本附录讨论的功能。Local 创建 Desktop scheduled task:它是另一项功能,在你的电脑上针对真实文件运行,包括尚未保存的改动,而且只有电脑开机时才会运行。仍然遵循概念 6 的规则:需要本地文件就用 Desktop task;需要合上笔记本后仍能运行、需要 connector,或需要 API / GitHub trigger,就用 cloud Routine。稳妥的第一步是先把 prompt 作为 Desktop task 或 one-off run 证明可行,表现稳定后再搬到 scheduled cloud Routine。
A2. 逐项理解创建表单

可以在 claude.ai/code/routines、Desktop 应用(Routines → New routine → Remote),或在 CLI 中用自然语言 /schedule 创建 Routine。三种入口都写入同一个账号,从一个入口创建的 Routine 会出现在其他入口中。CLI 只能创建由 schedule 触发的 Routine;API 和 GitHub trigger 需要之后在网页中添加。/schedule list、/schedule update 和 /schedule run 用于管理已有 Routine。
Name 与 prompt。 Prompt 就是完整工作说明,必须自包含。Routine 是完全自主的 cloud session,运行中没有 permission prompt,也没有人可问。因此,Claude 需要的一切,包括读取什么、做什么、成功是什么样、哪些内容_不能_碰,都必须位于 prompt 或 run 能访问的文件中。本课的建议在此汇合:让 prompt 指向提交到 repo 的 skill(概念 9),Routine 自身文本只保留几行。Prompt 框还有一个 model selector。Routine 每次都使用该模型,因此应让模型与任务匹配(概念 13)。
Repositories。 添加的每个 repo 都会在每次 run 中从默认 branch 全新 clone。Claude 默认只能 push 到名称以 claude/ 开头的 branch。Permissions 下的 Allow unrestricted branch pushes 开关会按 repo 移除该限制。除非有明确且经过审查的理由,否则应保持 unrestricted push 关闭。无人值守 agent 在一次糟糕 run 中直接 push 到 main,正是 human gate 要阻止的事情。
Environment。 每项 Routine 在 cloud environment 中运行,environment 控制 3 件事:network access、environment variable 和用于安装依赖的 setup script。Setup 结果会缓存,因此不必每个 session 重新运行。Default environment 使用 Trusted network access,也就是由 package registry、cloud provider API、container registry 和常见开发域名组成的固定 allowlist。其他地址会以 403 和 x-deny-reason: host_not_allowed 失败。若 Routine 必须访问你自己的服务,请改用 Custom,只允许那一个域名,同时保留默认列表。Full access 虽然存在,但授予的权限超过多数 loop 的需要,应谨慎扩大。
Connectors。 这里有一个默认值值得每次都改:默认包含你在 claude.ai 连接的全部 connector,Claude 无需询问即可使用其中所有工具,包括写操作。 保存前删除 Routine 不需要的一切。还要记住两个细节。Connector 流量经过 Anthropic 服务器,因此无需修改 network allowlist。使用 claude mcp add 添加的本地 MCP server 位于你的电脑上,而不是账号中,所以 Routine 看不到;要么在 claude.ai/customize/connectors 把它添加为 connector,要么在已提交的 .mcp.json 中声明,让它随 clone 一同到达。
A3. 三种 trigger
Schedule。 Preset 包括 hourly、daily、weekdays 和 weekly。输入的时间使用你的本地时区,系统会自动转换;为了有意错开负载,run 可能在整点后几分钟启动,同一 Routine 的偏移量每次保持相同。1 小时是最低频率,频率更高的 cron expression 会被拒绝。需要更高频率时,应使用 API trigger,并接入自己的 scheduler。自定义间隔,例如每 2 小时或每月 1 日,需要先选择最接近的 preset,再在 CLI 中用 cron expression 运行 /schedule update。One-off schedule 在指定时间运行一次,随后自行关闭,而且 one-off scheduled run 不计入每日 Routine 上限,因此最适合在正式设为重复计划前低成本排练 prompt。CLI 可直接使用自然语言:/schedule tomorrow at 9am, summarize yesterday's merged PRs。
API。 API trigger 为 Routine 提供独立的 /fire endpoint 和 bearer token。Token 生成时只显示一次,之后无法找回,因此应立即保存到告警工具的 secret store;同一个窗口可以 Regenerate 或 Revoke。任何能够发送认证 POST 的系统都能触发 Routine,例如告警 webhook、部署 pipeline、表单处理器,或你电脑上的 cron job。Request body 可以带可选 text 字段,作为本次 run 的 context,例如告警正文或失败日志;它会与保存的 prompt 一起作为未经解析的自由文本传入 Routine。Response 会返回新 session 的 ID 和 URL,让调用方直接链接到该 run:
curl -X POST https://api.anthropic.com/v1/claude_code/routines/<routine-id>/fire \
-H "Authorization: Bearer <routine-token>" \
-H "anthropic-beta: experimental-cc-routine-2026-04-01" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{"text": "Sentry alert SEN-4521 fired in prod. Stack trace attached."}'
带日期的 beta header 是必需的,并会随 preview 演进而变化。文档承诺继续支持最近两个历史版本,为调用方留出迁移时间。
/fire endpoint 没有内置去重,而 webhook sender 默认会重试。因此,重试的告警就是一次重复 run;错误配置后整夜重试的告警,就是一个你从未设计过的 loop。费用风险比表面更严重:API 触发的 run 会占用每日 Routine allowance,所以 retry storm 可能在你醒来前耗尽整天额度;若已启用按量额外用量,上限就不再是天花板,而会变成账单。概念 13 的规则不仅适用于 loop,也适用于 trigger。请在 sender 端去重或限速,并把 prompt 写成可安全重复,例如在起草修复前先检查对应 fix branch 是否已经存在。这正是概念 10 的 connector 规则。
GitHub events。 GitHub trigger 会在匹配事件到达已连接 repo 时启动全新 session。目前支持两类事件:pull request(opened、closed、labeled、synchronized 等)和 release。每类事件可以只在特定 action 或所有 action 上触发。Repo 必须安装 Claude GitHub App。请注意,/web-setup 会授予 clone access,但_不会_安装该 App,这个区别让许多第一次尝试失败。Filter 可以按 PR author、title、body、base branch、head branch、label、draft state 和 merged state 筛选,支持 contains、is one of、matches regex 等 operator。一个重要细节是:matches regex 检查整个字段,而不是其中一部分。 因此,hotfix 只匹配标题恰好等于 hotfix 的情况。请写 .*hotfix.*,或直接使用 contains。还有两个事实会影响 loop 设计。第一,在 preview 期间,GitHub event 存在按 Routine 和按账号计算的每小时上限,超过上限的事件会被丢弃,直到窗口重置。是丢弃,不是排队。因此,事件密集型设计需要 reconciliation sweep:每天夜间用 scheduled run 补上事件漏掉的工作。第二,每个匹配事件都会启动独立 session。 对同一个 PR push 两次,就会得到两个互不知情的 session。这是概念 12 的 spine 教训在 GitHub 场景中的版本。
A4. Secrets、state 与 identity
Secret 应放在 environment variables panel 中,绝不能放进 .env 文件。 原因很机械:.env 会被 gitignore;被 gitignore 的文件不会到达 GitHub;cloud clone 因而永远不包含它。Routine 触发后什么也找不到,只能失败,甚至可能自行猜测。请把每个 key 都放入 environment 的 variables panel,并在 prompt 中明确加上一行:「凭据以 environment variable 的形式提供;不要寻找 .env 文件」。若不说明,Claude 仍可能出于习惯去寻找 .env。
每次 run 都从零开始。 全新 clone、全新 environment,没有 working tree、cookie 或任何遗留物。Routine 必须记住的内容,要在 run 结束前离开机器:push 到 repo,通过 connector 写进外部系统,或发送到 board 的 API。这不是需要绕开的限制,而是概念 12 的 spine 规则由基础设施强制执行。对应模式值得单独命名:repo 中的 context file。提交到 repository 的 clients.txt、progress.md 或 triage-rules.md,每次 run 都能读取,而且无需修改 prompt 就能更新。客户列表变化时,只需编辑文件,Routine 文本保持不动。
Routine 以你的身份行动。 它们属于你的个人账号,占用你的每日额度;它们进行的一切操作,包括 commit、PR、Slack 消息、Linear ticket 和 draft email,都带着你的身份。即使你正在休假,一个代表你回复客户的 Routine 仍会继续以你的名义回复。把任何对外可见的动作放上 schedule 前,都要先考虑身份问题。
Run 中途没有批准步骤。 Routine 会从头到尾执行 prompt,无法暂停下来询问你。因此,当支付、外发 email 或 deploy 等决定确实需要人参与时,应把 gate 建在两个 Routine 之间,而不是塞进一个 Routine 内部。它分为 3 步。Routine A 起草工作,并发布到你可以审查的位置,例如 claude/ branch、Slack 消息或 draft email。人类阅读并批准。这个批准再通过 API trigger 触发 Routine B,由 B 执行动作。这就是第 5 部分的人工关口,只是改用两个 Routine 和一个 webhook 表达。

A5. 阅读 run
每次 run 都会作为完整 session 出现在 Routine 详情页中。Transcript 会显示每次 tool call、决定和修改,你可以继续手工对话,也可以把结果转成 PR。文档明确提出的一个警告,也与实际经验一致:绿色状态表示 session 在没有 infrastructure error 的情况下结束,不表示你的任务成功。 被阻止的 network request、缺少的 connector tool 和普通任务失败,都藏在 transcript 中,而不是 status column 里。请打开 run 并阅读。即使 scheduler 由平台托管,概念 14 仍然适用。
**简单来说:**绿色表示平台完成了 session,不足以证明要求的工作成功。
Run now 可以立即启动一次 run 进行测试。Repeats 区域中的 pause toggle 会停止 schedule,但不会删除配置。成本方面,run 会消耗 subscription usage,也计入每日 Routine cap。超过上限后,如果启用了 extra usage,就会按量计费;两项数据都能在 claude.ai/settings/usage 查看。如果 CLI 中的 /schedule 像是消失了,常见原因包括:正在使用 API key 或 cloud-provider authentication(它需要 claude.ai login)、设置了关闭 telemetry 的 environment variable,或 CLI 版本过旧。无论如何,web UI 仍然可以使用。
上表每一行在 GitHub Actions 中都有对应物,因为 OpenCode 路线早已用更直白的部件解决这些问题。Environment-variables panel 对应 repository secret(secrets.ANTHROPIC_API_KEY)。Connector list 对应已提交 opencode.json 中的 mcp section。Schedule 与 PR trigger 对应 on: schedule 和 on: pull_request。无状态特征完全相同,因为 CI runner 每次也都是全新的,因此 committed-context-file 模式原样适用。Identity 问题由 GitHub App 或 bot token 回答,而不是个人账号。Branch guardrail 则是你自行设置的 branch protection rule。名称与配置不同,但底层问题完全相同。这正是本课的中心论点。
A6. Routine 版安全清单
上面的最低安全 loop 清单可以直接转用于 Routine。
成功条件与上限写在 prompt 中,因为平台限制的是每日 run 数量,而不是单次 run 可能造成的损害。claude/ branch 前缀提供隔离,因此请保持开启。只读 checker 是定义在 clone 后 repo 中的 reviewer subagent。State file 必须是已提交的 context file,因为每次 clone 都是全新的。人工关口可以使用上面的双 Routine 模式,也可以简单规定「只起草 PR,绝不 merge」。日志则由 run transcript 加上一条发送到你真正会查看的位置的 connector 消息组成,同时牢记绿色不等于完成。
每次都按下面的清单逐项检查。只需 1 分钟,却能决定这是一份读过的附录,还是一个值得信任的 loop:
- Repository: 只选择正确的 repo,并保持 unrestricted branch push 关闭
- Prompt: 自包含,同时写明成功条件和上限
- Connector: 删除任务不需要的每个 connector
- Environment: secret 放在 variables panel,不放
.env;network access 只开放任务允许的最小范围 - Trigger: 有意选择 schedule、API 或 GitHub;避免意外高频触发;若 trigger 可能重试,则确保操作可安全重复
- State: 第一次 run 前,先选好已提交的 progress/context file 或外部 board
- 人工关口: 只起草 PR、branch 或消息,不直接 merge、deploy、付款或向客户发送
- 测试 run: 用 one-off schedule 或 Run now 触发一次,然后阅读 transcript,不要只看状态颜色
练习:三个 Routine drill
阅读 field guide 不等于亲手操作 form。前 3 个 drill 会在可丢弃 repo 中故意重现附录最重要的 failure case,让你在成本和风险很低时经历它们。这些练习也围绕每日运行上限设计。第一个使用完全不计入上限的 one-off run;后两个总共约需 5 次 run,相当于 Pro 套餐一天的额度。主项目的两条规则仍然适用:使用可丢弃 repo,并先设置上限。
(本节最后的项目 12 不是 drill,而是建立在项目 3 或 8 之上的第二个综合项目。它每周运行一次,因此请单独规划其 run。) 难度:简单 · 使用:A1、A3(one-off schedule)、A5(阅读 run)。 构建。 在 throwaway repo 中创建 Routine,让 prompt 完成一件小而可检查的事,例如把昨天的 commit 汇总到 完成标准: 看到两次绿色 run:一份 transcript 显示成功,另一份显示失败。你能用一句话解释 status column 为什么分不出两者:绿色只说明 session 没有 infrastructure error。 难度:简单到中等 · 使用:A4(secret)、A2(environment)。 构建。 写一个需要 secret 的 prompt,dummy token 即可。第一次把 token 放进 gitignored 完成标准: 第二次 run 能从 environment 读取 token,并能解释第一次失败的机械原因:gitignored file 不会到达 GitHub,所以 fresh cloud clone 中不存在。 难度:中等到困难 · 使用:A3(API trigger)、A4(gate)、A6(checklist)。 构建。 Routine A 通过 one-off schedule 起草可审查内容,例如 完成标准: B 只因你手动触发而运行;B 的 transcript 显示动作确实发生;你对两个 Routine 都跑完 A6 checklist,包括精简 connector、关闭 unrestricted push,并选好 state file。 难度:capstone · 使用:概念 12(spine 与 improvement loop)、概念 11、概念 6、第 5 部分。 构建。 先准备一个已运行一周并在 完成标准: PR 中的提议能追溯到真实 log entry;手工植入的重复 failure 会变成提议;在你 merge 前 rules file 不发生变化。若 loop 在没有证据时提出改动,请收紧 prompt。Project 920–30 分钟免费排练 Routine先用 one-off run 证明 prompt,再把它放上 schedule。
claude/summary branch。不要设置重复 schedule。用 one-off run(/schedule tomorrow at 9am, … 或 Run now)触发并阅读完整 transcript。然后把 prompt 改成必然失败,例如读取不存在的 file,再触发一次。Project 1030–45 分钟Secrets drill故意用错一次 .env,以后不要意外犯错。
.env 后触发 Routine,阅读 transcript 看它为何找不到。第二次把 token 移到 environment-variables panel,并在 prompt 中写明:「credential 通过 environment variable 提供;不要寻找 .env file。」Project 111–2 小时构建两个 Routine 的 gateA 起草,你决定,只有你的决定才触发 B。
claude/ branch 或通过 connector 发布的短摘要。Routine B 使用 API trigger,执行一个小型后续动作。B 的 bearer token 只显示一次,立即保存。亲自 review A 的 draft,再用 A3 的 curl call 触发 B 以表示批准。Project 122–3 小时构建 dreaming loop每周读取其他 loop 的 log,并把 rule change 作为 PR 提议。
progress.md 留下带日期记录的 loop(项目 3 或 8)。再为它构建第二个 loop:每周读取自 dreaming-state.md 中日期以来的所有 log entry,查找重复出现的 failure 或 correction,把能阻止它的最小 rules-file 或 skill change 起草成 claude/ branch 上的 PR,绝不直接 commit。PR description 必须引用证据:哪些 run、出现几次、为什么这条改动能阻止它;还要提议删除一条近期 run 不需要的 rule。最后更新 dreaming-state.md。
接下来去哪里
- 同时运行多个 loop? 一旦两个 loop 交换工作或共享 state,就需要接线方式和共享 memory。本系列再往前两步的 Graph Engineering 会讲解这两方面
- 为非编码工作构建 loop? Cowork 与 OpenWork 速成课 展示专业人士如何使用同一种 heartbeat 思想,并用 scheduled task 代替 cron
- 不从 terminal,而是从 API 运行 loop? Claude Platform 的 Managed Agents 现已支持 scheduled deployment(public beta):为 agent 提供 cron schedule,每次触发都会在 Anthropic 基础设施上启动 fresh session,无需你自行构建或托管 scheduler。这就是作为平台原语提供的 Routines 思想,形状完全相同。Schedule 是 heartbeat,prompt 是 beat,而 spine 仍由你提供
- 希望平台替你管理改进 loop? 同一个 Managed Agents 平台包含 memory 与 dreaming 工具,也就是概念 12 注释中的 out-of-band improvement pass,以产品形式提供。形状没有变化:batch job 是 heartbeat,memory store 是 spine,approval step 是 human gate
- 也希望平台管理 checker? 两项 research preview 把 verification skill 插曲变成了产品。Code Review 会在你启用的 repo 中对每个 PR 运行托管的多 agent review pass,也就是把第 4 个家做成服务。Claude Managed Agents 中的 Rubrics(beta)则把概念 2 的「带及格线 rubric」变成平台原语,由独立 grader agent 验证结果,并把失败工作送回再次尝试。关于模型评分究竟能信到什么程度,请参阅 Trusting the Checker 课程
- 为无人值守 run 调节 retry? Claude Code 的错误参考记录了自动 retry 设置,包括
CLAUDE_CODE_MAX_RETRIES,以及面向 CI 式无人值守 session 的CLAUDE_CODE_RETRY_WATCHDOG模式 - 想找 clone 后即可运行的起点? 社区 repo cobusgreyling/loop-engineering(MIT)收集了 production loop pattern,包括 daily triage、PR monitor、CI checker、dependency checker 和 changelog drafter;这些 starter kit 带有 readiness checklist,并映射到多种 agent CLI。它是年轻的第三方项目,使用前请确认仍在维护。不过,它的 primitives table 只是用另一组名称重述了本课的 6 个部分,因此可以作为有用的第二种讲法。Hugging Face 上由社区整理的 awesome-loop-engineering 集合,则把这条阅读路径中的主要文章集中到一起
- 你在 Spec-Driven Development 中写出的 spec 就是 loop 的停止条件:acceptance criteria 是 checker 评分的依据,也是
/goal停止前要证明的内容。当一个 loop 让你不敢放手运行时,修复方式几乎总是更清晰的 spec,而不是更多自动化
来源与延伸阅读
本课建立在少量一手资料上。框架和引文来自这些来源,技术细节则来自官方文档。
「循环工程」的起源
- Addy Osmani,《Loop Engineering》:为这种模式命名并提出「5 个部分加 1 条脊柱」模型的文章。https://addyosmani.com/blog/loop-engineering/
- Avi Chawla,《Loop Engineering, Clearly Explained》:讲解 inner-loop 结构、4 层工程体系(prompt → context → harness → loop)、doom loop,以及面向 loop 的 tool 设计规则。https://www.dailydoseofds.com/p/loop-engineering-clearly-explained/
- Data Science Dojo,《The 4 Layers of AI Engineering》:提出每层对应一种 failure mode,以及「哪一层仍由你手动完成」的自检问题。https://www.facebook.com/share/p/1HCxfwo5aC/
- Rakesh Gohel,《How to Actually Use Fable 5》(infographic):区分 self-learning 与 self-improving,并对比「加强 prompt 后重来」和「运行、记录、提炼、重复」。https://rakeshgohel.substack.com
- Sydney Runkle(LangChain),《The Art of Loop Engineering》:4 层 loop stack(agent、verification、event-driven、hill-climbing)、trace-driven improvement loop,以及 swyx 的「loopcraft」观点。https://www.langchain.com/blog/the-art-of-loop-engineering
- Lamis(Anthropic Applied AI),《Context Engineering: Memory and Dreaming》(AI DevCon 2026):in-band 与 out-of-band memory、dreaming consolidation、shared memory store 的 production guardrail,以及概念 12 中的学校与校长类比。https://www.youtube.com/watch?v=tQ41RxfZZVg
- Letta(Charles Packer、Sarah Wooders 等),《Sleep-time Compute》:最早将 dreaming 思想产品化的实践之一;background agent 在 idle 时重写 primary agent memory,并说明 offline consolidation 只有在未来任务与过去任务相似时才划算。https://www.letta.com/blog/sleep-time-compute/
- Stanford / SambaNova / UC Berkeley,《Agentic Context Engineering (ACE)》:为反复重写 memory 的两个 failure mode 命名,即 brevity bias 与 context collapse;修复方式是增量 delta update,而不是整体重写。https://arxiv.org/abs/2510.04618
- OWASP,《Top 10 for Agentic Applications (2026)》:把 Memory and Context Poisoning 定义为独立的 agentic threat;注入内容会留在 memory 中,在原始攻击消失后继续影响行为。
- Simon Willison,《Designing agentic loops》(2025 年 9 月):很早就明确指出,关键技能是设计 loop,而不是驾驶 agent;当时「loop engineering」这个名字尚未出现。https://simonwillison.net/
- TrueFoundry,《Loop Engineering at Enterprise Grade》:failure-stacking 计算、loop 部件作为 standing permission 的框架,以及团队规模下的 inventory problem。https://www.truefoundry.com/blog/loop-engineering-enterprise-agent-runtime
- The New Stack,「The Anthropic leader who built Claude Code says he ditched prompting — now he just writes loops.」https://thenewstack.io/loop-engineering/
- Boris Cherny 关于「我的工作是编写 loop」的说法来自 CNBC 采访,由 Business Insider 报道。Peter Steinberger 关于「设计能提示 agent 的 loop」的说法来自他在 X 上的帖子
- Andrew Ng:三个产品开发循环(编码约分钟、开发者反馈约小时、外部反馈约天),以及把「品味」重新表述为人的上下文优势。出自他在 X 上的帖子。https://x.com/AndrewYNg/status/2071988145667928442
- Andrej Karpathy:「不要告诉它怎么做;给出成功标准,然后看它行动」,以及 AutoResearch 项目。该 agent 会调整 training script、测量结果并保留有效改动,轮次之间无需人工编辑。
Claude Code(官方文档)
- Routines:云端定时自动化、trigger、运行上限,以及发布时每种 plan 的每日限制。https://code.claude.com/docs/en/routines 与 https://claude.com/blog/introducing-routines-in-claude-code
- Channels:向正在运行的会话输入事件驱动消息。https://code.claude.com/docs/en/channels
- Scheduled tasks:
/loop、cron tool、Desktop task,以及 background session 的延续规则。https://code.claude.com/docs/en/scheduled-tasks - Changelog:background session、retry watchdog、
ultracode改名,以及本章所有机械细节最先被更新的位置。https://code.claude.com/docs/en/changelog - Memory:
CLAUDE.md、auto memory,以及 Auto Dream research preview 背后的 consolidation pass。https://code.claude.com/docs/en/memory - Delba de Oliveira(Anthropic Claude Code 团队),《Building verification loops in Claude Code with skills》(2026-07-22):verification skill、4 种 deployment home(standalone、embedded、chained、on-every-PR)、graduation signal、wrapper-skill pattern、Anthropic 内部的
/code-review→/simplify→/verify→/designchain,以及/verify、Code Review 和 Managed Agents Rubrics。https://claude.com/blog/building-verification-loops-in-claude-code-with-skills - Code Review 与 Managed Agents Rubrics 在写作时仍是 research preview 或 beta;依赖其可用性或行为前,请查看实时文档。
OpenCode(官方文档)
- CLI:
opencode run、serve和--attach。https://opencode.ai/docs/cli/ - Agent 与子 agent:主 agent、子 agent,以及每个 agent 的模型。https://opencode.ai/docs/agents/
- GitHub 集成:Action、计划/PR/issue 触发器,以及
opencode github install。https://opencode.ai/docs/github/
模型标识符
- Anthropic,《What's new in Claude Sonnet 5》:
claude-sonnet-5model string、从 Sonnet 4.6 直接迁移、默认 adaptive thinking 和新 tokenizer 的来源。https://platform.claude.com/docs/en/about-claude/models/whats-new-sonnet-5 - Anthropic,《Model IDs and versioning》:解释无日期与带日期模型 ID 的版本语义;当前示例以 Sonnet 5 为准。https://platform.claude.com/docs/en/about-claude/models/model-ids-and-versions
- Anthropic,《Introducing Claude Fable 5 and Mythos 5》:2026 年中发布、位于 Opus 之上的旗舰 generation,也提醒读者本章 model 示例只是说明,不代表 frontier。https://www.anthropic.com/news/claude-fable-5-mythos-5
所有链接截至 2026 年 7 月上旬仍有效。这些工具经常更新,因此在依赖任何具体限制、参数或 model string 前,请先对照实时文档确认。
一句话小结
Prompt 说明要做什么,loop 则说明何时停止。不要再逐轮提示 agent。请设计一个替你提示它的 loop,也就是一个 heartbeat、4 个工作组件,以及一条负责记忆的 spine。同时,继续做那个阅读交付内容的工程师。