ReAct(Reasoning + Acting)模式
概述
ReAct 是 Reasoning + Acting 的缩写,由 Google Research 于 2022 年提出。该模式让 Agent 交替进行推理(Thought) 和行动(Action),并通过观察(Observation) 结果来动态调整下一步策略,形成一个思考→行动→观察→思考的循环。
原理
flowchart TD
Thought["Thought(思考)"]
Action["Action(行动)"]
Observation["Observation(观察)"]
Thought --> Action
Action --> Observation
Observation --> Thought
- Thought(思考):Agent 分析当前状态,推理下一步应该做什么
- Action(行动):Agent 执行具体操作(调用工具、搜索、计算等)
- Observation(观察):Agent 获取行动的结果反馈
- 重复上述过程,直到任务完成
核心在于将推理与行动交织在一起,让模型能够根据实时反馈动态调整行为,而非一次性生成最终答案。
使用场景
- 知识密集型问答:需要查阅外部知识库、搜索引擎的场景
- 多步推理任务:回答需要分步检索和推理的问题(如 "2024年诺贝尔物理学奖得主的导师是谁?")
- 交互式任务:需要与环境交互的任务,如网页导航、API 调用
- 事实核查:需要验证信息真实性的场景
- 数据分析:需要查询数据库、计算、再查询的迭代过程
示例代码
import re
from typing import List, Dict, Any
class ReActAgent:
"""ReAct 模式 Agent 实现"""
def __init__(self, llm, tools: Dict[str, callable]):
"""
Args:
llm: 大语言模型接口
tools: 可用工具字典 {工具名: 工具函数}
"""
self.llm = llm
self.tools = tools
def run(self, question: str, max_steps: int = 10) -> str:
"""
执行 ReAct 循环,解决用户问题
"""
# 构建提示词,要求 LLM 按 ReAct 格式输出
prompt = self._build_prompt(question, self.tools)
# 历史记录
history = []
for step in range(max_steps):
# 调用 LLM 生成下一步
response = self.llm.generate(prompt, history)
# 解析 Thought 和 Action
thought, action, action_input = self._parse_response(response)
history.append(f"Thought: {thought}")
# 判断是否得出最终答案
if action.lower() == "finish":
return action_input
# 执行工具调用
if action in self.tools:
observation = self.tools[action](action_input)
history.append(f"Action: {action}[{action_input}]")
history.append(f"Observation: {observation}")
else:
observation = f"Error: 未知工具 '{action}'"
history.append(f"Observation: {observation}")
if step == max_steps - 1:
return "任务未在最大步数内完成"
def _build_prompt(self, question: str, tools: Dict) -> str:
"""构建 ReAct 格式提示词"""
tool_descriptions = "\n".join([
f"- {name}: {func.__doc__ or '无描述'}"
for name, func in tools.items()
])
return f"""你需要使用以下工具回答问题。请严格按照以下格式回复:
可用工具:
{tool_descriptions}
回复格式(每次只能执行一个 Thought-Action 对):
Thought: 你的推理过程
Action: 工具名称[参数]
Observation: (等待工具返回结果)
当你得到最终答案时,使用:
Thought: 我现在有足够的信息回答问题
Action: Finish[最终答案]
问题:{question}
"""
def _parse_response(self, response: str) -> tuple:
"""解析 LLM 返回的 Thought 和 Action"""
thought_match = re.search(r"Thought:\s*(.+?)(?=\nAction:|\Z)", response, re.DOTALL)
action_match = re.search(r"Action:\s*(\w+)\[([^\]]*)\]", response)
thought = thought_match.group(1).strip() if thought_match else ""
action = action_match.group(1).strip() if action_match else "Finish"
action_input = action_match.group(2).strip() if action_match else ""
return thought, action, action_input
# ========== 使用示例 ==========
def search(query: str) -> str:
"""搜索引擎工具"""
# 模拟搜索
database = {
"2024诺贝尔物理学奖": "John Hopfield 和 Geoffrey Hinton",
"Geoffrey Hinton 导师": "Christopher Longuet-Higgins",
"深度学习之父": "Geoffrey Hinton",
}
return database.get(query, "未找到相关信息")
def calculate(expression: str) -> str:
"""数学计算工具"""
try:
return str(eval(expression))
except:
return "计算错误"
# 创建 Agent
agent = ReActAgent(
llm=YourLLM(), # 替换为实际的 LLM 实例
tools={
"Search": search,
"Calculate": calculate,
}
)
# 执行任务
result = agent.run("2024年诺贝尔物理学奖得主的导师是谁?")
print(result)
ReAct 对话示例
用户问题:2024年诺贝尔物理学奖得主的导师是谁?
Step 1:
Thought: 我需要先查2024年诺贝尔物理学奖得主是谁
Action: Search[2024诺贝尔物理学奖]
Observation: John Hopfield 和 Geoffrey Hinton
Step 2:
Thought: 得知得主是 Geoffrey Hinton,现在查他的导师
Action: Search[Geoffrey Hinton 导师]
Observation: Christopher Longuet-Higgins
Step 3:
Thought: 已经找到答案,Hinton的导师是 Christopher Longuet-Higgins
Action: Finish[2024年诺贝尔物理学奖得主Geoffrey Hinton的导师是Christopher Longuet-Higgins]
优点与局限
| 优点 | 局限 |
|---|---|
| 推理过程可解释、可审计 | 依赖 prompt 格式,解析不够鲁棒 |
| 能根据实时反馈动态调整 | 复杂任务可能需要大量步骤 |
| 减少幻觉,行动结果基于真实数据 | 对 LLM 的指令遵循能力要求高 |
| 与工具生态天然兼容 | 无长期记忆,历史对话依赖上下文窗口 |
变体与发展
- ReAct + CoT:在 Thought 阶段引入 Chain of Thought 推理
- ReAct + Reflexion:加入自我反思机制,从失败中学习
评论
评论加载中…