最简单的 Agent 是一个不断执行模型和工具的 while 循环。它适合演示,不适合长期任务。真实任务会遇到上下文耗尽、测试失败、用户打断、预算耗尽、并发 worker、进程崩溃和“模型说完成但其实没完成”。因此 Agent Loop 的核心不是 while,而是围绕 while 建立一套状态机。
本文以 Codex 当前 goal/turn 机制、Claude Code 的 session/stop 机制、Ralph、OMC、SWE-agent、OpenHands、LangGraph、Temporal、CrewAI 和 AutoGen 的公开实现为参照。
01 Loop 的完整模型
一个生产级 loop 至少有七个状态:
Goal -> Plan -> Attempt -> Observation -> Verification -> Transition -> Recovery
其中 Attempt 可以重复,Recovery 可以回到 Plan,也可以进入 blocked、paused、failed 或 complete。完成不是模型返回一句“好了”,而是目标状态满足外部验收条件。
02 Codex Goal 与 Claude Goal:目标放在哪里
Codex 的长期目标更接近线程级账本:目标、进度、阻塞、预算和继续条件由宿主状态管理,Agent 可以跨多个 turn 继续推进。Goal 是控制面状态,不是 prompt 里的一句话。
Claude Code 的 goal 类机制更接近 session/stop 时刻的完成审计:当 Agent 想停止时,系统检查目标是否满足,必要时再让模型继续。它的优势是侵入小,缺点是外层长期调度和多日恢复需要更多宿主设施。
两者的共同点是把完成条件外部化;差异是 Codex 更偏“目标运行时”,Claude 更偏“停止时的质量门”。
03 Ralph:最小外层循环为何有效
Ralph 的基本思想很朴素:
读取 PRD / tasks
-> 调用 coding agent
-> 运行测试与检查
-> 记录进度
-> 下一轮继续
它把任务状态放在文件或 git 工作树,外层脚本拥有循环,内层 Agent 只负责一次尝试。优点是透明、可替换、容易部署;缺点是状态一致性、重复工作、预算、并行和恢复语义都由脚本自行承担。
Ralph 适合把一个可拆分的 PRD 转成多个小任务,不适合没有明确验收条件的开放式探索。
04 OMC 与 OpenHands:从脚本变成运行时
OMC 将 worker、allocation policy、事件和团队状态引入 loop;OpenHands 则把对话、动作、观察、sandbox 和应用状态组织成 runtime。两者都说明一个事实:一旦存在多 worker 或真实环境,loop 就必须拥有 action/observation 事件、worker 生命周期、并发与取消、任务依赖与结果合并、独立验证者和可恢复持久化。
从 done 文件升级到事件流,不是为了“架构更高级”,而是为了避免轮询看不到中间状态,也无法区分“没完成”“执行中”“已失败”和“等待用户”。
05 SWE-agent:环境是 loop 的一等对象
SWE-agent 的 action/observation loop 很适合说明 coding agent 的本质:
issue + repo state
-> agent action
-> SWEEnv observation
-> trajectory
-> patch / test / retry
环境不是执行器的附属物。它决定 shell、文件、测试、补丁和反馈是否一致。RetryAgent 一类机制则说明失败不是异常终点,而是一次新的 attempt,应该记录失败原因并改变下一次上下文。
06 通用编排框架的不同取舍
| 框架 | 核心抽象 | 强项 | 代价 |
|---|---|---|---|
| LangGraph | 状态图、checkpoint、持久化 | 有状态 loop 和人工介入 | 需要显式建图 |
| Temporal | durable workflow | 崩溃恢复、长时间运行 | 工作流边界更严格 |
| CrewAI | 角色、任务、crew | 快速搭建角色协作 | 精细状态治理较弱 |
| AutoGen | Agent 对话与终止条件 | 多 Agent 交互灵活 | 终止和共享状态需自行设计 |
它们不是互斥方案。Temporal 可以作为 durable substrate,LangGraph 可以表达任务图,CrewAI/AutoGen 可以提供角色层,具体 Agent 仍要自己设计工具策略和验收。
07 关键工程问题
目标状态
目标应有 id、描述、验收条件、当前阶段、预算、负责人、阻塞原因、更新时间和证据链接。自然语言目标必须转成可观测状态。
停止判定
停止条件至少包括 success、blocked、paused、budget_exhausted、user_cancelled、failed 和 max_iterations。只靠模型自报完成会产生大量假阳性。
验证闭环
验证可以是测试、lint、类型检查、diff review、浏览器截图、人工批准或领域评测。验证结果必须写回状态,不能只在终端打印。
恢复语义
崩溃恢复需要知道最后一个 durable event、正在执行的副作用、是否可以重试、是否需要补偿。工具调用最好有 idempotency key,避免恢复时重复发送消息或重复迁移数据库。
预算与无进展
token、时间、工具调用次数和并发 worker 都应有预算;连续多轮没有文件、状态或验证变化时,应触发无进展保护,而不是继续烧钱。
08 内外两层 Loop
一个 coding agent 往往有两层循环:
外层 Goal Loop:拆任务、重试、验证、恢复、汇总
内层 Agent Loop:模型 -> 工具 -> 结果 -> 模型
把两层混在一起,会导致“模型自己决定是否完成全部任务”,也会让外层无法插入 review、预算和人工审批。合理做法是让内层专注单个 attempt,外层拥有目标和生命周期。
09 选型与实现顺序
个人脚本可以先用 Ralph 式文件状态;需要 session 恢复时引入 append-only event;需要复杂依赖时使用 LangGraph;需要跨天和崩溃恢复时考虑 Temporal;需要多 Agent 时再加入 worker runtime。
不要一开始就做“无限自主”。最小可用版本应该先具备:目标对象、一次 attempt、外部验证、失败状态、可读日志和人工停止。之后再加并行、记忆、计划自动修正和自我改进。
10 最后判断
Agent Loop 的演化方向很明确:从“重复调用模型”变成“管理一项可验证、可恢复、可审计的工作”。模型能力决定单次 attempt 的上限,loop 工程决定系统能否把多个 attempt 组织成可靠结果。