Agent 计划、进度与终止条件

本节目标

学完后,你应能:

  • 把计划设计为可修改的工作假设,而不是不可审计的长篇推理。
  • 为每个子目标定义状态变化、成功证据和失败分支。
  • 识别无进展、重复动作、振荡和“假完成”。
  • 为成功、失败、等待、取消、预算耗尽等所有退出路径建模。

计划解决什么

计划的价值不是预测所有未来步骤,而是:

  • 把目标拆成可验证子目标;
  • 显式记录依赖、约束和风险;
  • 让新 observation 能更新下一步;
  • 让用户和 runtime 知道进度与未决项;
  • 为停止、恢复和接管提供结构。

一个可用计划项:

id: verify-current-state # 子目标的稳定 ID,便于事件、测试和恢复引用
status: in_progress # 当前尚未完成,runtime 可据此继续安排下一步
goal: 确认 ticket-7 是否仍为 open # 将自然语言目标收窄为可验证的问题
allowed_actions: # 此子目标允许调用的最小工具集合
  - lookup_ticket # 只读查询,不允许借此阶段写入 ticket
success_evidence: # 达成子目标时必须出现的外部证据类型
  - 带版本/来源的 lookup receipt # receipt 要能证明查询对象与结果来源
failure_policy: # 不同失败类型采用不同、可预测的终止策略
  transient: 最多重试 2 次 # 短暂错误有限重试,避免无限循环
  permanent: failed # 不可恢复错误直接进入失败终态

“先分析,再解决问题”没有外部证据和边界,不是可执行计划。

计划是工作假设

Agent 开始时信息通常不完整。计划可在以下证据出现时更新:

  • 环境状态与假设不符;
  • 工具失败或权限不足;
  • 用户澄清目标;
  • verifier 暴露缺失子目标;
  • 更低风险、更低成本路径被发现。

但重规划不能修改不可变约束:

  • 用户明确范围;
  • tool allowlist 与授权;
  • 预算;
  • 审批结果;
  • 数据/安全 policy;
  • 终止规则。

保存“变更了哪些计划项、依据哪个 observation”,不要求保存或展示模型私有 reasoning chain。

三种计划粒度

反应式

每次只决定下一动作。适合短任务和高反馈环境,context 小;缺点是可能局部贪心或循环。

高层计划 + 逐步细化

先列子目标,执行到某一项时再生成具体动作。适合多数工程 Agent:既有全局方向,又不需要预言全部工具调用。

固定 workflow + 局部 Agent

高风险主路径由代码定义,Agent 只在某个节点探索。可预测性最高,适合生产。

不要默认“计划越长越聪明”。计划本身消耗 context,也会因环境变化迅速过期。

进度必须可观察

每个 step 至少改变一类东西:

  • 环境:文件、记录、任务状态改变;
  • 证据:新增测试、receipt、来源或冲突;
  • 不确定性:候选集合缩小,未决问题被回答;
  • 计划:某子目标完成/失败/阻塞;
  • 控制状态:进入等待审批、取消或明确终止。

可以定义 progress vector:

{ // 一组用于判断是否真实推进的可观测指标
  "subgoals_done": 2, // 已有外部证据支持的子目标数量
  "subgoals_total": 5, // 当前计划中的总子目标数,计划重写时应留下版本记录
  "open_questions": 1, // 仍未解决的问题数,持续不降可触发换路或暂停
  "verified_evidence": 3, // 已核验的 receipt、测试或人工决定数量
  "state_version": 8, // 指标对应的权威状态版本,避免混读旧快照
  "consecutive_failures": 0 // 连续失败计数,用于执行 failure budget
}

JSONC 教学表示

// 后是逐行说明;复制为严格 JSON payload 时请先删除注释。

它不必压成单一百分比。对研究任务,“找到更多链接”不一定是进展;“关键 claim 有两个独立一手来源”才是。

识别无进展与循环

重复动作

规范化 tool + arguments + relevant state version 后取 digest。若同一状态反复发同一动作并得到同一 observation,应停止盲目重试。

振荡

A→B→A→B,例如在两个工具或两个计划间来回切换。维护最近 N 个 action/phase signature,检测周期。

文字膨胀

每轮只产生更多计划/总结,没有新 evidence 或 state change。限制连续“无外部动作”的轮数。

同类失败

错误类别、目标与参数均相同。只有在退避、凭据刷新或环境变化等前提改变时,重试才有意义。

处置顺序:

  1. 用错误结构指导一次参数修正;
  2. 切换已允许策略;
  3. 请求人类输入;
  4. 以 blocked/failed/budget_exhausted 终止。

