返回文章列表
AgentReplanning可靠性状态机工程实践

任务拆解错了怎么救:Agent 动态重规划(Replanning)工程策略

Agent 真正的可靠性,不是“一次规划就做对”,而是“做错了还能自救”。本文用工程视角拆解重规划:如何检测计划失效、如何最小代价修补、如何避免重试风暴与重复执行,并给出可落地的事件日志、状态机与回滚/补偿设计。

2026年3月4日
Synthly 团队
预计阅读 14 分钟
多工具 Agent 在执行失败后进行重规划(replanning)的流程示意图

📷 Photo by RDNE Stock project via Pexels

先说结论:能上线的 Agent 必须“允许自己犯错”

很多团队把 Agent 的失败当成“模型不够聪明”。但在真实系统里,更常见的失败原因是:

  • 计划依赖了不存在的前提(用户权限、数据字段、工具可用性)
  • 执行中出现了新信息(工具返回与预期不同、数据被并发修改)
  • 副作用不可逆(邮件已发、工单已创建、库存已扣)

所以“动态重规划”不是可选项,而是可靠性的核心。

如果你还没读过 Agent 的最小工程基线,建议先看:


一、先把概念工程化:重规划的输入不是 Prompt,而是“事实”

在工程语境里,重规划至少要拿到这三类输入:

  1. 已发生的事实(Facts)
  • 已执行的动作(tool call)及其回执
  • 产生的外部实体(邮件 id、工单 id、文件 url)
  • 资源状态(余额、配额、锁)
  1. 约束(Constraints)
  • 不可逆操作的禁止重复
  • 合规/权限边界(scope)
  • 成本/时延预算(token、工具调用次数、端到端 p95)
  1. 目标(Goal)
  • 用户目标(可能被澄清/变更)
  • 验收条件(输出合同/格式约束)

这意味着:你做 replanning 的核心数据结构不是一段对话,而是一个可追溯执行记录


二、失败检测:什么时候判定“计划坏了”?

不要把“工具报错”才当失败。更可靠的做法是把失败分成 4 类触发器(Trigger),每类都有可观测信号。

1)工具失败(Tool Failure)

典型信号:

  • 超时、429、5xx
  • 返回空/字段缺失
  • 业务拒绝(权限不足、配额不足)

处理原则:

  • 可恢复错误(超时/429):有限重试 + 退避 + 预算
  • 不可恢复错误(权限/配额):立即停止,转为追问/提示升级权限

2)不变量被打破(Invariant Violation)

例子:你要求“创建工单后必须拿到 ticketId”,但工具返回没有。

这类失败不能盲重试,必须:

  • 记录“违反了哪个不变量”
  • 进入修补分支(补字段、换工具、变更流程)

3)进度停滞(No Progress / Stuck)

最隐蔽,也最常见:Agent 不断解释、不断尝试,但系统状态没有变化。

可操作判定:

  • 连续 N 次动作没有新增事实(facts)
  • 端到端耗时超过阶段预算(例如规划 5s、执行 60s)

4)结果校验失败(Output Contract Failed)

你应该把输出校验当作“执行的一部分”:

  • JSON schema 校验
  • 必填字段校验
  • 枚举值/范围校验
  • 关键事实引用校验(例如必须引用工具回执里的金额/日期)

校验失败后再 replanning,质量会稳定很多。


三、重规划策略谱系:从“局部修补”到“全量重算”

重规划不是只有一种做法。建议按代价从低到高分 4 档,优先走低代价。

1)局部修补(Local Repair):只修坏掉的一步

适用:

  • 某一步参数错、字段缺失
  • 工具小概率失败

做法:

  • 保留既有计划与已完成步骤
  • 仅替换失败节点(比如换一个工具、补一个参数)

关键:必须能定位“失败节点”。所以你需要把计划结构化(例如步骤列表/DAG)。

2)回退到检查点(Checkpoint Rollback):从最近可确认状态继续

适用:

  • 中间步骤产生了不确定状态
  • 并发导致状态被修改

做法:

  • 定义可持久化检查点:完成到哪一步、产物是什么
  • 从检查点重新执行后续步骤(注意幂等与补偿)

3)替代路径(Plan B / Fallback):换流程而非换参数

