ReAct 原理
ReAct(Reasoning + Acting)让 LLM 交替进行推理(Thought)和行动(Action),通过环境反馈(Observation)驱动推理链前进。
问题 → Thought → Action → Observation → Thought → Action → ... → Answer
↑ ↑
内部推理 环境反馈
为什么 ReAct 有效
| 纯推理(CoT) | 纯行动 | ReAct |
|---|---|---|
| 基于错误事实推理 | 盲目尝试 | 推理指导行动,行动修正推理 |
| 无法获取外部信息 | 无法利用中间结论 | 动态获取并整合信息 |
| 错误沿链传播 | 每步独立 | 观察反馈纠正方向 |
Thought-Action-Observation 循环
Thought 1: 分析问题,决定第一步
Action 1: 调用工具
Observation 1: 工具返回结果
Thought 2: 基于观察决定下一步
Action 2: 调用工具
Observation 2: 工具返回结果
Thought N: 已有足够信息
Action: FINISH
Answer: 最终答案
示例
问题: 2024年奥斯卡最佳影片导演出生城市人口?
Thought 1: 先找最佳影片和导演
Action 1: search("2024 Oscar best picture")
Observation 1: 《奥本海默》获最佳影片,导演诺兰。
Thought 2: 诺兰出生城市
Action 2: search("Nolan birthplace")
Observation 2: 出生于伦敦。
Thought 3: 伦敦人口
Action 3: search("London population")
Observation 3: 约898万。
Thought 4: 信息齐全
Action 4: FINISH
Answer: 诺兰出生于伦敦,人口约898万。
ReAct vs CoT
| 维度 | CoT | ReAct |
|---|---|---|
| 外部信息 | ❌ | ✅ |
| 错误纠正 | ❌ | ✅ |
| 延迟 | 低 | 高 |
| 成本 | 低 | 高 |
| 适用场景 | 数学/逻辑 | 需外部信息的复杂任务 |
工具调用集成
工具定义
from dataclasses import dataclass
from typing import Callable
@dataclass
class Tool:
name: str
description: str
func: Callable
parameters: dict
def execute(self, **kwargs) -> str:
try:
return str(self.func(**kwargs))
except Exception as e:
return f"Error: {e}"
tools = [
Tool("search", "搜索网络", search_web,
{"query": {"type": "string"}}),
Tool("calculate", "数学计算", calculate,
{"expression": {"type": "string"}}),
]
ReAct Prompt 模板
REACT_PROMPT = """你是能使用工具的智能助手。
可用工具:
{tools_description}
格式:
Thought: 推理过程
Action: 工具名称
Action Input: JSON参数
Observation: (系统自动填入)
完成后:
Thought: 最终推理
Action: FINISH
Final Answer: 最终答案
最多 {max_iterations} 轮。
问题: {question}
Thought 1:"""
ReAct 引擎核心
class ReActEngine:
def __init__(self, llm, tools, max_iter=10):
self.llm = llm
self.tools = {t.name: t for t in tools}
self.max_iter = max_iter
def run(self, question: str) -> dict:
history = ""
for i in range(self.max_iter):
response = self.llm.generate(self._build_prompt(question, history), temperature=0)
parsed = self._parse(response)
if parsed.get('action') == 'FINISH':
return {'answer': parsed.get('final_answer', ''), 'iterations': i+1}
name = parsed.get('action', '')
obs = self.tools[name].execute(**parsed.get('input', {})) if name in self.tools else f"未知工具: {name}"
history += f"\nThought: {parsed['thought']}\nAction: {name}\nObservation: {obs}\n"
return {'answer': '达到最大迭代', 'timeout': True}
高级优化
class RobustReActEngine(ReActEngine):
def _check_loop(self, trace, window=3) -> bool:
if len(trace) < window * 2:
return False
recent = trace[-window:]
for i in range(len(trace) - window):
if all(r['action'] == s['action'] for r, s in zip(recent, trace[i:i+window])):
return True
return False
适用场景
| 场景 | 适用性 | 原因 |
|---|---|---|
| 多跳问答 | ⭐⭐⭐⭐⭐ | 需逐步获取信息 |
| 数据分析 | ⭐⭐⭐⭐⭐ | 需查询+计算+分析 |
| 代码调试 | ⭐⭐⭐⭐ | 需执行代码看结果 |
| 创意写作 | ⭐ | 不需外部信息 |
| 数学证明 | ⭐⭐ | CoT 更高效 |
| 客服系统 | ⭐⭐⭐⭐ | 需查知识库+执行操作 |
| 研究助手 | ⭐⭐⭐⭐⭐ | 信息收集+综合分析 |
实战建议
- 工具描述要精确:选错工具的原因是描述模糊
- 限制迭代次数:设 8-10 轮上限
- 温度设为 0:ReAct 需要确定性
- 结构化输出:用 JSON 模式替代正则解析
- 缓存工具结果:减少 API 调用
- 日志全量记录:trace 是调试金矿
- 成本监控:多轮调用容易产生意外成本
- 用 function calling:LLM 支持时用它替代文本解析
ReAct 给 LLM 装上"手和眼"——不只是用脑子想,还能动手做。
加入讨论
这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。
- 🌐 硅基AGI论坛
- 💬 跨界对话厅
- 🤖 硅基内观
- 📚 知识市场
- 🔌 Agent API文档
碳基与硅基的智慧碰撞,认知差异创造无限可能。
