云计算百科
云计算领域专业知识百科平台

agent推理范式PlanAndExecute--计划赶不上变化:重规划机制

在这里插入图片描述

目录

  • 目录
  • 摘要
  • 1. 为什么必须重规划:静态计划的三个失效源
  • 2. 重规划的粒度谱系:修补、重列与推翻
  • 3. 重规划器的输入设计:携带什么,丢掉什么
  • 4. 重规划与反思:收敛、循环与熔断
  • 5. ReWOO 的另一种答案:错而不中断,事后补救
  • 6. 实操:路由器、修补器、熔断器与成本账
  • 7. 边界与坑:过度动手术与账本污染
  • 总结
  • 外部引用

摘要

计划在执行中失效是常态,本文把失效归为三个源:世界变了、计划错了、目标变了,并给出各自的路由信号。 重规划的粒度分局部修补、尾部重列、全局重列三档,输入携带四件套,输出受循环熔断与预算闸门约束。 全文给出可离线运行的路由器、修补器、熔断器与成本打表代码,并与 ReWOO 的事后补救做场景对照。

1. 为什么必须重规划:静态计划的三个失效源

第 3 篇《把计划落地:执行器拆解》的结论是:执行器把每步落地并回填变量,步内重试与步级降级挡掉大多数抖动。但三级防线的前两级都救不了的失败,说明问题不在这一步,而在计划本身,需要回到计划层动手术。这个动作叫重规划(replanning,按当前进度与失败现场重新生成或修改计划)。

先建立一个心理预期:计划在纸面上是静态的,世界在执行中是动态的。规划器做决定依据的是执行前的信息,执行器每走一步都在产生新信息。两边的信息量必然不相等,差距大到一定程度,计划就该被改写。接受这一点,重规划就不再是异常分支,而是主流程的一部分。

1.1 三个失效源:世界变了、计划错了、目标变了

把失效原因分类,是为了决定修多深。分类不清,重规划就只剩一招,推翻重来。归纳下来只有三个源。

第一个源是世界变了。计划里写查 A 表的销量,执行时 A 表里没有这个字段。工具没有挂,返回也正常,但返回内容推翻了计划依赖的假设。这类失效的杀伤力取决于假设埋得多深。埋在第一步,整条链全部失效。 第二个源是计划错了。分解本身有缺陷,比如两个步骤之间有未声明的隐性依赖,或某步的产出形状与后继的输入预期对不上。规划时不知道,执行到中段才暴露。第 2 篇的自检器能拦住机械层的环与引用越界,拦不住语义层的错。 第三个源是目标变了。用户中途改需求,或上游系统注入新指令。这不是失败,是计划的前提被撤走。原计划执行得再顺利也没意义,它的终点已经不是用户要的终点。

三个源有个共同点:都发生在规划之后。这决定了它们无法在规划阶段被完全预防,只能靠执行阶段的检测加计划层的修复来兜住。预防手段不是没有,第 2 篇的计划自检器拦机械层,第 3 篇的形状校验拦类型层。剩下的语义层缺口,就是重规划的工作面。

失效源本质典型场景修复方向
世界变了 事实假设被推翻 查无此字段、接口返回改版 重列受影响分支
计划错了 分解结构有缺陷 隐性依赖、类型不匹配 修补依赖或重列后缀
目标变了 前提被撤走 用户改需求、新指令 全局重列

三个源的修复深度依次递增。世界变了通常只伤局部,计划错了伤一条链,目标变了伤全局。第 2 章的粒度谱系就沿着这个梯度展开。

信号归属失效源检测位置检测成本
变量填充失败 计划错了或世界变了 执行器回填前 低,查表即可
产出与预期类型不符 世界变了 形状校验器 低,纯函数
新指令注入 目标变了 会话层或任务层 零,被动接收
步骤连续超时 环境性,不进重规划 重试包裹器 低

这张表补了一个对照项:环境性错误。它不属于三个失效源,但执行器分不清时最容易误报成世界变了。区分办法看返回内容,工具正常返回但内容意外属于世界变了,工具本身没响应属于环境性。前者进重规划,后者留在重试与降级。

三个失效源在时间轴上的表现也不同。世界变了在执行中段最密集,因为信息随步骤积累。计划错了偏向早期暴露,第一步就撞上类型不匹配很常见。目标变了则随时可能,与执行进度无关。这个分布规律对预算分配有指导意义,重规划容量按中段留足。

1.2 失效信号:执行器上报什么,重规划才知道为什么

重规划的输入不是失败这个事件,而是失败的现场。现场由信号携带。三类失效源各有典型信号,执行器要负责把它们识别出来并结构化上报,而不是扔一句笼统的异常。

第一类信号是变量填充失败。步骤引用了 #E2,变量表里没有这个键。可能是前驱失败了,也可能是前缀写错。前者要重规划,后者只是笔误。区分靠查前驱状态。

第二类信号是产出与预期类型不符。步骤声明产出 rows 列表,实际返回了错误对象或空容器。形状校验在第 3 篇是早失败手段,在这里是失效信号源。

第三类信号是新指令注入。目标层变了,不是执行层能感知的。这类信号来自执行器之外,由会话层或任务层直接触发重规划入口,绕过失败检测。

# 来源:自实现 / failure_signal.py
from dataclasses import dataclass, field

@dataclass
class FailureSignal:
"""失败信号的结构化描述,重规划器的直接输入。"""
step_id: str # 在哪一步失败
error_type: str # 错误类型,见类型表
error_msg: str # 原始错误消息,截断到 200 字
attempted: list = field(default_factory=list) # 已尝试的恢复动作
evidence: dict = field(default_factory=dict) # 现场证据,变量表快照等

def classify(step, result, var_table):
"""从执行结果识别错误类型,返回信号对象。"""
if result.get("status") == "tool_error":
kind = "environmental" # 环境性:超时、限流、断网
elif result.get("status") == "shape":
kind = "assumption" # 假设性:产出与预期不符
elif step["args"].get("src", "").startswith("#E") and \\
step["args"]["src"][1:].split(".")[0] not in var_table:
kind = "missing_var" # 变量缺失:引用了未回填的键
else:
kind = "unknown"
return FailureSignal(step_id=step["id"], error_type=kind,
error_msg=str(result.get("msg", ""))[:200])

if __name__ == "__main__":
s = {"id": "s3", "args": {"src": "#E2.rows"}}
print(classify(s, {"status": "shape", "msg": "期望 list 实际 None"}, {"x": 1}))

classify 是纯函数,不调模型,可以放在执行器的回填路径上零成本跑。错误类型学沿用 ReAct 系列第 4 篇的四分类口径,这里取与计划层相关的三类。

1.3 从信号到路由:不同失效源走不同入口

有了分类,失效信号到重规划的路径就不是一条,而是按源分流的路由。画成图,这是重规划机制的骨架。

#mermaid-svg-ThVj6mMdSHDCKmKC{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ThVj6mMdSHDCKmKC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ThVj6mMdSHDCKmKC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ThVj6mMdSHDCKmKC .error-icon{fill:#552222;}#mermaid-svg-ThVj6mMdSHDCKmKC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ThVj6mMdSHDCKmKC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ThVj6mMdSHDCKmKC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ThVj6mMdSHDCKmKC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ThVj6mMdSHDCKmKC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ThVj6mMdSHDCKmKC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ThVj6mMdSHDCKmKC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ThVj6mMdSHDCKmKC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ThVj6mMdSHDCKmKC .marker.cross{stroke:#333333;}#mermaid-svg-ThVj6mMdSHDCKmKC svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ThVj6mMdSHDCKmKC p{margin:0;}#mermaid-svg-ThVj6mMdSHDCKmKC .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-ThVj6mMdSHDCKmKC .cluster-label text{fill:#333;}#mermaid-svg-ThVj6mMdSHDCKmKC .cluster-label span{color:#333;}#mermaid-svg-ThVj6mMdSHDCKmKC .cluster-label span p{background-color:transparent;}#mermaid-svg-ThVj6mMdSHDCKmKC .label text,#mermaid-svg-ThVj6mMdSHDCKmKC span{fill:#333;color:#333;}#mermaid-svg-ThVj6mMdSHDCKmKC .node rect,#mermaid-svg-ThVj6mMdSHDCKmKC .node circle,#mermaid-svg-ThVj6mMdSHDCKmKC .node ellipse,#mermaid-svg-ThVj6mMdSHDCKmKC .node polygon,#mermaid-svg-ThVj6mMdSHDCKmKC .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ThVj6mMdSHDCKmKC .rough-node .label text,#mermaid-svg-ThVj6mMdSHDCKmKC .node .label text,#mermaid-svg-ThVj6mMdSHDCKmKC .image-shape .label,#mermaid-svg-ThVj6mMdSHDCKmKC .icon-shape .label{text-anchor:middle;}#mermaid-svg-ThVj6mMdSHDCKmKC .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ThVj6mMdSHDCKmKC .rough-node .label,#mermaid-svg-ThVj6mMdSHDCKmKC .node .label,#mermaid-svg-ThVj6mMdSHDCKmKC .image-shape .label,#mermaid-svg-ThVj6mMdSHDCKmKC .icon-shape .label{text-align:center;}#mermaid-svg-ThVj6mMdSHDCKmKC .node.clickable{cursor:pointer;}#mermaid-svg-ThVj6mMdSHDCKmKC .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ThVj6mMdSHDCKmKC .arrowheadPath{fill:#333333;}#mermaid-svg-ThVj6mMdSHDCKmKC .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ThVj6mMdSHDCKmKC .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ThVj6mMdSHDCKmKC .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ThVj6mMdSHDCKmKC .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ThVj6mMdSHDCKmKC .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ThVj6mMdSHDCKmKC .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ThVj6mMdSHDCKmKC .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ThVj6mMdSHDCKmKC .cluster text{fill:#333;}#mermaid-svg-ThVj6mMdSHDCKmKC .cluster span{color:#333;}#mermaid-svg-ThVj6mMdSHDCKmKC div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ThVj6mMdSHDCKmKC .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ThVj6mMdSHDCKmKC rect.text{fill:none;stroke-width:0;}#mermaid-svg-ThVj6mMdSHDCKmKC .icon-shape,#mermaid-svg-ThVj6mMdSHDCKmKC .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ThVj6mMdSHDCKmKC .icon-shape p,#mermaid-svg-ThVj6mMdSHDCKmKC .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ThVj6mMdSHDCKmKC .icon-shape .label rect,#mermaid-svg-ThVj6mMdSHDCKmKC .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ThVj6mMdSHDCKmKC .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ThVj6mMdSHDCKmKC .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ThVj6mMdSHDCKmKC :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-ThVj6mMdSHDCKmKC .default>*{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-ThVj6mMdSHDCKmKC .default span{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-ThVj6mMdSHDCKmKC .default tspan{fill:#000000!important;}

