logo

Agent 循环何时停止:从运行状态到长期目标的边界设计

作者:carzy2026.11.06 22:32浏览量:0

简介:智能体循环的“停止”并非一个布尔值,而是一组分布在模型流、工具调用、回合、驱动器活动与长期目标五个层级的边界条件。本文从“创建 PR 遇到 504”这类真实生产场景出发,梳理各层停止语义的差异、常见的误判风险,以及如何通过显式状态设计避免重复执行、漏报失败或过早退出。读者可获得一套可落地的状态建模与边界确认思路,用于工单、CI、审批流等需要副作用审计的智能体系统。

agent-">Agent 循环何时停止:从运行状态到长期目标的边界设计

一个智能体在工单系统中承接“升级 SDK”任务时,经历了代码修改、测试通过、调用接口创建 PR,然后接口返回 504。此时页面没有 PR,但服务端可能已经创建成功,只是响应没有回来。如果让智能体再试一次,可能多出重复 PR;如果就此收工,又可能漏掉真正的失败。这个看似简单的现场,暴露出一个被低估的问题:智能体循环的“结束”从来不是单一判断,而是分布式地发生在多个层级上。

要理解这一点,不能只看循环代码有多少行,而要看谁有权在什么时候按下停止键。把一次完整运行拆开,至少可以识别出五个不同层面:模型流、工具调用、回合、驱动器活动、长期目标。每一层都有独立的结束条件,混淆任何一层都会导致严重的副作用问题。

一、模型流结束:只是“这一次请求”停止了

模型流结束是最容易被误认为“任务结束”的信号。它只说明当前模型请求收到了结束标记,不再输出新的 token。但这个结束信号可能来自几种完全不同的原因:

  • 模型生成了最终的文本回复,正常结束;
  • 输出触达 token 上限,被运行时截断;
  • 请求报错,模型没有完成生成;
  • 上游取消了请求。

这些原因对应着不同的后续策略。正常结束可能意味着进入状态总结;token 截断则可能需要继续生成或压缩上下文;报错要区分可重试错误与确定性错误;取消则可能带有主动退出意图。把“模型停止了”等同于“任务完成了”,是很多智能体系统第一个错误的来源。

二、工具调用结束:工具停了,不等于回合停了

当模型发起的一批工具调用全部返回结果后,很多实现会认为“这一轮跑完了”。但工具返回只是让执行环境暂时无事可做,模型还需要审阅工具结果并做出下一步决策。这个审阅过程本身就是一次新的模型请求,可能产生新的工具调用,也可能产生最终回答。

一个更隐蔽的情况是并行工具调用。假设两个工具同时执行:一个返回了可以收口的明确信号,另一个却留下了需要模型进一步解释的半结构化结果。如果只看“所有工具都已返回”就决定停止,很容易忽略后者的隐含需求。工具调用结束只是把控制权交回给模型,真正的决策才刚刚开始。

三、回合结束:需要显式确认“没有待处理事项”

一个回合(turn)真正结束,需要同时满足以下条件:工具结果已交回模型、模型完成了后续推理、当前没有未回复的消息、下一步调度中没有新消息。这个定义比直觉上复杂得多。

有两个容易被忽视的“重新拉起”因素。一是用户在命令执行中途插入的新指令或 steering 消息:它可能会改变当前回合的意图,甚至推翻已生成的结论。二是工具或系统补入的上下文:一旦有新的结构化信息进入,就需要让模型再消化一次,否则就会出现“信息已经变了,但智能体还拿着旧结论往前走”的风险。

可以引入一个显式的 TurnEndReason 状态集合来记录回合结束原因,至少区分以下几类:

  • completed:正常完成,无待处理事项;
  • blocked:被某个条件阻塞,需要外部介入;
  • max-tokens:输出超长,被截断;
  • aborted:运行时收到取消请求,有机会执行清理逻辑;
  • error:发生了确定性错误;
  • interrupted:进程异常消失后,恢复层为尚未闭合的回合补上的状态。

其中 aborted 和 interrupted 的差异尤其值得审计。aborted 表示取消是“有感知的”——运行时知道要取消,可以执行清理,比如回滚临时文件、释放锁、记录日志。interrupted 则表示进程可能突然崩溃或被杀掉,恢复层在后续启动时发现这个回合没有正常收尾,于是补记状态。在做副作用审计、决定是否重试时,这两者不能混为一谈:一个有清理机会,一个可能残留到一半的写操作。

四、驱动器活动结束:回到 idle 只是暂停,不是终点

当一个回合结束后,驱动器(driver)需要决定是否开启下一个回合。如果没有新的消息需要处理,驱动器进入 idle 状态。这是很多系统里“停止”的默认实现:只要没有待处理消息,循环就退出。

但驱动器 idle 只说明当前没有下一回合,不代表目标已经完成。它在语义上更接近“暂时没有要立即处理的事情”,而不是“这件事结束了”。如果系统存在长期目标或持久化状态,idle 之后仍可能被后续事件唤醒。典型情况是:智能体提交了一个外部流程,比如 CI 任务,然后进入 idle;等 CI 完成事件写入消息队列后,它需要被重新拉起来处理结果。这个“过一会儿再回来”的能力,必须与“这一轮到此为止”分开设计。

五、长期目标结束:只有持久化状态能说了算

最高的停止层级是长期目标。假设目标状态持久化在存储中,其结束条件应明确为:状态变为 complete、blocked 或 paused。智能体当前处于 idle 并不代表目标完成;反过来,目标已标记完成时,即使某些回合被意外中断,也不应当随意重放。

长期目标的驱动器通常由一个独立的调度循环维护。它定期检查持久化状态和目标条件,如果发现目标尚未完成且存在可执行动作,就会调用类似 followup() 的入口把智能体重新唤醒。这种设计可以把“单次运行结束”和“目标是否结束”解耦,避免在长周期任务中过早宣告胜利。

六、把状态建模落实到代码结构

上述分析可以映射到一套相对清晰的代码结构:分别维护模型流状态、工具调用状态、回合状态、驱动器状态和目标状态。每一层都有自己的结束条件与转换规则,层与层之间通过事件或状态变更通信。

一个可参考的状态流转是:模型流返回结束标记后,先判断结束原因,再决定是否进入工具执行;工具全部返回后,如果没有待处理消息且模型已经完成推理,回合标记为 completed;驱动器看到回合完成且没有后续消息,进入 idle;目标驱动器检查持久化状态,发现目标未完成且存在可唤醒条件时,重新拉起智能体。

关键不是把每个字段都命名为某种标准,而是让每一层的停止原因可记录、可审计、可后续查询。比如在 PR 创建遇到 504 的场景中,如果接口返回超时,系统应该把这次工具调用的结果标记为“不确定”,而不是简单地归为失败或成功。后续可以由审阅逻辑或人工确认来决定是否需要重试,以及重试时是否能做到幂等。

七、实践的三个建议

第一,把取消与中断分开。取消是有意识的退出,中断是被动的消失。审计和重试策略必须依赖这个区分。

第二,把“这一轮继续跑”和“过一会儿再回来”分开。前者是回合内部的决策循环,后者是驱动器或调度器的职责,二者不要用同一个循环变量控制。

第三,永远用持久化状态来定义目标的最终结束。idle 只是运行态,不是终态。对于工单、CI、审批流等有持久副作用的场景,只有明确的状态跃迁才能避免“多做了一次”或“少做了一次”。

智能体系统的复杂度不在于循环有多长,而在于停止的条件到底有多少层、是否每一层都被显式建模。把停止边界设计清楚,远比增加循环次数更能提升生产环境中的可靠性。

发表评论

活动