Agent规划能力的工程化对比:ReAct、Plan-and-Execute与Tree-of-Thought的适用边界
一、Agent"想不清楚"的根本原因:规划能力的三种失效模式
Agent产品的核心价值在于自主完成多步骤任务。但生产环境中,Agent最常见的失败不是工具调用错误,而是规划本身的逻辑链断裂。三种典型失效模式:第一,目标分解偏差——将"分析Q2营收下降原因"分解为"拉取营收数据"+"生成图表",跳过了竞品对比和异常归因等关键步骤。第二,执行路径刚性——一旦选择了错误的工具调用序列,后续步骤会将偏差持续放大。第三,状态空间爆炸——面对开放域任务,Agent无法评估不同行动序列的优劣,陷入随机游走。
这些问题的本质是:Agent缺少对"如何规划和修正计划"的元认知能力。解决方向分为三种范式——ReAct的边做边想、Plan-and-Execute的先想后做、Tree-of-Thought的分支探索。
二、三种规划范式的信息流模型:推理与行动的耦合方式
flowchart TB subgraph React["ReAct模式:推理与行动交替"] R1["Thought: 分析当前状态"] --> R2["Action: 执行工具调用"] R2 --> R3["Observation: 获取结果"] R3 --> R4{"目标达成?"} R4 -->|否| R1 R4 -->|是| R5["Final Answer"] end subgraph PE["Plan-and-Execute模式:先规划再执行"] P1["Plan: 生成完整步骤列表"] --> P2["Step1: 执行→验证"] P2 --> P3["Step2: 执行→验证"] P3 --> P4{"偏离预期?"} P4 -->|是| P5["Replan: 修正剩余步骤"] P5 --> P3 P4 -->|否| P6["Step N → Final Answer"] end subgraph ToT["Tree-of-Thought模式:多路径探索评估"] T1["生成候选行动A/B/C"] --> T2["并行评估各路径"] T2 --> T3["剪枝: 淘汰低分路径"] T3 --> T4["扩展最优路径"] T4 --> T5{"到达叶节点?"} T5 -->|否| T1 T5 -->|是| T6["回溯选择最优完整路径"] end style React fill:#e3f2fd,stroke:#1565c0 style PE fill:#e8f5e9,stroke:#2e7d32 style ToT fill:#fff3e0,stroke:#e65100ReAct 的推理-行动循环最低延迟:每一步的思考仅基于上一步的观察,不涉及全局规划。这种设计的优势是"认知开销小"——LLM不需要在单次推理中处理整个任务,每一步只需处理当期上下文。代价是"局部最优陷阱"——缺少全局视图时,早期步骤的选择偏差会累积为结果的系统性错误。
Plan-and-Execute 将任务分解和步骤执行解耦。规划阶段生成完整的步骤列表,执行阶段逐一验证。关键创新在于"Replan 机制"——当某一步执行结果偏离预期时,系统重规划剩余步骤而非从头开始。这种设计避免了 ReAct 的瞎子摸象问题,但增加了规划阶段的一次性推理成本。
Tree-of-Thought 将思考过程建模为树搜索。每个节点是一个"部分解决方案",系统并行生成多个候选后续步骤,通过评估函数对路径打分,保留高分路径继续扩展。ToT 理论上可以找到全局最优解,但其 Token 消耗是 ReAct 的 3-5 倍。
三、ReAct 和 Plan-and-Execute 的对比实现:Token效率与任务完成率的工程取舍
""" 两种规划范式的核心实现对比 重点展示Replan机制和评估函数的工程优化 """ from dataclasses import dataclass, field from typing import Callable import asyncio @dataclass class AgentState: task: str history: list[dict] = field(default_factory=list) plan: list[str] = field(default_factory=list) current_step: int = 0 class ReactAgent: """ReAct模式:每步基于观察决定下一步行动""" def __init__(self, llm_call: Callable, max_steps: int = 10): self.llm = llm_call self.max_steps = max_steps async def run(self, task: str) -> str: state = AgentState(task=task) for step_idx in range(self.max_steps): # 构造ReAct格式prompt:Thought → Action → Observation循环 prompt = self._build_react_prompt(state) try: response = await asyncio.wait_for( self.llm(prompt), timeout=30.0 ) except asyncio.TimeoutError: # 单步推理超时不中止——记录后允许重试 state.history.append({ "step": step_idx, "status": "timeout", "action": "retry_with_truncated_context" }) continue thought, action = self._parse_react_output(response) if not action: # 无法解析有效Action时中断循环 state.history.append({ "step": step_idx, "status": "parse_failure", "raw": response[:200] }) break observation = await self._execute_action(action) state.history.append({ "step": step_idx, "thought": thought, "action": action, "observation": observation }) if "FINAL:" in observation: return observation.replace("FINAL:", "").strip() # 达最大步数仍未完成——返回当前最有信息量的观察 return self._format_partial_result(state) def _build_react_prompt(self, state: AgentState) -> str: """每步重建完整历史——ReAct的主要Token成本来源""" base = f"Question: {state.task}\n" for h in state.history: base += ( f"Thought: {h.get('thought','')}\n" f"Action: {h.get('action','')}\n" f"Observation: {h.get('observation','')}\n" ) return base + "Thought:" async def _execute_action(self, action: str) -> str: # Action执行层:实际项目中此处对接工具调用框架 return f"EXECUTED: {action}" class PlanExecuteAgent: """Plan-and-Execute:先规划,执行中动态修正""" def __init__(self, llm_call: Callable, evaluation_fn: Callable): self.llm = llm_call self.evaluate = evaluation_fn # 步骤执行结果评估函数 async def run(self, task: str) -> str: state = AgentState(task=task) # 阶段1:一次性生成完整计划 plan_prompt = ( f"将以下任务分解为可执行的步骤列表,每步一个具体的工具调用:\n" f"任务:{task}\n" f"输出格式:STEP1: xxx\nSTEP2: xxx\n..." ) plan_response = await self.llm(plan_prompt) state.plan = self._parse_plan(plan_response) if len(state.plan) == 0: return "规划阶段失败:未能生成有效步骤" # 阶段2:逐步骤执行,触发条件重规划 while state.current_step < len(state.plan): step = state.plan[state.current_step] observation = await self._execute_step(step) state.history.append({ "step": state.current_step, "plan": step, "result": observation }) # 关键机制:评估当前步骤与预期的偏差 eval_result = self.evaluate( expected_outcome=step, actual_outcome=observation ) if eval_result["deviation"] == "significant": # 偏差显著——重规划剩余步骤 replan_prompt = ( f"原始任务:{state.task}\n" f"已完成步骤:{state.history}\n" f"当前异常:{observation}\n" "请为剩余任务重新生成步骤列表:\n" ) new_plan_response = await self.llm(replan_prompt) remaining = self._parse_plan(new_plan_response) # 保留已完成步骤,替换待执行步骤 state.plan = state.plan[:state.current_step+1] + remaining state.current_step += 1 elif eval_result["deviation"] == "minor": # 轻微偏差——继续但调整下一步预期 state.current_step += 1 else: # 符合预期——正常前进 state.current_step += 1 return self._synthesize_final_answer(state) def _parse_plan(self, response: str) -> list[str]: """从LLM响应中提取步骤列表——容错解析""" steps = [] for line in response.split("\n"): line = line.strip() if line.upper().startswith("STEP") and ":" in line: step_content = line.split(":", 1)[1].strip() if step_content: steps.append(step_content) return steps两种模式的核心差异体现在 Token 效率上:ReAct 每步都重建完整历史上下文,随着对话步数增长,Prompt Token 呈线性增长。Plan-and-Execute 的规划阶段一次性消耗较大 Token 量,但执行阶段每步仅需传递步骤指令和上一步结果,总体 Token 消耗可降低约40%。
Replan 机制是 Plan-and-Execute 的质量保障。评估函数的精度直接影响 replan 的触发时机——过于敏感会导致频繁重规划浪费资源,过于迟钝则丧失了纠正偏差的能力。
四、三种范式的适用边界:在Token成本与任务质量之间取舍
ReAct 适合简单到中等复杂度的任务(2-5步),以及需要实时信息检索的场景。它在 Token 成本上有优势,但难以处理需要全局规划的多步骤推理。
Plan-and-Execute 适合步骤可预先定义的确定性任务(如数据报表生成、多步骤代码重构),以及总步数在 5-15 步之间的任务。它在质量-成本比上最优,但规划质量严重依赖 LLM 对任务的先验理解。
Tree-of-Thought 目前仅适合探索性推理任务(如数学证明、复杂逻辑推理),在生产 Agent 中面临两个工程障碍:Token 消耗是 ReAct 的3-5倍,并行评估多条路径需要并发 LLM 调用能力,受限于 API 速率限制。
五、总结
Agent 规划能力的选择不存在万能解。工程上的最优策略是混合模式:对常规任务使用 Plan-and-Execute 降低 Token 成本,对不确定性高的子步骤动态切换为 ReAct 模式,仅在关键决策节点引入 ToT 的多路径评估。
具体落地时,应先实现 Plan-and-Execute 作为主路径,投入精力打磨评估函数和 Replan 触发条件的阈值调优。ReAct 作为降级方案,当前序偏差超出可修复范围时回退。ToT 作为实验性优化,在成本监控框架内逐步引入,评估投入产出比后再决定是否生产化。