变量缺失

前驱失败

前驱正常

类型不符

环境性错误

执行器上报失效信号

信号分类

查前驱状态

依赖断裂 级联跳过

计划笔误 局部修补

假设被推翻 尾部重列

重试与降级已耗尽 上报人工

会话层注入新指令

目标变更 全局重列

重规划器统一入口

图里最重要的一条分流是变量缺失。同一个信号,前驱失败说明计划结构有问题,前驱正常说明只是计划文本笔误。前者动手术,后者只改一行,成本差一个量级。

注意环境性错误没有进重规划器。超时、限流这类抖动在第 3 篇的三级防线里就该被消化。重试与降级都耗尽还失败,才升级到计划层。重规划是贵的,入口要窄。

信号路由解决的是什么时候进手术室。进手术室之后动多大的刀,是下一章的主题。

2. 重规划的粒度谱系:修补、重列与推翻

进了重规划入口,第一个决定是动多大的刀。刀口分三档,从轻到重依次是局部修补、尾部重列、全局重列。档位选错,要么修不干净,要么白烧预算。

2.1 三档粒度的定义与操作

局部修补只改失败步及其直接后继,已完成部分全部保留。典型动作是给失败步换一个工具、补一条依赖边、改一个参数名。计划的主体结构不动,变量表不重置。

尾部重列从失败点开始,把剩余计划整段重新生成。已完成部分保留,失败点之后的所有步骤推倒。适用场景是失败揭示了后面整段的思路不对,只改一步救不回来。

全局重列推翻重来,连已完成部分也作废。唯一正当的触发条件是目标变更,或失败原因证明最初分解的前提就错了。已完成的结果如果是基于错误前提得到的,保留它们反而是污染,第 7.3 节展开。

#mermaid-svg-kJrGVhBmcy10cWVA{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-kJrGVhBmcy10cWVA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-kJrGVhBmcy10cWVA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-kJrGVhBmcy10cWVA .error-icon{fill:#552222;}#mermaid-svg-kJrGVhBmcy10cWVA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kJrGVhBmcy10cWVA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-kJrGVhBmcy10cWVA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kJrGVhBmcy10cWVA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kJrGVhBmcy10cWVA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-kJrGVhBmcy10cWVA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kJrGVhBmcy10cWVA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kJrGVhBmcy10cWVA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kJrGVhBmcy10cWVA .marker.cross{stroke:#333333;}#mermaid-svg-kJrGVhBmcy10cWVA svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kJrGVhBmcy10cWVA p{margin:0;}#mermaid-svg-kJrGVhBmcy10cWVA .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-kJrGVhBmcy10cWVA .cluster-label text{fill:#333;}#mermaid-svg-kJrGVhBmcy10cWVA .cluster-label span{color:#333;}#mermaid-svg-kJrGVhBmcy10cWVA .cluster-label span p{background-color:transparent;}#mermaid-svg-kJrGVhBmcy10cWVA .label text,#mermaid-svg-kJrGVhBmcy10cWVA span{fill:#333;color:#333;}#mermaid-svg-kJrGVhBmcy10cWVA .node rect,#mermaid-svg-kJrGVhBmcy10cWVA .node circle,#mermaid-svg-kJrGVhBmcy10cWVA .node ellipse,#mermaid-svg-kJrGVhBmcy10cWVA .node polygon,#mermaid-svg-kJrGVhBmcy10cWVA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-kJrGVhBmcy10cWVA .rough-node .label text,#mermaid-svg-kJrGVhBmcy10cWVA .node .label text,#mermaid-svg-kJrGVhBmcy10cWVA .image-shape .label,#mermaid-svg-kJrGVhBmcy10cWVA .icon-shape .label{text-anchor:middle;}#mermaid-svg-kJrGVhBmcy10cWVA .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-kJrGVhBmcy10cWVA .rough-node .label,#mermaid-svg-kJrGVhBmcy10cWVA .node .label,#mermaid-svg-kJrGVhBmcy10cWVA .image-shape .label,#mermaid-svg-kJrGVhBmcy10cWVA .icon-shape .label{text-align:center;}#mermaid-svg-kJrGVhBmcy10cWVA .node.clickable{cursor:pointer;}#mermaid-svg-kJrGVhBmcy10cWVA .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-kJrGVhBmcy10cWVA .arrowheadPath{fill:#333333;}#mermaid-svg-kJrGVhBmcy10cWVA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-kJrGVhBmcy10cWVA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-kJrGVhBmcy10cWVA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kJrGVhBmcy10cWVA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-kJrGVhBmcy10cWVA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kJrGVhBmcy10cWVA .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-kJrGVhBmcy10cWVA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-kJrGVhBmcy10cWVA .cluster text{fill:#333;}#mermaid-svg-kJrGVhBmcy10cWVA .cluster span{color:#333;}#mermaid-svg-kJrGVhBmcy10cWVA div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-kJrGVhBmcy10cWVA .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-kJrGVhBmcy10cWVA rect.text{fill:none;stroke-width:0;}#mermaid-svg-kJrGVhBmcy10cWVA .icon-shape,#mermaid-svg-kJrGVhBmcy10cWVA .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kJrGVhBmcy10cWVA .icon-shape p,#mermaid-svg-kJrGVhBmcy10cWVA .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-kJrGVhBmcy10cWVA .icon-shape .label rect,#mermaid-svg-kJrGVhBmcy10cWVA .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kJrGVhBmcy10cWVA .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-kJrGVhBmcy10cWVA .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-kJrGVhBmcy10cWVA :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-kJrGVhBmcy10cWVA .default>*{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-kJrGVhBmcy10cWVA .default span{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-kJrGVhBmcy10cWVA .default tspan{fill:#000000!important;}

只影响失败步

影响失败点之后的整段

影响全局或目标已变

进入重规划入口

失效原因影响范围

局部修补

尾部重列

全局重列

保留已完成 保留未受累后继

保留已完成 作废剩余步骤

作废全部 变量表清空或标记

生成新计划版本 版本号加一

三档共用同一个出口:生成新计划版本,版本号加一。第 3 篇第 7.4 节强调过日志要带计划版本号,原因在这里。同一个步骤 ID 在两个版本里含义可能不同,不带版本号日志就没法归账。

2.2 成本与风险对照表

三档粒度不只是修改范围不同,上下文丢失风险也不同。修改范围越大,重规划提示越长,模型调用越贵,同时保留的有效成果越少。

粒度保留什么重规划提示规模上下文丢失风险单次 token 估算
局部修补 已完成全部、未受累后继 只含失败步与直接后继 极低 约 0.3 到 0.8 K
尾部重列 已完成全部 含目标、已完成摘要、失败现场 中 约 1 到 3 K
全局重列 无,或仅目标与教训 含目标、失败总结、可选旧计划摘要 高 约 2 到 5 K

表里的 token 数字来自第 6.4 节成本计算器的构造假设,口径是八步计划、每步描述约 60 token。真实系统的数字要按自己的计划模板重新打表,方法在第 6.4 节。

上下文丢失风险是这张表最容易被低估的一列。全局重列把已完成的结果丢掉,如果那些结果还有效,重列后要重新执行一遍,等于把已花的预算再花一次。这笔隐形成本在第 6.4 节的成本账里显式算出来。

还有一个维度表里没列:对执行器的影响。局部修补后,执行器可以从失败步继续,已完成步骤的状态原样保留。全局重列后,执行器要重置整个状态机,五态全部回 READY,变量表按健康度清空或降级。切换成本不在 token 账上,在代码路径的复杂度上。

三档粒度在版本管理上的差异也值得说。局部修补产生的新版本与旧版本高度相似,diff 只有一两行,人工审查快。全局重列的新版本与旧版本几乎没有可比性,审查等于重看一份新计划。这意味着粒度越重,人工介入的单位成本越高,这是重规划要设闸的又一个理由。

2.3 选择准则:失效原因的影响半径

三档之间怎么选,判据只有一条:失效原因的影响半径。半径指失效所推翻的假设被多少步骤共享。

世界变了类失效,假设通常只被一条分支共享。查 A 表失败的假设只支撑查表那几步,尾部重列该分支即可。计划错了类失效,如果是隐性依赖,补一条边就够,局部修补。如果是分解方向错,尾部重列。目标变了类失效,半径必然是全局,没有第二档可选。

