ReAct 模式(Reasoning + Acting)详解

⚠️ 注意:这里的 ReAct 不是前端框架 React.js,而是 LLM Agent 领域的一个经典范式,全称 Reasoning + Acting,出自 2022 年论文 ReAct: Synergizing Reasoning and Acting in Language Models


🔹 核心思想

LLM 在一个循环中交替进行 思考(Reasoning)行动(Acting)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
循环开始

├─ 🤔 Thought: "用户要提取 CSV 第一列,我先看看文件结构" ← 推理
├─ 🛠️ Action: read_file(path="questions.csv", n_rows=5) ← 调用工具
├─ 👁️ Observation: "type,question,options,answer,..." ← 工具返回

├─ 🤔 Thought: "第一列是 type,我用 pandas 提取" ← 推理
├─ 🛠️ Action: python_runner(code="import pandas...") ← 调用工具
├─ 👁️ Observation: "执行成功,生成 output.csv" ← 工具返回

├─ 🤔 Thought: "验证一下结果文件" ← 推理
├─ 🛠️ Action: read_file(path="output.csv", n_rows=3) ← 调用工具
├─ 👁️ Observation: "type\nmultiple-choice\n..." ← 工具返回

├─ 🤔 Thought: "结果正确,任务完成" ← 推理
└─ ✅ Final Answer: "已成功提取..." ← 结束循环

🔄 循环三要素

阶段 英文 作用 示例
思考 Thought 分析当前状态,决定下一步行动 “文件有 5 列,我需要用 pandas 提取第一列”
行动 Action 调用工具执行具体操作 python_runner(code="...")
观察 Observation 获取工具执行结果,反馈给模型 “执行成功,生成 output.csv”

🔹 在代码中的体现

adk.NewChatModelAgent 本身就实现了 ReAct 循环——MaxIterations 控制的就是这个循环的最大轮数:

1
2
3
4
5
// agents/code_agent.go
adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
// ...
MaxIterations: 1000, // ← ReAct 循环最多跑 1000 轮
})

🔄 每一轮迭代的流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌─────────────────────────────────────────┐
│ 第 N 轮 ReAct 循环 │
├─────────────────────────────────────────┤
│ 1️⃣ 模型推理 │
│ • 分析历史对话 + 工具返回结果 │
│ • 决定调用哪个工具、传什么参数 │
│ │
│ 2️⃣ 执行工具 │
│ • 解析 LLM 生成的 Tool Call │
│ • 调用实际函数(如 bash/python_runner)│
│ • 返回结果给模型 │
│ │
│ 3️⃣ 模型再推理 │
│ • 判断:任务完成了吗? │
│ • 是 → 输出 Final Answer,结束循环 │
│ • 否 → 继续下一轮 Thought/Action │
└─────────────────────────────────────────┘

🔹 Instruction 中这句话的目的

1
"You are in a react mode, and you should use the following libraries..."

🎯 这是在提示 LLM:

你现在处于一个可以反复调用工具的循环中,不需要一次性给出答案,可以分步骤通过工具来完成任务。

💡 这样 LLM 就知道自己可以:

能力 说明
✅ 先 read_file 看看数据长什么样 不盲目写代码,先理解输入
✅ 再 python_runner 写代码处理 基于观察结果制定行动方案
✅ 出错了可以再改代码重试 支持迭代调试,提升成功率
✅ 验证结果后再提交 确保输出质量,避免错误传播
❌ 而不是试图一次性在回复中把所有事情说完 避免”幻觉式”回答

🔹 对比:普通模式 vs ReAct 模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌─────────────────────────────────────────────────────────┐
│ 普通模式(一次性回答) │
├─────────────────────────────────────────────────────────┤
│ 用户提问 → LLM 一次性回答 → 结束 │
│ │
│ 优点:快,适合简单问答 │
│ 缺点:无法调用工具,无法处理复杂/多步任务 │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│ ReAct 模式(思考-行动循环) │
├─────────────────────────────────────────────────────────┤
│ 用户提问 → [思考→调用工具→观察结果] × N轮 → 最终回答 │
│ │
│ 优点:能调用工具、支持多步推理、可迭代调试 │
│ 缺点:耗时更长,需要控制最大迭代次数防止死循环 │
└─────────────────────────────────────────────────────────┘

