开篇

之前的文章拆解了 Claude Code 收到一个任务后,内部到底发生了什么?

当时是把它的核心 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 ,沿着一个任务从产生、执行、提交 PR,到 CI 失败后再次唤醒 Agent 的完整生命周期走了一遍,终于看清楚:

  • Claude Code 里的两层循环自然是真正的 Loop,但它们解决的是:一次 Agent 运行内部如何推进;

  • Loop Engineering 还要再向外包一层,解决:多次 Agent 运行之间,工作如何持续推进。

这不是否定 while True,而是在追问一个更外层的问题:当这次循环退出以后,下一次由谁启动?

这篇文章从 Optio 的源码出发,拆开以下四个问题,希望可以帮助你更好的理解 loop ~😊

  • Claude Code 明明已经有两层循环,为什么还不等于完整的 Loop Engineering?

  • Automation、Worktree、Skill、Connector、Sub-agent、State 分别解决什么问题?

  • 为什么有了这六个组件,仍然不一定形成真正的 Loop?

  • 一个可以长期运行的 Loop,还需要哪些工程化护栏?

项目地址:https://github.com/jonwiggins/optio

ps. Optio,是古罗马军队中协助百夫长维持队伍运转的副手。而这个项目做的也是类似的事:它不亲自写代码,而是负责叫醒 Agent、分配任务、检查结果,再决定下一轮行动,文章后面会提到。

明明已经有两层循环,为什么还不够?

先回到之前文章中的 query_loop

把那些流式事件、消息类型和容错逻辑暂时去掉,它大致可以压缩成下面这样:

def query_loop(user_message):
    messages = [user_message]

    while True:
        # 外层回合循环:把当前完整上下文交给模型
        response = call_model(messages)

        # 内层工具循环:模型只负责开“工具调用单”
        for tool_call in response.tool_calls:
            # Python 程序才是真正干活的跑腿小弟
            result = execute_tool(tool_call)
            messages.append(result)

        # 模型认为本回合结束,退出整次 Agent 运行
        if response.stop_reason == "end_turn":
            break

    return response

这个 Loop 发生在一次 Agent 运行内部:模型规划,程序执行,结果重新放回上下文,模型再判断要不要继续。

这当然是循环,而且还是两层嵌套循环。

内层让模型和工具来回协作,外层让 Agent 可以连续推进多个回合。它们共同回答的是:

人给 Claude Code 一个任务后,它内部如何把这个任务做完?

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

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

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

任务生命周期循环:发现任务 → 启动 Agent → 验证 → 记录 → 再次启动或停止
    └── Agent 回合循环:调用模型 → 回填结果 → 下一回合 → end_turn
            └── 工具执行循环:tool_use → Python 执行 → tool_result

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

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

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

把传统的使用方式画出来后,这个区别就更明显了:

我发现 CI 失败
→ 我复制错误日志给 Agent
→ Agent 修改代码
→ 我重新运行测试
→ 我查看 PR 和 Review
→ 我决定继续、重试或者停止

在这里,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 定时查看有没有新工作 闹钟
Worktree 给不同任务准备独立代码目录 独立工位
Skill 反复解释项目规范和操作方法 作业指导书
Connector 登录 GitHub、Slack、数据库等系统 门禁卡和通讯录
Sub-agent 找另一个人独立检查结果 质检员
State 记住做到哪里、发生过什么 值班记录本

它们把原来散落在人脑和手工操作里的东西,逐一搬到系统外部。👇

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

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

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

可以把这个核心过程压缩成一段伪代码:

async function fireSchedule(trigger) {
  // 1. 找到可重复使用的任务蓝图
  const config = await getTaskConfig(trigger.targetId);

  // 2. 把本次触发参数填入 Prompt
  const prompt = renderTemplate(config.prompt, trigger.params);

  // 3. 生成一张真实、可追踪的任务单
  const task = await createTask({ ...config, prompt });

  // 4. 持久化状态,并交给执行队列
  await transitionTask(task.id, "queued");
  await taskQueue.add("process-task", { taskId: task.id });
}

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

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

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

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

# 同一份仓库历史,为当前任务创建独立目录和分支
git worktree add \
  /workspace/tasks/<task-id> \
  -b optio/task-<task-id> \
  origin/main

最终的目录更像这样:

/workspace/repo              # 共享的主仓库
/workspace/tasks/task-A      # Agent A 的独立工位
/workspace/tasks/task-B      # Agent B 的独立工位
/workspace/tasks/task-C      # Agent C 的独立工位

👉 这一步决定了多个 Agent 能不能真正并行。

如果没有 Worktree,就像让三位程序员共用一台电脑、同时编辑同一份文件。Agent 数量越多,冲突反而越快。并行不是多开几个对话框,而是先隔离它们会修改的环境。

Skill 与 Connector:一个管「会不会」,一个管「能不能」

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

第一类是 Skill:

源码中的 buildSkillSetupFiles() 会把技能写成类似下面的文件:

.claude/skills/code-review/SKILL.md
.claude/skills/code-review/checklist.md

它告诉 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 不是一次临时调用,而是一条真正写入数据库、可以排队、失败和重试的子任务:

const reviewTask = await createSubtask({
  parentTaskId: codingTask.id, // 指向原始开发任务
  taskType: "review", // 这是一张审查工单
  blocksParent: true, // 审查未完成,父任务不能自动合并
  prompt: buildReviewPrompt(),
});

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

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

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

定时器创建任务
→ Coding Agent 修改代码
→ Review Agent 检查
→ 结束
  • 如果 CI 失败,谁来决定「要不要继续」?

  • 如果 Review 要求修改,谁把反馈交回 Coding Agent?

  • 如果进程中途崩溃,谁知道应该从哪里恢复?

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

1、Reconciler:谁来下达下一道命令

在 Optio 中,负责把这些能力连接起来的角色叫 Reconciler。

也是到这里,我才突然意识到这个项目的名字很有意思:在古罗马军队中,Optio 是协助百夫长维持队伍运转的副手。它会观察队伍、传递命令,并在情况变化时及时补位。

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

它反复做三件事:

async function reconcile(taskId) {
  // Observe:重新读取任务、PR、CI、Review 等真实状态
  const snapshot = await buildWorldSnapshot(taskId);

  // Decide:根据确定性规则,决定下一步动作
  const action = reconcileRepo(snapshot);

  // Act:执行唤醒、重试、审查、合并或结束
  await executeAction(action, snapshot);
}

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,并通过状态机限制它如何变化:

pending → queued → provisioning → running → pr_opened → completed
                                    │           │
                                    └─────┬─────┘
                                          ↓
                                  needs_attention
                                          ↓
                                       queued

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

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

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

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

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

顺着这个问题继续读源码,这才明白那些一开始让我觉得很繁琐的代码,几乎都在处理这个问题:如果 Loop 在无人盯着时出错怎么办?

Optio 加了至少三道护栏。

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

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

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

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

最后

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

现在可以这样回答:

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

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

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

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