# 从 while True 到 Loop Engineering：AI Agent 的古罗马副官

> 作者：晨旭｜发布：2026-06-29｜系列：动手落地AI
> 来源：https://chenxu.xin/writing/loop-engineering-optio

Claude Code 明明已经有两层循环，为什么还不等于 Loop Engineering？沿着开源项目 Optio 走完一条任务从生成、提交 PR 到 CI 失败后再次唤醒 Agent 的完整生命周期。

---

之前的文章拆解了 [Claude Code 收到一个任务后，内部到底发生了什么](/writing/claude-code-query-loop)。当时是把它的核心 `query_loop` 拆成了两层循环：

- 内层是工具执行循环：模型发出 tool_use，Python 程序执行工具，再把 tool_result 塞回消息列表
- 外层是回合循环：模型拿到工具结果后继续思考，直到输出 `stop_reason == "end_turn"`

既然这里都有两层 Loop 了，那它是不是就是 Loop Engineering 呢？这是我最近学习 Loop Engineering 时最把我绕晕的问题。

- 如果它不是，为什么不呢？难道代码里的 `while True` 还不算循环吗？
- 如果它是，Automation、Worktree、Skill、Connector、Sub-agent 和 State 又是在解决什么呢？

带着这些疑问，我找到了一个开源项目 [Optio](https://github.com/jonwiggins/optio)，沿着一个任务从产生、执行、提交 PR，到 CI 失败后再次唤醒 Agent 的完整生命周期走了一遍，终于看清楚：

- Claude Code 里的两层循环自然是真正的 Loop，但它们解决的是：**一次 Agent 运行内部如何推进**
- Loop Engineering 还要再向外包一层，解决：**多次 Agent 运行之间，工作如何持续推进**

这不是否定 `while True`，而是在追问一个更外层的问题：**当这次循环退出以后，下一次由谁启动？**

这篇文章从 Optio 的源码出发，拆开以下四个问题：

- Claude Code 明明已经有两层循环，为什么还不等于完整的 Loop Engineering？
- Automation、Worktree、Skill、Connector、Sub-agent、State 分别解决什么问题？
- 为什么有了这六个组件，仍然不一定形成真正的 Loop？
- 一个可以长期运行的 Loop，还需要哪些工程化护栏？

补充一句题外话：Optio，是古罗马军队中协助百夫长维持队伍运转的副手。而这个项目做的也是类似的事：它不亲自写代码，而是负责叫醒 Agent、分配任务、检查结果，再决定下一轮行动。

## 明明已经有两层循环，为什么还不够？

先回到之前文章中的 `query_loop`。把那些流式事件、消息类型和容错逻辑暂时去掉，它大致可以压缩成一个很短的循环。



这个 Loop 发生在**一次 Agent 运行内部**：模型规划，程序执行，结果重新放回上下文，模型再判断要不要继续。这当然是循环，而且还是两层嵌套循环。内层让模型和工具来回协作，外层让 Agent 可以连续推进多个回合。它们共同回答的是：

> 人给 Claude Code 一个任务后，它内部如何把这个任务做完？

但问题恰恰出现在 `end_turn` 以后。

假设 Coding Agent 提交了 PR，然后结束运行。半小时后 CI 才失败。这时 `query_loop` 已经退出，原来的 Agent 进程可能也不存在了。这时谁去读取 CI 日志？谁来重新唤醒 Agent？谁告诉它只修复这次失败？如果连续失败十次，又由谁来叫停呢？

这些都不是增加一层工具调用循环能够解决的。



之前的 Claude Code 文章拆解的是里面两层，Loop Engineering 重点关注的是最外面一层。

还有一个容易混淆的地方：Claude Code 的 Runtime 外面确实有 Supporting Systems，包括权限、Hooks、Memory、Session 和 MCP。但**架构上的外层，不一定等于循环上的外层**。这些模块在支撑 `query_loop` 运行，却不一定会在 CI 失败后主动生成下一次 Agent 任务。

判断是不是 Loop Engineering，关键不是代码里嵌套了几个 `while`，而是看：**最外层那个负责发现工作、检查结果、记录进度和启动下一轮的人，有没有被系统替代？**



把传统的使用方式画出来后，这个区别就更明显了：在这里，Agent 只负责其中一段，真正的循环控制器一直是人。

所以「不要再亲自 Prompt Agent，而要设计 Prompt Agent 的 Loop」，并不是说以后不需要写 Prompt，也不是说 Agent 内部的循环不重要。它真正想替代的，是人反复复制错误、重新运行命令、追问 Agent、记录进度这些机械操作。

小结一下：Agent Loop 负责的是一次运行内部如何行动；而 Loop Engineering 负责的则是多次运行之间如何持续推进工作。

## Loop Engineering 的六个组件

带着这个问题我们来到 Optio 这个项目。这里不按文件夹来读源码，而是追踪一条具体链路：

> 每天自动生成一个代码维护任务，Agent 在独立目录中修改代码并提交 PR；CI 失败就重新修，CI 通过后启动另一个 Agent 审查；Review 通过才允许结束。

这时，Loop Engineering 的六个组件就从抽象名词变成了六个具体的问题。它们把原来散落在人脑和手工操作里的东西，逐一搬到系统外部。

### Automation：并不是「定时调用一次模型」

Optio 中的 Automation 是由几样东西配合完成：Task Config 保存「每次做什么」，Trigger 保存「什么时候做」，Trigger Worker 定期检查有没有到期任务。

源码里，`workflow-trigger-worker.ts` 默认每隔一段时间查询到期的 Trigger。当目标类型是 `task_config` 时，它调用 `instantiateTask()`。后者会渲染 Prompt 参数、创建一条真实 Task，把状态从 pending 变成 queued，最后放进 BullMQ 队列。



原来可能会把 Automation 理解成「每天早上八点调用一次模型」。但从这里会发现，一个可靠的 Automation 不是定时器本身，而是**「触发条件 + 任务蓝图 + 实例化 + 入队」的完整链路**。

因为闹钟只负责响，它不能直接代替一张工作单。

### Worktree：给每个 Agent 一张不会串台的办公桌

任务进入队列后，Optio 不会让所有 Agent 在同一个仓库目录里修改文件。它为每个 Task 创建独立的 Git Worktree。



这一步决定了多个 Agent 能不能真正并行。如果没有 Worktree，就像让三位程序员共用一台电脑、同时编辑同一份文件。Agent 数量越多，冲突反而越快。

**并行不是多开几个对话框，而是先隔离它们会修改的环境。**

### Skill 与 Connector：一个管「会不会」，一个管「能不能」

在 Agent 启动前，Optio 会向 Worktree 注入两类不同的信息。

第一类是 **Skill**：源码中的 `buildSkillSetupFiles()` 会把技能写成文件，告诉 Agent：这个项目怎么测试、代码规范是什么、哪些目录不能碰、出现某类问题应该按什么步骤处理。

第二类是 **Connector**：Optio 会根据当前仓库、Agent 类型和权限找到可用 Connection，再生成 `.mcp.json`。Agent 因此可以访问 GitHub、Slack、Linear、数据库等外部系统。



Skill 决定 Agent「应该怎样工作」，Connector 决定 Agent「被允许去哪里工作」。

### Sub-agent：不是多叫一个模型，而是把「检查」变成独立任务

这部分是我感受最深的地方。我原来以为 Review Agent 就和普通的 Sub-agent 一样，是由主 Agent 在对话中临时喊来一个小助手。但 Optio 的 Review Agent 不是一次临时调用，而是**一条真正写入数据库、可以排队、失败和重试的子任务**。



这相当于把开发和验收完全交给两个人。写代码的 Agent 不能自己说一句「测试通过，完成了」，系统就当真。另一个 Agent 会拿到原始需求、PR 内容、已有评论和测试命令，独立给出审查结果。

## 真正让 Loop 闭合的，是那位不写代码的「副官」

有了队伍，不等于队伍会自己持续向前走。仔细看会发现，这条链路仍然可能只是一条直线：

- 如果 CI 失败，谁来决定「要不要继续」？
- 如果 Review 要求修改，谁把反馈交回 Coding Agent？
- 如果进程中途崩溃，谁知道应该从哪里恢复？

上面的组件提供的是循环所需的**执行能力**，而真正把首尾接起来的，是 **State + Feedback + Decision**。

### 1、Reconciler：谁来下达下一道命令

在 Optio 中，负责把这些能力连接起来的角色叫 Reconciler。也是到这里，我才突然意识到这个项目的名字很有意思：在古罗马军队中，Optio 是协助百夫长维持队伍运转的副手，它会观察队伍、传递命令，并在情况变化时及时补位。

那么是不是可以这么比喻：如果说 Coding Agent 是在前线执行任务的士兵，那么源码中的 Reconciler 就很像站在队伍后方的 Optio——它不亲自修改代码，只负责观察现场、传递命令，再决定下一步行动。

它反复做三件事。`buildWorldSnapshot()` 像是重新巡视现场：任务现在处于什么状态？PR 有没有合并？CI 是通过还是失败？Review 有没有要求修改？`reconcileRepo()` 则根据这些事实，生成下一道命令。源码中的判断很具体：

1. PR 已合并：把 Task 变成 completed
2. CI 第一次变为失败：重新唤醒 Coding Agent
3. CI 通过且开启自动审查：创建 Review Task
4. Review 要求修改：把评论重新交给 Coding Agent
5. 自动恢复次数达到上限：进入 needs_attention，等待人处理

最后，`executeAction()` 才会真正执行唤醒 Agent、创建 Review Task、自动合并或者结束任务。

但这里还有一个问题：Reconciler 每次被唤醒时，原来的 Agent 进程可能早已退出。它怎么知道任务上一次进行到哪里了呢？

答案不是 Reconciler 自己拥有记忆，而是系统把现场写在了外部 State 中。

### 2、State：这位副官靠什么记住现场

Optio 把任务状态写入 Postgres，并通过状态机限制它如何变化。



这里不只保存了一个简单的完成 / 未完成：

- `tasks.state` 记录任务现在进行到哪里
- `task_events` 记录任务为什么从一个状态进入另一个状态
- PR、CI 和 Review 状态记录外部世界发生了什么
- Git 分支和 Worktree 保留 Agent 已经完成的代码修改

每一次 `transitionTask()` 也不只是修改一个字段。它还会校验状态迁移是否合法、记录事件，并唤醒下一轮 Reconcile。

### 3、CAS、Resync 与预算：如何防止「副官」自己失控

如果同时有两个 Worker 下达命令，或者某次唤醒消息丢了，这该怎么办呢？

顺着这个问题继续读源码，这才明白那些一开始让我觉得很繁琐的代码，几乎都在处理这个问题：如果 Loop 在无人盯着时出错怎么办？Optio 加了至少三道护栏。

**第一道是 CAS**：更新任务状态时，SQL 会附带「当前状态仍然等于我刚才看到的状态」这个条件。两个 Worker 同时处理同一任务时，只有一个能成功，另一个会重新读取最新事实。

**第二道是 Resync**：即使某次消息丢了，系统仍会周期性扫描未完成任务，再次放入 Reconcile 队列。这个很像夜班交接时重新点一遍未结工单，避免某个任务永远躺在角落里。

**第三道是预算和停止条件**：自动恢复不是无限的，源码会比较 `recentAutoResumeCount` 与 `maxAutoResumes`。超过上限，就不再继续烧 Token，而是把任务交回人类。

能重复运行的脚本，不等于可靠的 Loop。**可靠的 Loop 必须知道何时继续、何时重试、何时恢复，以及何时承认自己做不到。**

## 最后

再回到开篇的那个问题：Claude Code 明明已经有两层循环，为什么还需要 Loop Engineering？

现在可以这样回答：Claude Code 的 `query_loop` 解决了一个 Agent 被叫醒之后，如何调用模型、执行工具，再一轮轮把任务做下去；而 Optio 继续追问的是，当这次运行结束以后，谁检查结果，谁记住现场，谁决定要不要再次叫醒它。

前者让 Agent 能够行动，后者让行动可以跨越一次会话，持续地向一个目标收敛。

Agent 负责向前走，而人负责决定方向，以及哪里才是终点。

## 核心结论

- Agent Loop 负责的是一次运行内部如何行动，Loop Engineering 负责的是多次运行之间如何持续推进工作。判断是不是 Loop Engineering，关键不是代码里嵌套了几个 while，而是看那个负责发现工作、检查结果、记录进度、启动下一轮的人，有没有被系统替代。（判断 · 把握较大）
  永久链接：https://chenxu.xin/writing/loop-engineering-optio#agent-loop-vs-loop-engineering
- 架构上的外层不等于循环上的外层。Claude Code 的 Supporting Systems（权限、Hooks、Memory、Session、MCP）确实包在 Runtime 外面，但它们只是在支撑 query_loop 运行，不会在 CI 失败后主动生成下一次 Agent 任务。（判断 · 把握较大）
  永久链接：https://chenxu.xin/writing/loop-engineering-optio#architectural-outer-is-not-loop-outer
- 可靠的 Automation 不是定时器本身，而是「触发条件 + 任务蓝图 + 实例化 + 入队」的完整链路。闹钟只负责响，它不能直接代替一张工作单。（判断 · 把握较大）
  永久链接：https://chenxu.xin/writing/loop-engineering-optio#automation-is-not-a-timer
- 并行不是多开几个对话框，而是先隔离它们会修改的环境。没有 Worktree，就像让三位程序员共用一台电脑同时编辑同一份文件，Agent 数量越多冲突反而越快。（判断 · 把握较大）
  永久链接：https://chenxu.xin/writing/loop-engineering-optio#parallel-requires-isolation-first
- 能重复运行的脚本不等于可靠的 Loop。可靠的 Loop 必须知道何时继续、何时重试、何时恢复，以及何时承认自己做不到——对应 CAS 乐观锁、周期性 Resync 扫描和自动恢复次数上限三道护栏。（判断 · 把握较大）
  永久链接：https://chenxu.xin/writing/loop-engineering-optio#reliable-loop-needs-guardrails

---

本文出自晨光里的AI（https://chenxu.xin），作者晨旭。
引用时请保留来源链接。文中标注「判断」「假设」的部分是作者的个人看法，不是事实。
