从 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 推理模式的核心世界。我们将:

  1. 回顾历史:从最基础的推理模式讲起,梳理其演变脉络。
  2. 深度拆解:详细剖析 ReActPlan-and-Solve 这两个主流范式的核心原理、算法流程和数学模型。
  3. 硬核对比:从多个维度(概念对比、效率对比)进行分析。
  4. 实战演练:我们将用 Python 代码,基于 LangChain 框架,分别实现这两种 Agent,并在相同的测试环境下跑分对比。
  5. 最佳实践:最后,我们会总结在什么场景下选择什么样的推理模式。

读完本文,你将不仅掌握这两种技术的理论知识,还能亲手写出代码进行实验,成为 LLM Agent 应用开发的进阶玩家。


二、 基础知识/背景铺垫 (Foundational Concepts)

在我们深入 ReAct 和 Plan-and-Solve 之前,让我们先搭建一个知识地基,了解一下 LLM Agent 推理模式的基石。

什么是 LLM Agent?

核心概念:
LLM Agent 可以被定义为一个由大语言模型驱动的自主实体,它能够感知环境、进行推理、制定决策并执行动作以实现特定目标。

在经典的 Agent 循环通常包含以下几个核心要素:

  1. 感知 (Perception): 接收用户输入或环境反馈。
  2. 推理 (Reasoning): 核心大脑,处理信息,决定下一步。
  3. 行动 (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 的循环通常包含以下三个核心步骤,无限循环直到停止:

  1. Thought (思考/推理): 基于当前的状态,我现在心里想什么?
  2. Action (行动): 基于上面的思考,我决定采取什么具体行动?(例如:Search[查询词])
  3. Observation (观察): 执行行动后,外界给我反馈了什么结果?

这就像一个经典的 **OODA 循环(Observe-Orient-Decide-Act)在 LLM 世界的微观体现。

**概念之间的关系:

为了让你更直观地理解,我画了一张 ER 图和一张交互流程图。

**Mermaid ER 图 (实体关系图):

提出任务

生成

决定执行

调用

返回结果

作为上下文

USER

REACT_AGENT

THOUGHT

ACTION

TOOL

OBSERVATION

**Mermaid 交互关系图 (ReAct 循环图):

Tool/Environment ReAct Agent (LLM) User Tool/Environment ReAct Agent (LLM) User alt [任务完成?] loop [ReAct Loop] 初始任务/Query 1. Thought (我需要先做什么? 2. Action (执行动作) 3. Observation (观察结果) Final Answer
3.1.3 ReAct 的数学模型与 Prompt 设计

虽然 ReAct 很直观,但它本质上是一个 Auto-regressive(自回归) 的生成过程。

我们可以用数学语言来描述它。

假设我们有一个任务输入 xxx,我们的目标是得到最终答案 yyy

在标准的 Fine-tuning 或 Prompting 中,我们建模的是 P(y∣x)P(y|x)P(yx)

在 Chain-of-Thought 中,我们建模的是 P(y,r∣x)P(y, r|x)P(y,rx),其中 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(τ,yx)=P(yτ,x)t=1nP(otat,τ<t,x)P(attht,τ<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 的逻辑抽成了标准的算法流程。

渲染错误: Mermaid 渲染失败: Parse error on line 2: ...题 Q, 历史记录 History = []] Init --> Loo -----------------------^ Expecting 'SQE', 'DOUBLECIRCLEEND', 'PE', '-)', 'STADIUMEND', 'SUBROUTINEEND', 'PIPE', 'CYLINDEREND', 'DIAMOND_STOP', 'TAGEND', 'TRAPEND', 'INVTRAPEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'SQS'
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 要解决的问题):

  1. **缺乏全局观 (Myopia):ReAct 在第 3 步的时候,ReAct 很容易在第 5 步的时候忘记第 1 步的目标是什么,俗称"迷路"。
  2. **计算效率低:因为缺乏计划,可能会做很多无用功(Redundant Actions)。比如为了解决一个复杂数学题,ReAct 可能会试错很多次,而 PS 先列好提纲,一次性搞定。

Plan-and-Solve 的核心思想:
将复杂任务解耦为两个明确的阶段:

  1. **计划阶段 (Planning Phase):先不忙着动手,先制定一个清晰的、多步骤的计划。
  2. **解决阶段 (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 架构图:

渲染错误: Mermaid 渲染失败: Parse error on line 2: ... Planner[📅 Planner (LLM)] Planner - -----------------------^ Expecting 'SQE', 'DOUBLECIRCLEEND', 'PE', '-)', 'STADIUMEND', 'SUBROUTINEEND', 'PIPE', 'CYLINDEREND', 'DIAMOND_STOP', 'TAGEND', 'TRAPEND', 'INVTRAPEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'PS'
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(yx)=rP(y,rx)

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,rx)=P(yr,p,x)P(rp,x)P(px)

推理时,我们通常先通过 Scaling(或者说 Greedy decoding)得到最可能的计划 p∗p^*p
p∗=arg⁡max⁡pP(p∣x) p^* = \arg\max_p P(p | x) p=argpmaxP(px)

然后,固定 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 项目介绍与环境安装

**项目目标:
构建一个“数学应用

  • 任务: 解决一个包含“数据检索”和“逻辑推理”的复合问题。
  • **评测维度:
    1. 成功率 (Success Rate):答案对不对?
    2. 步骤数 (Steps Taken):用了多少步才搞定?
    3. 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 输出):

    1. Thought: 我需要先搜索 2023年巴黎马拉松冠军是谁。
    2. Action: Search[2023 Paris Marathon winner]
    3. Observation: [搜索结果:Eliud Kipchoge… 时间是 2:01:09…]
    4. Thought: 好的,冠军是 Kipchoge,时间是 2小时01分09秒。现在我需要把这个时间换算成秒。
    5. Action: calculate[(23600) + (160) + 9]
    6. Observation: 7269
    7. Thought: 现在需要开平方乘以2…
  • Plan-and-Solve 的典型思考过程:

    • Step 1 (Planner:
      • Plan:
        1. 搜索 2023年巴黎马拉松的冠军姓名。
        2. 提取冠军的完成时间(小时:分钟:秒)。
        3. 将时间转换为总秒数。
        4. 计算该数字的平方根并乘以 2。
        5. 给出最终答案。
    • **Step 2 (Executor):
      • 执行 Task 1… 执行 Task 2… 执行 Task 3…

**关键差异观察:

  1. 心理安全感: Plan-and-Solve 的输出让人类看着更安心,因为你一上来就知道它"它要做这5件事就能搞定。
  2. Token 效率: 在复杂多步骤任务中,如果 Plan-and-Solve 因为中间的 LLM 不需要反复回顾冗长的历史(因为它知道自己在 Plan 的第几步),通常在 **思考部分的 Token 消耗稍微优化,或者持平,但在极其复杂的任务(比如写代码大纲)会更省。
  3. 纠错: 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)。

  1. **Model Routing (模型路由):

    • Planner: 尽可能用 GPT-4o mini (Haiku 来做 Planner。因为定计划需要强的能力。
    • Executor/Worker: 对于执行具体的小事(比如根据已经定好的大纲写一段代码,或者搜索一个简单事实),可以用 GPT-3.5-turboLlama 3
  2. **History Compression (历史压缩):

    • ReAct 的 History 会越来越长。每次都把所有 Observation 塞进去很贵。
    • 技巧: 使用 LangChain 的 ConversationSummaryMemory,或者让 LLM 每 3 步总结一次之前的内容,只保留精华。
  3. **结构化输出 (Structured Outputs):

    • 尽量用 OpenAI 的 response_format={ "type": "json_object" }
    • 这比让 LLM 自由生成 “Action: Search[xxx]” 更可靠,减少因为格式解析失败导致的重试(Retry)费用。

4.3 最佳实践总结 (The Golden Rules)

基于我的实战经验,给你一套决策流程图(Decision Tree):

  1. **你的任务是否是 单一明确 的?
    • **Yes -> **直接用 LLM / CoT。
  2. **你的任务是否需要 多步工具调用,且 下一步高度依赖上一步的结果(比如:探索性数据分析,你不知道数据里有什么)?
    • **Yes -> **强烈推荐 ReAct。灵活应变最重要。
  3. **你的任务是否有 明确的最终状态,且可以被拆解为 子任务(比如:写一份 10 页的报告,做一个复杂的数据流水线)?
    • **Yes -> **强烈推荐 Plan-and-Solve。先给大纲,再填充血肉。
  4. 终极方案 (The Ultimate Solution):
    • Agents 正在融合。现在最先进的范式是:“Plan -> ReAct (Act) -> Reflect -> RePlan”。**
    • 也就是:先计划,再用 ReAct 方式执行,执行完了自我反思一下,如果走偏了就重新计划,继续干。

五、 结论 (Conclusion)

核心要点回顾 (The Summary)

在这篇万字长文中,我们深入探讨了 LLM Agent 的两大核心推理模式:

  1. ReAct:
    • 它是 Thought, Action, Observation 的交织循环。
    • 它像一个敏捷的探险家,灵活,适合动态环境,但有时会迷路。
  2. 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)

好了,理论讲了这么多,是时候动手了!

  1. 去代码里试一试: 把上面的 simple_react.py 跑通,试着加一个新工具(比如查询天气)。
  2. 思考你的业务场景: 如果你正在做 RAG 应用或者 AI 助手,你的用户现在的交互模式是什么?如果引入 Plan-and-Solve 会不会让体验更好?
  3. 关注 LangGraph: 这是 LangChain 推出的下一代 Agent 编排框架,它允许你非常灵活地定义 “Plan”, “Act”, “Reflect” 节点,是目前最火热的方向。

欢迎在评论区留下你的想法:你在使用 ReAct 或 Plan-and-Solve 时遇到过什么有趣的坑?


(全文完)

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