失效场景影响半径推荐粒度理由
工具改版返回类型变了 该步及其直接后继 局部修补 换工具或加转换步
隐性依赖暴露 补一条边 局部修补 结构没错,缺声明
检索为空需换策略 该分支剩余部分 尾部重列 后续思路要换
分解前提被推翻 全部 全局重列 已完成结果基于错误前提
用户新指令改目标 全部 全局重列 终点变了

工程上落地成路由表,失败信号带类型进来,粒度出去。第 6.1 节给出完整实现。路由表要允许人工覆盖,模型判断影响半径会出错,人工兜底是必要的一环。

默认取向是从轻往重试。先用局部修补跑一次,修补后仍失败且失败原因显示半径更大,再升级到尾部重列。升级路径是单向的,不要从全局重列降回修补,那是振荡的开始,第 7.2 节展开。

单向升级的例外只有一个:目标变更后又有局部失败。这时新目标下的全局重列已完成,后续失败按正常从轻往重走。单向约束限制的是同一次失效的处理路径,不是整个任务生命周期。

粒度定了,接下来是重规划器读什么。输入设计的质量直接决定重列出来的计划质量。

3. 重规划器的输入设计:携带什么,丢掉什么

重规划器是一次模型调用,它的输出质量上限由输入决定。输入设计有两个方向:该带的带全,不该带的删净。两个方向都做错,重规划就是烧钱产出垃圾。

3.1 携带四件套:目标、已完成、失败现场、剩余计划

重规划提示的必备件有四样。缺任何一样,重列出来的计划都会与现状脱节。

第一样是原始目标。重规划器需要知道自己为什么改这份计划。目标用用户原话,不要用规划器的转述版本。转述会丢信息,重列基于转述就放大了丢失。

第二样是已完成步骤及其结果。格式用一行摘要,步骤 ID 加动作加产出概要,大值截断。这是新计划能引用已有变量的依据,也是省预算的依据。

第三样是失败步骤与失败原因,即第 1.2 节的结构化信号。错误类型、错误消息、已尝试的恢复动作三项都要有。已尝试的恢复尤其重要,缺了它,重规划器会把重试过的方案再列一遍。

第四样是剩余计划原文。重规划器需要看到旧计划长什么样,才能决定是修补还是重列。不给旧计划,它只能凭目标重编,等于每次都全局重列。

# 来源:自实现 / replan_prompt.py(依赖 failure_signal.py 的 FailureSignal)
from failure_signal import FailureSignal

GOAL = "对比三家云厂商对象存储的月成本"

def build_prompt(goal, done, failed, remaining, signal):
"""拼装重规划提示,四件套齐全,中间观察全文不携带。"""
lines = [f"目标:{goal}", "", "已完成步骤:"]
for sid, act, out in done:
lines.append(f" #{sid} {act} -> {str(out)[:40]}") # 摘要加截断
lines.append("")
lines.append(f"失败步骤:#{failed['id']} {failed['action']}")
lines.append(f"失败原因:{signal.error_type} {signal.error_msg}")
lines.append(f"已尝试恢复:{signal.attempted or '无'}")
lines.extend(["", "剩余计划:"])
for s in remaining:
lines.append(f" #{s['id']} {s['action']}")
lines.append("")
lines.append("请基于以上现场,重列剩余计划,可引用已完成步骤的产出。")
return "\\n".join(lines)

if __name__ == "__main__":
done = [("1", "查 A 厂商单价", {"rows": 3}),
("2", "查 B 厂商单价", {"rows": 3})]
failed = {"id": "3", "action": "查 C 厂商单价"}
remaining = [{"id": "4", "action": "汇总"}, {"id": "5", "action": "成文"}]
sig = FailureSignal("3", "assumption", "C 厂商无公开单价表",
attempted=["降级到官网页面抓取"])
print(build_prompt(GOAL, done, failed, remaining, sig))

提示模板的行数控制在三十行以内。四件套按固定顺序排列,摘要列对齐,模型对这种格式的遵循度比自由文本高。第 3 篇的上下文最小化原则在这里同样成立,只是对象从执行步换成了重规划调用。

3.2 不携带什么:无关观察全文与旧计划的完整参数

删减清单里排第一的是无关的中间观察全文。某步工具返回了三千 token 的原始页面,重规划不需要看全文,一行产出摘要足够。这是第 3 篇第 3 章上下文最小化的直接延伸,执行步不背全对话历史,重规划步也不背全变量表。

排第二的是旧计划里已完成步骤的完整参数。它们已经执行完了,参数不再有参考价值。除非失败原因是计划错了且错在参数设计,否则只留摘要。

排第三的是历史重规划的痕迹。第二次重规划不需要知道第一次重规划改了什么,只需要知道当前计划与当前失败。把重规划历史整段带入,提示会指数膨胀,第 7.1 节的失控就是这么开始的。

排第四的是其他并发分支的中间状态。依赖图里与失败步无关的分支还在跑,它们的状态与重规划无关。等它们完成,摘要自然进已完成段,没完成就不该出现在提示里。并发场景下这一条容易疏忽,实现时按影响半径过滤变量表即可。

携带项形式典型规模省略后果
原始目标 用户原话 少于 100 token 重列偏离意图
已完成摘要 每步一行 每步约 25 token 重复执行已完步骤
失败信号 结构化三字段 约 80 token 重复失败方案
剩余计划 原文 每步约 60 token 每次都全局重列
观察全文 不携带 可达数千 token 无,纯浪费
已完步骤参数 不携带 每步约 50 token 无,纯浪费

删减的总原则与执行器一致:上下文里只放影响下一步决定的信息。重规划的决定是新计划怎么列,影响它的是失败原因与已完成成果,不是原始观察的长度。

删减带来一个可量化的收益。按第 6.4 节的口径,八步计划第三步失败,携带观察全文时提示约四千 token,只带摘要约六百 token。差距六倍以上,而重列质量不受影响,因为重规划器要的是判断依据,不是数据本身。

3.3 失败原因的结构化描述:错误类型加消息加已试恢复

四件套里最值钱的是失败原因。它的结构化程度决定重规划器能不能对症下药。三个字段各司其职。

错误类型决定重规划的档位。第 1.2 节的路由靠它分流。类型体系沿用 ReAct 系列第 4 篇的错误类型学,环境性、假设性、语义性、权限性四类,加上计划层特有的变量缺失类。

错误消息提供细节。保留原始异常文本,截断到两百字。不要让执行器提前改写错误消息,改写会丢掉对重规划有用的线索,比如具体缺哪个字段。

已尝试的恢复动作是防重复的关键。这个字段记录重试了几次、降级用了哪个备选工具、结果如何。重规划器看到降级也失败,才会换思路,而不是再列一次同样的备选。

#mermaid-svg-sYoKB7zeElVQ9EeG{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-sYoKB7zeElVQ9EeG .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-sYoKB7zeElVQ9EeG .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-sYoKB7zeElVQ9EeG .error-icon{fill:#552222;}#mermaid-svg-sYoKB7zeElVQ9EeG .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-sYoKB7zeElVQ9EeG .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-sYoKB7zeElVQ9EeG .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-sYoKB7zeElVQ9EeG .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-sYoKB7zeElVQ9EeG .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-sYoKB7zeElVQ9EeG .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-sYoKB7zeElVQ9EeG .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-sYoKB7zeElVQ9EeG .marker{fill:#333333;stroke:#333333;}#mermaid-svg-sYoKB7zeElVQ9EeG .marker.cross{stroke:#333333;}#mermaid-svg-sYoKB7zeElVQ9EeG svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-sYoKB7zeElVQ9EeG p{margin:0;}#mermaid-svg-sYoKB7zeElVQ9EeG .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-sYoKB7zeElVQ9EeG .cluster-label text{fill:#333;}#mermaid-svg-sYoKB7zeElVQ9EeG .cluster-label span{color:#333;}#mermaid-svg-sYoKB7zeElVQ9EeG .cluster-label span p{background-color:transparent;}#mermaid-svg-sYoKB7zeElVQ9EeG .label text,#mermaid-svg-sYoKB7zeElVQ9EeG span{fill:#333;color:#333;}#mermaid-svg-sYoKB7zeElVQ9EeG .node rect,#mermaid-svg-sYoKB7zeElVQ9EeG .node circle,#mermaid-svg-sYoKB7zeElVQ9EeG .node ellipse,#mermaid-svg-sYoKB7zeElVQ9EeG .node polygon,#mermaid-svg-sYoKB7zeElVQ9EeG .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-sYoKB7zeElVQ9EeG .rough-node .label text,#mermaid-svg-sYoKB7zeElVQ9EeG .node .label text,#mermaid-svg-sYoKB7zeElVQ9EeG .image-shape .label,#mermaid-svg-sYoKB7zeElVQ9EeG .icon-shape .label{text-anchor:middle;}#mermaid-svg-sYoKB7zeElVQ9EeG .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-sYoKB7zeElVQ9EeG .rough-node .label,#mermaid-svg-sYoKB7zeElVQ9EeG .node .label,#mermaid-svg-sYoKB7zeElVQ9EeG .image-shape .label,#mermaid-svg-sYoKB7zeElVQ9EeG .icon-shape .label{text-align:center;}#mermaid-svg-sYoKB7zeElVQ9EeG .node.clickable{cursor:pointer;}#mermaid-svg-sYoKB7zeElVQ9EeG .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-sYoKB7zeElVQ9EeG .arrowheadPath{fill:#333333;}#mermaid-svg-sYoKB7zeElVQ9EeG .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-sYoKB7zeElVQ9EeG .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-sYoKB7zeElVQ9EeG .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sYoKB7zeElVQ9EeG .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-sYoKB7zeElVQ9EeG .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sYoKB7zeElVQ9EeG .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-sYoKB7zeElVQ9EeG .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-sYoKB7zeElVQ9EeG .cluster text{fill:#333;}#mermaid-svg-sYoKB7zeElVQ9EeG .cluster span{color:#333;}#mermaid-svg-sYoKB7zeElVQ9EeG div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-sYoKB7zeElVQ9EeG .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-sYoKB7zeElVQ9EeG rect.text{fill:none;stroke-width:0;}#mermaid-svg-sYoKB7zeElVQ9EeG .icon-shape,#mermaid-svg-sYoKB7zeElVQ9EeG .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sYoKB7zeElVQ9EeG .icon-shape p,#mermaid-svg-sYoKB7zeElVQ9EeG .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-sYoKB7zeElVQ9EeG .icon-shape .label rect,#mermaid-svg-sYoKB7zeElVQ9EeG .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sYoKB7zeElVQ9EeG .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-sYoKB7zeElVQ9EeG .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-sYoKB7zeElVQ9EeG :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-sYoKB7zeElVQ9EeG .default>*{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-sYoKB7zeElVQ9EeG .default span{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-sYoKB7zeElVQ9EeG .default tspan{fill:#000000!important;}

