跳转至

Plan execute cn

Plan-Execute 节点工厂 (create_plan_execute_node) 是 Jianmu 提供的第二种预设复合节点模式,它将复杂的任务处理分解为 规划 (Plan) → 执行 (Execute) → 审查 (Review) 三个顺序阶段,并在审查不通过时自动回环重试。相较于 ReAct 模式中将推理与工具调用交织在一起的单一循环,Plan-Execute 显式地将"思考如何做"与"动手去做"分离到独立的 LLM 调用中,适用于需要结构化分解的复杂任务场景。

架构全景:三阶段回环与条件退出

Plan-Execute 的核心结构是一个 LoopUntilSuccess 装饰器包裹的 Sequence 组合节点。外层循环控制整体重试轮次 (max_rounds),内层序列按固定顺序执行规划、执行、审查(可选)、退出条件检查四个步骤。当退出条件未满足时,LoopUntilSuccess 将序列重置为 INVALID 状态并触发新一轮 tick,形成完整的回环机制。

flowchart TD
    LUS["LoopUntilSuccess<br/>(max_iterations = max_rounds)"]
    SEQ["Sequence (memory=True)<br/>PlanExecute/Loop"]
    PLANNER["AgentLLMNode<br/>PlanExecute/Planner<br/>无工具 · 仅规划"]
    EXECUTOR["AgentLLMNode<br/>PlanExecute/Executor<br/>有工具 · 产生最终答案"]
    TOOL_EXEC["ToolExecutor<br/>PlanExecute/ToolExecutor<br/>(仅在有工具时添加)"]
    REVIEWER["EvaluationNode<br/>PlanExecute/Reviewer<br/>(仅在 enable_review 时)"]
    EXIT{"退出条件"}
    EXIT_SIMPLE["StateCondition: IsDone"]
    EXIT_FULL["Sequence: IsDone ∧ Score ≥ threshold"]

    LUS --> SEQ
    SEQ --> PLANNER
    PLANNER --> EXECUTOR
    EXECUTOR --> TOOL_EXEC
    TOOL_EXEC --> REVIEWER
    REVIEWER --> EXIT
    EXIT --> EXIT_SIMPLE
    EXIT --> EXIT_FULL
    EXIT -- "FAILURE → 回环" --> LUS
    EXIT -- "SUCCESS → 完成" --> DONE["Status.SUCCESS"]

树中每个节点的添加顺序和条件是精确控制的:planner 和 executor 始终存在;当 tools 非空时,在 executor 之后插入 ToolExecutor;当 config.enable_review 为 True 时,在工具执行器之后追加 EvaluationNode,并使用复合退出条件 IsDone AND ScoreOK,否则仅使用 IsDone 单一条件。

状态键隔离:规划输出与最终答案的分治

Plan-Execute 最精妙的设计在于规划器与执行器使用不同的 StateKeys 配置,将规划产物与用户可见的最终答案存储到不同的状态字段中。这是通过 PlanExecuteConfig 中的 plan_key 和 keys.final_answer 两个参数实现的。

节点 final_answer 写入目标 实际写入的状态字段 用途
Planner config.plan_key "plan" (默认) 内部规划步骤,用户不可见
Executor keys.final_answer "final_answer" (默认) 用户可见的最终回复
Reviewer config.score_key "score" (默认) 审查评分,用于退出条件判定

这种设计保证了规划器生成的中间计划不会被当作最终答案暴露给用户。在创建 planner_keys 时,只有 final_answer 被替换为 plan_key,其余字段 (messages、actions、done) 与执行器共享同一组键,确保两个阶段在同一个对话历史和终止信号上协同工作。

上下文构建器:三个阶段的三套 Prompt 装配

每个阶段拥有独立的 ContextBuilder,注入阶段专属的系统提示词。规划器仅注入静态规划提示词和对话历史,没有工具描述——这确保了规划阶段是纯粹的推理,不受工具可用性的干扰。执行器则额外注入了 ToolsDescProvider,使 LLM 知晓可用工具。

flowchart LR
    subgraph PlannerContext["Planner ContextBuilder"]
        SPP_P["StaticPromptProvider<br/>(plan_prompt)"]
        SHP_P["StateHistoryProvider<br/>(消息历史)"]
    end

    subgraph ExecutorContext["Executor ContextBuilder"]
        SPP_E["StaticPromptProvider<br/>(execute_prompt)"]
        TDP["ToolsDescProvider<br/>(工具描述)"]
        SHP_E["StateHistoryProvider<br/>(消息历史)"]
    end

    subgraph ReviewerEval["Reviewer (EvaluationNode)"]
        EVAL_CONFIG["AgentLLMConfig<br/>(system_prompt = review_prompt)"]
    end

默认的三段提示词定义在 jianmu/config/prompts.py 中,分别将模型角色塑造为"规划者"、"执行者"和"严格审查者"。规划提示词明确禁止执行工具("Do NOT execute tools; just plan"),执行提示词则鼓励使用工具,审查提示词要求输出 0-10 的评分和简短反思。

审查回路:EvaluationNode 的评分与门控