📊 场景对比

场景 普通模式 ReAct 模式
简单问答:”1+1=?” ✅ 适合 ⚠️ 大材小用
文件处理:”提取 CSV 第一列” ❌ 无法执行 ✅ 适合
网络搜索:”查一下今天天气” ❌ 无实时数据 ✅ 可调用搜索工具
代码调试:”为什么这段代码报错” ❌ 无法执行验证 ✅ 可运行→观察→修正

🔹 ReAct 模式的关键配置

1️⃣ 控制迭代次数

1
2
MaxIterations: 1000   // CodeAgent:允许复杂任务多轮迭代
MaxIterations: 10 // WebSearchAgent:搜索任务通常很快完成

⚠️ 必须设置上限,防止模型陷入死循环或无限调用工具。

2️⃣ 工具描述要清晰

1
2
3
4
// ToolInfo 的 Desc 字段直接影响 LLM 的决策质量
Desc: `Run commands in a bash shell.
Use this for file operations, git commands, or running scripts.
Output includes stdout, stderr, and exit code.`

3️⃣ 结果反馈要简洁

1
2
3
4
5
// 后处理函数将复杂结果格式化为模型易读的文本
func FilePostProcess(...) string {
return fmt.Sprintf("✅ Success\nstdout: %s\nstderr: %s",
result.Stdout, result.Stderr)
}

🔹 常见陷阱与最佳实践

⚠️ 陷阱 1:模型忘记调用工具,直接编答案

1
2
3
4
5
6
7
8
❌ 错误示例:
Thought: "我需要读取文件"
Final Answer: "文件第一列是 type..." ← 没调用工具就瞎编

✅ 解决方案:
• Instruction 明确强调 "必须使用工具"
• 工具返回结果中携带明确的成功/失败标识
• 后处理函数格式化结果,让模型容易理解

⚠️ 陷阱 2:模型陷入循环,反复调用同一工具

1
2
3
4
5
6
7
8
9
❌ 错误示例:
Round 1: read_file → "文件太大"
Round 2: read_file → "文件太大"
Round 3: read_file → "文件太大" ...

✅ 解决方案:
• 设置 MaxIterations 上限
• 在 Observation 中给出明确错误提示:"文件过大,请指定 start_row 和 n_rows"
• Instruction 引导模型:"如果工具报错,先分析原因再重试"

⚠️ 陷阱 3:工具参数格式错误

1
2
3
4
5
6
7
❌ 错误示例:
Action: python_runner(code=import pandas...) ← 缺少引号,不是合法 JSON

✅ 解决方案:
• 使用 WrapTool + jsonrepair 自动修复参数
• ToolInfo 中明确参数类型和必填项
• 预处理函数清除模型生成的格式残留

🔑 关键要点速记

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌─────────────────┬───────────────────────────────────────────┐
│ 要点 │ 说明 │
├─────────────────┼───────────────────────────────────────────┤
│ ReAct 全称 │ Reasoning + Acting,思考与行动交替循环 │
├─────────────────┼───────────────────────────────────────────┤
│ 循环三要素 │ Thought → Action → Observation │
├─────────────────┼───────────────────────────────────────────┤
│ MaxIterations │ 控制最大循环轮数,防止死循环 │
├─────────────────┼───────────────────────────────────────────┤
│ Instruction 作用│ 引导模型"分步做",而非"一次性答" │
├─────────────────┼───────────────────────────────────────────┤
│ 工具描述质量 │ 直接影响 LLM 能否正确选择和使用工具 │
├─────────────────┼───────────────────────────────────────────┤
│ 结果反馈格式 │ 简洁明确,便于模型理解并决定下一步 │
├─────────────────┼───────────────────────────────────────────┤
│ 适用场景 │ 复杂任务、多步推理、需要外部工具的场景 │
└─────────────────┴───────────────────────────────────────────┘

🎯 一句话总结

ReAct 模式 = 让 LLM 学会”边想边做”:通过 Thought-Action-Observation 循环,模型可以调用工具、观察结果、迭代修正,从而完成单靠语言生成无法解决的复杂任务。理解这一范式,是构建实用 Agent 系统的核心基础。