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。限制连续“无外部动作”的轮数。
同类失败
错误类别、目标与参数均相同。只有在退避、凭据刷新或环境变化等前提改变时,重试才有意义。
处置顺序:
- 用错误结构指导一次参数修正;
- 切换已允许策略;
- 请求人类输入;
- 以
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,且必须审批。”
计划:
- 读取 ticket-7;证据是带来源的状态 observation。
- 若不存在或已关闭,正常终止而非重试写入。
- 生成只针对 ticket-7 的 action,冻结 fingerprint。
- 等审批;审批过期/参数变化则重新请求。
- 用幂等键关闭;保存 receipt。
- verifier 查询 ticket-7 为 closed 且 receipt 匹配,才能 completed。
恶意 customer note 要求关闭其他 ticket,不会改变计划约束。
动手练习
为“迁移 100 个文件”设计:
- 5 个可验证子目标;
- 每 20 个文件的 checkpoint 与 progress vector;
- 重名、权限失败和取消分支;
- 8 类终止状态中适用的状态;
- 完成证据与“不做多余修改”的负向证据。
模拟第 70 个文件失败:用户能否知道前 69 个是否已提交、哪些需补偿、从哪个状态恢复?
自测
- 计划为何是 hypothesis,而不是不可变剧本?
- 连续生成新文字为什么可能没有进展?
- 模型说 “done” 后 verifier 还查什么?
waiting_approval与failed有何运行差异?- 最大步数为何不足以检测振荡?
能写出计划项、progress vector、verifier 和终止 envelope,才算掌握。
下一步
进入 长任务检查点、恢复与幂等,处理进程崩溃和不确定副作用。
参考资料
以下为原始论文或第一方工程资料,获取/复核日期:2026-07-21。