6 智能体循环设计
智能体循环——在 LLM 推理与动作执行之间交替的核心控制流——是每个 SWE 智能体的架构主干。 我们在所研究的系统中识别出三种不同的范式。
6.1 智能体循环范式分类
全部 11 个系统实现的都是 ReAct [4] 模式的变体,但实现之间在复杂度、并发与状态管理上差异巨大。 我们识别出三种范式:迭代式动作-观察(9 个系统)、反思增强(Aider,Hermes 则把同样的功能挪进了循环的停止条件),以及协调者-工作者(迭代循环之上的叠加层,在 Claude Code 与 Codex 中是规定性的,在 Hermes 中由配置门控)。 Rombaut 的同步分类 [34] 给出了更细的有用观点:多数生产级循环组合多种原语(ReAct、生成-测试-修复、计划-执行、重试),而非实现单一一种;四个厂商原生系统现在都随附的 plan 模式(第 10 节),就是嫁接到 ReAct 上的计划-执行。
这个子系统的领域词汇仍在铸造之中。 循环工程(loop engineering)于 2026 年 6 月进入流通,恰在本研究的两次快照之间:Steinberger(OpenClaw 的原作者)把这一转变凝练为一句广为流传的格言——别再给编码智能体写提示词了,要“设计循环来提示你的智能体” [31]——几天后 Osmani 为这一实践命名并搭起结构,给循环一副由触发器、拓扑、验证器与停止规则组成的解剖学 [32]。 这个新词与本节主题互补而非同义:Harness 工程构建的是下文解剖的内层动作-观察循环,而循环工程从外部把这个循环组合成自我维持的外层循环,在没有人类逐轮撰写的情况下提示、验证并重跑智能体。 两个学科的接缝在语料库中已经可见,即外层验证循环模式(表 12):OpenHands 的 /goal 端点在每次运行结束后跑一个 LLM 裁判,要么重新提示要么停止;Hermes 的停止时校验守卫则否决内层循环自己的退出——这些 Harness 特性的唯一目的,就是把一个外层循环套在内层循环外面。
6.2 迭代的动作-观察循环
最常见的模式遵循“提示词构建 → LLM 推理 → 工具执行 → 观察收集”的循环。 9 个系统实现了这一模式,各有特色。
OpenHands:事件溯源的对话引擎。
OpenHands 的循环——2026 年年中重构后位于 software-agent-sdk 仓库而非应用仓库——是一台事件溯源的对话引擎:一个 LocalConversation 驱动 Agent.step(),每个事件都追加到持久化的 EventLog(经 FileStore 一事件一文件,用基于 flock 的锁,并通过 SecretRegistry 做密钥脱敏),LLM 看到的历史是活动分支的缓存投影。 会话状态是一棵树,头部可移动,因此重放、分叉与分支导航都是一等公民。 每个 step() 做一次 LLM 调用,但把响应中的所有工具调用作为一个动作批次执行——可选地经 ParallelToolExecutor 并行执行,由 tool_concurrency_limit 和一个以每个工具声明资源(文件、终端会话、浏览器)为键的资源锁管理器约束,因此只有同资源的调用才串行化(下文 Claude Code 段落和表 18 将此与 Claude Code 的布尔划分相对照)。 StuckDetector 在重构中完好保留:与 V0 代码库相同的五种失败场景(重复的动作-观察对、带错误的重复动作、独白循环、交替模式、上下文窗口错误循环),现在阈值可配置且默认启用。
Claude Code:流式循环与并发工具批量执行。
Claude Code 的循环响应经 Server-Sent Events(SSE)流式传输,带细粒度事件处理(content_block_start、content_block_delta、content_block_stop)。 最有特色的功能是智能工具分批:通过单趟归约算法,按并发安全性把工具调用划分成批次。 工具默认 isConcurrencySafe = false(安全默认值),必须显式选择加入并行执行。 只读工具(grep、glob、文件读取)是安全的;写工具(edit、bash)则不是。 当 LLM 同时请求多个独立读取时,这一设计能显著降低延迟。
Codex:Tokio 异步状态机。
Codex 的循环用 Rust 实现,是一台基于 Tokio 的异步状态机。 一个 Session 结构体经由来自 OpenAI Responses API 的流式 ResponseItem 事件(从 SSE 或 WebSocket 流帧反序列化)编排各轮次,工具调用经 FuturesOrdered 处理以有序并行执行,如今被拆分进一个专门的 ToolCallRuntime。 关键入口:Codex::spawn() 创建新会话,submit_with_id() 提交操作,next_event() 为流式响应提供非阻塞事件读取(曾经单体巨石的 codex.rs 已被拆为 session/ 模块和一个 CodexThread 抽象,但入口保留了下来)。
Mini-SWE-Agent:极简线性循环。
Mini-SWE-Agent 实现了最简单的变体(清单 1):
没有状态机,没有事件溯源,也没有并发机制。 消息列表线性且无界地增长,完全依赖 LLM 原生的上下文窗口。 极简是刻意的:这一设计有意把 LLM 能力与智能体脚手架复杂度隔离,看两者之间有多少可以互相替代。 不过连地板也在上浮:v2.4 线增加了对运行的真实时间限制和对连续格式错误响应的上限——循环现在在 次连续格式错误后中止,而不是无限打转——于是这个语料库里的最小系统也装上了一个最小的卡死检测器。
Mistral Vibe:中间件管线循环。
Mistral Vibe 的循环是 Python async/await 迭代循环,把轮次级策略抽离成可组合的中间件管线。 核心会话循环驱动标准的提示词 LLM 工具循环,但每次迭代先走一遍中间件栈的轮前检查,然后随完成把事件产给调用方。 开箱即用的中间件有六个:TurnLimitMiddleware、PriceLimitMiddleware(每会话成本上限)、TokenLimitMiddleware(会话总 token 上限,2.9 线新增)、AutoCompactMiddleware(在 token 阈值处触发摘要)、ContextWarningMiddleware(在对话逼近上下文窗口上限时警告)与 ReadOnlyAgentMiddleware(为只读智能体档案门控写工具);用户取消不作为中间件,而是通过 is_user_cancellation_event() 检查内联处理。 单个 LLM 响应内的工具调用并发执行:每个工具派生为一个 asyncio.create_task(),完成即产出事件——这是 Codex 的 FuturesOrdered 的更细粒度变体。 中间件设计在语料库中独一无二。 新增轮次级策略无需修改循环体;同一循环驱动六个注册的智能体档案(default、plan、accept-edits、auto-approve、explore、lean),只需换掉中间件组合;第七个 chat 档案不注册给 CLI 会话,而是由 ACP 层为 IDE 集成动态注册。 与此同时,plan 模式已从仅靠提示词变为结构化:专用 plans 目录下的计划文件是该档案唯一的可写目标,而 exit_plan_mode 工具发出一个 PlanReviewRequestedEvent,门控回可写档案的转换。
Gemini CLI:异步生成器循环与混合循环检测。
Gemini CLI 的循环用 TypeScript 实现为异步生成器。 每次迭代向 UI 产出类型化的 ServerGeminiStreamEvent 元组,经 ModelRouterService 运行活动模型,通过 @google/genai SDK 流式读取响应,并经一个状态机 Scheduler 派发函数调用——每个工具走过校验 执行 完成/出错。 一个有特色的设计是 LoopDetectionService:它是混合式而非纯确定性的——SHA-256 哈希低成本捕获常见失败模式(五次相同工具调用或十段相同内容块即中止循环),而在单条提示词进行三十轮之后,一个基于 LLM 的自检以自适应间隔运行——与 OpenHands 的五个手工枚举场景形成对照。 会话轮次限制(默认 100)提供硬上限;11 个生命周期 Hook 事件(BeforeAgent/AfterAgent、BeforeModel/AfterModel、BeforeToolSelection、PreCompress 等)让扩展可以检查或否决循环。 调度器并行执行工具调用,且自 v0.45 起按并发安全性划分:改文件的工具(edit、write_file)与 update_topic 被硬性强制串行;而且——语料库中独一份——模型本身拿到一个并发旋钮:每个工具模式上自动注入的 wait_for_previous 布尔量,模型可以借它把任何调用串行化。 调度器经 outputUpdateHandler 回调把工具的实时输出流给 UI,用户能看到长命令的进度。
Hermes:带停止守卫的预算循环。
Hermes 每个用户轮次跑一个迭代式工具调用循环,由线程安全的 IterationBudget 约束(默认 90,execute_code 的迭代会退还,预算耗尽时还有最后一次宽限调用让模型做总结)。 它的独特之处不在循环体,而在出口:11 个枚举的轮次退出原因,以及两个可以否决过早最终答案的停止守卫。 停止时校验守卫在本轮改动了代码文件却未产出新鲜验证证据时,把纯文本响应改写为继续执行——这是反思循环的功能,从循环内部挪到停止条件上,成本只是零头。 卡死检测对“工具名 + 排序 JSON 参数”做哈希(SHA-256),两次相同失败后警告、八次后停止——但硬停止默认关闭:默认姿态是把警告附加到工具结果上,信任模型自我纠正。 工具批次只在每个调用都只读安全、或路径作用域前缀互不重叠时,才在八个工人组成的池上并行;轮次中途的用户引导被拼进最后一个工具结果,置于防注入标记之后。
Pi:带引导队列的函数式内核。
继 Mini-SWE-Agent 之后,Pi 的循环是语料库中最纯粹的迭代实现:约 790 行的函数式内核,没有规划器、没有反思步骤、没有轮次上限、没有卡死检测、没有成本急停——循环一直跑到模型不再调用工具,而所有这些缺席都是成文的设计拒绝。 两个原语尤为突出。 运行中途的用户输入是一等公民:steer() 队列在当前轮次的工具调用之后注入消息,而 followUp() 只在智能体本要停下时才排空。 工具调用默认经 Promise.all 并行执行,配一个以 realpath 为键的逐文件修改队列来串行化编辑,还有一个独特的截断投毒守卫:当助手消息在长度上限处被截断时,它的所有工具调用都被标记为失败、不予执行——因为抢救解析出的流式参数可能“校验通过”却在静默地不完整。 恢复策略是分层而非枚举的:一个约 40 种模式的瞬时错误重试白名单,加上上下文溢出时的一次性压缩重试。
OpenCode:以日志为队列的循环。
OpenCode 的循环是一个字面意义的 while(true),但有一个不寻常的转折:消息日志兼作工作队列。 待处理的子智能体派生和压缩不是控制流分支,而是持久化的消息部件,循环每次迭代出队一个——这让循环在进程重启后极易恢复,因为队列就是转录本身。 没有默认步数上限(maxSteps 默认无穷);达到配置的上限时,Harness 追加一条宣布工具已禁用的助手消息,而不是抛错。 卡死检测是单条启发式,且照例经由权限系统路由:连续三次字节级相同的工具调用会引发一个 doom_loop 权限询问而非自动中止——重复是不是 bug,由用户而非 Harness 决定。 并行工具调用并发执行,没有 Harness 层上限;重试在 Harness 层,带感知 retry-after 的指数退避;上下文溢出从不重试——直接切换到压缩。
6.3 反思增强循环
Aider 用反思机制扩展了基础循环。 run_one() 方法实现一个嵌套循环:每次 LLM 响应后,系统应用编辑,然后检查 lint 错误(经三阶段 Python 管线:语法检查 编译检查 flake8)、测试失败与未解析的文件提及。 若发现问题,一条 reflected_message 会带着纠正性上下文重新调用 LLM,上限可配置(默认 3 次反思)。
lint 集成尤为出彩:它用 grep_ast 的 TreeContext 展示错误行周围的代码上下文,为 LLM 的修正提供精确的局部性信息。 Aider 仍是唯一主循环呈反思形态的系统,但语料库长出了三个结构上的表亲:Gemini CLI 的编辑工具以 LLM“编辑修复器”子调用收尾,修复失败的匹配(第 8.4 节);OpenCode 把 LSP 诊断回灌进每次编辑结果;Hermes 的停止时校验守卫在轮次出口做一次反思检查,而非每轮迭代都做。 这一模式是语言模型自我修正研究脉络(Reflexion [54]、Self-Refine [55]、CRITIC [56]、Self-Debug [57])在野外的实例化——那条脉络大体还留在学术文献里。 Aider 的贡献在集成:lint 与测试信号作为那些论文理论化的纠正性反馈,被路由回循环。 关于它的停止条件有个注脚:Aider 根本没有工具调用循环——补全流结束即一轮结束,run_one 在没有待处理反思或达到上限时退出;它从不检查 end_turn 结束原因。
6.4 协调者-工作者模式
Claude Code 与 Codex 支持协调者模式:父智能体编排多个工作者智能体;Hermes 把同样的形态放在配置之后(一个 orchestrator 角色加上派生深度设置即可解锁嵌套委派树,另一个独立的看板集群模式则以子进程方式在 SQLite 黑板上跑 计划根 工作者 验证器)。 这是迭代循环之上的叠加层,增加了一层分级派发。
在 Claude Code 中,协调者经 AgentTool 派生子智能体,每个子智能体拿到一个分叉的上下文,带隔离的 AbortController、克隆的文件状态缓存和被抑制的权限对话框。 工作者经 <task-notification> XML 块把结果传回,协调者在委派下一阶段之前综合各处发现。 工作流是结构化的:研究 综合 实现 验证。
在 Codex 中,子智能体经 AgentControl::spawn_agent() 派生,拿到专门的 ThreadId,并通过类型化的 Mailbox 通道通信。 每个线程维护自己的历史,SpawnAgentForkMode 控制上下文继承(FullHistory 或 LastNTurns(N))。
多智能体编排的详细分析推迟到第 11 节。
6.5 对比分析
图 2 对比三种范式。
表 5 给出循环特性的详细对比。
| 系统 | 循环类型 | 并发 | 卡死检测 | 停止条件 |
| OpenHands | 事件溯源对话 | 并行批次(资源锁;上限默认 1) | 5 种场景 | State=FINISHED |
| Aider | 生成器 + 反思 | 顺序 | 反思上限 | 流结束且无待处理反思 |
| Claude Code | 流式 + 分批 | 按安全性划分 | 无(手动) | end_turn |
| Codex | Tokio 异步状态机 | FuturesOrdered | 无 | end_turn |
| Gemini CLI | 异步生成器 + 调度器 | 并行;编辑强制串行;模型可见的 wait_for_previous | 混合:SHA-256 + 30 轮后 LLM 自检 | end_turn / 100 |
| Mistral Vibe | 异步 + 中间件 | asyncio.create_task | 中间件(轮次/价格/token/压缩) | end_turn |
| Mini-SWE-Agent | 线性 while | 顺序 | 步数上限;格式错误上限;真实时钟 | exit 消息 |
| Hermes | 预算 while + 停止守卫 | 并行批次(8 工人,按安全性门控) | SHA-256 调用签名(先警告) | 文本响应,除非停止时校验否决;90 迭代预算 |
| Pi | 函数式 while + steer/followUp 队列 | Promise.all;逐文件修改队列 | 无(刻意设计) | end_turn(无上限) |
| OpenCode | 以日志为队列的 while(true) | 并行,无上限 | 死循环 权限询问 | 无工具调用;steps ?? Infinity |
| OpenClaw | 事件驱动 ACP | RPC 隔离 | 速率限制 | 会话结束 |