Skip to content

ReAct 模式

ReAct 由 Yao et al. (2022) 提出,论文标题:“ReAct: Synergizing Reasoning and Acting in Language Models”

核心洞见:将推理(Reasoning)和行动(Acting)交织在一起,比单独做推理或单独做行动效果更好

flowchart LR
    subgraph CoT["只有推理(CoT)"]
        T1["思考"] --> T2["思考"] --> T3["思考"] --> Ans1["答案"]
    end
    subgraph ActionOnly["只有行动"]
        A1["行动"] --> A2["行动"] --> A3["行动"] --> Ans2["答案"]
    end
    subgraph ReAct["ReAct(推理+行动)"]
        Th1["思考"] --> Act1["行动"] --> Ob1["观察"] --> Th2["思考"] --> Act2["行动"] --> Ob2["观察"] --> Ans3["答案"]
    end

类比:ReAct 就像一个侦探破案的过程——先分析线索(思考),然后去现场调查(行动),根据发现调整推理方向(观察),再决定下一步调查什么。

ReAct 的核心是一个 Thought → Action → Observation 循环:

flowchart TD
    Thought["Thought\n(推理)"] --> Action["Action\n(行动)"]
    Action --> Observation["Observation\n(观察结果)"]
    Observation -->|"未完成"| Thought
    Observation -->|"任务完成 / 达到最大步数"| Final["最终答案"]
  • Thought:模型用自然语言推理当前状态,决定下一步做什么。这个过程对外部不可见(内部独白)。
  • Action:模型选择一个工具并调用它(如搜索、计算器、数据库查询)。
  • Observation:工具返回的结果,作为新的输入供模型继续推理。
用户问题: "比较 Tesla 和 BYD 2024 年的电动车销量"
Thought 1: 我需要分别查询 Tesla 和 BYD 的 2024 年销量数据。
先查 Tesla。
Action 1: search("Tesla 2024 annual EV sales")
Observation 1: Tesla 在 2024 年共交付约 178.9 万辆电动车。
Thought 2: 好的,Tesla 数据有了。现在查 BYD。
Action 2: search("BYD 2024 annual EV sales")
Observation 2: BYD 2024 年新能源汽车销量约 427 万辆,
其中纯电动约 176 万辆。
Thought 3: 现在我有了两组数据。需要注意 BYD 有纯电和
混动之分。我可以做比较了。
Action 3: finish(answer="Tesla 2024 年交付约 178.9 万辆
纯电动车;BYD 纯电动约 176 万辆,但如果算上混动
则总计约 427 万辆。纯电动领域两者接近,但 BYD
在整体新能源市场大幅领先。")
import json
from dataclasses import dataclass
@dataclass
class Tool:
name: str
description: str
function: callable
def react_agent(
query: str,
tools: list[Tool],
llm,
max_steps: int = 10,
) -> str:
"""一个简单的 ReAct Agent 实现"""
# 构建工具描述
tool_descriptions = "\n".join(
f"- {t.name}: {t.description}" for t in tools
)
tool_map = {t.name: t.function for t in tools}
system_prompt = f"""你是一个 ReAct Agent。面对用户问题,你需要交替进行思考和行动。
可用工具:
{tool_descriptions}
请严格按以下格式输出(每次只输出一轮):
Thought: <你的推理过程>
Action: <工具名称>
Action Input: <工具参数,JSON 格式>
当你有足够信息回答时:
Thought: <最终推理>
Final Answer: <最终答案>
"""
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": query},
]
for step in range(max_steps):
response = llm.chat(messages)
messages.append({"role": "assistant", "content": response})
# 检查是否有最终答案
if "Final Answer:" in response:
return response.split("Final Answer:")[-1].strip()
# 解析 Action
if "Action:" in response and "Action Input:" in response:
action = response.split("Action:")[-1].split("\n")[0].strip()
action_input = response.split("Action Input:")[-1].strip()
# 执行工具
if action in tool_map:
try:
args = json.loads(action_input)
observation = tool_map[action](**args)
except Exception as e:
observation = f"错误: {e}"
else:
observation = f"未知工具: {action}"
# 将观察结果加入消息
messages.append({
"role": "user",
"content": f"Observation: {observation}",
})
return "达到最大步数,未能完成任务。"
# 使用示例
def search(query: str) -> str:
"""模拟搜索工具"""
# 实际中调用搜索 API
return f"搜索 '{query}' 的结果: ..."
def calculator(expression: str) -> str:
"""计算数学表达式"""
return str(eval(expression))
tools = [
Tool("search", "搜索互联网信息", search),
Tool("calculator", "计算数学表达式", calculator),
]
# result = react_agent("...", tools, llm)

当任务需要 10+ 步推理时,模型可能”迷失方向”或陷入循环。

Step 1-3: 推理清晰
Step 4-6: 开始重复之前的思考
Step 7+: 可能偏离主题或循环

每一步的错误会传递到后续步骤。一个工具调用失败或返回错误信息,可能导致整个推理链崩溃。

标准 ReAct 是线性的——一旦走错路,无法回退到之前的状态重新尝试。这催生了 Reflection 和 Planning 模式(后续章节)。

模型必须严格遵循 Thought/Action/Observation 格式。格式错误会导致解析失败。现代框架(如 LangChain、Claude Tool Use)通过结构化工具调用部分解决了这个问题。

局限改进方案详见章节
无回溯Reflection 模式第 2 章第 3 节
无全局规划Planning 模式第 2 章第 4 节
单点故障Multi-Agent第 2 章第 5 节
自测题 1:ReAct 相比纯 CoT 的核心优势是什么?
ReAct 引入了 Action 阶段,使模型能调用外部工具获取实时信息,而非仅依赖训练数据进行推理。这解决了 CoT 容易产生幻觉的问题——模型可以通过搜索、计算等工具验证自己的推理。
自测题 2:ReAct 循环中 Thought 阶段的作用是什么?能否跳过?
Thought 阶段让模型在行动前先进行推理和规划,决定下一步应该做什么、为什么要做。如果跳过 Thought 直接行动(Action-only),模型会缺乏规划能力,可能盲目调用工具或遗漏重要步骤。论文实验表明,去掉 Thought 会显著降低效果。
自测题 3:现代 Agent 框架如何改进了原始 ReAct 的格式依赖问题?
现代框架使用结构化的工具调用协议(如 OpenAI Function Calling、Claude Tool Use),模型直接输出 JSON 格式的工具调用请求,而非在自然语言中嵌入特定格式。这消除了格式解析错误,使工具调用更可靠。