Agent 的规划与执行范式:Plan-Execute-Replan 与 ReAct
一.什么是 Agent
在大模型应用中,Agent 通常指一个系统。它围绕目标,能够选择行动、调用工具,并根据环境反馈继续工作。它常用于需要多个步骤才能完成的任务,例如客服查询、资料调研,也包括故障排查。
大模型可以解释"订单通常为什么延迟发货"。但要回答"我的这笔订单能不能在周五前收到",就需要查询具体订单、库存和配送信息,再根据查到的结果作出判断。
这个过程中,模型负责理解目标和选择行动,工具负责查询或操作外部系统,运行程序负责执行调用、保存结果和控制流程。
这里有一个容易混淆的地方:模型提出工具调用,运行程序实际执行调用。 工具返回的结果进入上下文后,模型才有条件据此决定下一步。
有了这些能力,还需要解决任务怎样组织的问题:先做什么,后做什么?原来的安排走不通时,接下来怎样调整?
下面按照 Plan-Execute → 暴露局限 → 引入 Replan → ReAct → 对比 → 协作 的顺序,逐步看清这些问题。
二.先规划,再执行:Plan-Execute
1.Plan 和 Execute 分别做什么
Plan-Execute 将任务处理分成两个逻辑阶段。
Plan,也就是规划。 根据用户目标和约束,形成一组步骤,明确先后顺序和依赖关系。这部分职责通常称为 Planner。
Execute,也就是执行。 选取计划中的步骤,调用工具或其他处理逻辑,得到对应结果。这部分职责通常称为 Executor。
例如,Planner 安排"核对原仓的预计到货时间",Executor 再决定需要查询哪些信息。
一个计划步骤不一定对应一次工具调用。 核对到货时间,可能需要先查订单,再查备货时间,最后查询配送时效。
2.先采用一个简化版本
为了看清后面的演变过程,我们先约定:
Planner 只生成一次总体计划。执行器可以根据反馈完成当前步骤,但系统暂时不修改总体计划。
这里限制的是总体计划的修订。执行器仍然可以补查信息、记录失败,或者调整当前步骤里的工具调用方式。
这是本文的教学简化。LangChain 官方的 Plan-and-Execute 介绍本身就包含执行后的重规划,因此不能把"计划始终不变"当成所有 Plan-Execute 实现的共同定义。
3.用订单到货查询串起整个流程
**案例说明:**下文的订单、仓库、费用、工具名称与返回结果均为教学模拟。工具调用使用示意写法,不对应某个平台的实际 API;到货时间表示当前信息下的预计结果。
假设现在是周二上午,用户提出下面的需求,时间统一使用北京时间:
帮我看看订单
O1001能不能在本周五 18:00 前收到。如果原来的安排赶不上,查一下其他办法。商品、数量和收货地址保持不变,可以考虑其他仓库或配送方式,额外总费用不能超过 20 元。本次只查询并给建议。
订单购买了 1 件 SKU-01 商品,目前安排由 A 仓发货。库存和配送情况还需要进一步查询。
先从原仓路线开始,给出一份尚未展开替代方案的初始计划。下面观察执行反馈如何暴露计划中的缺项,再通过重规划补齐剩余工作。
| 步骤 | 工作内容 | 预期产物 |
|---|---|---|
| P1 | 确认订单、商品、数量、收货地址和原发货仓 | 订单基础信息 |
| P2 | 核对 A 仓供货情况和预计到货时间 | 原仓路线的时效判断及依据 |
| P3 | 在原仓路线能满足期限的前提下,整理到货安排 | 原仓方案 |
| P4 | 汇总到货判断、处理建议及相关条件 | 给用户的查询结果 |
注意,P3 依赖"原仓路线能够按时到货"这个待验证前提。用户允许评估其他仓库,并没有要求必须沿用 A 仓。
如果 P2 确认原仓能够按时送达,后面就可以直接整理安排和建议。接下来我们会看到,这个前提也可能被查询结果推翻。
4.再看控制流程
现在有了具体任务,再看流程图就容易对应了:Planner 生成 P1~P4,执行器依次处理步骤,每次结果都会被保存,但图中没有返回 Planner 修改计划的路径。
#mermaid-svg-FEjdeiQILmSTPk3D{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-FEjdeiQILmSTPk3D .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FEjdeiQILmSTPk3D .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FEjdeiQILmSTPk3D .error-icon{fill:#552222;}#mermaid-svg-FEjdeiQILmSTPk3D .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FEjdeiQILmSTPk3D .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FEjdeiQILmSTPk3D .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FEjdeiQILmSTPk3D .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FEjdeiQILmSTPk3D .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FEjdeiQILmSTPk3D .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FEjdeiQILmSTPk3D .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FEjdeiQILmSTPk3D .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FEjdeiQILmSTPk3D .marker.cross{stroke:#333333;}#mermaid-svg-FEjdeiQILmSTPk3D svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FEjdeiQILmSTPk3D p{margin:0;}#mermaid-svg-FEjdeiQILmSTPk3D .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-FEjdeiQILmSTPk3D .cluster-label text{fill:#333;}#mermaid-svg-FEjdeiQILmSTPk3D .cluster-label span{color:#333;}#mermaid-svg-FEjdeiQILmSTPk3D .cluster-label span p{background-color:transparent;}#mermaid-svg-FEjdeiQILmSTPk3D .label text,#mermaid-svg-FEjdeiQILmSTPk3D span{fill:#333;color:#333;}#mermaid-svg-FEjdeiQILmSTPk3D .node rect,#mermaid-svg-FEjdeiQILmSTPk3D .node circle,#mermaid-svg-FEjdeiQILmSTPk3D .node ellipse,#mermaid-svg-FEjdeiQILmSTPk3D .node polygon,#mermaid-svg-FEjdeiQILmSTPk3D .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FEjdeiQILmSTPk3D .rough-node .label text,#mermaid-svg-FEjdeiQILmSTPk3D .node .label text,#mermaid-svg-FEjdeiQILmSTPk3D .image-shape .label,#mermaid-svg-FEjdeiQILmSTPk3D .icon-shape .label{text-anchor:middle;}#mermaid-svg-FEjdeiQILmSTPk3D .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-FEjdeiQILmSTPk3D .rough-node .label,#mermaid-svg-FEjdeiQILmSTPk3D .node .label,#mermaid-svg-FEjdeiQILmSTPk3D .image-shape .label,#mermaid-svg-FEjdeiQILmSTPk3D .icon-shape .label{text-align:center;}#mermaid-svg-FEjdeiQILmSTPk3D .node.clickable{cursor:pointer;}#mermaid-svg-FEjdeiQILmSTPk3D .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-FEjdeiQILmSTPk3D .arrowheadPath{fill:#333333;}#mermaid-svg-FEjdeiQILmSTPk3D .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FEjdeiQILmSTPk3D .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FEjdeiQILmSTPk3D .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FEjdeiQILmSTPk3D .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-FEjdeiQILmSTPk3D .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FEjdeiQILmSTPk3D .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-FEjdeiQILmSTPk3D .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FEjdeiQILmSTPk3D .cluster text{fill:#333;}#mermaid-svg-FEjdeiQILmSTPk3D .cluster span{color:#333;}#mermaid-svg-FEjdeiQILmSTPk3D 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-FEjdeiQILmSTPk3D .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-FEjdeiQILmSTPk3D rect.text{fill:none;stroke-width:0;}#mermaid-svg-FEjdeiQILmSTPk3D .icon-shape,#mermaid-svg-FEjdeiQILmSTPk3D .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FEjdeiQILmSTPk3D .icon-shape p,#mermaid-svg-FEjdeiQILmSTPk3D .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-FEjdeiQILmSTPk3D .icon-shape .label rect,#mermaid-svg-FEjdeiQILmSTPk3D .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FEjdeiQILmSTPk3D .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-FEjdeiQILmSTPk3D .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-FEjdeiQILmSTPk3D :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 继续执行
无剩余步骤
前提失效
用户目标与约束
生成初始计划
选取当前步骤
执行步骤
保存结果
后续安排
检查并汇总
返回阶段结果
下面用语言无关的伪代码表示,重点看计划怎样被使用:
text
计划 ← Planner(目标, 约束)
历史 ← 空记录
按依赖顺序处理计划中的步骤:
结果 ← Executor(当前步骤, 约束, 历史)
保存步骤状态、结果和依据
如果后续步骤的关键前提不成立:
返回阶段结果、未完成项和原因
核对目标与交付内容,返回汇总结果
这里的历史需要由程序保存,再提供给后续步骤。新的模型调用不会自动知道之前查到了什么。
三.当新信息推翻了原来的假设
1.工具成功返回,原仓路线却赶不上了
执行到 P2 时,查询得到以下信息:
text
【教学模拟:E1,原仓供货与配送信息】
订单:O1001,尚未出库
原发货仓:A
当前可用库存:0
预计补货时间:本周六 10:00
按该备货时间估算的到货时间:下周一 18:00
库存与配送工具正常返回了信息,但这些信息说明:按照当前供货安排,A 仓路线无法满足本周五 18:00 前到货的要求。
再看原来的 P3:
在原仓路线能满足期限的前提下,整理到货安排。
它的执行前提已经不成立了。接下来要调查的内容变成了:其他仓库有没有同款现货?能否送到原地址?到货时间和额外费用是否符合要求?
这些工作还没有包含在原计划中。
2.原计划还能交付什么
基础版本可以停止这条路线,并告诉用户:原仓缺货,按现有安排预计下周一到货,赶不上要求的时间。
这个回答有依据,但只完成了需求的一部分。用户还要求了解其他办法,而我们尚未调查其他发货方案。
初始计划建立在不完整的信息上,执行过程中得到的新信息,可能改变剩余工作本身。
如果初始计划已经安排了其他仓库评估,这里可以按条件继续执行。固定工作流可以覆盖预先设计的分支,Replan 则允许系统根据新结果补充或调整安排。
四.通过 Replan 更新剩余计划
Replan,也就是重规划,负责结合执行结果检查:目标是否已经满足,剩余计划是否仍然合适,需要继续、修改,还是结束。
加入这项职责后,就形成了 Plan → Execute → 检查结果 → Replan 的循环。本文将其称为 Plan-Execute-Replan。
Planner、Executor 和 Replanner 是逻辑职责,可以使用同一个模型配合不同输入和程序控制完成,不必对应三个独立 Agent。
1.重规划需要哪些信息
如果只把"A 仓没货"交给 Replanner,它还不知道用户是否着急、能否接受加价,也不知道哪些事情已经查过。
在当前案例中,它需要一起看到这些信息。
- **原始目标:**判断能否按时到货,并给出处理建议。
- **用户约束:**本周五 18:00 前收到,商品、数量和地址不变,额外总费用不超过 20 元。
- **已完成工作:**P1 已确认订单信息,P2 已完成原仓供货与时效核查。
- **新观察及依据:**E1 表明原仓预计周六补货,按当前安排预计下周一到货。
- **剩余计划:**P3 原本准备整理原仓方案,P4 再汇总建议。
这些信息放在一起,才能判断哪些步骤需要保留,哪些步骤需要调整。原仓赶不上,并不意味着可以自行放宽用户的到货期限或费用上限。
2.计划究竟改了什么
前面准备沿用原仓,现在查询结果已经否定了这个前提。原来的 P3 需要调整,先把其他方案的调查补上:
| 原计划 | 更新后的安排 | 修改原因 |
|---|---|---|
| P1:确认订单基础信息 | 保留完成状态和查询结果 | 商品、数量和地址仍然是后续查询的依据 |
| P2:核对原仓供货与时效 | 保留完成状态和 E1 | 已经查明原仓路线赶不上期限 |
| P3:整理原仓方案 | P3:评估其他仓库的配送方案,寻找满足到货期限和费用上限的候选 | 原仓赶不上,需要评估其他仓库的现货和配送方案 |
| P4:汇总建议 | 汇总候选方案的费用、预计到货时间及办理条件 | 最终建议要建立在新调查上 |
这里将不同配送方式放在同一个计划步骤中评估,具体查询顺序由执行器根据反馈决定;实际工具如果能一次返回完整报价,也可以直接比较。
这次调整保留了已经完成的调查,只修改受影响的剩余工作。它也没有提前断言"其他仓库一定有合适方案",P3 还是待验证的任务。
3.什么时候需要检查计划
本例由一条成功取得的新信息触发重规划,过程中并没有必须发生的工具报错。
其他需要检查的情况还包括:取得的报价违反费用约束,且当前步骤内没有可继续评估的方案;必要接口持续不可用;多次查询没有带来进展;已有结果已经足够回答用户,可以取消多余步骤。
实现时,可以每完成一个步骤就检查,也可以在关键发现出现时提前交接。
检查计划,不等于每次都必须改写计划。 如果新的情况已经被剩余步骤覆盖,继续执行就可以了。
4.怎样判断可以结束
完成判断要对照用户需求:原仓能否按时到货、有哪些符合约束的候选方案,以及办理时需要满足哪些条件,这些问题是否已经回答清楚。
计划列表为空,只表示没有剩余条目,还需要检查有没有遗漏工作。
如果查过的方案都不满足要求,可以给出"本次已核查的方案均不符合约束"的结论。如果只是关键资料没有查到,则应说明"目前还不能判断"。两种结果的依据不同。
调用次数、运行时间和无进展限制也会影响何时停止,后面结合双层循环的伪代码一起说明。
五.让推理与行动交替进行:ReAct
1.ReAct 的核心思想
ReAct 来自 Reasoning 与 Acting,核心是让推理与行动交替进行:根据目标和已有观察决定下一步,通过行动获得新信息,再让新信息影响后续决策。
ReAct 原论文中常见的表示是:
text
Thought:决定下一步
↓
Action:执行行动
↓
Observation:获得观察
↓
结合目标与新观察,继续决定下一步
这是一种组织决策与行动的方式,不要求实现必须公开模型的内部思考。下文使用人为编写的简短决策摘要来解释过程。
2.用三个连续阶段看清执行过程
先让 ReAct 直接负责完整的订单查询目标,看看下一次行动怎样受到上一条结果的影响。到第七章,再把它放进具体的计划步骤中。
2.1确认订单,发现原仓赶不上
先确认订单对应的商品和仓库,再核对当前到货安排。查询发现订单还没有明确的预计到货日期,就继续补查供货信息。
text
【教学模拟:原仓查询过程】
查询订单("O1001")
→ 1 件 SKU-01,原仓 A,尚未出库,取得原收货地址。
查询原订单预计到货("O1001")
→ 等待补货,预计到货日期暂缺。
查询补货("A", "SKU-01")
→ 当前可用库存为 0,预计本周六 10:00 补货。
估算配送("A", 原收货地址, "本周六 10:00 备货", "普通")
→ 预计下周一 18:00 到货。
这里在"到货日期暂缺"后继续查询,根据缺货信息补查备货时间,再取得配送估算,形成前面的 E1。
原仓路线赶不上期限,下一步就需要调查其他仓库。
2.2找到加急方案,发现费用超出约束
接下来查询同一商品的其他发货方案,先核对加急配送是否可行。
text
【教学模拟:E2,其他仓库与加急报价】
查询其他仓库方案("O1001")
→ B 仓同一 SKU 可用库存为 4 件。
→ 该订单支持申请改由 B 仓发货,原地址可配送。
→ 可查询加急和普通两种配送报价。
查询配送报价("O1001", "B", "加急")
→ 预计本周四 18:00 到货。
→ 额外总费用:35 元。
→ 报价要求:本周二 16:00 前确认改仓与配送安排。
这个方案的预计到货时间符合要求,但额外费用是 35 元,超过用户给出的 20 元上限。加急方案因此不能作为符合本次要求的建议。
工具已经返回 B 仓还支持普通配送,接下来就可以继续查它的时效和费用。
2.3核对普通配送,形成候选建议
我们已经取得 B 仓库存和该订单的改仓条件,可以沿用这些信息,继续核对普通配送报价。
text
【教学模拟:E3,B 仓普通配送报价】
查询配送报价("O1001", "B", "普通")
→ 预计本周五 16:00 到货。
→ 额外总费用:10 元。
→ 报价要求:本周二 16:00 前确认改仓与配送安排。
按当前报价,普通配送的预计时间早于周五 18:00,额外费用也没有超过 20 元,因此可以作为候选方案。
回头看这三个阶段:没有明确到货日期,就补查供货;原仓赶不上,就查其他仓库;加急超预算,就继续核对普通配送。后一个动作都有前一个观察作为依据。
3.ReAct 能不能规划
ReAct 也能拆解目标、形成计划,还能跟踪进展,并根据反馈调整安排。原论文就将这些列为推理的用途。
它与显式 Plan-Execute-Replan 的主要区别在于:是否把总体计划及其修订设置为独立的控制层。 典型 ReAct 循环可以直接围绕目标选择行动,不要求先创建一份单独维护的完整计划对象。
在较长任务中,如果状态管理不足,ReAct 可能重复查询已经查过的仓库,或者忽略先前的费用限制。这时就需要加强结果记录、阶段目标和进展检查。
六.Plan-Execute-Replan 与 ReAct 怎样比较
下面比较两种典型组织方式。它们的能力范围存在重叠,也可以组合使用。
| 比较维度 | Plan-Execute-Replan | ReAct |
|---|---|---|
| 主要关注 | 显式管理总体步骤、依赖和剩余工作 | 根据目标与观察选择下一步行动 |
| 怎样使用反馈 | 更新步骤记录,检查目标与剩余计划 | 直接影响接下来的行动选择 |
| 调整时机 | 步骤完成、关键发现或异常等检查点 | 获得新观察后即可调整下一步 |
| 长任务中的特点 | 便于检查进度、遗漏和路线变化 | 需要注意历史管理、重复行动和目标偏离 |
| 成本与延迟 | 增加规划和重规划调用,也可能减少无效探索 | 多轮决策会产生开销,短任务也可能很快结束 |
| 常见适用情况 | 多阶段、有依赖、需要持续检查进度的任务 | 需要根据工具结果不断决定下一步的任务 |
成本需要结合具体分工判断。如果几次查询就能完成任务,再加入规划和重规划可能增加等待;如果任务包含多个相互依赖的调查阶段,显式计划则有助于管理剩余工作。
还有一种组织方式:保留总体计划,在执行某个步骤时使用 ReAct。下面重点看它们怎样配合。
七.Plan-Execute-Replan 与 ReAct 如何协作
1.先看两层怎样连接
一种常见的组合是:外层通过 Plan-Execute-Replan 管理总体任务,内层 ReAct Executor 完成当前步骤。
LangGraph 的官方归档教程展示了这种结构:用 ReAct 执行器处理计划中的当前步骤,再将结果交给重规划逻辑。这里参考其架构,归档代码不作为当前 API 的使用指南。
看图时关注两个循环,以及内层向外层交接的位置:
#mermaid-svg-lpIEP1QrSeeLElvm{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-lpIEP1QrSeeLElvm .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-lpIEP1QrSeeLElvm .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-lpIEP1QrSeeLElvm .error-icon{fill:#552222;}#mermaid-svg-lpIEP1QrSeeLElvm .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-lpIEP1QrSeeLElvm .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-lpIEP1QrSeeLElvm .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-lpIEP1QrSeeLElvm .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-lpIEP1QrSeeLElvm .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-lpIEP1QrSeeLElvm .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-lpIEP1QrSeeLElvm .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-lpIEP1QrSeeLElvm .marker{fill:#333333;stroke:#333333;}#mermaid-svg-lpIEP1QrSeeLElvm .marker.cross{stroke:#333333;}#mermaid-svg-lpIEP1QrSeeLElvm svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-lpIEP1QrSeeLElvm p{margin:0;}#mermaid-svg-lpIEP1QrSeeLElvm .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-lpIEP1QrSeeLElvm .cluster-label text{fill:#333;}#mermaid-svg-lpIEP1QrSeeLElvm .cluster-label span{color:#333;}#mermaid-svg-lpIEP1QrSeeLElvm .cluster-label span p{background-color:transparent;}#mermaid-svg-lpIEP1QrSeeLElvm .label text,#mermaid-svg-lpIEP1QrSeeLElvm span{fill:#333;color:#333;}#mermaid-svg-lpIEP1QrSeeLElvm .node rect,#mermaid-svg-lpIEP1QrSeeLElvm .node circle,#mermaid-svg-lpIEP1QrSeeLElvm .node ellipse,#mermaid-svg-lpIEP1QrSeeLElvm .node polygon,#mermaid-svg-lpIEP1QrSeeLElvm .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-lpIEP1QrSeeLElvm .rough-node .label text,#mermaid-svg-lpIEP1QrSeeLElvm .node .label text,#mermaid-svg-lpIEP1QrSeeLElvm .image-shape .label,#mermaid-svg-lpIEP1QrSeeLElvm .icon-shape .label{text-anchor:middle;}#mermaid-svg-lpIEP1QrSeeLElvm .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-lpIEP1QrSeeLElvm .rough-node .label,#mermaid-svg-lpIEP1QrSeeLElvm .node .label,#mermaid-svg-lpIEP1QrSeeLElvm .image-shape .label,#mermaid-svg-lpIEP1QrSeeLElvm .icon-shape .label{text-align:center;}#mermaid-svg-lpIEP1QrSeeLElvm .node.clickable{cursor:pointer;}#mermaid-svg-lpIEP1QrSeeLElvm .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-lpIEP1QrSeeLElvm .arrowheadPath{fill:#333333;}#mermaid-svg-lpIEP1QrSeeLElvm .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-lpIEP1QrSeeLElvm .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-lpIEP1QrSeeLElvm .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lpIEP1QrSeeLElvm .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-lpIEP1QrSeeLElvm .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lpIEP1QrSeeLElvm .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-lpIEP1QrSeeLElvm .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-lpIEP1QrSeeLElvm .cluster text{fill:#333;}#mermaid-svg-lpIEP1QrSeeLElvm .cluster span{color:#333;}#mermaid-svg-lpIEP1QrSeeLElvm 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-lpIEP1QrSeeLElvm .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-lpIEP1QrSeeLElvm rect.text{fill:none;stroke-width:0;}#mermaid-svg-lpIEP1QrSeeLElvm .icon-shape,#mermaid-svg-lpIEP1QrSeeLElvm .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lpIEP1QrSeeLElvm .icon-shape p,#mermaid-svg-lpIEP1QrSeeLElvm .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-lpIEP1QrSeeLElvm .icon-shape .label rect,#mermaid-svg-lpIEP1QrSeeLElvm .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lpIEP1QrSeeLElvm .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-lpIEP1QrSeeLElvm .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-lpIEP1QrSeeLElvm :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 外层:Plan-Execute-Replan
内层:ReAct Executor
下发步骤输入
否
是
继续
调整
验收通过
阻塞或停止
用户目标与约束
生成初始计划
选择步骤
决定下一步
需要交接?
调用工具
记录观察
返回步骤结果
更新执行历史
检查目标与计划
修改剩余计划
交付最终结果
交付阶段结果
"需要交接"包括步骤完成、发现影响总体安排的问题、无法继续调查,或局部预算到限。内层可以调用多次工具,再向外层返回一次结果。
全局预算由运行程序统一检查,可以在任一调用前触发停止;图中只保留主要交接路径,具体控制放在后面的伪代码中。
2.把订单案例连续执行一遍
2.1外层选择 P2,并下发步骤输入
P1 已经完成,外层选择 P2。此时发送给执行器的内容可以是:
text
【教学模拟:P2 的步骤输入】
步骤目标:核对 A 仓供货与配送安排,判断原路线是否符合到货期限。
已有上下文:
订单 O1001,1 件 SKU-01,原发货仓为 A,原收货地址已取得。
用户约束:
本周五 18:00 前收到;商品、数量和地址不变;
额外总费用不超过 20 元;本次只查询并给建议。
局部完成条件:
取得原仓供货与预计到货依据,给出明确判断。
关键资料缺失时,返回尚未完成及缺失原因。
工作范围:
本步骤核查原仓,替代路线交给外层安排。
这里的完成条件是"查明原路线是否符合期限"。得出"不符合"的结论,也因此可以完成这个调查步骤。
2.2内层补查信息,并返回结果包
执行器先查询原订单预计到货时间,发现日期暂缺,于是补查备货情况,再估算配送时效:
text
【教学模拟:P2 内部的行动与观察】
查询原订单预计到货 → 等待补货,暂缺到货日期
查询 A 仓补货安排 → 预计本周六 10:00 补货
根据备货时间估算配送 → 预计下周一 18:00 到货
这个过程中,执行器调整了调查路径,但始终围绕 P2 的目标工作。取得 E1 后,它向外层返回:
text
【教学模拟:P2 的返回结果】
步骤状态:已完成
结论:按当前供货与时效信息,原仓路线不能满足到货期限。
依据:
E1,A 仓补货查询与配送估算。
预计本周六 10:00 补货,按该备货时间预计下周一 18:00 到货。
对剩余计划的影响:
P3 所依赖的"原仓能够按时到货"前提不成立。
还缺少的工作:
其他仓库及配送方式的调查。
实际系统还应保留查询时间、关键参数和来源,便于复核库存、报价等会变化的信息。
注意,此时这三个状态同时存在。
- **工具调用成功:**取得了供货和配送信息。
- **当前步骤完成:**已经回答原仓能否满足期限。
- **原路线受阻:**沿用原仓无法满足本次要求,需要评估其他办法。
如果只返回一个"失败",外层就难以分清是接口没有查通,还是已经查明路线不合适。前一种情况可能需要补查,后一种情况需要调整剩余工作。
2.3Replanner 调整剩余计划
Replanner 结合目标和约束,参考 E1 与当前计划,保留 P1、P2 的完成记录,将原来的 P3 替换为第四章中的候选方案调查:
text
【教学模拟:更新后的剩余计划】
P3:评估其他仓库的配送方案,寻找满足到货期限和费用上限的候选。
P4:汇总候选方案、费用、预计到货时间及办理条件。
这里增加了原计划没有覆盖的工作,原始到货期限和费用限制继续生效。
2.4ReAct 执行新步骤,外层继续检查
外层下发新的 P3。ReAct Executor 查询其他仓库和加急报价,取得 E2:B 仓有同款现货,但加急需要额外支付 35 元,超出上限。
评估还没有结束。执行器根据 20 元的费用上限排除加急方案,继续核对普通配送,这些行动都在 P3 内部完成。
查询得到 E3:预计周五 16:00 到货,额外费用 10 元,报价要求周二 16:00 前确认安排。执行器将普通配送作为候选,连同 E2、E3 的依据一起返回外层。
Replanner 检查候选与剩余计划,确认可以继续执行 P4,整理给用户的建议。这一次需要检查,但没有必要再生成一份全新计划。
2.5完成整体交付检查
P4 可以形成下面这样的结果:
text
【教学模拟:最终建议】
原仓安排:
A 仓预计本周六补货,按当前安排预计下周一到货,赶不上要求。
依据:E1。
加急方案:
B 仓加急预计周四到货,但额外总费用为 35 元,超过费用上限。
依据:E2。
候选建议:
申请改由 B 仓使用普通配送,商品、数量和地址不变。
按当前报价,预计周五 16:00 到货,额外总费用为 10 元。
依据:E2 中的改仓条件,以及 E3 的普通配送报价。
办理条件:
需在本周二 16:00 前确认改仓和配送安排,办理时复核库存与报价。
到货时间为预计值。
当前进度:
已形成查询建议,尚未修改订单或锁定库存。
外层检查的重点是:原仓判断有没有依据,候选方案是否符合商品、地址、时间和费用要求,办理条件与不确定性有没有交代清楚。
这些内容齐全,才满足本次"查询并给建议"的目标。仅有 P4 的"已完成"标记还不够。
3.局部调整与总体重规划的边界
刚才的过程可以这样区分。
- 到货日期暂缺,转而查询补货时间:仍在回答 P2 的问题,由内层 ReAct 继续调查。
- 查明原仓赶不上,新增其他仓库与配送方式的评估:改变了剩余工作,由外层 Replanner 调整。
- 加急超预算,继续核对普通配送:仍在完成 P3 的候选评估,由内层 ReAct 处理。
这个边界取决于当前步骤被赋予的目标和范围。本例把配送方案评估放在同一个 P3 中,因此加急与普通配送的切换都由内层处理。
第五章的 ReAct 直接负责完整查询目标,可以自行推进整条调查;本章将它放入明确的计划步骤,交接范围就随之收窄。
4.用伪代码看两层怎样衔接
先看内层。与第二章相比,这里把 Executor 展开成一个循环,能够继续调查,也能够向外层交接:
text
过程 ReAct执行(步骤, 上下文, 约束, 共享预算):
局部记录 ← 必要上下文
循环:
如果满足局部完成条件:
返回已完成的结果包
如果发现需要外层处理的问题:
返回步骤进展、依据和对总体计划的影响
如果全局停止条件触发,或局部调查无法继续:
返回阶段结果、缺失信息和停止原因
行动 ← 根据步骤目标、约束与局部记录决定下一步
结果 ← 执行行动并记录结果
更新局部记录、证据、预算和进展状态
行动可以是工具调用,也可以是整理结论。模型提出结束时,仍要按局部完成条件检查证据是否足够;交接结果还要区分步骤是否完成。
再看外层。结果先进入历史,Replanner 才能据此安排下一步:
text
共享预算 ← 初始化一次任务的全局预算
计划 ← Planner(目标, 约束)
历史 ← 空记录
循环:
如果全局停止条件触发:
返回阶段成果、未完成项和原因
步骤 ← 从计划中选取当前可执行步骤
如果步骤存在:
结果 ← ReAct执行(步骤, 必要上下文, 约束, 共享预算)
更新历史中的步骤状态、结果和依据
如果全局停止条件触发:
返回阶段成果、未完成项和原因
决策 ← Replanner(目标, 约束, 计划, 历史)
如果决策为完成:
如果整体交付检查通过:
返回最终结果
否则:
将缺项补入剩余计划,继续循环
如果决策为阻塞:
返回已有成果和缺失条件
根据决策保留或更新剩余计划
这些伪代码用于说明控制结构,省略了工具参数校验、异常处理等实现细节。每次实际模型或工具调用前,都需要由运行程序检查预算和超时;达到全局上限时直接走停止出口,不能继续调用 Replanner。
可以为教学实现设置一组限制:单个步骤最多调用工具 6 次,整个任务最多调用工具 20 次、模型 30 次,总耗时不超过 5 分钟。连续两次外层检查都没有新增证据、排除候选或推进步骤时,也停止并报告进展不足。
这些数字只是配置示例。模型调用次数应覆盖 Planner、ReAct 内部决策、Replanner,以及额外的模型检查;进入新步骤时,局部额度可以重新分配,全局预算继续累计。
5.什么时候值得使用这种组合
这种组合适合有多个总体阶段的任务,阶段内部又需要根据反馈继续探索,例如复杂订单异常处理和跨系统故障调查,也包括多来源资料分析。
它也会增加模型调用、状态维护和交接成本。步骤划分过细,外层会频繁介入;步骤过大,外层又难以掌握实际进展。
对于规则固定的订单查询,如果一次接口就能返回完整结果,直接使用业务工作流会更简单。需要持续管理剩余任务,同时又无法预先确定每一次工具行动时,外层规划与内层 ReAct 的分工才更有价值。