Agent 工程对照
← 返回框架对照

循环 · 第 2 章

停止条件

凭什么判断任务已经做完了?

四家都不让模型一个人说「做完了」,区别在否决权交给评估器、钩子与插件,还是宿主。

场景建议

编辑判断

按你的需求,倾向哪种做法

借鉴的是那一家的设计,不等于选用那个框架。

场景

任务必须在预算内收手

它内置轮数、token 预算与墙钟超时三种评估器,任一命中即停。

场景

模型说做完了,但要先过一道外部检查

Codex 在停止前跑 Stop 钩子,钩子可以否决并注入续写提示;DeepSeek 停前派发 turn-stopping,插件可以往收件箱塞消息把回合续上。

场景

输出可能被截断

stopReason 为 length 时,Pi 把这条消息里的工具调用全部判为失败,不执行可能被截断的参数。

容易踩的坑

  • 允许钩子否决停止时,一定要给它一个「已经续过」的信号:DeepSeek 的桥接代码自己承认 stop_hook_active 恒为 false,无条件阻止的钩子会让每一步都被迫继续。

四家怎么做

差异与源码

openJiuwen

外层任务循环挂一串停止评估器,任一命中即停(OR 语义)

内层 ReAct 用迭代上限(见「主循环形态」),外层 Task Loop 由 LoopCoordinator 决定还要不要下一轮:先看是否被请求中止,再依次询问评估器链,第一个返回 should_stop 的评估器名字记为 stop_reason。同目录 schema/stop_condition.py 里内置了轮数上限、token 预算、墙钟超时、完成承诺(CompletionPromiseEvaluator,由 TaskCompletionRail 检测到约定标记后通知,可要求连续确认 N 次、中途缺席即清零)与自定义谓词五种。值得注意的是:评估器自己抛异常时只记一条 warning 并跳过——坏掉的停止条件不会让循环停下来。

适合
长任务要同时受轮数、token、时间约束,并且想把「完成」定义成可检测的约定。
代价
评估器抛异常时会被跳过,停止条件写错了不会让循环停下,要靠日志发现。
Codex

模型不再需要跟进且无排队输入时才考虑停,Stop 钩子还能否决

是否继续由两件事共同决定:模型这一步是否需要跟进(比如还有工具调用),以及用户有没有排队的新输入。两者都没有,才进入停止流程——先跑 Stop 钩子:钩子可以阻止停止并给出一段续写提示,这段提示会作为新输入写回会话、回合继续;钩子要求继续却不给提示时,Codex 发一条警告并忽略这次阻止。循环本身没有轮数上限,另外两条出口是回合被中止(取消令牌)和错误事件——出错时 break 而不是抛出,注释写明是为了让用户能继续对话。

适合
需要让外部规则在模型认为做完之后再把一道关(例如测试没过就继续)。
代价
循环没有轮数上限,一个总是阻止的 Stop 钩子可能让回合一直续下去。
DeepSeek Harness

每个回合都以带类型的结束原因收尾,停前先广播 turn-stopping

回合内逐步推进,某一步返回结束原因、且下一步的收件箱为空时才准备收尾;收尾前先串行派发 agent/turn-stopping,插件可以在这里往收件箱塞新消息把回合续上(Codex 钩子桥接的 Stop 钩子就挂在这个点上)。所有出口都会写入一条 turn/end 事件并带上原因:completed、blocked(pre-step 被拒)、max-tokens、aborted、error;结束原因的类型定义里还有第六种 interrupted,循环自己从不写,只由持久化层在重新加载时关闭崩溃遗留的回合(见「中断与恢复」一行)。max-tokens 是黏性的:同一回合里只要有一步撞到输出上限,后面正常完成的步也不会把结果改回 completed。外层的 while 只在收件箱还有待处理消息时才开新回合。

适合
要对每个回合为什么结束做统计、告警或审计的系统。
代价
结束原因是可扩展的类型,下游消费方要处理未知变体。
Pi

没有工具调用、没有插话、没有后续消息就停;截断的输出不执行工具

自然停止条件是三个「没有」:助手消息里没有工具调用、没有用户插话、也没有排队的后续消息。错误或中止立即结束;一个批次里的工具可以声明 terminate 让循环在本轮后停下;宿主还能通过 shouldStopAfterTurn 在任意一轮之后叫停。最细致的是对 length 停止原因的处理:输出被 token 上限截断时,消息里的工具调用参数可能不完整,于是全部判为失败而不是执行,避免跑一条被截断的命令。

适合
希望停止规则简单可读,并且由宿主应用决定何时叫停。
代价
没有内置预算型停止条件,轮数、token 上限要宿主自己实现。
横向读这一行

四家都不信模型一个人说「做完了」,但把否决权交给了不同的人:openJiuwen 交给一串可组合的评估器(轮数、token、时间、完成承诺),Codex 与 DeepSeek 交给钩子与插件——停之前先问一圈,谁都可以把回合续上,Pi 则交给宿主的 shouldStopAfterTurn。细节里藏着两处容易被忽略的工程判断:openJiuwen 的评估器抛异常时跳过而不是停下,Pi 遇到被截断的输出时拒绝执行其中的工具调用。

这个问题为什么存在

第 2 章

Agent 循环

收集上下文 → 行动 → 检查 → 重复,直到满足停止条件