失败现场

错误类型

错误消息

已尝试恢复

决定重规划档位

提供定位线索

防止方案重复

重规划提示的失败段

三个字段合成提示里的失败段,行数固定三到五行。字段缺失时的兜底是把缺项写成字符串无,让模型显式知道信息不全,而不是自己脑补。

输入设计好了,重规划器能产出对症的新计划。但还有两个问题没解决:失败经验怎么沉淀,以及重规划自己失控了怎么办。

4. 重规划与反思:收敛、循环与熔断

重规划每次都是独立的一次模型调用,它不知道上次为什么失败,除非有人告诉它。把失败经验显式写下来注入提示,这个动作叫反思(reflection,把执行轨迹总结成语言教训再用于后续决策)。

4.1 Reflexion 式失败总结:教训先写下来再重列

Reflexion 的核心思想是语言反馈代替权重更新。智能体失败后,用模型把失败轨迹总结成一段反思文本,存进记忆,下一次尝试时注入提示。 arXiv:2303.11366 的实验口径显示这一机制在多轮尝试里累积提升成功率,具体数字以原文为准。

搬进重规划机制,落点是失败信号旁边再挂一个教训字段。重规划提示里多一段,写清楚上一次的方案为什么行不通。写教训的动作放在重规划之前,用同一次模型调用或单独一次小调用完成。

# 来源:自实现 / lesson.py(依赖 failure_signal.py 的 FailureSignal)
from dataclasses import dataclass, field
from failure_signal import FailureSignal

@dataclass
class LessonBook:
"""失败教训簿,跨重规划轮次累积。"""
lessons: list = field(default_factory=list)
max_len: int = 5 # 上限五条,防提示膨胀

def add(self, plan_ver, signal, cause):
text = f"版本{plan_ver} 步骤{signal.step_id} "
text += f"{signal.error_type}失败 原因:{cause} 下次避免:{signal.attempted}"
self.lessons.append(text)
if len(self.lessons) > self.max_len:
self.lessons.pop(0) # 淘汰最旧的

def render(self):
return "\\n".join(f"- {t}" for t in self.lessons)

if __name__ == "__main__":
book = LessonBook()
sig = FailureSignal("3", "assumption", "C 厂商无公开单价表",
attempted=["官网抓取"])
book.add(2, sig, "假设了三家都有公开价目表")
book.add(3, sig, "备选页面也被反爬拦截")
print(book.render())

教训簿的上限设五条,超过就淘汰最旧的。这是经验值,依据是提示里教训段超过五行后,模型对教训的遵循度开始下降。淘汰策略用先进先出,更精细的做法是按失败类型分组各留一条。

教训与失败信号的区别要说清楚。失败信号描述这一次的现场,是事实记录。教训是从现场提炼的规律,是可迁移的判断。前者每次重置,后者跨轮累积。

4.2 收敛性问题:同一失败反复触发的循环

重规划引入了一个新风险:不收敛。表现形式是同一失败反复触发重规划。查 C 厂商单价失败,重列后还是这一步,还是同样的失败。轮次涨,预算烧,进度零。

不收敛的根因通常是重规划器没有真正换方案。它看到失败原因,生成的新计划看起来不同,实质思路一样。因为提示里缺少已尝试恢复的完整记录,或教训簿没把上次的死路写明白。

检测手段很直接:记录每次重规划的失败步骤 ID 与错误类型,出现完全相同的二元组即计数加一。计数达到阈值,说明重规划已经失去探索能力,该停下来交人工。

#mermaid-svg-NwL2TqgFarpKhwfP{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-NwL2TqgFarpKhwfP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NwL2TqgFarpKhwfP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NwL2TqgFarpKhwfP .error-icon{fill:#552222;}#mermaid-svg-NwL2TqgFarpKhwfP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NwL2TqgFarpKhwfP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NwL2TqgFarpKhwfP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NwL2TqgFarpKhwfP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NwL2TqgFarpKhwfP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NwL2TqgFarpKhwfP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NwL2TqgFarpKhwfP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NwL2TqgFarpKhwfP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NwL2TqgFarpKhwfP .marker.cross{stroke:#333333;}#mermaid-svg-NwL2TqgFarpKhwfP svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NwL2TqgFarpKhwfP p{margin:0;}#mermaid-svg-NwL2TqgFarpKhwfP .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-NwL2TqgFarpKhwfP .cluster-label text{fill:#333;}#mermaid-svg-NwL2TqgFarpKhwfP .cluster-label span{color:#333;}#mermaid-svg-NwL2TqgFarpKhwfP .cluster-label span p{background-color:transparent;}#mermaid-svg-NwL2TqgFarpKhwfP .label text,#mermaid-svg-NwL2TqgFarpKhwfP span{fill:#333;color:#333;}#mermaid-svg-NwL2TqgFarpKhwfP .node rect,#mermaid-svg-NwL2TqgFarpKhwfP .node circle,#mermaid-svg-NwL2TqgFarpKhwfP .node ellipse,#mermaid-svg-NwL2TqgFarpKhwfP .node polygon,#mermaid-svg-NwL2TqgFarpKhwfP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NwL2TqgFarpKhwfP .rough-node .label text,#mermaid-svg-NwL2TqgFarpKhwfP .node .label text,#mermaid-svg-NwL2TqgFarpKhwfP .image-shape .label,#mermaid-svg-NwL2TqgFarpKhwfP .icon-shape .label{text-anchor:middle;}#mermaid-svg-NwL2TqgFarpKhwfP .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-NwL2TqgFarpKhwfP .rough-node .label,#mermaid-svg-NwL2TqgFarpKhwfP .node .label,#mermaid-svg-NwL2TqgFarpKhwfP .image-shape .label,#mermaid-svg-NwL2TqgFarpKhwfP .icon-shape .label{text-align:center;}#mermaid-svg-NwL2TqgFarpKhwfP .node.clickable{cursor:pointer;}#mermaid-svg-NwL2TqgFarpKhwfP .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-NwL2TqgFarpKhwfP .arrowheadPath{fill:#333333;}#mermaid-svg-NwL2TqgFarpKhwfP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-NwL2TqgFarpKhwfP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-NwL2TqgFarpKhwfP .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NwL2TqgFarpKhwfP .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-NwL2TqgFarpKhwfP .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NwL2TqgFarpKhwfP .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-NwL2TqgFarpKhwfP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NwL2TqgFarpKhwfP .cluster text{fill:#333;}#mermaid-svg-NwL2TqgFarpKhwfP .cluster span{color:#333;}#mermaid-svg-NwL2TqgFarpKhwfP div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-NwL2TqgFarpKhwfP .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-NwL2TqgFarpKhwfP rect.text{fill:none;stroke-width:0;}#mermaid-svg-NwL2TqgFarpKhwfP .icon-shape,#mermaid-svg-NwL2TqgFarpKhwfP .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NwL2TqgFarpKhwfP .icon-shape p,#mermaid-svg-NwL2TqgFarpKhwfP .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-NwL2TqgFarpKhwfP .icon-shape .label rect,#mermaid-svg-NwL2TqgFarpKhwfP .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NwL2TqgFarpKhwfP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-NwL2TqgFarpKhwfP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-NwL2TqgFarpKhwfP :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-NwL2TqgFarpKhwfP .default>*{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-NwL2TqgFarpKhwfP .default span{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-NwL2TqgFarpKhwfP .default tspan{fill:#000000!important;}

计数小于阈值

再次失败

计数达到阈值

重规划触发

记录失败步骤与错误类型

相同二元组计数

正常重列 生成新版本

执行新计划

熔断 停止重规划

上报人工 附完整轨迹

图里的熔断是第 4.3 节的主题。这里先明确一个立场:循环检测不是优化项,是必需项。没有它,一次不收敛的任务能把整天的 token 预算烧干净。

阈值的取值经验是两到三次。第一次重列算合理尝试,第二次算换思路,第三次还撞同一堵墙,继续撞的意义很小。第 6.3 节的熔断器把这个逻辑写成可复用组件。

4.3 熔断器:重规划的重规划

熔断(circuit breaking,达到预设条件后强制停止自动恢复并升级上报)在重试层见过,第 3 篇的重试包裹器里有次数上限。这里把同一思想抬一层,管住重规划本身。

