ReAct 模式详解:Agent 是怎么"思考"的
系列:AI Agent 从入门到实战 | 第 7 篇
前面几篇我们一直在用 Agent,调工具、查记忆、生成回答------但 Agent 到底是怎么决定"先做什么、再做什么"的?这一篇我们深入 Agent 的核心思维模式:ReAct,看看它的"脑子"里到底在想什么。
一、什么是 ReAct
ReAct 是 2022 年由 Google 和普林斯顿大学提出的 Agent 范式,论文名字就叫 ReAct: Synergizing Reasoning and Acting in Language Models。
ReAct = Re asoning(推理)+ Acting(行动)
核心思想:让 LLM 交替进行"思考"和"行动",一步一步解决问题。
传统 LLM:
问题 → 一次性回答(容易瞎编)
ReAct:
问题 → 思考 → 行动 → 观察 → 思考 → 行动 → 观察 → ... → 最终回答
二、一个直觉类比
想象你是一个侦探在破案:
erlang
传统 LLM 的做法(不推理):
问:"凶手是谁?"
答:"根据我的直觉,是管家干的。"(瞎猜)
ReAct 的做法(推理 + 行动):
问:"凶手是谁?"
💭 思考:我需要先查看案发现场
🔧 行动:去案发现场勘查
👁️ 观察:发现了一把带血的刀
💭 思考:刀上有指纹,我需要比对
🔧 行动:拿指纹去数据库比对
👁️ 观察:匹配到了嫌疑人 A
💭 思考:嫌疑人 A 有作案动机,但需要确认不在场证明
🔧 行动:调取监控录像
👁️ 观察:监控显示嫌疑人 A 案发时不在现场
💭 思考:A 有不在场证明,排除。查看其他线索...
...
ReAct 就是这样:每一步都先想清楚为什么做,做完看看结果,再决定下一步。
三、ReAct 的完整流程
arduino
┌─────────────┐
│ 用户输入问题 │
└──────┬──────┘
│
▼
┌────────────────────────┐
│ 💭 Thought(思考) │
│ "我需要做什么?为什么?" │
└────────────┬───────────┘
│
▼
┌────────────────────────┐
│ 🔧 Action(行动) │
│ 调用某个工具执行操作 │
└────────────┬───────────┘
│
▼
┌────────────────────────┐
│ 👁️ Observation(观察)│
│ 查看工具返回的结果 │
└────────────┬───────────┘
│
▼
┌─────────────┐
│ 任务完成了吗? │
└──────┬──────┘
/ \
是 否
│ │
▼ │
┌──────────┐ │
│ 输出最终回答│ └──→ 回到 Thought ↑
└──────────┘
三个核心要素:
| 要素 | 英文 | 作用 |
|---|---|---|
| 思考 | Thought | 分析当前情况,决定下一步该做什么 |
| 行动 | Action | 调用工具执行具体操作 |
| 观察 | Observation | 查看行动的结果,为下一轮思考提供信息 |
四、用代码看 ReAct 的实际运行
LangChain 默认的 Agent 就是基于 ReAct 模式的。我们直接看 verbose 输出:
示例:回答一个需要搜索的问题
python
from langchain_openai import ChatOpenAI
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.tools import tool
# 定义工具
@tool
def search(query: str) -> str:
"""搜索互联网获取最新信息"""
results = {
"2024年诺贝尔物理学奖": "2024年诺贝尔物理学奖授予了 John Hopfield 和 Geoffrey Hinton,表彰他们在人工神经网络和机器学习方面的基础性发现。",
"Geoffrey Hinton": "Geoffrey Hinton,被誉为'深度学习之父',2023年从 Google 离职,专注于 AI 安全研究。",
}
for key, value in results.items():
if key in query:
return value
return f"未找到关于 '{query}' 的信息"
# 创建 Agent
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
tools = [search]
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个智能助手,可以使用搜索工具获取信息。回答要简洁准确。"),
("human", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad"),
])
agent = create_tool_calling_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
# 运行
executor.invoke({"input": "2024年诺贝尔物理学奖颁给了谁?他们做了什么贡献?"})
verbose 输出(ReAct 的思考过程)
yaml
> Entering new AgentExecutor chain...
# ── 第 1 轮 ReAct ──
💭 Thought: 用户问的是2024年诺贝尔物理学奖,这是一个事实性问题,
我需要搜索最新信息来回答,不能靠自己的记忆。
🔧 Action: 调用 search 工具
Action Input: "2024年诺贝尔物理学奖"
👁️ Observation: 2024年诺贝尔物理学奖授予了 John Hopfield 和 Geoffrey Hinton,
表彰他们在人工神经网络和机器学习方面的基础性发现。
# ── 第 2 轮 ReAct ──
💭 Thought: 我知道了获奖者是谁,但用户还问了"他们做了什么贡献",
我需要更详细的信息,特别是关于他们贡献的部分。
🔧 Action: 调用 search 工具
Action Input: "Geoffrey Hinton"
👁️ Observation: Geoffrey Hinton,被誉为'深度学习之父',2023年从 Google 离职,
专注于 AI 安全研究。
# ── 第 3 轮 ReAct ──
💭 Thought: 我已经收集到了足够的信息,可以回答用户的问题了。
诺贝尔奖授予了 Hopfield 和 Hinton,
表彰他们在人工神经网络和机器学习方面的贡献。
🤖 Final Answer: 2024年诺贝尔物理学奖授予了 John Hopfield 和 Geoffrey Hinton,
表彰他们在人工神经网络和机器学习方面的基础性发现。
其中 Hinton 被誉为"深度学习之父",
他于2023年从 Google 离职,专注于 AI 安全研究。
> Finished chain.
看到了吗?Agent 并不是一次性回答的,而是:
思考 → 搜索 → 看结果 → 思考 → 再搜索 → 看结果 → 思考 → 回答
这就是 ReAct 的精髓。
五、ReAct 的思考过程拆解
把上面的例子拆开看每一轮:
第 1 轮
| 阶段 | 内容 |
|---|---|
| 💭 Thought | 用户问诺贝尔奖,我需要搜索,不能靠记忆 |
| 🔧 Action | search("2024年诺贝尔物理学奖") |
| 👁️ Observation | 得到了获奖者名字和贡献概述 |
第 2 轮
| 阶段 | 内容 |
|---|---|
| 💭 Thought | 用户还问了具体贡献,信息不够,需要再搜 |
| 🔧 Action | search("Geoffrey Hinton") |
| 👁️ Observation | 得到了 Hinton 的详细背景 |
第 3 轮
| 阶段 | 内容 |
|---|---|
| 💭 Thought | 信息够了,可以回答了 |
| 🤖 Final Answer | 输出最终回答 |
关键观察:每一轮的 Thought 都在做判断------该不该搜、搜什么、够不够。 这就是"推理"能力的体现。
六、ReAct vs 其他 Agent 范式
ReAct 不是唯一的 Agent 范式,来对比一下:
1. ReAct(推理 + 行动)
erlang
思考 → 行动 → 观察 → 思考 → 行动 → 观察 → ...
- ✅ 灵活,随时调整策略
- ✅ 适合不确定性高的任务
- ❌ 可能走弯路,效率不是最高
2. Plan-and-Execute(先规划后执行)
规划全部步骤 → 按顺序执行 → 汇总结果
- ✅ 效率高,步骤清晰
- ✅ 适合目标明确的任务
- ❌ 计划可能不准,中途调整能力弱
3. Reflexion(反思)
执行 → 评估结果 → 反思哪里做错了 → 改进后再执行
- ✅ 能从错误中学习
- ✅ 适合需要反复试错的任务
- ❌ 需要多次执行,成本高
对比表
| 范式 | 核心思想 | 适合场景 | 效率 | 灵活性 |
|---|---|---|---|---|
| ReAct | 边想边做 | 探索性任务 | 中 | 高 |
| Plan-and-Execute | 先想后做 | 目标明确的任务 | 高 | 中 |
| Reflexion | 做完再改 | 需要试错的任务 | 低 | 中 |
七、手写一个 ReAct Agent
不用框架,从零实现一个最简版的 ReAct Agent,帮助理解原理:
python
from langchain_openai import ChatOpenAI
def simple_react_agent(question: str, tools: dict, max_steps: int = 5):
"""
最简版 ReAct Agent
:param question: 用户问题
:param tools: 工具字典 {工具名: 工具函数}
:param max_steps: 最大循环次数
"""
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
# 系统提示词:教 LLM 使用 ReAct 格式
system_prompt = """你是一个 ReAct Agent。请按以下格式逐步思考和行动:
Thought: 分析当前情况,决定下一步
Action: 工具名称[参数]
Observation: [工具返回的结果,由系统填入]
当你有足够信息回答时,使用:
Thought: 我已经收集到足够信息
Answer: 最终回答
可用工具:{tools}
""".format(tools=", ".join(tools.keys()))
# 构建对话
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": question}
]
for step in range(max_steps):
print(f"\n--- 第 {step + 1} 轮 ---")
# 让 LLM 思考
response = llm.invoke(messages)
output = response.content
print(output)
# 检查是否有最终回答
if "Answer:" in output:
answer = output.split("Answer:")[-1].strip()
print(f"\n✅ 最终回答: {answer}")
return answer
# 解析 Action
if "Action:" in output:
action_line = [l for l in output.split("\n") if l.startswith("Action:")]
if action_line:
action_text = action_line[0].replace("Action:", "").strip()
# 解析:工具名[参数]
tool_name = action_text.split("[")[0].strip()
tool_input = action_text.split("[")[1].rstrip("]").strip()
# 执行工具
if tool_name in tools:
observation = tools[tool_name](tool_input)
print(f"Observation: {observation}")
# 把结果加入对话
messages.append({"role": "assistant", "content": output})
messages.append({"role": "user", "content": f"Observation: {observation}"})
else:
print(f"❌ 未知工具: {tool_name}")
break
print("⚠️ 达到最大步数,未能得出最终答案")
return None
# ============ 测试 ============
def search(query: str) -> str:
"""模拟搜索"""
data = {
"Python": "Python 是一种解释型、面向对象的高级编程语言,由 Guido van Rossum 于 1991 年创建。",
"Java": "Java 是一种面向对象的编程语言,由 Sun Microsystems 于 1995 年发布。",
}
for key, value in data.items():
if key in query:
return value
return f"未找到关于 '{query}' 的信息"
# 运行
simple_react_agent(
question="Python 是谁创造的?什么时候?",
tools={"search": search}
)
运行输出
yaml
--- 第 1 轮 ---
Thought: 用户问的是 Python 的创造者和时间,我需要搜索相关信息
Action: search[Python 是谁创造的]
Observation: Python 是一种解释型、面向对象的高级编程语言,由 Guido van Rossum 于 1991 年创建。
--- 第 2 轮 ---
Thought: 我已经从搜索结果中获得了 Python 的创造者和创建时间,可以回答了
Answer: Python 由 Guido van Rossum 于 1991 年创建。
✅ 最终回答: Python 由 Guido van Rossum 于 1991 年创建。
八、ReAct 的局限性
ReAct 不是万能的,了解它的局限:
| 局限 | 说明 | 应对方案 |
|---|---|---|
| 依赖 LLM 能力 | 模型太弱,推理就会出错 | 用更强的模型 |
| 可能死循环 | 思考不出正确方向,反复尝试 | 设置 max_iterations 限制 |
| 效率不高 | 每一步都要调 LLM,token 消耗大 | 用 Plan-and-Execute 替代 |
| 工具选择错误 | 可能选错工具或传错参数 | 写清楚工具描述 |
九、总结
| 问题 | 答案 |
|---|---|
| ReAct 是什么? | Reasoning + Acting,推理和行动交替进行 |
| 核心循环是什么? | Thought → Action → Observation → 重复 |
| 和普通 LLM 的区别? | 普通 LLM 一次性回答,ReAct 边想边做 |
| 适合什么任务? | 需要多步推理、调用工具的复杂任务 |
| 有什么局限? | 依赖模型能力、可能死循环、效率不是最高 |
ReAct 是目前最主流的 Agent 范式,理解了它,你就理解了 Agent 的核心工作原理。
下一篇预告
📖 第 8 篇:多 Agent 协作:让 AI 自己组队干活
一个 Agent 能力有限,那如果让多个 Agent 组成团队,分工协作呢?下一篇我们探索 Multi-Agent 系统,看看 Agent 是怎么"组队"完成复杂任务的。
👉 关注我,不迷路!
如果觉得有帮助,点个赞 👍 收藏 ⭐ 支持一下,是我持续更新的动力!