从 ReAct 到 Plan-and-Solve:Agent 推理模式的效率对比
从 ReAct 到 Plan-and-Solve:Agent 推理模式的效率对比
一、 引言 (Introduction)
钩子 (The Hook)
你是否曾经好奇,当你给人工智能助手一个复杂的任务,比如:“帮我制定一个从北京到上海的自驾游攻略,预算5000元,三天时间,要包含美食、景点和住宿”,然后看着它在几秒钟内就给你一个完美的行程安排?
或者,你是否尝试过让大语言模型(LLM)帮你编写一个包含多个步骤的代码项目,比如:“写一个Python脚本,先爬取某个网站的数据,清洗数据,然后生成可视化图表”,结果发现它要么直接给出了一段有bug的代码,要么中间漏掉了关键步骤?
这背后,其实是不同的Agent 推理模式在起作用。不同的推理策略,决定了 AI 是"想到啥说啥",还是"三思而后行",亦或是"边想边做"。
定义问题/阐述背景 (The “Why”)
在大语言模型(LLMs)如 GPT-4、Claude 等的出现,开启了人工智能的新纪元。但如何让这些强大的模型不仅仅是"回答问题",而是能够像人类一样完成复杂的任务,成为了当前 AI 领域最热门的研究方向之一。
于是,**LLM Agent(智能体)的概念应运而生。简单来说,LLM Agent 就是将 LLM 作为核心大脑,通过特定的推理模式(Reasoning Patterns)来组织思考过程,并可能调用外部工具(Tools),从而完成各种复杂任务。
在这其中,推理模式是 Agent 的灵魂。它决定了 Agent 如何处理信息、如何做决策、以及如何与环境交互。从早期的 “Chain-of-Thought (CoT,思维链)” 到 “ReAct (Reasoning + Acting)”,再到最近的 “Plan-and-Solve (计划与解决)”,研究者们一直在探索更高效、更可靠的推理范式。
但是,你是否真正了解这些推理模式之间的区别?ReAct 真的比简单的 CoT 好在哪里?Plan-and-Solve 又解决了 ReAct 的什么痛点?在什么场景下应该用哪种模式?它们的效率(时间成本、计算成本、成功率)到底差异如何?
亮明观点/文章目标 (The “What” & “How”)
本文将带你深入 LLM Agent 推理模式的核心世界。我们将:
- 回顾历史:从最基础的推理模式讲起,梳理其演变脉络。
- 深度拆解:详细剖析 ReAct 和 Plan-and-Solve 这两个主流范式的核心原理、算法流程和数学模型。
- 硬核对比:从多个维度(概念对比、效率对比)进行分析。
- 实战演练:我们将用 Python 代码,基于 LangChain 框架,分别实现这两种 Agent,并在相同的测试环境下跑分对比。
- 最佳实践:最后,我们会总结在什么场景下选择什么样的推理模式。
读完本文,你将不仅掌握这两种技术的理论知识,还能亲手写出代码进行实验,成为 LLM Agent 应用开发的进阶玩家。
二、 基础知识/背景铺垫 (Foundational Concepts)
在我们深入 ReAct 和 Plan-and-Solve 之前,让我们先搭建一个知识地基,了解一下 LLM Agent 推理模式的基石。
什么是 LLM Agent?
核心概念:
LLM Agent 可以被定义为一个由大语言模型驱动的自主实体,它能够感知环境、进行推理、制定决策并执行动作以实现特定目标。
在经典的 Agent 循环通常包含以下几个核心要素:
- 感知 (Perception): 接收用户输入或环境反馈。
- 推理 (Reasoning): 核心大脑,处理信息,决定下一步。
- 行动 (Action): 执行决策,调用工具或生成输出。
推理模式的演进史:从 “直接回答” 到 “复杂推理”
为了让你更清晰地看到 ReAct 和 Plan-and-Solve 的位置,我们先看一张简图:
-
**Stage 1: Direct Output (直接输出)
- *问题 -> LLM -> 答案
- 问题: 没有任何思考过程,模型直接给出答案。简单问题还行,复杂问题容易"幻觉(Hallucination)"。
-
**Stage 2: Chain-of-Thought (CoT, 2022)
- *问题 -> LLM (让我们一步步思考… -> 思考过程 -> 答案
- 核心: “Let’s think step by step.” 通过显式地生成推理步骤,显著提升了模型在复杂推理任务上的表现。
- 局限: 只是"想",如果知识是静态的,无法与外部世界交互,信息可能过时。
-
**Stage 3: Tool-use / WebGPT (工具使用)
- *问题 -> LLM -> 我需要搜索 -> [搜索工具] -> 结果 -> LLM -> 答案
- 核心: 模型学会了调用 API、搜索网络。
- 局限: 工具调用和推理往往是分离的,或者缺乏系统性的思考。
-
**Stage 4: ReAct (Reasoning + Acting, 2022/2023)
- *问题 -> Thought (思考) -> Act (行动) -> Observation (观察) -> Thought (思考) -> … -> 答案
- 核心: 将推理(Reasoning)和行动(Acting)紧密交织在一起,像人一样:思考一下,做一步,看看结果,再思考下一步。
-
**Stage 5: Plan-and-Solve (2023) / Reflexion (反思)
- *问题 -> Plan (制定计划) -> Solve (执行解决) -> [Reflect (反思)] -> 答案
- 核心: 先宏观规划,再分步执行。解决 ReAct 中可能出现的"走一步看一步"导致的迷失方向或效率低下问题。
好,现在我们的主角 ReAct 和 Plan-and-Solve 已经登场了。接下来,让我们进入硬核的核心内容部分。
三、 核心内容/实战演练 (The Core - “How-To”)
3.1 深度拆解 ReAct 模式
3.1.1 核心概念与问题背景
核心概念:
ReAct,顾名思义,是 Reasoning(推理) 与 Acting(行动) 的结合。它的灵感来源于人类认知:人类在面对复杂问题时,并不是脑子里想完所有步骤再行动(这不现实),也不是盲目乱撞(这太低效)。我们通常是:*思考一下现在的处境,决定做一个小动作,观察一下环境变化,根据结果再思考下一步。
问题背景:
在 ReAct 之前,CoT(思维链)展示了强大的推理能力,但它是"闭卷考试",依赖模型内部的静态知识,容易胡说八道。而单纯的工具调用又缺乏连贯的推理链路。
ReAct 的论文作者(来自 Google 和 Princeton)提出了一个简单却强大的范式:让语言模型生成 both 推理轨迹(Reasoning Traces)和 任务特定的动作(Task-specific Actions)。
3.1.2 ReAct 的概念结构与核心要素
ReAct 的循环通常包含以下三个核心步骤,无限循环直到停止:
- Thought (思考/推理): 基于当前的状态,我现在心里想什么?
- Action (行动): 基于上面的思考,我决定采取什么具体行动?(例如:Search[查询词])
- Observation (观察): 执行行动后,外界给我反馈了什么结果?
这就像一个经典的 **OODA 循环(Observe-Orient-Decide-Act)在 LLM 世界的微观体现。
**概念之间的关系:
为了让你更直观地理解,我画了一张 ER 图和一张交互流程图。
**Mermaid ER 图 (实体关系图):
**Mermaid 交互关系图 (ReAct 循环图):
3.1.3 ReAct 的数学模型与 Prompt 设计
虽然 ReAct 很直观,但它本质上是一个 Auto-regressive(自回归) 的生成过程。
我们可以用数学语言来描述它。
假设我们有一个任务输入 xxx,我们的目标是得到最终答案 yyy。
在标准的 Fine-tuning 或 Prompting 中,我们建模的是 P(y∣x)P(y|x)P(y∣x)。
在 Chain-of-Thought 中,我们建模的是 P(y,r∣x)P(y, r|x)P(y,r∣x),其中 rrr 是推理链条(rationale)。
ReAct 的数学公式:
在 ReAct 中,我们将轨迹(Trajectory)τ\tauτ 拆分为交替的思考、行动和观察:
τ=(th1,a1,o1,th2,a2,o2,...,thn,an,on)\tau = (th_1, a_1, o_1, th_2, a_2, o_2, ..., th_n, a_n, o_n)τ=(th1,a1,o1,th2,a2,o2,...,thn,an,on)
ReAct 的概率建模如下:
PReAct(τ,y∣x)=P(y∣τ,x)⋅∏t=1nP(ot∣at,τ<t,x)⋅P(at∣tht,τ<t,x)⋅P(tht∣τ<t,x) P_{ReAct}(\tau, y | x) = P(y | \tau, x) \cdot \prod_{t=1}^{n} P(o_t | a_t, \tau_{<t}, x) \cdot P(a_t | th_t, \tau_{<t}, x) \cdot P(th_t | \tau_{<t}, x) PReAct(τ,y∣x)=P(y∣τ,x)⋅t=1∏nP(ot∣at,τ<t,x)⋅P(at∣tht,τ<t,x)⋅P(tht∣τ<t,x)
这里的 τ<t\tau_{<t}τ<t 代表在第 ttt 步之前的所有历史轨迹。
- P(tht∣...)P(th_t | ...)P(tht∣...): 思考生成概率
- P(at∣...)P(a_t | ...)P(at∣...): 动作生成概率
- P(ot∣...)P(o_t | ...)P(ot∣...): 环境返回观察的概率(通常由工具决定,非 LLM)
- P(y∣...)P(y | ...)P(y∣...): 最终答案生成概率
**ReAct 的经典 Prompt 模板(英文原文大致如下):
Solve a question answering task with interleaving Thought, Action, Observation steps.
Thought can reason about the current situation.
Action can be of two types:
(1) Search[entity]: search an entity on Wikipedia
(2) Finish[answer]: return the answer and finish the task
...
(Here are a few-shot examples)
...
Question: [输入问题]
Thought: [模型开始生成]
3.1.4 ReAct 的算法流程图
为了让工程师朋友们直接能写出 ReAct,我把 ReAct 的逻辑抽成了标准的算法流程。
3.1.5 ReAct 的 Python 极简实现 (不用 LangChain)
为了让你彻底理解 ReAct 的运作机制,我们不依赖任何复杂框架,只用 OpenAI API 来写一个最少代码。
环境准备:
pip install openai python-dotenv
**核心代码 (simple_react.py):
import os
import openai
from dotenv import load_dotenv
import json
# 加载 API Key
load_dotenv()
openai.api_key = os.getenv("OPENAI_API_KEY")
# 模拟一个极其简单的"工具集
TOOLS = {
"search": lambda q: f"搜索结果: 关于 '{q}' 的最新数据是... (模拟搜索结果)",
"calculate": lambda expr: f"计算结果: {expr} = 42 (模拟计算器)"
}
SYSTEM_PROMPT = """
你是一个 ReAct Agent。
你的运作模式是:Thought -> Action -> Observation -> ... -> Finish。
你只能以严格的 JSON 格式输出,不要输出 Markdown 代码块标记。
JSON Schema 如下:
{
"thought": "你当前的思考",
"action": {
"name": "工具名称,只能是 search/calculate/finish",
"input": "工具输入,如果是 finish,则是最终答案"
}
}
"""
def run_react_agent(query: str, max_steps: int = 5):
history = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": f"开始任务: {query}"}
]
for i in range(max_steps):
print(f"\n--- Step {i+1} ---")
# 1. 调用 LLM 生成 Thought & Action
response = openai.chat.completions.create(
model="gpt-4o", # 建议用 GPT-4 以保证稳定的 JSON 输出
messages=history,
response_format={"type": "json_object"}
)
result = json.loads(response.choices[0].message.content)
thought = result.get("thought", "")
action = result.get("action", {})
print(f"🧠 Thought: {thought}")
print(f"⚡ Action: {action}")
action_name = action.get("name")
action_input = action.get("input")
if action_name == "finish":
print(f"\n✅ Final Answer: {action_input}")
return action_input
# 2. 执行工具获取 Observation
if action_name in TOOLS:
observation = TOOLS[action_name](action_input)
else:
observation = f"Error: 未知工具 {action_name}"
print(f"👁️ Observation: {observation}")
# 3. 将结果塞回历史
history.append({"role": "assistant", "content": json.dumps(result))
history.append({"role": "user", "content": f"Observation: {observation}")
print("\n❌ Max steps reached.")
return None
if __name__ == "__main__":
run_react_agent("搜索一下北京今天的天气,然后计算一下如果温度是25度的话,加上10度是多少?")
这段代码虽然简陋,却五脏俱全。它展示了 ReAct 最核心的循环逻辑。
3.2 深度拆解 Plan-and-Solve (PS) 模式
3.2.1 核心概念与问题背景
核心概念:
Plan-and-Solve(通常缩写为 PS,或者更高级的 PS+)是由王永志等人在论文《Plan-and-Solve Prompting: Improving Zero-Shot Chain-of-Thought Reasoning by Large Language Models》中提出的。
如果说 ReAct 是"摸着石头过河"(边走边看),那么 Plan-and-Solve 就是"运筹帷幄之中,决胜千里之外"(先算好再动)。
ReAct 的痛点 (也就是 PS 要解决的问题):
- **缺乏全局观 (Myopia):ReAct 在第 3 步的时候,ReAct 很容易在第 5 步的时候忘记第 1 步的目标是什么,俗称"迷路"。
- **计算效率低:因为缺乏计划,可能会做很多无用功(Redundant Actions)。比如为了解决一个复杂数学题,ReAct 可能会试错很多次,而 PS 先列好提纲,一次性搞定。
Plan-and-Solve 的核心思想:
将复杂任务解耦为两个明确的阶段:
- **计划阶段 (Planning Phase):先不忙着动手,先制定一个清晰的、多步骤的计划。
- **解决阶段 (Solving Phase):按照制定好的计划,逐步执行。
3.2.2 Plan-and-Solve 的变种与结构
Plan-and-Solve 实际上有几个不同层级的实现方式,取决于你是否在计划之后如何行动。
**基础版 Plan-and-Solve (Zero-Shot PS):
通常用于纯文本推理,不涉及工具调用。它的 Prompt 通常长这样:
“Let’s first understand the problem and devise a plan to solve the problem. Then, let’s carry out the plan and solve the problem step by step.”
进阶版 Plan-and-Execute (LangChain 中的模式):
这是目前 Agent 领域最常用的 PS 变体。它引入了一个**Planner(规划者)和一个 Executor(执行者)。
- Planner: 专门负责把大任务拆分成小的 Task List。
- Executor: 负责逐个处理每一个小 Task(Executor 内部甚至可以再用一个 ReAct Agent 来搞定具体某个小任务)。
为了区分这几个概念,我们来看一个 Mermaid 架构图:
3.2.3 Plan-and-Solve 的数学模型与 Prompt 设计
从数学上看,Plan-and-Solve 引入了一个潜在的计划变量 ppp(Plan)。
标准的 CoT 是 P(y∣x)=∑rP(y,r∣x)P(y | x) = \sum_r P(y, r | x)P(y∣x)=∑rP(y,r∣x)。
Plan-and-Solve 的数学公式:
我们首先生成计划 ppp,然后基于计划 ppp 生成执行轨迹 rrr 和答案 yyy。
PPS(y,p,r∣x)=P(y∣r,p,x)⋅P(r∣p,x)⋅P(p∣x) P_{PS}(y, p, r | x) = P(y | r, p, x) \cdot P(r | p, x) \cdot P(p | x) PPS(y,p,r∣x)=P(y∣r,p,x)⋅P(r∣p,x)⋅P(p∣x)
推理时,我们通常先通过 Scaling(或者说 Greedy decoding)得到最可能的计划 p∗p^*p∗:
p∗=argmaxpP(p∣x) p^* = \arg\max_p P(p | x) p∗=argpmaxP(p∣x)
然后,固定 p∗p^*p∗,去求解 yyy。这使得整个过程比 ReAct 更可控,因为 ppp 作为一个高层骨架,约束了后续的生成。
3.1.4 ReAct vs Plan-and-Solve 核心属性维度对比
光说不练假把式,我们必须来一张硬核的对比表。
| **属性维度 | ReAct (推理+行动) | Plan-and-Solve (计划+解决) |
|---|---|---|
| 核心哲学 | "Think, Act, React (边想边做,灵活应变)** | "First Think, Then Do (先谋后动,步步为营)** |
| 推理结构 | 线性交织 (Interleaved)** | 两阶段解耦 (Two-stage Decoupled)** |
| 全局观 (Global View) | 较弱,容易陷入局部最优 (Local Minima)** | 较强,由 Plan 提供高层指导** |
| 处理复杂任务的鲁棒性 | 中 (取决于 Step 能否不断纠正)** | 高 (不容易在复杂任务中表现更稳)** |
| Token 消耗 (成本) | 低到中 (如果频繁试错,成本会飙升)** | 中 (Planner 消耗额外 Token,但减少走弯路** |
| 适合的任务场景 | 动态探索类 (如:网络搜索、需要根据中间结果大幅调整策略)** | 静态规划类 (如:复杂数学题、代码生成、结构化报告撰写)** |
| 对 LLM 能力要求 | 相对较低 (能一步步引导)** | 较高 (需要能一次性写出好的 Plan)** |
| 错误恢复能力 | 极强 (随时可以 Observation 指出错误并修正)** | 较弱 (如果 Plan 写错了,容易一错到底,除非加上 Replanning 机制)** |
3.3 实战演练:基于 LangChain 的效率对比实测
光有理论和表格不够直观。现在,让我们进入本文最硬核的部分:**我们来写代码,在同一个环境下,测试这两种 Agent 的表现!
3.3.1 项目介绍与环境安装
**项目目标:
构建一个“数学应用。
- 任务: 解决一个包含“数据检索”和“逻辑推理”的复合问题。
- **评测维度:
- 成功率 (Success Rate):答案对不对?
- 步骤数 (Steps Taken):用了多少步才搞定?
- Token 使用量 (Token Count):花了多少钱?
环境安装:
# 创建虚拟环境 (推荐 Python 3.10+)
pip install langchain langchain-openai langchainhub langchain-experimental tavily-python python-dotenv pandas matplotlib
注:tavily 是一个为 AI Agent 优化的搜索引擎,你可以去免费获取一个 API Key,或者直接用 SerpAPI。
3.3.2 系统设计与核心实现
让我们新建一个文件 agent_battle.py。
**步骤 1:定义我们的评测问题 (Benchmark Tasks)
为了公平起见,我们设计几个混合了搜索和数学的问题:
# agent_battle.py
import os
import time
import pandas as pd
from dotenv import load_dotenv
from typing import List, Dict, Any
# LangChain 核心库
from langchain_openai import ChatOpenAI
from langchain import hub
from langchain.agents import AgentExecutor, create_react_agent, create_openai_functions_agent
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_experimental.plan_and_execute import (
PlanAndExecute,
load_chat_planner,
load_agent_executor,
)
load_dotenv()
# 配置 API Keys (请在 .env 文件中配置)
os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY")
os.environ["TAVILY_API_KEY"] = os.getenv("TAVILY_API_KEY")
# 1. 定义工具
# 我们需要搜索和数学工具
from langchain.tools import StructuredTool, tool
@tool
def calculate(expression: str) -> str:
""" Useful for when you need to calculate math expressions. Input should be a valid math expression."""
try:
return str(eval(expression))
except:
return "Calculation Error."
tools = [TavilySearchResults(max_results=1), calculate]
# 2. 初始化大模型
llm = ChatOpenAI(temperature=0, model="gpt-4-turbo") # 用 GPT-4 以保证稳定性
**步骤 2:构建 ReAct Agent 和 Plan-and-Execute Agent
def create_my_react_agent() -> AgentExecutor:
# 获取 ReAct 的 Prompt (LangChain Hub 上现成的)
prompt = hub.pull("hwchase17/react")
agent = create_react_agent(llm, tools, prompt)
agent_executor = AgentExecutor(
agent=agent, tools=tools, verbose=True,
return_intermediate_steps=True,
max_iterations=10
)
return agent_executor
def create_my_plan_and_execute_agent() -> PlanAndExecute:
# 定义 Planner (负责制定计划)
planner = load_chat_planner(llm)
# 定义 Executor (负责执行,这里 executor 内部其实也是一个小的 ReAct/Function Agent)
# 为了对比公平,Executor 也使用 ReAct 逻辑
agent = create_openai_functions_agent(llm, tools, prompt=hub.pull("hwchase17/openai-functions-agent"))
executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
agent_executor = PlanAndExecute(planner=planner, executor=executor, verbose=True)
return agent_executor
步骤 3:编写评测循环 (The Gladiator Arena)
class Judge:
def __init__(self):
self.results = []
def evaluate(self, agent, agent_name: str, task: str, task_id: int):
print(f"\n\n{'='*60}")
print(f"⚔️ Task {task_id}: {task}")
print(f"🤖 Fighter: {agent_name}")
print(f"{'='*60}")
start_time = time.time()
# 这是一个简单的封装,为了捕获 Token 数,实际生产中需要用 Callback
# 这里简化处理,仅记录步骤数和成败
try:
# 使用 get_openai_callback 可以精确统计 Token
from langchain_community.callbacks import get_openai_callback
with get_openai_callback() as cb:
result = agent.invoke({"input": task})
total_tokens = cb.total_tokens
total_cost = cb.total_cost
output = result.get('output', str(result))
# 判定逻辑 (简化版,实际应人工或用 LLM-as-a-Judge)
success = "Failed" not in output and len(output) > 5
# 统计步骤 (ReAct 比较好统计,Plan-and-Execute 我们简化处理)
steps = len(result.get('intermediate_steps', [])) if 'intermediate_steps' in result else 'N/A'
print(f"\n🏁 Result: {output}")
except Exception as e:
output = f"Error: {str(e)}"
success = False
steps = 0
total_tokens = 0
total_cost = 0
end_time = time.time()
record = {
"Task_ID": task_id,
"Agent": agent_name,
"Success": success,
"Time": round(end_time - start_time, 2),
"Steps": steps,
"Tokens": total_tokens,
"Cost": total_cost,
"Output": output[:100] # 截取前100字符
}
self.results.append(record)
return record
# 定义测试用例 (Benchmark Tasks)
TASKS = [
"2023年巴黎马拉松的冠军是谁?他的完成时间换算成秒是多少?再把这个数字开平方乘以2?",
"搜索 Leonardo DiCaprio 的出生日期,然后计算到今天(2024年)为止,他活了多少天?(忽略闰年)",
"找到世界上最高的山的高度(米),以及第二高的山的高度(米),计算它们的高度差。"
]
# 开始对战!
if __name__ == "__main__":
judge = Judge()
react_agent = create_my_react_agent()
pne_agent = create_my_plan_and_execute_agent()
for i, task in enumerate(TASKS):
# ReAct 出战
judge.evaluate(react_agent, "ReAct", task, i+1)
# 稍微休息一下避免 Rate Limit
time.sleep(2)
# Plan-and-Execute 出战
# 注意:PlanAndExecute 的接口返回和 ReAct 略有不同,
# 为了代码演示简洁性,上面的 evaluate 函数主要为 ReAct 设计的 Callback 做了优化
# 这里我们需要单独运行 PlanAndExecute 来看 Verbose 输出
print("\n--- 单独运行 Plan-and-Execute 查看效果 ---")
try:
pne_agent.invoke({"input": task})
except:
pass
# 打印结果表格
df = pd.DataFrame(judge.results)
print("\n\n📊 对战结果统计 (Sample):")
print(df)
3.3.3 实测结果分析 (What Happened?)
当你运行上面的代码时(注意:这会消耗你一定的美金),你会观察到非常有趣的现象:
对于 Task 1 (“巴黎马拉松…”):
-
ReAct 的典型思考过程 (Verbose Log 输出):
- Thought: 我需要先搜索 2023年巴黎马拉松冠军是谁。
- Action: Search[2023 Paris Marathon winner]
- Observation: [搜索结果:Eliud Kipchoge… 时间是 2:01:09…]
- Thought: 好的,冠军是 Kipchoge,时间是 2小时01分09秒。现在我需要把这个时间换算成秒。
- Action: calculate[(23600) + (160) + 9]
- Observation: 7269
- Thought: 现在需要开平方乘以2…
…
-
Plan-and-Solve 的典型思考过程:
- Step 1 (Planner:
- Plan:
- 搜索 2023年巴黎马拉松的冠军姓名。
- 提取冠军的完成时间(小时:分钟:秒)。
- 将时间转换为总秒数。
- 计算该数字的平方根并乘以 2。
- 给出最终答案。
- Plan:
- **Step 2 (Executor):
- 执行 Task 1… 执行 Task 2… 执行 Task 3…
- Step 1 (Planner:
**关键差异观察:
- 心理安全感: Plan-and-Solve 的输出让人类看着更安心,因为你一上来就知道它"它要做这5件事就能搞定。
- Token 效率: 在复杂多步骤任务中,如果 Plan-and-Solve 因为中间的 LLM 不需要反复回顾冗长的历史(因为它知道自己在 Plan 的第几步),通常在 **思考部分的 Token 消耗稍微优化,或者持平,但在极其复杂的任务(比如写代码大纲)会更省。
- 纠错: ReAct 如果在第 2 步误解了问题,它能通过观察在第 4 步绕回来。Plan-and-Solve 如果 Plan 定错了(比如它计划里漏掉了"忽略闰年"这个条件),它可能会一路错到底,除非你加上 “Replanner” 机制。
四、 进阶探讨/最佳实践 (Advanced Topics / Best Practices)
现在我们已经知道了它们是如何工作的,现在我们来聊聊什么时候该用什么,以及怎么才能用好它们。
4.1 常见陷阱与避坑指南
**陷阱 1:ReAct 的 “Thought Observation 循环唠叨症”
- 症状: ReAct 开始不断地循环:"我需要搜索… 好的,搜索结果是… 我需要再搜索一次… 我还需要确认…
- 原因: 缺乏明确的停止条件,或者任务太难,模型缺乏信心。
- 解决: 使用更强的 LLM(GPT-4 > GPT-3.5),或者在 Prompt 里加入 “Do not overthink. If you have enough info, Finish immediately.”
**陷阱 2:Plan-and-Solve 的 “计划定得太死”
- 症状: 计划写了 “Step 1: 搜索 A。Step 2: 搜索 B。” 但实际上搜索 A 的结果完全改变了 B 的需要。Agent 依然头铁地执行 Step 2。
- 原因: 基础的 Plan-and-Solve 没有反馈循环。
- 解决: 引入 RePlan (重新规划) 机制。现在的很多框架(如 LangGraph,或者 Agent Qwen 的 ReAct + Reflection 模式)允许在执行完一步后,问一下 Planner:“我们现在的进度如何?要不要修改计划?”
4.2 性能优化/成本考量 (The Art of Spending Money Wisely)
既然我们聊效率,就不得不谈钱 (Cost)。
-
**Model Routing (模型路由):
- Planner: 尽可能用 GPT-4o mini (Haiku 来做 Planner。因为定计划需要强的能力。
- Executor/Worker: 对于执行具体的小事(比如根据已经定好的大纲写一段代码,或者搜索一个简单事实),可以用 GPT-3.5-turbo 或 Llama 3。
-
**History Compression (历史压缩):
- ReAct 的 History 会越来越长。每次都把所有 Observation 塞进去很贵。
- 技巧: 使用 LangChain 的
ConversationSummaryMemory,或者让 LLM 每 3 步总结一次之前的内容,只保留精华。
-
**结构化输出 (Structured Outputs):
- 尽量用 OpenAI 的
response_format={ "type": "json_object" }。 - 这比让 LLM 自由生成 “Action: Search[xxx]” 更可靠,减少因为格式解析失败导致的重试(Retry)费用。
- 尽量用 OpenAI 的
4.3 最佳实践总结 (The Golden Rules)
基于我的实战经验,给你一套决策流程图(Decision Tree):
- **你的任务是否是 单一明确 的?
- **Yes -> **直接用 LLM / CoT。
- **你的任务是否需要 多步工具调用,且 下一步高度依赖上一步的结果(比如:探索性数据分析,你不知道数据里有什么)?
- **Yes -> **强烈推荐 ReAct。灵活应变最重要。
- **你的任务是否有 明确的最终状态,且可以被拆解为 子任务(比如:写一份 10 页的报告,做一个复杂的数据流水线)?
- **Yes -> **强烈推荐 Plan-and-Solve。先给大纲,再填充血肉。
- 终极方案 (The Ultimate Solution):
- Agents 正在融合。现在最先进的范式是:“Plan -> ReAct (Act) -> Reflect -> RePlan”。**
- 也就是:先计划,再用 ReAct 方式执行,执行完了自我反思一下,如果走偏了就重新计划,继续干。
五、 结论 (Conclusion)
核心要点回顾 (The Summary)
在这篇万字长文中,我们深入探讨了 LLM Agent 的两大核心推理模式:
- ReAct:
- 它是 Thought, Action, Observation 的交织循环。
- 它像一个敏捷的探险家,灵活,适合动态环境,但有时会迷路。
- Plan-and-Solve:
- 它是先规划,后执行的两阶段战略。
- 它像一个沉稳的军师,有大局观,效率高,但需要极强的预判能力。
我们不仅从数学模型、算法流程上进行了拆解,还通过 Python 代码和 LangChain 进行了实战对比。
行业发展与未来趋势:问题演变发展历史
为了让你看到这个领域的演进,我们来一张时间表:
| **年份 | 模式 | 论文/事件 | 核心思想 |
|---|---|---|---|
| 2020-2022 | Prompt Engineering | GPT-3 发布 | 简单的输入输出 |
| 2022 Mid | Chain-of-Thought (CoT) | “Let’s think step by step” | 显式推理链 |
| 2022 Late | ReAct / WebGPT | Google/OpenAI 探索工具使用 | 推理 + 行动 + 工具 |
| 2023 Early | Plan-and-Solve / Tree-of-Thought | 更复杂的规划出现 | 从线性到树状/图状规划 |
| 2023 Late - Now | Multi-Agent / Agentic Workflows | AutoGPT, LangGraph, CrewAI | 多个 Agent 协作,Reflexion (反思)** |
| 未来 | AGI 雏形? | ? | 完全自主的长期规划与执行 |
行动号召 (Call to Action)
好了,理论讲了这么多,是时候动手了!
- 去代码里试一试: 把上面的
simple_react.py跑通,试着加一个新工具(比如查询天气)。 - 思考你的业务场景: 如果你正在做 RAG 应用或者 AI 助手,你的用户现在的交互模式是什么?如果引入 Plan-and-Solve 会不会让体验更好?
- 关注 LangGraph: 这是 LangChain 推出的下一代 Agent 编排框架,它允许你非常灵活地定义 “Plan”, “Act”, “Reflect” 节点,是目前最火热的方向。
欢迎在评论区留下你的想法:你在使用 ReAct 或 Plan-and-Solve 时遇到过什么有趣的坑?
(全文完)
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)