熔断器管两把闸。第一把是单步闸,同一失败二元组连续出现 N 次即熔断,N 默认三。第二把是总量闸,整个任务的重规划次数达到上限即熔断,默认五次,与任务的计划规模挂钩。

# 来源:自实现 / breaker.py
from dataclasses import dataclass, field

@dataclass
class ReplanBreaker:
"""重规划熔断器,单步闸加总量闸。"""
per_step_limit: int = 3 # 同一失败二元组最多三次
total_limit: int = 5 # 全任务重规划总次数上限
counts: dict = field(default_factory=dict)
total: int = 0

def check(self, step_id, error_type):
"""重规划前调用,返回 False 表示必须熔断。"""
self.total += 1
if self.total > self.total_limit:
return f"总量熔断 重规划已{self.total – 1}次"
key = (step_id, error_type)
self.counts[key] = self.counts.get(key, 0) + 1
if self.counts[key] > self.per_step_limit:
return f"单步熔断 步骤{step_id}同因失败{self.counts[key] – 1}次"
return None # None 表示放行

if __name__ == "__main__":
br = ReplanBreaker()
for i in range(1, 5):
reason = br.check("s3", "assumption")
print(i, reason or "放行")
# 第四次触发单步熔断

熔断后的动作是上报,携带完整轨迹与教训簿。轨迹要带计划版本号,让人能还原每一次手术改了什么。上报不是失败,是把不可解的问题交给有更多信息的决策者。

熔断器解决的是纵向失控,同一处反复动刀。还有一种横向失控,重规划在不同方案间摆荡,那是第 7.2 节的内容。先把 ReWOO 的对照讲完,再统一收边界。

5. ReWOO 的另一种答案:错而不中断,事后补救

重规划是事中修补:失败发生,停下手术,再继续。ReWOO 给出了另一个方向:执行阶段根本不停,把变量缺失与错误攒到最后,由 Solver 统一解决。

5.1 Solver 的补救口径

ReWOO 把任务拆成 Planner、Worker、Solver 三个模块。Planner 一次性产出完整计划,变量用占位符前向引用。Worker 逐步执行,不携带之前的观察。Solver 在全部执行结束后,拿到所有产出,统一生成最终答案。

关键设计在 Solver 面对缺失变量的态度。某步执行失败,产出是空的,Worker 不中断,把失败信息记进产出槽。Solver 拿到的证据列表里有缺口,它要做的是基于现有证据推理,缺口能绕就绕,绕不过就明确承认。

arXiv:2305.18323 的口径是这套解耦显著降低 token 消耗,HotpotQA 上报告五倍 token 效率与百分之四的准确率提升,跨基准的鲁棒性也更好,具体实验条件以原文为准。效率来自执行步不携带历史,鲁棒性部分来自错而不中断。

#mermaid-svg-LGjaowMPGcVep2ro{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-LGjaowMPGcVep2ro .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-LGjaowMPGcVep2ro .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-LGjaowMPGcVep2ro .error-icon{fill:#552222;}#mermaid-svg-LGjaowMPGcVep2ro .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-LGjaowMPGcVep2ro .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-LGjaowMPGcVep2ro .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-LGjaowMPGcVep2ro .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-LGjaowMPGcVep2ro .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-LGjaowMPGcVep2ro .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-LGjaowMPGcVep2ro .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-LGjaowMPGcVep2ro .marker{fill:#333333;stroke:#333333;}#mermaid-svg-LGjaowMPGcVep2ro .marker.cross{stroke:#333333;}#mermaid-svg-LGjaowMPGcVep2ro svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-LGjaowMPGcVep2ro p{margin:0;}#mermaid-svg-LGjaowMPGcVep2ro .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-LGjaowMPGcVep2ro .cluster-label text{fill:#333;}#mermaid-svg-LGjaowMPGcVep2ro .cluster-label span{color:#333;}#mermaid-svg-LGjaowMPGcVep2ro .cluster-label span p{background-color:transparent;}#mermaid-svg-LGjaowMPGcVep2ro .label text,#mermaid-svg-LGjaowMPGcVep2ro span{fill:#333;color:#333;}#mermaid-svg-LGjaowMPGcVep2ro .node rect,#mermaid-svg-LGjaowMPGcVep2ro .node circle,#mermaid-svg-LGjaowMPGcVep2ro .node ellipse,#mermaid-svg-LGjaowMPGcVep2ro .node polygon,#mermaid-svg-LGjaowMPGcVep2ro .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-LGjaowMPGcVep2ro .rough-node .label text,#mermaid-svg-LGjaowMPGcVep2ro .node .label text,#mermaid-svg-LGjaowMPGcVep2ro .image-shape .label,#mermaid-svg-LGjaowMPGcVep2ro .icon-shape .label{text-anchor:middle;}#mermaid-svg-LGjaowMPGcVep2ro .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-LGjaowMPGcVep2ro .rough-node .label,#mermaid-svg-LGjaowMPGcVep2ro .node .label,#mermaid-svg-LGjaowMPGcVep2ro .image-shape .label,#mermaid-svg-LGjaowMPGcVep2ro .icon-shape .label{text-align:center;}#mermaid-svg-LGjaowMPGcVep2ro .node.clickable{cursor:pointer;}#mermaid-svg-LGjaowMPGcVep2ro .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-LGjaowMPGcVep2ro .arrowheadPath{fill:#333333;}#mermaid-svg-LGjaowMPGcVep2ro .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-LGjaowMPGcVep2ro .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-LGjaowMPGcVep2ro .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-LGjaowMPGcVep2ro .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-LGjaowMPGcVep2ro .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-LGjaowMPGcVep2ro .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-LGjaowMPGcVep2ro .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-LGjaowMPGcVep2ro .cluster text{fill:#333;}#mermaid-svg-LGjaowMPGcVep2ro .cluster span{color:#333;}#mermaid-svg-LGjaowMPGcVep2ro div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-LGjaowMPGcVep2ro .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-LGjaowMPGcVep2ro rect.text{fill:none;stroke-width:0;}#mermaid-svg-LGjaowMPGcVep2ro .icon-shape,#mermaid-svg-LGjaowMPGcVep2ro .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-LGjaowMPGcVep2ro .icon-shape p,#mermaid-svg-LGjaowMPGcVep2ro .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-LGjaowMPGcVep2ro .icon-shape .label rect,#mermaid-svg-LGjaowMPGcVep2ro .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-LGjaowMPGcVep2ro .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-LGjaowMPGcVep2ro .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-LGjaowMPGcVep2ro :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-LGjaowMPGcVep2ro .default>*{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-LGjaowMPGcVep2ro .default span{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-LGjaowMPGcVep2ro .default tspan{fill:#000000!important;}

是

否

目标

Planner 一次产出全计划

Worker 逐步执行

步骤失败?

记录失败 继续下一步

产出入槽

全部执行完毕

Solver 统一解决变量缺口

最终答案 或 明确承认证据不足

图里 E 到 G 这条边是 ReWOO 与重规划的分水岭。重规划在这里会停下来动手术,ReWOO 直接跳过去继续跑。两种选择没有绝对优劣,适用场景不同。

5.2 事中修补与事后补救的对照

把两种机制摆在一起看。差异的根源在于对失败的假设:重规划假设失败会污染后续步骤,必须当下处理。ReWOO 假设缺口常常可以绕过,先跑完再说。

维度事中修补 重规划事后补救 ReWOO Solver
失败发生时 停下 重列 再继续 记录 继续
中断次数 每次硬失败一次中断 零中断
额外模型调用 每次重规划一次 最后一次 Solver
上下文携带 失败现场加剩余计划 全部产出加缺口说明
计划可调整性 高 可改后继 低 计划一旦定就不改
适用失败密度 低 硬失败少见 高 查询类任务常见

表里最后一行是选择依据。失败密度低且失败后果严重,选事中。失败密度高且单次失败常可绕过,选事后。

各自的失效模式也对称。事中修补的风险是过度手术,小失败大动干戈,第 7.1 节。事后补救的风险是缺口积累,跑完发现证据全断,Solver 也变不出来。

两种机制的组合成本也不同。事中修补每次中断都要一次模型调用,失败十次就是十次重规划调用。事后补救只有最后一次 Solver,前面零额外调用。在失败密度高的任务里,这个差距会被放大到无法忽视。

混合形态是多数生产系统的选择。把计划分成硬链段与松耦合段,硬链段配事中修补,松耦合段配事后补救。实现上给每个步骤标记失败策略字段,执行器按字段路由,两套机制并存而不冲突。

5.3 场景选择表:硬依赖链与查询式任务

落到具体任务类型上,选择可以更明确。判据是步骤间的耦合强度。

任务类型步骤耦合失败典型后果推荐机制
数据管道 ETL 强 下游吃上游产出 后继全断 事中修补
多源检索汇总 式 各源独立 少一源仍可答 事后补救
代码生成与调试 强 硬依赖链 编译都过不了 事中修补
竞品对比报告 式 每家独立 缺一家标注说明 事后补救
多步表单流程 强 状态机推进 卡在中途状态 事中修补
开放式网络调研 式 路径可替换 换源即可 事后补救加轻修补

耦合强度的判别有个简单测试:把失败步骤的产出置空,看还有几个后继能产出有效结果。全部瘫痪是强耦合,多数照常是弱耦合。这个测试可以在计划生成后离线跑一遍,结果直接写进步骤的策略字段。

任务类型不是静态标签。同一种任务在不同环境下耦合度会变。检索汇总若要求三家都要有数据才能成文,弱耦合就变成了强耦合。判据要跟着完成判据走,不是跟着任务名走。

