ReAct模式:推理与行动的协同框架
ReAct(Reasoning + Acting)是一种将推理与行动相结合的 Prompt 设计模式。它让 LLM 交替进行「思考 → 行动 → 观察」的循环,在复杂任务中显著优于纯推理(CoT)或纯行动(Act-only)的方法。
一、从思考到行动
1.1 为什么需要结合?
纯推理(CoT):
思考 → 思考 → 思考 → 答案
(缺少外部信息,可能产生幻觉)
纯行动(Act-only):
行动 → 观察 → 行动 → 观察 → 答案
(缺少推理,行动可能没有方向)
ReAct(结合):
思考 → 行动 → 观察 → 思考 → 行动 → 观察 → 思考 → 答案
↑ 规划 ↑ 执行 ↑ 反馈 ↑ 调整 ↑ 执行 ↑ 验证 ↑ 结论
1.2 三种模式对比
| 维度 | CoT(纯推理) | Act-only(纯行动) | ReAct(结合) |
|---|
| 推理深度 | 高 | 无 | 高 |
| 信息获取 | 依赖内部知识 | 外部检索 | 内外结合 |
| 幻觉风险 | 高 | 低 | 低 |
| 可追溯性 | 推理过程可见 | 行动可追踪 | 两者兼有 |
| 适用场景 | 纯推理题 | 信息检索 | 复杂任务 |
二、核心原理
2.1 ReAct 循环
"""
ReAct 的标准循环:
Thought: 当前状态分析,规划下一步
Action: 执行具体操作(搜索、计算、API调用等)
Observation: 获取行动结果,更新状态
重复以上步骤直到得出最终答案
"""
2.2 基础实现
import json
from typing import List, Dict, Optional
class ReActAgent:
"""
ReAct 模式的基础实现
"""
def __init__(self, llm, tools: Dict, max_steps=10):
self.llm = llm
self.tools = tools
self.max_steps = max_steps
def run(self, question: str) -> str:
"""
执行 ReAct 循环
"""
context = f"问题:{question}\n\n"
for step in range(self.max_steps):
# Step 1: 思考(Thought)
thought = self._think(context)
context += f"思考:{thought}\n"
# Step 2: 决定是行动还是给出最终答案
if "最终答案" in thought or step == self.max_steps - 1:
return self._extract_answer(thought)
# Step 3: 行动(Action)
action = self._decide_action(thought)
context += f"行动:{action}\n"
# Step 4: 观察(Observation)
observation = self._execute_action(action)
context += f"观察:{observation}\n"
return "无法在最大步数内完成任务。"
def _think(self, context: str) -> str:
"""生成思考推理步骤"""
prompt = f"""
{context}
基于以上信息,思考下一步应该怎么做。
如果已有足够信息回答问题,请输出"最终答案:[你的答案]"
否则,考虑需要哪些信息并描述你的思考。
"""
return self.llm(prompt)
def _decide_action(self, thought: str) -> str:
"""从思考中提取行动指令"""
prompt = f"""
基于以下思考,决定需要执行什么操作:
{thought}
可用工具:
{json.dumps(list(self.tools.keys()), ensure_ascii=False)}
输出格式:工具名(参数)
示例:search("最新AI新闻")
"""
return self.llm(prompt)
def _execute_action(self, action: str) -> str:
"""执行工具调用并返回观察结果"""
for tool_name, tool_func in self.tools.items():
if tool_name in action:
try:
# 提取参数
args = action.split('(')[1].rstrip(')')
return tool_func(args)
except Exception as e:
return f"执行错误:{str(e)}"
return f"未知工具:{action}"
2.3 ReAct Prompt 模板
# ReAct 标准 Prompt
react_prompt = """
请使用 ReAct 模式解决以下问题。
你的工作方式:
1. 思考(Thought):分析当前状态,规划下一步
2. 行动(Action):选择并执行一个操作
3. 观察(Observation):获取操作结果
4. 重复以上步骤,直到得出最终答案
可用工具:
- search(query): 搜索互联网获取信息
- calculate(expression): 执行数学计算
- lookup(key): 在知识库中查找信息
示例:
问题:特斯拉2024年的全球交付量是多少?
思考:我需要查找特斯拉2024年的交付数据。
行动:search("特斯拉 2024 全球交付量")
观察:特斯拉2024年全球交付量为178.9万辆
思考:已经获得数据,可以给出答案。
最终答案:特斯拉2024年全球交付量为178.9万辆。
现在请解决:
问题:{question}
"""
三、实战案例
3.1 信息检索型任务
问题:Python 中列表和元组的主要区别是什么?
# ReAct 执行过程
"""
思考:这是一个常见的 Python 概念对比问题,我可以直接回答。
思考:创建、修改、性能三个维度对比最清晰。
最终答案:
| 特性 | 列表 (list) | 元组 (tuple) |
|------|------------|-------------|
| 可变性 | 可变 | 不可变 |
| 语法 | [] | () |
| 性能 | 较慢 | 较快 |
| 使用场景 | 动态数据 | 固定数据 |
"""
3.2 需要外部信息的任务
问题:2024年诺贝尔物理学奖得主是谁?他们有哪些主要贡献?
# ReAct 执行过程
"""
思考:这需要查询最新信息,我无法只靠训练数据确定。
行动:search("2024 Nobel Prize Physics winners")
观察:2024年诺贝尔物理学奖授予 John Hopfield 和 Geoffrey Hinton
思考:我需要进一步了解他们的贡献。
行动:search("John Hopfield Geoffrey Hinton contributions")
观察:Hopfield 发明了Hopfield网络,Hinton 推动了深度学习
最终答案:
2024年诺贝尔物理学奖授予:
1. John Hopfield - 霍普菲尔德网络
2. Geoffrey Hinton - 深度学习先驱
主要贡献在于人工神经网络和机器学习的基础性发现和发明。
"""
3.3 多步骤推理任务
问题:如果A比B大3岁,B比C大2岁,三人的年龄之和是65岁。求各人年龄。
# ReAct 执行过程
"""
思考:这是一个三元一次方程问题。设C的年龄为x。
行动:我可以用代数方法推理。
观察:
设 C = x
则 B = x + 2
则 A = (x + 2) + 3 = x + 5
总和:x + (x+2) + (x+5) = 65
思考:解方程。
行动:calculate("3x + 7 = 65")
观察:3x = 58, x = 19.33... 这不对,让我重新检查。
思考:总和我计算错误。
A + B + C = (x+5) + (x+2) + x = 3x + 7 = 65
3x = 58
x = 19.33... 年龄应该是整数,让我重新检查。
思考:等等,我设定有问题。
A = x + 5, B = x + 2, C = x
但 A + B + C = (x+5) + (x+2) + x = 3x + 7
3x + 7 = 65
3x = 58
x = 19.33 这不合理。
思考:让我再想想。A比B大3岁,B比C大2岁。
所以 A = B + 3, B = C + 2
A + B + C = (C+5) + (C+2) + C = 3C + 7 = 65
3C = 58... 还是不对,65-7=58,58/3不是整数。
思考:让我检查一下年龄之和。
行动:calculate("65 - 7")
观察:58
思考:58/3 ≈ 19.33,这不是整数。
这也许是一个分数年龄的问题?
最终答案:这不是一个整数解问题。
C = 19.33, B = 21.33, A = 24.33
但通常年龄问题有整数解。让我们重新检查题意...
"""
四、ReAct 与 Agent 框架
4.1 现代 Agent 中的 ReAct
当代 Agent 框架普遍采用 ReAct 模式或其变体:
| Agent 框架 | ReAct 变体 | 特色扩展 |
|---|
| LangChain Agent | 标准 ReAct | 工具注册+错误重试 |
| AutoGen | 多 Agent ReAct | 多角色对话+任务委派 |
| CrewAI | 角色化 ReAct | 团队协作模式 |
| OpenAI Assistant | Function Calling ReAct | API 原生支持 |
| Claude Agent | Tool Use ReAct | 工具调用协议 |
4.2 ReAct 在 Agent 中的增强模式
"""
增强 ReAct 模式
"""
# 0. 标准 ReAct
# Thought → Action → Observation → Thought → ...
# 1. ReAct + 记忆
# Thought → [检索记忆] → Action → Observation → [存储记忆] → ...
# 2. ReAct + 规划
# [全局规划] → Thought → Action → Observation → [重新规划] → ...
# 3. Multi-Agent ReAct
# Agent A: Thought → Action → Observation → ...
# Agent B: Thought → Action → Observation → ...
# [协调器]: 监控所有 Agent,分配任务
五、ReAct 的局限性及应对
5.1 常见问题
| 问题 | 表现 | 原因 | 解决方案 |
|---|
| 循环陷阱 | 重复相同的思考-行动循环 | 无法从观察中学习 | 设置最大步数+去重检测 |
| 工具误导 | 过度依赖工具 | 所有问题都先搜索 | 限制工具调用次数 |
| 思考过深 | 思考时间过长 | 试图完美推理 | 设置思考超时+渐进式决策 |
| 上下文膨胀 | ReAct 记录太长 | 每步都追加全文 | 定期摘要压缩历史 |
5.2 改进策略
class ImprovedReAct(ReActAgent):
"""改进版 ReAct:增加循环检测和上下文压缩"""
def __init__(self, llm, tools, max_steps=15):
super().__init__(llm, tools, max_steps)
self.visited_states = set()
def _detect_loop(self, state: str) -> bool:
"""检测是否进入循环"""
state_hash = hash(state)
if state_hash in self.visited_states:
return True
self.visited_states.add(state_hash)
return False
def _compress_context(self, context: str) -> str:
"""当上下文过长时进行压缩"""
if len(context) > 4000:
prompt = f"""
压缩以下 ReAct 执行记录,保留关键信息:
{context[-3000:]}
压缩要求:
- 保留所有观察结果
- 保留最终结论
- 省略重复的思考
"""
return self.llm(prompt)
return context
六、最佳实践
6.1 何时使用 ReAct
| 任务类型 | 推荐模式 | 原因 |
|---|
| 简单问答 | CoT | ReAct 过度设计 |
| 需要外部的信息 | ReAct | 需要工具交互 |
| 代码生成 | Act-only | 直接输出代码 |
| 复杂推理+信息检索 | ReAct | 两者兼顾 |
| 多步骤规划 | ReAct+规划 | 需要全局视角 |
6.2 Prompt 设计要点
# ✅ 好的 ReAct Prompt
react_prompt_good = """
使用以下格式:
思考:[分析当前状态]
行动:[工具名(参数)]
观察:[行动结果]
...(可重复)
最终答案:[最终回答]
注意事项:
- 只有需要外部信息时才使用工具
- 一个思考步骤对应一个行动
- 得到足够信息后立即给出答案
"""
# ❌ 差的 ReAct Prompt
react_prompt_bad = """
你是一个Agent,需要思考然后行动。
思考要深入,行动要准确。
但是有时候也需要考虑...
可以多试几次...
"""
总结
ReAct 模式通过「思考→行动→观察」的循环协同,让 LLM 既具备深度推理能力,又能通过工具获取外部信息,有效弥补了纯 CoT 的幻觉问题和纯 Act 的盲目性问题。它在 Agent 系统中已成为事实标准,是构建可靠 LLM 应用的必备模式。