适用:

  • 工具不可用或不稳定
  • 数据源缺失

例子:

  • CRM 查不到 → 改为让用户上传 CSV
  • 邮件接口超时 → 改为生成草稿给用户确认

4)全量重算(Full Replan):重新生成一份新计划

适用:

  • 目标变化
  • 上下文/事实变化太大,局部修补会越来越脏

注意:全量重算不是“忘掉过去”。它必须把“已发生事实”作为硬约束输入,否则会重复执行写操作。


四、一个可落地的 Replanning 循环(含状态机 + 事件日志)

建议把 Agent 执行抽象成一个“可重入”的循环:

  1. 生成/更新计划(plan)
  2. 执行一步(act)
  3. 写入事件(event)
  4. 校验与判定(verify + decide)
  5. 需要时重规划(replan)

1)最小状态机

  • PLANNING:生成计划
  • RUNNING:执行计划步骤
  • WAITING_INPUT:向用户追问
  • WAITING_TOOL:等待异步工具
  • REPLANNING:基于事实修补计划
  • DONE / FAILED

关键不是状态名称,而是:状态必须持久化,否则断线/重启就无法安全重入。

2)事件日志的最小结构

建议每条事件都能回答“发生了什么”以及“为何发生”。例如:

{
  "taskId": "t_123",
  "planVersion": 3,
  "stepId": "send_email",
  "eventType": "TOOL_CALL",
  "tool": "gmail.send",
  "idempotencyKey": "t_123:send_email:v3",
  "inputHash": "...",
  "startedAt": "...",
  "durationMs": 842,
  "result": { "success": false, "error": { "type": "429" } },
  "decision": { "next": "RETRY", "backoffMs": 2000 }
}

有了它,你才能做到:

  • 复盘失败原因分布
  • 控制重试预算
  • 防止重复执行

3)幂等与补偿:重规划“敢做”的前提

把动作分两类:

  • 读操作:可重复(但要限流/缓存)
  • 写操作:必须幂等,且尽量提供补偿

原则:如果某个写操作既不可幂等、也不可补偿,那它就不该自动执行,而应该走审批(HITL)。


五、重规划的质量控制:别让 Agent 越修越乱

1)把“修补范围”写进策略

常见灾难:每次失败都在原计划上打补丁,最后变成无法理解的“意大利面计划”。

建议设置阈值:

  • maxRepairCountPerTask(例如 3 次)
  • maxPlanVersion(例如 5 版)
  • 超过阈值则:转为全量重算或人工介入

2)重规划也要评测

不要只评测“最终答案好不好”。建议增加:

  • 自救成功率:触发 replanning 后最终完成率
  • 重复执行率:同一幂等键触发次数
  • 重试风暴指标:单任务工具调用次数分布(p95/p99)
  • 修补类型分布:参数修补/回退/换路径/追问

指标可观测,迭代就有方向。


六、可直接复用的 Checklist

  • 失败检测:工具失败/不变量/停滞/校验失败四类触发器
  • 状态机:状态可持久化,可重入执行
  • 事件日志:每步 tool call 有输入摘要、耗时、回执、决策
  • 幂等:所有写操作有 idempotencyKey,冲突可观测
  • 检查点:定义可复用产物与回退点
  • 重试预算:按阶段/按工具设置次数与时间上限
  • 退出策略:超过修补阈值转全量重算或人工/追问

常见问题

“重规划”会不会让模型更容易幻觉?

如果你把 replanning 做成“对话补丁”,确实会更乱。正确做法是:以事实(tool receipts)为约束输入,所有关键输出都要引用或可追溯到回执,然后再做局部修补。

我没有工作流引擎,也能做 replanning 吗?

能。你不需要一开始就上 DAG 引擎。最小可行是:结构化步骤列表 + 事件日志 + 幂等键 + 输出校验。很多团队缺的不是引擎,而是“可追溯执行记录”。

重规划是不是一定要让 Agent 自己决定?

不一定。高风险场景更适合“策略驱动”:系统根据错误类型与风险等级决定是否重试/降级/追问,而不是把所有选择权交给模型。

想看更多工程化文章见 /articles,也可以在 /apps/new 体验 Agent 能力。