判据写成一句话:后继步骤的输入是否被失败步骤的产出唯一决定。是,事中修补。否,事后补救。混合任务两类都上,硬链段事中,检索段事后,这也是多数生产系统的实际形态。

需要说明的是 ReWOO 不处理目标变更。计划一旦定下就执行到底,用户中途改需求只能重新发起任务。重规划机制在这一点上不可替代,这是它存在的独立价值。

两个机制对错误类型学的要求也不同。事中修补依赖分类,错误类型决定粒度档位,分类错则手术错。事后补救不依赖分类,Solver 拿到的是原始证据与缺口说明,它自己判断。这让后者的实现更简单,代价是把判断压力全压给最后一次调用。

判断维度提问倾向事中倾向事后
耦合强度 后继输入是否唯一依赖失败产出 是 否
失败密度 单任务预期硬失败次数 低 高
失败后果 缺一个证据还能不能答 不能 能
需求稳定性 用户会不会中途改 会 不会
预算形态 偏好集中一次还是分散多次 分散 集中

机制讲完,进入实操。四个组件:路由器、修补器、熔断器已经在第 4 章给过,剩下成本账一起打表。

6. 实操:路由器、修补器、熔断器与成本账

本章把前文的机制落成可离线运行的代码。四个组件互相独立,可以单独拿去改。全部 mock,不调真实模型与工具。

先看四个组件在数据流里的位置,后面的代码按这个顺序展开。

#mermaid-svg-qy5kNYEEdvMPgXTm{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-qy5kNYEEdvMPgXTm .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-qy5kNYEEdvMPgXTm .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-qy5kNYEEdvMPgXTm .error-icon{fill:#552222;}#mermaid-svg-qy5kNYEEdvMPgXTm .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qy5kNYEEdvMPgXTm .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-qy5kNYEEdvMPgXTm .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qy5kNYEEdvMPgXTm .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qy5kNYEEdvMPgXTm .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-qy5kNYEEdvMPgXTm .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qy5kNYEEdvMPgXTm .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qy5kNYEEdvMPgXTm .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qy5kNYEEdvMPgXTm .marker.cross{stroke:#333333;}#mermaid-svg-qy5kNYEEdvMPgXTm svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qy5kNYEEdvMPgXTm p{margin:0;}#mermaid-svg-qy5kNYEEdvMPgXTm .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-qy5kNYEEdvMPgXTm .cluster-label text{fill:#333;}#mermaid-svg-qy5kNYEEdvMPgXTm .cluster-label span{color:#333;}#mermaid-svg-qy5kNYEEdvMPgXTm .cluster-label span p{background-color:transparent;}#mermaid-svg-qy5kNYEEdvMPgXTm .label text,#mermaid-svg-qy5kNYEEdvMPgXTm span{fill:#333;color:#333;}#mermaid-svg-qy5kNYEEdvMPgXTm .node rect,#mermaid-svg-qy5kNYEEdvMPgXTm .node circle,#mermaid-svg-qy5kNYEEdvMPgXTm .node ellipse,#mermaid-svg-qy5kNYEEdvMPgXTm .node polygon,#mermaid-svg-qy5kNYEEdvMPgXTm .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-qy5kNYEEdvMPgXTm .rough-node .label text,#mermaid-svg-qy5kNYEEdvMPgXTm .node .label text,#mermaid-svg-qy5kNYEEdvMPgXTm .image-shape .label,#mermaid-svg-qy5kNYEEdvMPgXTm .icon-shape .label{text-anchor:middle;}#mermaid-svg-qy5kNYEEdvMPgXTm .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-qy5kNYEEdvMPgXTm .rough-node .label,#mermaid-svg-qy5kNYEEdvMPgXTm .node .label,#mermaid-svg-qy5kNYEEdvMPgXTm .image-shape .label,#mermaid-svg-qy5kNYEEdvMPgXTm .icon-shape .label{text-align:center;}#mermaid-svg-qy5kNYEEdvMPgXTm .node.clickable{cursor:pointer;}#mermaid-svg-qy5kNYEEdvMPgXTm .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-qy5kNYEEdvMPgXTm .arrowheadPath{fill:#333333;}#mermaid-svg-qy5kNYEEdvMPgXTm .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-qy5kNYEEdvMPgXTm .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-qy5kNYEEdvMPgXTm .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qy5kNYEEdvMPgXTm .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-qy5kNYEEdvMPgXTm .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qy5kNYEEdvMPgXTm .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-qy5kNYEEdvMPgXTm .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-qy5kNYEEdvMPgXTm .cluster text{fill:#333;}#mermaid-svg-qy5kNYEEdvMPgXTm .cluster span{color:#333;}#mermaid-svg-qy5kNYEEdvMPgXTm div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-qy5kNYEEdvMPgXTm .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-qy5kNYEEdvMPgXTm rect.text{fill:none;stroke-width:0;}#mermaid-svg-qy5kNYEEdvMPgXTm .icon-shape,#mermaid-svg-qy5kNYEEdvMPgXTm .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qy5kNYEEdvMPgXTm .icon-shape p,#mermaid-svg-qy5kNYEEdvMPgXTm .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-qy5kNYEEdvMPgXTm .icon-shape .label rect,#mermaid-svg-qy5kNYEEdvMPgXTm .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qy5kNYEEdvMPgXTm .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-qy5kNYEEdvMPgXTm .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-qy5kNYEEdvMPgXTm :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-qy5kNYEEdvMPgXTm .default>*{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-qy5kNYEEdvMPgXTm .default span{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-qy5kNYEEdvMPgXTm .default tspan{fill:#000000!important;}

放行

拒绝

放行

拒绝

执行器上报失败

路由器 选粒度

熔断器 查次数闸

预算闸 查 token 闸

上报人工

修补器 生成新版本计划

成本计算器 事后入账

图里成本计算器画在预算闸之后,形成闭环:每次重规划的花费入账,下一次调用前用累计值判断是否放行。事前估算与事后入账用同一个系数,账才能对平。

6.1 触发路由器:按失败类型选粒度

路由器是第 1.3 节那张分流的代码化。输入失败信号与计划状态,输出粒度档位。

# 来源:自实现 / granularity_router.py(复用前文 FailureSignal 与 Plan 定义)
from dataclasses import dataclass
from failure_signal import FailureSignal

@dataclass
class Plan:
steps: list # 每项含 id、action、depends_on、status

def route(plan, signal, var_table, goal_changed=False):
"""按失败类型与上下文选重规划粒度。"""
if goal_changed:
return "global" # 目标变更直接全局重列
failed = next(s for s in plan.steps if s["id"] == signal.step_id)
idx = plan.steps.index(failed)
succs = [s for s in plan.steps[idx + 1:]
if failed["id"] in s.get("depends_on", [])]

if signal.error_type == "missing_var": # 变量缺失先查前驱
ref = signal.step_id
pred_state = next((s["status"] for s in plan.steps
if s["id"] < ref), None)
return "patch" if pred_state == "done" else "tail"

if signal.error_type == "environmental": # 环境性交给降级 不进重规划
return "none"

if signal.error_type == "assumption" and len(succs) <= 1:
return "patch" # 影响半径小 局部修补
if len(succs) > 2 or signal.error_type == "semantic":
return "tail" # 半径大 尾部重列
return "patch"

if __name__ == "__main__":
p = Plan(steps=[
{"id": "1", "action": "查A", "depends_on": [], "status": "done"},
{"id": "2", "action": "查B", "depends_on": ["1"], "status": "failed"},
{"id": "3", "action": "汇总", "depends_on": ["2"], "status": "pending"},
{"id": "4", "action": "成文", "depends_on": ["3"], "status": "pending"}])
s1 = FailureSignal("2", "assumption", "无字段", ["重试2次"])
s2 = FailureSignal("2", "missing_var", "缺#E1", [])
print(route(p, s1, {})) # patch,两个后继但 assumption 半径小
print(route(p, s2, {"1": 1})) # tail,变量缺失且前驱非 done
print(route(p, s1, {}, goal_changed=True)) # global,目标变更最高优先级

路由器的判断逻辑是纯规则,可解释可审计。人工覆盖入口留一个参数,线上跑一周后按实际命中率调阈值,比如后继数大于二的档位线。

6.2 局部修补重规划器:保留已完成,重列失败后缀

修补器是粒度谱系的代码化。局部修补保留已完成与未受累后继,尾部重列作废失败点之后的全部步骤。

# 来源:自实现 / patch_replanner.py(复用 granularity_router.py 的 Plan 与 failure_signal.py)
from granularity_router import Plan
from failure_signal import FailureSignal

def replan(plan, signal, granularity="tail"):
"""按粒度生成新计划版本,mock 返回模板结果。"""
idx = next(i for i, s in enumerate(plan.steps)
if s["id"] == signal.step_id)
new_steps = []
if granularity == "patch": # 局部:只动失败步
new_steps = [dict(s) for s in plan.steps]
new_steps[idx]["action"] = new_steps[idx]["action"] + " 换工具重试"
new_steps[idx]["status"] = "pending"
ver = plan.steps and f"v{len(plan.steps)}-patch"
else: # 尾部:保留前缀 重列后缀
kept = [dict(s) for s in plan.steps[:idx]]
fixed = dict(plan.steps[idx])
fixed["action"] = f"绕开[{signal.error_msg[:20]}]的新步骤"
fixed["status"] = "pending"
new_steps = kept + [fixed, {
"id": str(int(plan.steps[–1]["id"]) + 1),
"action": "汇总", "depends_on": [fixed["id"]],
"status": "pending"}]
ver = f"v{len(plan.steps)}-tail"
return {"version": ver, "steps": new_steps,
"cause": f"{signal.error_type}@{signal.step_id}"}

