← 返回知识目录

Agent 动态重规划与长任务恢复

一个任务运行很久后失败,最容易想到的补救是“再试一次”或“重新生成计划”。但重复执行可能再次依赖错误事实,全量重做又会浪费已经验证的结果,甚至重复触发外部操作。可靠的恢复机制首先要回答:哪里出错、哪些状态受到影响、从哪里继续才可信。

长任务的难点包括上下文取舍,也包括工具可靠性、数据时效、持久化、并发和副作用。上下文窗口变大不会自动解决这些问题;恢复能力必须成为运行时的一部分。

1. 不同问题需要不同恢复动作

Replan 指在已有目标和执行状态的基础上修订路径。它只是纠偏的一种方式,适用于原路径、关键假设或业务条件已经失效的情况。

情况 优先动作 需要注意
只读接口偶发超时 限次重试 Retry 保留超时和退避预算,避免内外层重复重试
写接口超时,结果未知 查询外部状态并对账 超时不等于未执行,不能直接重发
缺少决定性条件 澄清 Clarify 先检查已有证据能否补齐
某数据源持续不可用但有替代 局部 Replan 替代来源也要符合权限、时效与质量要求
上游事实错误,污染后继结果 失效传播及回滚/补偿 不能只改最终答案
没有授权、没有可行路径或预算耗尽 中断或返回部分结果 说明已完成、未完成及当前外部状态

是否需要重规划与如何生成新计划,最好分成可观测的两个决策。异常码、权限失败、重试次数、工具熔断状态等先由代码处理;业务前提是否被新证据推翻,再交给语义判断。

2. 分清失败点、根因点、回滚点和重规划起点

考虑一个假想导购案例:最终推荐都太贵,原因是系统把电子产品的历史价格偏好错误用于服装。四个位置并不相同。

位置 在例子中的含义
失败点 用户看到推荐结果并表示价格不合适
根因点 提取服装预算时错误复用了其他类目的偏好
回滚点 回到基础资料已确认、服装预算尚未确定的状态
重规划起点 重新确认服装预算,再决定检索范围

参考商品、已确认城市等可能仍能复用;错误预算、由它生成的候选集及排序应当失效。尺码或颜色偏好能否复用,也要检查其证据是否适用于本次任务,不能因为“以前存在”就默认可信。

回滚点是状态恢复边界,重规划起点是重新做决策的位置。二者可以重合,也可能需要额外回到更早节点刷新证据。最近一次通过的检查点只是候选:它可能校验不完整,也可能依赖已经过期的数据。

3. 记录依赖,才能做局部恢复

仅保存聊天记录,难以可靠定位污染范围。关键中间结果应有稳定 ID,并记录来源、时间、依赖和校验状态。

{
  "artifact_id": "clothing_budget_v2",
  "value": {"max": 300, "currency": "CNY"},
  "status": "verified",
  "evidence_refs": ["message_18"],
  "depends_on": ["requirements_v4"],
  "constraint_version": 4,
  "checked_at": "2026-09-22T10:00:00+08:00",
  "scope": {"category": "服装", "task_id": "task_12"}
}

状态可以从简单版本开始:available 表示可参考但未充分验证,verified 表示在指定条件下通过检查,invalid 表示已被否定。需要更精细恢复时增加 suspicious(待复核)与 dirty(自身未必错,但依赖失效)。这些名称是实现约定,不是统一协议。

当事实被否定时,代码沿依赖图标记受影响的后继,并阻止它们被继续消费。失效传播只覆盖实际依赖者;没有依赖的分支可保留。但任务取消、权限撤销等全局条件可能影响整个运行,应采用更高优先级的控制规则。

invalidate(fact_id, evidence):
    append_event("fact_invalidated", fact_id, evidence)
    set_status(fact_id, "invalid")
    for item in descendants_of(fact_id):
        set_status(item, "dirty")
        suspend_pending_consumers(item)

已经在执行中的后继也要处理。可取消的请求发送取消信号;无法取消的请求完成后先进入隔离区,再核对版本,不能继续写入当前有效状态。

4. 从证据出发缩小恢复范围

恢复过程可以按四层展开:先重试当前动作,再替换当前步骤的路径,再回到阶段入口,最后才考虑全局重规划。层级扩大应当由失败证据驱动,而不是机械地每失败一次就多退一步。

回溯时依次检查当前动作是否执行正确、输入是否完整、上游产物是否有效、支撑事实是否过期、原假设是否成立,以及用户目标是否发生变化。某类错误已反复出现时,可以建立“错误签名—候选恢复位置”的规则,但命中规则后仍需核对当前上下文。

新计划应明确列出保留、修改、删除和新增内容。例如:

{
  "base_plan_version": 3,
  "new_plan_version": 4,
  "trigger_evidence": ["budget_correction_18"],
  "preserve": ["reference_product", "confirmed_city"],
  "invalidate": ["old_budget", "candidates_v3", "ranking_v3"],
  "restart_from": "confirm_clothing_budget",
  "changes": ["询问预算后重新检索"],
  "remaining_replan_budget": 1
}

这个差异是一份待校验提案。运行时检查版本是否仍然匹配,用户约束是否被擅自修改,是否加入了未经允许的工具或副作用,再原子地更新有效计划。模型提供的简要理由有助于审计,但并不能证明它展示了真实完整的内部推理过程。

系统还要保存尝试过的路径与失败原因,避免 A、B 两个方案来回切换。失败路径可以重新启用,但需要状态变化证据,例如依赖服务已恢复,而不是模型忘记了上一次失败。

5. 借用 Try/Confirm/Cancel 做计划预检