当 enable_review=True 时,审查器 (EvaluationNode) 通过结构化工具调用 (evaluate_result) 强制模型输出 JSON 格式的 {score, reflection}。这个评分随后被写入 score_key 字段,并与 review_threshold(默认 8.0)比较,决定是否允许退出循环。

审查器使用独立的模型配置(默认 models.evaluate),与规划器和执行器可以选用不同的模型。其工作原理是构造一个包含输入键(默认为 messages 和 answer)的 prompt,调用 LLM 并强制其调用 evaluate_result 函数,从中提取评分和反思。

退出条件的构建逻辑清晰地体现在代码中:

  • 无审查模式: 单一 StateCondition,检查 keys.done 是否为 truthy
  • 有审查模式: Sequence 组合两个条件——IsDone AND Score >= review_threshold,两者必须同时满足

配置模型:PlanExecuteConfig 参数全览

PlanExecuteConfig 继承自 Pydantic BaseModel,所有参数均有合理的默认值,多数从全局配置中继承。

参数 类型 默认值来源 说明
model str config.models.plan_execute 规划器和执行器共用模型
temperature float config.llm.temperature (0.7) 采样温度
max_tokens int \| None config.llm.max_tokens 最大输出 token 数
top_p float \| None config.llm.top_p (0.95) 核采样参数
top_k int \| None config.llm.top_k (40) Top-K 采样参数
timeout float config.llm.timeout (60.0) 每次 LLM 调用超时(秒)
max_budget_tokens int \| None config.llm.max_budget_tokens 全局 token 预算上限
stream bool False 执行器是否启用流式输出
max_rounds int config.limits.max_rounds (10) 最大规划-执行-审查轮次
enable_review bool True 是否启用审查阶段
review_threshold float 8.0 审查通过的最低评分
plan_prompt str \| None None (使用内置默认) 规划阶段自定义提示词
execute_prompt str \| None None (使用内置默认) 执行阶段自定义提示词
review_prompt str \| None None (使用内置默认) 审查阶段自定义提示词
plan_key str "plan" 规划输出写入的状态字段名
score_key str "score" 审查评分写入的状态字段名
keys StateKeys StateKeys() 共享状态键名配置

与 ReAct 模式的对比

Plan-Execute 与 ReAct 共享 LoopUntilSuccess 外层结构和 Sequence 内层骨架,但在阶段分离、状态隔离和退出策略上存在根本差异。

维度 ReAct Plan-Execute
阶段数量 单阶段(LLM + 工具交替) 2-3 阶段(规划 → 执行 → 审查)
规划与执行 交织在同一 LLM 调用中 显式分离为独立 LLM 节点
规划器工具访问 N/A 无工具——纯推理
状态隔离 所有节点共享同一 StateKeys 规划器 final_answer 重定向到 plan_key
退出条件 done truthy + 可选 token 预算中止 done truthy + 可选评分阈值
Token 预算控制 abort_condition 实时检查 无内置 token 预算中止
上下文构建 单一 ContextBuilder 规划器和执行器各有一个独立 ContextBuilder
适用场景 工具驱动型任务(搜索、计算、代码执行) 需要结构分解的复杂推理任务

使用示例与工具集成

在最简配置下,创建一个无工具的纯推理 Plan-Execute Agent:

from jianmu.node.presets import create_plan_execute_node
from jianmu import Agent

root = create_plan_execute_node(
    name="MyPlanner",
    config=PlanExecuteConfig(enable_review=False, max_rounds=3),
)
agent = Agent(root)
await agent.run(input_data={"messages": [Message(role="user", content="解释量子纠缠")]})

当传入 tools 参数时,ToolExecutor 节点会自动插入到执行器之后,使执行器能够调用工具执行具体操作。工具描述通过 ToolsDescProvider 注入到执行器的上下文中。

内部实现细节:LoopUntilSuccess 的重试机制

Plan-Execute 依赖 LoopUntilSuccess 装饰器实现多轮重试。当内层 Sequence 返回 FAILURE(意味着子节点失败或退出条件不满足),LoopUntilSuccess.update() 将已装饰子节点停止为 INVALID 状态,并通过 state_manager.signal() 触发新一轮调度,使 Sequence 从第一个子节点重新开始执行。每次重试时 iteration_count 递增,达到 max_iterations(即 max_rounds)后记录终止元数据并永久失败。

阅读路径建议

Plan-Execute 节点工厂建立在多个核心模块之上。建议按以下顺序深入阅读:

  1. ReAct 节点工厂 — 理解共享的 LoopUntilSuccess 循环机制和预设模式的设计范式
  2. LLM 节点 — 深入了解 Planner 和 Executor 底层使用的 AgentLLMNode 上下文构建与模型调用流程
  3. 工具与技能节点 — 理解 Executor 关联的 ToolExecutor 如何调度工具执行
  4. 上下文构建器 — 掌握 ContextBuilder 的 provider-filter 架构,理解三个阶段如何独立装配 prompt
  5. 项目配置体系 — 了解如何通过 jianmu.yaml 覆盖默认的 Plan-Execute 提示词和模型选择