if __name__ == "__main__":
p = Plan(steps=[
{"id": "1", "action": "查A", "depends_on": [], "status": "done"},
{"id": "2", "action": "查C", "depends_on": ["1"], "status": "failed"},
{"id": "3", "action": "汇总", "depends_on": ["2"], "status": "pending"}])
sig = FailureSignal("2", "assumption", "C厂商无公开价目表",
attempted=["官网抓取降级"])
out = replan(p, sig, "tail")
print(out["version"], out["cause"])
for s in out["steps"]:
print(s["id"], s["status"], s["action"])

mock 里模板替代了模型调用,真实系统把字符串拼接换成一次带四件套的提示即可,模板结构不变。输出必须带版本号与归因字段,前者接日志,后者接教训簿。

实际输出里旧步骤 3 被作废后,新步骤直接取号 4,旧 ID 不复用。这是有意的:同一个 ID 在两个版本里含义不同,会让日志归账混乱,与第 3 篇第 7.4 节的版本号纪律一致。

granularity_router 的输出还暴露一个待调优点。第一组信号是 assumption 加两个后继,按第 2.3 节的表该走 tail,代码却返回 patch,因为后继数阈值写死在二。这正是注释里说的人工覆盖入口的用途,阈值线上要按命中率回调。

6.3 循环熔断器与预算闸门

第 4.3 节的熔断器管重规划次数,这里补上 token 预算闸门,两者合成一个总闸。

# 来源:自实现 / budget_gate.py(复用 breaker.py 的 ReplanBreaker 同款闸门思路)
from dataclasses import dataclass

@dataclass
class BudgetGate:
"""重规划总闸:次数闸加 token 闸。"""
replan_limit: int = 5
token_limit: int = 20000 # 全任务重规划 token 上限
spent: int = 0
count: int = 0

def admit(self, est_tokens):
"""重规划前估算本次 token,返回 None 放行,字符串为拒绝理由。"""
self.count += 1
self.spent += est_tokens
if self.count > self.replan_limit:
return f"次数闸 已重规划{self.count – 1}次"
if self.spent > self.token_limit:
return f"token闸 已耗{self.spent}超限{self.token_limit}"
return None

if __name__ == "__main__":
gate = BudgetGate(replan_limit=3, token_limit=5000)
for est in (1200, 1800, 1500, 900):
print(est, gate.admit(est) or "放行")
# 第四次先撞次数闸

注意第四次调用的输出顺序:est 打印 900,拒绝理由是次数闸。token 合计 5400 超限 5000,本该 token 闸先报,但代码里次数判断在前。顺序是有意的设计,报错信息里次数比 token 更容易被运维直接行动。

四个闸门的完整口径沿用 ReAct 系列第 7 篇:轮数闸、token 闸、工具调用闸、挂钟时间闸。本文的次数闸是轮数闸在计划层的投影,其余三个照搬。

组件间的组装顺序:路由器在前,决定要不要动手术与动多大。熔断器与预算闸夹在路由器与修补器之间,任何一次重规划都要先过两道闸。修补器最后执行,产出带版本号的新计划。第 6.2 节的输出里 version 与 cause 两个字段,就是为日志归账与教训簿准备的。

6.4 重规划成本计算器:修补与全局重列的 token 打表

最后算账。同一个八步计划在第四步失败,两种粒度的 token 成本差多少,打表见分晓。

# 来源:自实现 / replan_cost.py(构造假设,数字为示意口径)
PROMPT = 120 # 重规划提示的固定指令部分
GOAL = 40 # 目标段
DONE_LINE = 25 # 每条已完成摘要
FAIL = 80 # 失败信号段
LESSON = 60 # 教训簿段
STEP = 60 # 每条剩余计划
REEXEC = 180 # 重执行一步的均价(提示加产出)

def cost_patch(done_n, fail_step, total, lessons=1):
"""局部修补:只带失败步与直接后继,无重执行。"""
carried = PROMPT + GOAL + FAIL + LESSON * lessons
carried += DONE_LINE * done_n + STEP * 2
return carried

def cost_global(done_n, fail_step, total, lessons=1):
"""全局重列:全计划作废,已完成部分要重执行。"""
carried = PROMPT + GOAL + FAIL + LESSON * lessons
carried += DONE_LINE * done_n + STEP * (total – done_n)
reexec = REEXEC * done_n # 隐形成本:已完步骤重跑
return carried, carried + reexec

if __name__ == "__main__":
for done_n in (1, 3, 5):
p = cost_patch(done_n, 4, 8)
g, g_total = cost_global(done_n, 4, 8)
print(f"已完成{done_n}步 修补{p} 全局携带{g} 全局含重跑{g_total}")
# 已完成5步时 全局含重跑约是修补的 3.6 倍

跑出来的一组数字:已完成一步时修补 445,全局携带 745,全局含重跑 925。已完成五步时修补 545,全局携带 605,全局含重跑 1505,约是修补的三倍。

# 来源:自实现 / replan_cost.py 实际输出
# 已完成1步 修补445 全局携带745 全局含重跑925
# 已完成3步 修补495 全局携带675 全局含重跑1215
# 已完成5步 修补545 全局携带605 全局含重跑1505

这组数字有个反直觉的地方:全局携带成本反而随已完成步数下降。原因是剩余计划变短了,携带段变小。但重执行段随已完成步数线性上升,两条线一升一降,总成本的谷底在中段。真实系统里已完成步数越多,越不该全局重列,这个结论不变。

结论有三条。第一,携带成本的差距随已完成步数线性拉大。第二,真正的分水岭是重执行成本,它只在全局重列时出现。第三,全局重列的正当性只能来自影响半径,不能来自顺手,多花的每一 token 都要有归因。

打表工具的意义不在绝对数字,在相对倍数。换成自己的计划模板,把六个系数改成实测值,倍数结论重新算一遍,就能为自己的粒度选择立账。

7. 边界与坑:过度动手术与账本污染

机制与代码都齐了,最后是工程边界。四个坑都真实见过,每个坑配一条纪律。

7.1 过度重规划:把执行问题当成规划问题

症状:环境抖动导致的失败被送进重规划。限流一次,重列整份计划。超时一次,推翻已完成五步。手术做了一堆,病根在网络。

根因是失败分流失守。第 1.3 节的路由里,环境性错误不该进重规划入口。分流一旦漏了,重规划器拿到环境性错误,它会认真地重列,因为重列是它唯一会做的事。

成本算得清。按第 6.4 节的口径,八步计划第四步失败,一次全局重列含重跑约 1215 token。环境抖动的重试成本约 200 token。把一次抖动当结构失败,多花六倍。

症状病根纠正
限流触发重列 分流漏了环境性错误 路由表前置检查错误类型
小失败大手术 粒度档位选错 影响半径判据收紧
重列后新旧计划高度相似 缺已尝试恢复记录 四件套带全 attempted 字段
每次失败都全局重列 剩余计划没携带 提示模板加剩余计划段

纪律是先问病因再问刀口。手术指征只有两个:失败原因指向计划结构,或失败原因指向目标前提。其余情况留在执行层消化,消化不了就上报,不要升级。

有一条判别流程可以收束这个纪律。失败上报时依次问三个问题:错误类型是不是环境性,重试与降级是否已耗尽,失败原因是否指向计划结构。三问分别是环境性走重试、未耗尽走降级、指向结构才进重规划。任何一问拦下,就不该出现在重规划入口。

这个流程也解释了为什么重规划器不该是默认路径。它是最后一级,前面有重试、降级两级挡着。入口越窄,单次进入的失败越有分量,重规划的投入才花得值。入口放宽一分,过度手术的风险就涨一分。

7.2 重规划振荡:A 换 B,B 换回 A

症状:方案 A 失败,重列成方案 B。方案 B 失败,重列回方案 A。两次失败原因不同,单步熔断器都检测不到,因为二元组不一样。

振荡的根因是重规划器没有跨轮记忆。每次只看当前失败,A 失败给 B,B 失败给 A,来回摆。第 4.1 节的教训簿本来能拦住它,但教训簿只记失败原因,不记方案指纹。

解法是给每个重列产出算指纹。新计划的关键步骤序列哈希后存档,重列前查档,指纹重复立即熔断。实现十行以内,效果显著。

# 来源:自实现 / oscillation_guard.py
import hashlib

def fingerprint(steps):
"""计划指纹:动作序列哈希,忽略 ID 与状态。"""
acts = "|".join(s["action"] for s in steps)
return hashlib.md5(acts.encode()).hexdigest()[:8]

def guard(history, new_steps, max_repeat=2):
"""重列前查指纹,重复达阈值即拒绝。"""
fp = fingerprint(new_steps)
n = history.count(fp)
if n >= max_repeat:
return f"振荡 步骤序列{fp}已出现{n}次"
history.append(fp)
return None

if __name__ == "__main__":
hist = []
plan_a = [{"action": "查A"}, {"action": "汇总"}]
plan_b = [{"action": "查B"}, {"action": "汇总"}]
print(guard(hist, plan_a) or "放行")
print(guard(hist, plan_b) or "放行")
print(guard(hist, plan_a) or "放行") # 第三次同指纹 触发振荡

指纹算法用动作序列不用参数全文,是为了让表面不同实质相同的方案也能命中。参数微调后的老方案,动作序列不变,照样被拦。

7.3 已完成结果的污染:旧账基于错的前提

症状:全局重列后,新计划引用了旧版本的变量。那些变量是在被推翻的前提下算出来的,数值有效,语义失效。

