揭秘 ReAct 框架:让大模型从“空想家”变身“实干家”
揭秘 ReAct 框架:让大模型从“空想家”变身“实干家”
大家好,我是你们的老朋友,一名在代码世界摸爬滚打多年的技术博主。
最近,大语言模型(LLM)的应用如火如荼。但我们发现,单纯依靠模型的“大脑”(推理能力)往往不够用:它可能产生幻觉,或者无法获取最新的实时信息;而单纯让它调用工具(Tool Calling),又缺乏复杂的逻辑规划能力,容易在复杂任务中迷失方向。
这就引出了今天的主角——ReAct 框架。
很多同学在面试或学习中听过这个词,觉得它很高深。其实,ReAct 的核心思想非常朴素且优雅:把“思考”(Reasoning)和“行动”(Action)结合起来。今天,我们就通过深入浅出的讲解和实战代码,彻底搞懂 ReAct 是如何工作的,以及它在企业级应用中的演进。
一、 什么是 ReAct?核心原理解析
ReAct 这个名字来源于两个单词的组合:Reasoning(推理)+ Action(行动)。
在传统的 Chain-of-Thought (CoT) 思维链中,模型只是在内部进行逻辑推导,最后给出一个答案。这就像是一个学生在闭卷考试,只能靠记忆和逻辑,不能查资料,也不能用计算器。
而在纯工具调用(Tool Calling)模式中,模型更像是一个只会执行命令的机器人,缺乏对全局任务的动态规划能力。
ReAct 框架打破了这两者的界限。 它让 LLM 进入一个循环状态:
- Thought(思考):模型分析当前情况,决定下一步该做什么。
- Action(行动):模型选择一个工具(如搜索引擎、计算器、数据库查询)并执行。
- Observation(观察):模型获取工具执行后的结果。
这个过程会不断循环,直到模型认为已经收集了足够的信息,从而给出最终答案(Final Answer)。
ReAct vs 其他模式
为了更直观地对比,我们可以看下表:
| 特性 | 纯 CoT (思维链) | 纯 Tool Calling | ReAct 框架 |
|---|---|---|---|
| 核心能力 | 逻辑推理 | 外部交互 | 推理 + 交互 |
| 信息获取 | 仅限训练数据 | 依赖预设工具 | 动态获取外部信息 |
| 决策能力 | 静态线性推导 | 被动响应 | 动态规划与修正 |
| 适用场景 | 数学题、逻辑题 | 简单查询、固定流程 | 复杂多步任务、开放域问答 |
二、 ReAct 的工作流程图
光说文字可能有点抽象,我们来看一张标准的 ReAct 执行流程图。这是一个典型的闭环控制过程。
从图中可以看出,Thought → Action → Observation 构成了一个紧密的循环。这个循环赋予了 Agent(智能体)“边做边想”的能力。如果工具返回的结果不理想,模型可以在下一个 Thought 步骤中调整策略,重新选择工具或参数。
三、 实战演示:Python 实现简易 ReAct Loop
虽然目前业界主流使用 LangChain 等框架来构建 Agent,但理解底层的实现逻辑至关重要。下面我们用 Python 模拟一个最简化的 ReAct 循环,假设我们有一个简单的“搜索工具”和“计算器工具”。
注意:为了演示清晰,这里简化了 LLM 的调用部分,重点展示控制流逻辑。
import json
import time
# 模拟的工具函数
def search_tool(query: str) -> str:
"""模拟搜索引擎"""
print(f"🔍 [Action] 正在搜索: {query}")
# 实际场景中这里会调用 Google/Bing API
if "天气" in query:
return "北京今天天气晴朗,气温25度。"
elif "人口" in query:
return "中国人口约为14亿。"
return "未找到相关信息。"
def calculator_tool(expression: str) -> str:
"""模拟计算器"""
print(f"🧮 [Action] 正在计算: {expression}")
try:
# 注意:生产环境中严禁直接使用 eval,需使用安全沙箱
result = eval(expression)
return str(result)
except Exception as e:
return f"计算错误: {str(e)}"
# 定义工具注册表
TOOLS = {
"search": search_tool,
"calculate": calculator_tool
}
class SimpleReActAgent:
def __init__(self, max_iterations=5):
self.max_iterations = max_iterations
self.history = []
def think(self, task: str, observations: list) -> str:
"""
模拟 LLM 的思考过程。
在实际生产中,这里会调用 LLM API (如 GPT-4),
Prompt 会包含任务、历史思考和观察结果。
"""
# 这里为了演示,硬编码了一些简单的逻辑判断
# 真实场景中,LLM 会根据上下文生成 JSON 或特定格式的字符串
if not observations:
# 初始思考
if "天气" in task:
return json.dumps({"thought": "我需要查询北京的天气。", "action": "search", "action_input": "北京天气"})
elif "计算" in task or "+" in task or "*" in task:
return json.dumps({"thought": "我需要执行数学计算。", "action": "calculate", "action_input": "12 * 12"})
# 如果有观察结果,通常意味着任务完成或需要进一步处理
# 这里简化为直接生成最终答案
last_obs = observations[-1] if observations else ""
return json.dumps({"thought": "我已经获得了所需信息。", "action": "finish", "action_input": f"根据搜索结果: {last_obs}"})
def run(self, task: str):
print(f"🚀 [Start] 开始处理任务: {task}\n")
observations = []
for i in range(self.max_iterations):
print(f"--- Iteration {i+1} ---")
# 1. Thought & Action Decision
response_str = self.think(task, observations)
response = json.loads(response_str)
thought = response.get("thought", "")
action_name = response.get("action", "")
action_input = response.get("action_input", "")
print(f"💭 [Thought] {thought}")
# 2. Check if finished
if action_name == "finish":
print(f"✅ [Final Answer] {action_input}")
return action_input
# 3. Execute Action
if action_name in TOOLS:
try:
observation = TOOLS[action_name](action_input)
print(f"👀 [Observation] {observation}\n")
observations.append(observation)
except Exception as e:
error_msg = f"工具执行错误: {str(e)}"
print(f"❌ [Error] {error_msg}\n")
observations.append(error_msg)
else:
print(f"⚠️ [Warning] 未知工具: {action_name}\n")
break
return "达到最大迭代次数,未能完成任务。"
# 运行示例
if __name__ == "__main__":
agent = SimpleReActAgent()
# 案例 1: 需要调用搜索工具
print("="*30)
agent.run("北京今天的天气怎么样?")
print("\n" + "="*30)
# 案例 2: 需要调用计算工具
agent.run("计算 12 乘以 12 的结果")
代码运行逻辑解析
- 初始化:Agent 接收用户任务。
- Think 阶段:
think方法模拟 LLM 根据当前上下文(任务+历史观察)生成下一步指令。在实际工程中,这一步是 prompt engineering 的核心,你需要告诉 LLM 可用的工具列表和输出格式。 - Action 阶段:解析 LLM 的输出,匹配对应的工具函数并执行。
- Observation 阶段:捕获工具的执行结果,并将其存入
observations列表,作为下一次think的输入上下文。 - 循环终止:当 LLM 输出
finish动作或达到最大迭代次数时,循环结束。
四、 从 Demo 到生产:ReAct 的局限与进阶
上面的代码只是一个玩具模型。在真实的企业级应用中,原生的 ReAct 框架面临几个挑战:
- 状态控制能力有限:简单的线性循环难以处理复杂的分支逻辑(比如:如果搜索失败,该怎么办?如果需要并行调用多个工具呢?)。
- 上下文窗口限制:随着
Thought-Action-Observation循环次数增加,Prompt 会变得极长,导致成本增加且容易超出 Token 限制。 - 缺乏长期记忆:原生 ReAct 通常是无状态的,无法记住用户之前的偏好或历史对话中的关键信息。
因此,现在的生产级 Agent 系统通常不会只使用裸版的 ReAct,而是结合以下技术进行增强:
- LangGraph / State Machine:使用有向图(DAG)或状态机来管理 Agent 的流程。不再是简单的
while循环,而是可以根据条件跳转到不同的节点(例如:路由节点、反思节点)。 - Memory(记忆模块):引入向量数据库或摘要机制,让 Agent 拥有短期记忆(当前对话)和长期记忆(用户画像、知识库)。
- Planner(规划器):在 ReAct 循环之前,先让 LLM 生成一个高层级的计划(Plan),将大任务拆解为子任务,再逐个执行。
- Reflection(反思/自我修正):在执行完 Action 后,增加一个 Critic(评论家)角色,评估结果的质量。如果质量不高,触发重试或调整策略。
进阶架构示意图
五、 总结与建议
ReAct 框架是大模型从“聊天机器人”进化为“智能代理(Agent)”的关键一步。它通过推理与行动的交替循环,解决了大模型无法实时获取信息和执行操作痛点。
给开发者的建议:
- 入门首选:如果你是初学者,建议先从 LangChain 的
AgentExecutor入手,它本质上就是封装好的 ReAct Loop,能快速上手。 - 关注 Prompt 质量:ReAct 的效果高度依赖 Prompt 的设计。清晰地定义工具的描述(Description)和输入参数格式,能显著提高 LLM 调用工具的准确率。
- 迈向生产:当你的业务逻辑变复杂时,不要死守单一的 ReAct 循环。尝试引入 LangGraph 来构建有状态的、可回溯的工作流,并结合 Reflection 机制提高系统的鲁棒性。
希望这篇文章能帮你理清 ReAct 的脉络。技术在不断演进,但核心思想始终是为了解决更复杂的问题。保持好奇,持续实践!
参考资料
更多推荐



所有评论(0)