最大步数只是最后保险,不能替代进度语义。

完成是 verifier 的结论

模型可返回 finish_candidate,runtime 再检查:

任务外部成功证据
修代码相关测试、diff、lint/类型检查
数据迁移输入/输出计数、checksum、失败清单
业务写入下游持久状态、receipt、目标资源版本
研究claim–source 覆盖、日期、未决问题
文件整理目标清单、链接、无遗漏/冲突

Verifier 还要检查负向条件:没有越界改动、没有真实密钥、没有未确认副作用。模型自己给计划项打勾不是证据。

终止状态机

推荐至少区分:

状态含义可否恢复
completed成功标准经 verifier 证实通常无需
failed不可恢复或重试耗尽修条件后新 run/恢复
waiting_input缺用户信息是
waiting_approval动作已冻结,等审批是
blocked外部依赖/权限/服务不可用条件变化后
budget_exhausted步数/时间/成本/调用上限调整预算需新授权
cancelled用户或系统取消按产品策略
rejected人类明确拒绝动作可换无副作用方案

不要把 waiting 当 failed,也不要把 “policy 没有下一动作” 自动当 completed。

Stop reason 与结果 envelope

终止时返回:

{ // 预算耗尽时返回的可恢复、但不伪装成成功的结果
  "run_id": "run-42", // 指向需要用户/调度器处理的具体运行
  "phase": "budget_exhausted", // 明确说明终止由预算触发,而不是目标完成
  "stop_reason": "max_tool_calls", // 细化是哪一种预算被耗尽
  "goal_progress": { // 让用户看到已完成与未完成的边界
    "completed": ["lookup-current-ticket"], // 已有证据支持、不应重复执行的动作
    "pending": ["close-current-ticket"] // 尚未执行的动作,恢复前仍需重新满足所有 gate
  }, // 结束进度对象
  "side_effects": [], // 已知外部副作用;未知时应显式标为需要 reconciliation
  "evidence": ["lookup-receipt-abc"], // 支撑当前判断的受控证据 ID
  "safe_resume": { // 恢复前必须满足的前置条件
    "condition": "增加写工具预算并重新审批", // 恢复不是自动重试,而是受控地补齐条件
    "checkpoint_version": 7 // 仅在该检查点仍有效时才可继续恢复
  } // 结束恢复建议对象
}

这样用户知道“做了什么、没做什么、是否有副作用、怎样继续”。

预算的层次

  • 单模型调用 timeout/token;
  • 单工具 timeout/output size;
  • 单 step deadline;
  • 整个 run 的 wall-clock deadline;
  • 模型/工具调用次数与成本;
  • 连续失败/重试;
  • 人工等待 expiry;
  • 子任务/并发上限。

收到 progress event 可以帮助判断任务仍在运行,但仍需绝对 deadline,避免恶意或失控工具无限续期。

贯穿示例

目标:“关闭 ticket-7,但只能关闭当前 ticket,且必须审批。”

计划:

  1. 读取 ticket-7;证据是带来源的状态 observation。
  2. 若不存在或已关闭,正常终止而非重试写入。
  3. 生成只针对 ticket-7 的 action,冻结 fingerprint。
  4. 等审批;审批过期/参数变化则重新请求。
  5. 用幂等键关闭;保存 receipt。
  6. verifier 查询 ticket-7 为 closed 且 receipt 匹配,才能 completed。

恶意 customer note 要求关闭其他 ticket,不会改变计划约束。

动手练习

为“迁移 100 个文件”设计:

  • 5 个可验证子目标;
  • 每 20 个文件的 checkpoint 与 progress vector;
  • 重名、权限失败和取消分支;
  • 8 类终止状态中适用的状态;
  • 完成证据与“不做多余修改”的负向证据。

模拟第 70 个文件失败:用户能否知道前 69 个是否已提交、哪些需补偿、从哪个状态恢复?

自测

  1. 计划为何是 hypothesis,而不是不可变剧本?
  2. 连续生成新文字为什么可能没有进展?
  3. 模型说 “done” 后 verifier 还查什么?
  4. waiting_approval 与 failed 有何运行差异?
  5. 最大步数为何不足以检测振荡?

能写出计划项、progress vector、verifier 和终止 envelope,才算掌握。

下一步

进入 长任务检查点、恢复与幂等,处理进程崩溃和不确定副作用。

参考资料

以下为原始论文或第一方工程资料,获取/复核日期:2026-07-21。