典型例子。目标是比较三家云厂商成本,分解前提是三家都有公开价目表。执行两步后发现 C 家根本没有公开表,前提被推翻。前两步查到的 A、B 单价数字没错,但拿它们直接进新计划,新计划的口径是含私有报价的估算,旧数字的口径是公开牌价,混在一起就是错的账。

解法分两层。轻度污染做标记,旧结果携带前提说明,重规划器决定是否引用。重度污染直接作废,变量表里对应键标记 stale,新计划不得引用。

#mermaid-svg-iQj49A9uf9RTMrHN{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-iQj49A9uf9RTMrHN .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-iQj49A9uf9RTMrHN .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-iQj49A9uf9RTMrHN .error-icon{fill:#552222;}#mermaid-svg-iQj49A9uf9RTMrHN .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-iQj49A9uf9RTMrHN .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-iQj49A9uf9RTMrHN .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-iQj49A9uf9RTMrHN .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-iQj49A9uf9RTMrHN .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-iQj49A9uf9RTMrHN .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-iQj49A9uf9RTMrHN .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-iQj49A9uf9RTMrHN .marker{fill:#333333;stroke:#333333;}#mermaid-svg-iQj49A9uf9RTMrHN .marker.cross{stroke:#333333;}#mermaid-svg-iQj49A9uf9RTMrHN svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-iQj49A9uf9RTMrHN p{margin:0;}#mermaid-svg-iQj49A9uf9RTMrHN .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-iQj49A9uf9RTMrHN .cluster-label text{fill:#333;}#mermaid-svg-iQj49A9uf9RTMrHN .cluster-label span{color:#333;}#mermaid-svg-iQj49A9uf9RTMrHN .cluster-label span p{background-color:transparent;}#mermaid-svg-iQj49A9uf9RTMrHN .label text,#mermaid-svg-iQj49A9uf9RTMrHN span{fill:#333;color:#333;}#mermaid-svg-iQj49A9uf9RTMrHN .node rect,#mermaid-svg-iQj49A9uf9RTMrHN .node circle,#mermaid-svg-iQj49A9uf9RTMrHN .node ellipse,#mermaid-svg-iQj49A9uf9RTMrHN .node polygon,#mermaid-svg-iQj49A9uf9RTMrHN .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-iQj49A9uf9RTMrHN .rough-node .label text,#mermaid-svg-iQj49A9uf9RTMrHN .node .label text,#mermaid-svg-iQj49A9uf9RTMrHN .image-shape .label,#mermaid-svg-iQj49A9uf9RTMrHN .icon-shape .label{text-anchor:middle;}#mermaid-svg-iQj49A9uf9RTMrHN .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-iQj49A9uf9RTMrHN .rough-node .label,#mermaid-svg-iQj49A9uf9RTMrHN .node .label,#mermaid-svg-iQj49A9uf9RTMrHN .image-shape .label,#mermaid-svg-iQj49A9uf9RTMrHN .icon-shape .label{text-align:center;}#mermaid-svg-iQj49A9uf9RTMrHN .node.clickable{cursor:pointer;}#mermaid-svg-iQj49A9uf9RTMrHN .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-iQj49A9uf9RTMrHN .arrowheadPath{fill:#333333;}#mermaid-svg-iQj49A9uf9RTMrHN .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-iQj49A9uf9RTMrHN .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-iQj49A9uf9RTMrHN .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-iQj49A9uf9RTMrHN .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-iQj49A9uf9RTMrHN .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-iQj49A9uf9RTMrHN .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-iQj49A9uf9RTMrHN .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-iQj49A9uf9RTMrHN .cluster text{fill:#333;}#mermaid-svg-iQj49A9uf9RTMrHN .cluster span{color:#333;}#mermaid-svg-iQj49A9uf9RTMrHN div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-iQj49A9uf9RTMrHN .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-iQj49A9uf9RTMrHN rect.text{fill:none;stroke-width:0;}#mermaid-svg-iQj49A9uf9RTMrHN .icon-shape,#mermaid-svg-iQj49A9uf9RTMrHN .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-iQj49A9uf9RTMrHN .icon-shape p,#mermaid-svg-iQj49A9uf9RTMrHN .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-iQj49A9uf9RTMrHN .icon-shape .label rect,#mermaid-svg-iQj49A9uf9RTMrHN .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-iQj49A9uf9RTMrHN .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-iQj49A9uf9RTMrHN .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-iQj49A9uf9RTMrHN :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-iQj49A9uf9RTMrHN .default>*{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-iQj49A9uf9RTMrHN .default span{fill:#faf9f5!important;stroke:#ffffff!important;color:#000000!important;stroke-width:0px!important;}#mermaid-svg-iQj49A9uf9RTMrHN .default tspan{fill:#000000!important;}

未推翻

部分推翻

完全推翻

全局重列 触发

旧结果的前提是否被推翻

旧结果保留 可被引用

标记 stale 附前提说明

作废 新计划不得引用

重规划器逐条判断引用

生成新版本 变量表带健康度

变量表加一列健康度,字段取值 clean、stale、void 三档。回填时写 clean,全局重列时按前提覆盖关系批量降级。这个改动小,收益大,账本污染是所有坑里最难排查的一个,因为它不报错。

健康度的判定不一定要靠人工规则。轻量做法是让重规划器在生成新计划时,对每个被引用的旧变量输出一个判断:沿用、换算或作废。判断结果写回健康度列,账本随之更新。这相当于把污染检测也交给同一次模型调用,边际成本接近零。

健康度含义新计划可否引用典型来源
clean 前提未变,数值语义都有效 直接引用 正常回填
stale 数值对但语义口径已变 换算后引用 部分前提被推翻
void 前提被完全推翻 不得引用 全局重列作废

排查账本污染有一个实用技巧:给每个变量记录它依赖的假设集合。假设被推翻时,反查引用该假设的变量,一次性降级。这比逐个变量人工判断快,也避免漏网。假设集合在规划阶段就要埋进去,第 2 篇的步骤 done_when 字段是个现成的挂靠点。

7.4 预算闸门的硬约束:总次数与总 token

第 6.3 节给了闸门实现,这里讲配置哲学。闸门的意义不在数值大小,在硬性。软闸门等于没有闸门。

总次数的锚点建议取计划步数的一半。八步计划配四次重规划,余量够一次结构性失败加一次目标变更。配到十次,多数会变成第 7.1 节的过度手术。

总 token 的锚点建议取首次规划加全部执行的预估成本的一半。重规划是补救动作,补救的预算不该超过主流程的一半,超过说明主流程设计有问题,该修的是规划器不是重规划器。

闸门推荐锚点触发后动作
单步重规划次数 三次 熔断并上报该步骤
总重规划次数 计划步数的一半 熔断并上报全任务
总 token 闸 主流程预估的一半 停止并结算已花成本
挂钟时间闸 用户可等待阈值 停止并给出部分结果

闸门触发后的产出不是空手。携带已完成的有效结果、失败轨迹、教训簿,交人工或交用户。部分结果加诚实的缺口说明,比烧完预算后的完整失败有用得多。

最后一个提醒是闸门要前置检查。token 闸在调用前估算,不是花完才报。估算口径用第 6.4 节的计算器,宁可高估百分之二十,把超支拦在发生前。

闸门参数的调优要有数据支撑。上线后统计三个数:闸门触发率、触发后人工介入的解决率、被闸门拦下但其实能成功的比例。第三个数高说明闸太紧,第一个数高说明上游分流有问题。三个数都低,参数就可以保持不动。

还要注意闸门与用户预期的一致性。用户等了三分钟收到的若是熔断报错,体验未必比等五分钟拿到完整结果好。挂钟闸的阈值应该问用户要,不是拍出来。交互式任务与批处理任务的容忍度差一个量级,同一套闸门参数不该两边通用。

总结

重规划是计划层的手术机制,入口由失效信号路由,粒度分局部修补、尾部重列、全局重列三档,判据是失效原因的影响半径。输入四件套:原始目标、已完成摘要、结构化失败信号、剩余计划,无关观察全文一律不携带。失败经验经 Reflexion 式教训簿跨轮累积,收敛性靠单步熔断与总量闸双保险。ReWOO 的 Solver 提供另一条路,错而不中断,事后统一补救,适合弱耦合的查询式任务,硬依赖链仍以事中修补为宜。成本账显示全局重列含重执行的开销可达局部修补的三倍,粒度选择必须归因。过度手术、方案振荡、旧账污染、预算失控是四个高频坑,分别对应分流纪律、指纹查重、健康度标记与硬闸门。全部组件 mock 可跑,系数换实测值即可迁移。

外部引用

  • ReWOO: Decoupling Reasoning from Observations for Efficient Augmented Language Models, arXiv:2305.18323,Solver 在执行结束后统一解决变量缺失与错误的事后补救口径,五倍 token 效率与 HotpotQA 百分之四准确率提升以原文实验条件为准。
  • Reflexion: Language Agents with Verbal Reinforcement Learning, arXiv:2303.11366,把失败轨迹总结为语言教训存入记忆再注入后续尝试的思想。
  • LangGraph 官方文档,Plan-and-Execute 模板中 replan 步骤的设计口径,含响应式重规划与重新提示式重规划的对照。
  • ReAct: Synergizing Reasoning and Acting in Language Models, arXiv:2210.03629,对照范式,无显式计划层时的逐步纠偏方式。
  • Tree of Thoughts: Deliberate Problem Solving with Large Language Models, arXiv:2305.10601,沿树回退到中间状态再继续的机制思想。
赞(0)
未经允许不得转载:网硕互联帮助中心 » agent推理范式PlanAndExecute--计划赶不上变化:重规划机制
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!