模型: openai/gpt-5.4
生成日期: 2026-04-01
书名: Claude Code VS OpenCode:架构、设计与未来
章节: 第3章 — 核心循环:ReAct范式
Token用量: 约 18,600 input + 2,500 output
3.3 停止条件与循环控制
一个 agent 是否优秀,往往不体现在它“能循环多久”,而体现在它“知道什么时候该停,什么时候不该停”。ReAct 的最后一层因此不是推理,也不是工具,而是 loop control。正常完成时,模型在没有进一步 tool_use 的情况下结束,系统输出最终文本并收束本轮;遇到上下文溢出时,可能先 compact 再继续;命中 maxTurns、token budget、task budget、用户中断或不可恢复错误时,循环则必须终止。停止条件本质上是对三种资源的治理:上下文窗口、金钱/算力预算、以及用户注意力。
OpenCode 的停止逻辑最简洁,也最能看出 ReAct 的原型形态。session/processor.ts 在 while (true) 中处理一轮流后,最终只返回三类结果:continue、compact、stop。如果 SessionCompaction.isOverflow() 判断超窗,则进入 compact;如果工具被拒绝且配置要求中断,或消息本身记录了 error,则 stop;否则继续下一轮。它还有一个很有代表性的保护:对连续三次完全相同的 tool call 做 doom_loop 检测。这里的 doom loop 也不是经典 CS 术语,而是 agent 工程里的经验概念,指模型反复调用同一工具、带着同样输入、却没有取得新信息的“自陷循环”。
Claude Code 把停止条件扩展成完整的控制面。query.ts 不仅有 maxTurns,还有 blocking limit、reactive compact、MAX_OUTPUT_TOKENS_RECOVERY_LIMIT、stop hooks、token budget continuation 与用户 abort。tokenBudget.ts 甚至明确区分“还可以继续”与“虽然没完成但已经收益递减”,即所谓 diminishing returns。这里很重要的一点是:Claude Code 不是只问“能不能继续”,还问“继续是否值得”。因此它同时处理两类失败:一类是 agent 想停但不该停,例如输出被 max_output_tokens 截断,于是系统自动注入“直接继续,不要道歉”的恢复消息;另一类是 agent 不停但该停,例如 stop hook 明确返回 preventContinuation,或 token budget 已进入收益递减区间。这种双向控制,使 Claude Code 更像一个带刹车与离合的循环控制器,而不是单纯的 while-loop。
OMO 的问题则更有研究价值,因为它展示了“增强控制”如何滑向“过度控制”。Ralph Loop 通过 session.idle 事件检测完成承诺 completion_promise;若未完成,就自动注入 continuation prompt,并在达到 max_iterations 前不断续接。从执行力角度看,这很强,因为它把“别中途停下”编码成了系统机制。但它也带来一个明显反模式:Ralph Loop 可能覆盖正常停止。也就是说,主循环本来已经在一个合理局部点停下,准备把控制权还给用户;而外层 continuation 机制却把“还能继续”误判成“必须继续”。类似地,todo-continuation-enforcer 在 session.idle 后检查未完成 Todo,只要条件满足就启动倒计时并再次注入。这种设计解决了 agent 过早收工的问题,但也可能把“阶段性完成”与“任务未完结”混为一谈。
于是,第一个经典失衡是“智能体想停但不该停”。典型场景包括:输出因 token 截断、中间工具结果尚未回灌、用户原目标尚未完成却只产出阶段性总结。对这种情况,继续机制是必要的。第二个失衡则是“智能体不停但该停”。典型场景包括:重复调用同一工具、为了满足 Todo 形式而机械续写、Ralph Loop 在用户并不需要更多动作时仍持续注入。前者损害完成率,后者损害可控性与成本。很多 2025—2026 年的 agent 产品,实际上都在这两个极端之间反复摆动:太容易停,用户觉得“它半途而废”;太不容易停,用户又会觉得“它开始自我表演”。
这也是为什么“停止”不能只被实现成一个 finish reason。更准确地说,它应该是一张原因表:模型自然结束、工具链未闭环、预算耗尽、窗口溢出待压缩、用户显式中断、hook 禁止继续、外层 orchestration 强制续接、或错误恢复中止。只有当这些原因在系统内部被区分开,产品层才能进一步决定:是提示用户、自动恢复、进入 compact、还是彻底结束。Claude Code 在这方面最成熟,因为它已经把中断消息、max turns attachment、stop hook summary、budget continuation 等都显式化;OpenCode 则提供了简洁但清楚的 continue/compact/stop 三岔口;OMO 的经验价值在于提醒开发者,外层 continuation 必须认识宿主循环的停止语义,否则就会把“协助完成”变成“强制不停”。
从更好的 agent 设计看,停止策略应遵循四条原则。第一,终止必须分层:模型层停止、工具层停止、会话层停止、外层编排停止不能混成一个布尔值。第二,停止要可解释:是 budget、max turns、permission denial 还是 hook 阻止,系统应显式记录。第三,继续要有证据:只有存在未闭环工具、明确未完成任务、或恢复消息时才自动续接,而不是因为“还有 Todo”就一律继续。第四,用户优先于编排器:任何外层 loop 都不能长期压过用户中断与显式完成。
因此,循环控制的最高原则不是“让 agent 永不放弃”,而是“让 agent 在正确的边界上坚持”。OpenCode 提供最朴素的循环出口,Claude Code 展示生产级多重刹车系统,OMO 则提醒我们:自治增强一旦缺乏停止层级,就会从可靠性优化转化为控制权侵蚀。这正是构建下一代编码智能体时最值得吸取的教训之一。