高成本或关键外部依赖的任务,可以在正式执行前先验证最脆弱的前提。这借用了 TCC 的阶段划分,但普通计划预检并不具备分布式事务的原子性和一致性保证

预检成功不等于稍后执行必然成功:库存、权限、价格都可能变化。涉及资源预留、确认和释放时,需要底层服务提供对应协议、幂等键和补偿语义,不能仅靠三个同名提示词实现。

对于只读推荐,轻量探测通常已经足够。不要为了统一概念引入完整事务编排。资金或跨系统写入失败后的补偿也可能不完全可逆,系统必须记录实际完成的部分,而不是一概显示“已回滚”。

6. 长任务要分开保存状态和上下文

模型窗口只是当前决策所需信息的视图,不能成为任务状态的唯一存储。至少应持久化目标和约束版本、计划及节点状态、证据索引、产物位置、工具调用账本、失败历史和预算。

长期运行中常见的漂移包括:目标变成了另一个任务,硬约束被摘要弱化,失败动作被标为成功,旧证据继续支持新结论,或一个字段误读污染多个后续产物。检测这些问题需要对照原始事件、外部真实状态和当前计划。

可以在阶段结束、压缩上下文后、重规划前后或重要异常出现时触发上下文校验。异步校验器应记录自己读取的状态版本,返回问题和修复建议;若主任务已推进到新版本,先重新核对影响范围,不能用过期结论直接覆盖当前状态。

多加一个校验器也会增加成本和误报。确定性检查可持续运行,昂贵语义检查优先放在高影响节点。不同模块发现同一问题时应合并,避免多个修复动作并发修改同一任务。

7. 用事件与快照支持中断恢复

事件日志保存“发生了什么”,快照保存“某一时点状态是什么”。例如约束更新、工具执行完成、事实失效、检查通过和计划换版都可以成为事件。

{
  "event_id": "event_204",
  "task_id": "task_12",
  "sequence": 204,
  "type": "constraint_updated",
  "expected_state_version": 18,
  "payload": {"field": "budget_max", "old": 500, "new": 300},
  "evidence_ref": "message_18"
}

通过最近可信快照加后续事件,可以重建内部状态。快照需要同时关联事件序号、数据结构版本和产物校验信息;关键变更持久化失败时,不应向用户宣称完成。日志中发现错误,也应追加纠正事件,保留历史用于审计,而不是抹去发生过的事实。

中断恢复至少有两种情况。等待用户补充时,保存待回答的问题与恢复位置;进程崩溃重启时,先获取任务执行权、加载持久化状态、检查进行中的调用,再恢复可执行节点。并行工作者需要租约、锁或版本比较,防止两个进程同时继续同一任务。

重放内部状态不等于重新执行外部动作。 若“创建工单”已在外部成功,但进程在记录成功前崩溃,恢复后再次调用就可能产生重复工单。执行账本应保存稳定操作 ID 和幂等键,先查询或对账未知结果;已完成动作重用回执,需要重做的动作才重新调度。具体框架的重放边界仍需核对,例如 LangGraph 的 Graph API 文档明确说明,受影响节点恢复时可能从函数起点重跑,副作用需考虑幂等。LangGraph Graph API 文档

可以用一个恢复检查表控制重启过程:先确认任务仍获准继续,再校验当前计划和约束版本;随后清点外部动作账本,核对未知结果;再验证仍要复用的产物及证据;最后重新计算就绪节点。加载快照成功只完成了其中一步,不能直接当成“任务已恢复”。

例如进程停止前已经拿到一份配送报价,重启时它可能已经过期。正确处理是保留该调用的历史记录,并把报价标记为需刷新;不应为了与旧快照一致而继续承诺旧到货时间。相反,已经成功创建的工单,即使当前窗口中没有相关消息,也应通过账本和外部回执被识别为已执行。

用户取消需要优先于普通恢复。运行时应停止派发新动作,尝试取消进行中的动作,并核对已经发生的外部影响。取消之后的晚到结果只能用于记录和对账,不应重新激活任务。若清理或补偿失败,应保存待处理项及恢复入口,使下一次处理有明确起点。

8. 分阶段交接,保留可回查的细节

长任务可以划分为需求确认、证据收集、生成、执行和验证等阶段。分阶段不必等同于多 Agent;同一个运行时也可以切换局部上下文。

交接包需要保留总目标、已完成事项、当前计划版本、仍然有效的约束、已确认事实、证据和产物索引、未解决问题及下一阶段输入。原始工具响应和失败尝试可以留在外部存储,需要时按 ID 召回。

交接后应检查接收方是否知道哪些结果已经过期、哪些动作已经执行、哪些问题仍待确认。压缩历史时尤其不能把“禁止修改接口”改成“尽量少修改”,也不能把“已提出执行方案”改成“已完成执行”。

9. 用故障注入验证恢复能力

恢复测试不能只模拟网络超时。还应覆盖:上游事实被纠正、证据过期、外部写入成功但本地记录失败、等待用户时进程重启、旧计划的并发任务晚到、补偿本身失败。

每个案例都应明确标准根因、允许保留的产物、必须失效的后继、实际外部状态和预期恢复终点。观察根因定位是否正确、恢复范围是否过大或过小、是否错误复用脏结果、是否重复副作用,以及恢复后是否真正满足目标。

恢复成功率的分母应说明是全部触发恢复的任务,还是预先标注为可恢复的故障。已有结果复用率也应只统计仍可信的结果;提高复用率不能以继续使用错误数据为代价。

对于失败路径振荡、无进展重试、重复外部操作和约束违规,应有独立计数与停止条件。可靠性体现在遇到这些情况时,系统能够保留证据、终止扩大影响,并从明确的状态继续工作。

关联阅读:任务规划与执行计划 · 执行循环与工具选择