本篇属于「AI Agent 开发实战系列」第 2 篇
前言
在上一篇文章中,我们了解了 AI Agent 的核心概念:感知环境、自主决策、执行动作。
但有一个关键问题:Agent 如何做出「思考」与「行动」的协调?
这就是今天要介绍的 ReAct 模式——目前最广泛使用的 Agent 推理范式。
前置知识
阅读本文前,建议:
- 了解 AI Agent 基本概念(可阅读第 1 篇)
- 具备基础 Python 编程能力
- 了解 LLM API 的基本调用方式
核心概念
一、为什么需要 ReAct?
传统的 LLM 推理有两种模式:
1. Chain-of-Thought(CoT)—— 只想不做
问题:3 + 5 = ?
思考:3 + 5 就是把3和5相加...
回答:3 + 5 = 8 ✅
2. Action-Only —— 只做不想
问题:帮我查下北京天气
直接行动:调用天气API
返回结果:北京今天 25°C ❌
问题在哪?
- CoT 虽然能推理复杂问题,但无法与外部环境交互
- Action-Only 能执行动作,但缺乏中间推理过程
ReAct 的核心思想:将推理(Reasoning)和行动(Acting)交替进行,让模型「边想边做,边做边想」。
二、ReAct 模式的原理
ReAct = Reasoning + Acting
┌─────────────────────────────────────────────────────────────┐
│ ReAct 核心循环 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ │
│ │ 思考 │◀─────────────────────┐ │
│ │(Thought)│ │ │
│ └────┬────┘ │ │
│ │ │ │
│ ▼ │ │
│ ┌─────────┐ │ │
│ │ 行动 │ │ │
│ │(Action) │──────────────────────┘ │
│ └────┬────┘ │
│ │ │
│ ▼ │
│ ┌─────────┐ │
│ │ 观察 │────── 反馈到下一步思考 ────────────────────── │
│ │(Observe)│ │
│ └─────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
一个具体例子:
用户:帮我买一杯星巴克的大杯拿铁,用优惠券
Thought: 用户想买咖啡,需要先了解:
1. 星巴克有什么口味?
2. 大杯拿铁的价格?
3. 有哪些优惠券可用?
Action: search_coffee_store(store="星巴克")
Observe: 找到星巴克门店 nearby...
Thought: 现在知道了附近有星巴克门店,继续查询...
Action: get_menu(store_id="starbucks_001")
Observe: 菜单获取成功,拿铁中杯35元,大杯42元
Thought: 找到了大杯拿铁42元,现在看看优惠券...
Action: get_coupons(user_id="user_123")
Observe: 找到优惠券:「首单满50减10」
Thought: 42元不够满50,不能用这张券。我需要先凑单
或者找其他优惠券...
Action: search_coupons(keyword="拿铁")
Observe: 找到可用券:「咖啡类单品9折」
Thought: 42元打9折是37.8元,比原价便宜4.2元。
用户说用优惠券,那我就用这张。
Action: create_order(item="大杯拿铁", coupon="咖啡9折")
Observe: 订单创建成功!总价37.8元,已自动使用优惠券
Answer: 已为您下单星巴克大杯拿铁,使用「咖啡9折」券后
实付 37.8 元。预计30分钟内送达。✅
三、ReAct vs 其他推理模式对比
| 模式 | 思考 | 行动 | 观察 | 适用场景 |
|---|---|---|---|---|
| ReAct | ✅ | ✅ | ✅ | 复杂任务、多步骤推理 |
| CoT | ✅ | ❌ | ❌ | 数学推理、逻辑分析 |
| ToT | ✅✅ | ❌ | 树搜索 | 探索多种方案 |
| Reflexion | ✅ | ✅ | ✅+反思 | 需要自我改进 |
实践指南
环境准备
pip install langchain langchain-openai langchain-core
基础 ReAct Agent 实现
import os
from typing import List, Dict, Any
from openai import OpenAI
from dotenv import load_dotenv
load_dotenv()
class ReActAgent:
"""ReAct 模式的简单实现"""
def __init__(self, tools: List[Dict]):
self.client = OpenAI()
self.tools = tools
self.max_iterations = 10
def _format_tools(self) -> str:
"""格式化工具描述"""
tool_desc = []
for i, tool in enumerate(self.tools):
tool_desc.append(
f"{i}. {tool['name']}: {tool['description']}"
)
return "\n".join(tool_desc)
def _create_prompt(self, question: str, history: List[Dict]) -> List[Dict]:
"""创建带 ReAct 格式的系统提示"""
system_prompt = f"""你是一个使用 ReAct 模式的 AI Agent。
## 工作流程
对于每个问题,你需要按以下格式思考和行动:
Thought: 你现在的思考,分析问题
Action: 你要执行的行动(如:search_web, calculator, get_weather)
Action_Input: 行动的具体输入参数
Observation: 观察行动的结果
最后用以下格式回答:
Answer: 最终答案
## 可用工具
{self._format_tools()}
## 重要规则
1. 每一步必须包含 Thought 和 Action
2. 如果无法回答,说 "Answer: 无法回答"
3. 最多执行 {self.max_iterations} 步
"""
messages = [{"role": "system", "content": system_prompt}]
messages.extend(history)
messages.append({"role": "user", "content": question})
return messages
def _execute_tool(self, tool_name: str, tool_input: str) -> str:
"""执行工具调用"""
# 简化实现:实际项目中这里会真正调用工具
print(f"🔧 执行工具: {tool_name}({tool_input})")
# 模拟工具返回
if tool_name == "calculator":
try:
result = eval(tool_input)
return f"计算结果: {result}"
except:
return "计算错误"
elif tool_name == "search":
return f"搜索「{tool_input}」的结果: [示例内容]"
else:
return f"工具 {tool_name} 执行完成"
def _parse_response(self, response: str) -> Dict[str, str]:
"""解析 LLM 返回的响应"""
lines = response.strip().split("\n")
result = {}
for line in lines:
if line.startswith("Thought:"):
result["thought"] = line.replace("Thought:", "").strip()
elif line.startswith("Action:"):
result["action"] = line.replace("Action:", "").strip()
elif line.startswith("Action_Input:"):
result["action_input"] = line.replace("Action_Input:", "").strip()
elif line.startswith("Answer:"):
result["answer"] = line.replace("Answer:", "").strip()
elif line.startswith("Observation:"):
result["observation"] = line.replace("Observation:", "").strip()
return result
def run(self, question: str) -> str:
"""运行 ReAct Agent"""
history = []
for i in range(self.max_iterations):
# 1. 获取 LLM 响应
messages = self._create_prompt(question, history)
response = self.client.chat.completions.create(
model="gpt-4",
messages=messages
)
llm_output = response.choices[0].message.content
print(f"\n📝 步骤 {i+1} LLM输出:\n{llm_output}\n")
# 2. 解析响应
parsed = self._parse_response(llm_output)
# 3. 记录历史
history.append({"role": "assistant", "content": llm_output})
# 4. 检查是否得到答案
if "answer" in parsed:
return parsed["answer"]
# 5. 执行工具
if "action" in parsed and "action_input" in parsed:
tool_result = self._execute_tool(
parsed["action"],
parsed["action_input"]
)
observation = f"Observation: {tool_result}"
print(f"📍 {observation}\n")
history.append({
"role": "assistant",
"content": observation
})
return "达到最大迭代次数,未能得到答案"
# 定义工具
tools = [
{
"name": "calculator",
"description": "执行数学计算,如: 25 * 3 + 10"
},
{
"name": "search",
"description": "搜索网络信息,如: 北京天气"
}
]
# 运行 Agent
if __name__ == "__main__":
agent = ReActAgent(tools)
question = "25美元兑换人民币,按7.2汇率计算"
result = agent.run(question)
print(f"\n🎯 最终答案: {result}")
使用 LangChain 实现 ReAct
LangChain 提供了更完善的 ReAct 实现:
from langchain import hub
from langchain.agents import AgentType, initialize_agent
from langchain.agents.format_scratchpad import format_xml
from langchain.tools import Tool
from langchain_openai import ChatOpenAI
# 定义工具
def calculate(expression: str) -> str:
"""计算器工具"""
try:
return f"计算结果: {eval(expression)}"
except:
return "计算错误"
def search_wikipedia(query: str) -> str:
"""搜索工具(示例)"""
return f"关于「{query}」的信息..."
tools = [
Tool.from_function(
func=calculate,
name="calculator",
description="用于数学计算,输入数学表达式"
),
Tool.from_function(
func=search_wikipedia,
name="wikipedia",
description="搜索维基百科获取信息"
)
]
# 加载 ReAct 提示词
prompt = hub.pull("hwchase17/react")
# 初始化 Agent
llm = ChatOpenAI(model="gpt-4", temperature=0)
agent = initialize_agent(
tools=tools,
llm=llm,
agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,
verbose=True
)
# 运行
result = agent.run("北京的人口是多少?然后计算如果每人发100元需要多少钱?")
进阶拓展
ReAct 的优缺点
优点:
- ✅ 推理过程透明,可追踪
- ✅ 易于调试和优化
- ✅ 适用于需要多步推理的任务
- ✅ 与工具调用天然结合
缺点:
- ❌ 迭代次数多,token 消耗大
- ❌ 推理速度受限于 LLM 调用
- ❌ 复杂的推理链可能出错
优化策略
1. 控制推理长度
# 设置最大 token 限制
max_tokens = 500 # 限制单次输出
# 提前终止
if len(steps) > 5 and confidence < 0.8:
return "需要更多信息"
2. 使用更强的模型
# 简单问题用小模型
model = "gpt-3.5-turbo" # 快速
# 复杂推理用大模型
model = "gpt-4" # 准确
3. 添加自我反思
Thought: 我已经完成了计算,但让我检查一下...
Reflection: 计算过程看起来正确,但可能遗漏了...
Action: verify_calculation
总结
本文深入讲解了 ReAct 模式:
- ReAct = Reasoning + Acting,让 Agent 边想边做
- 核心循环:Thought → Action → Observation → Thought...
- 优势:推理透明、易调试、适合复杂任务
- 实践:可以用纯 Python 或 LangChain 实现
ReAct 是 Agent 开发的基础模式,后续的复杂功能(工具调用、多Agent协作)都是基于此构建。
下篇预告: 在下一篇文章中,我们将介绍 MCP 协议——Anthropic 推出的标准化 Agent 通信协议。你将学习到:
- MCP 协议的概念和优势
- 如何快速搭建一个 MCP Server
- MCP 与现有框架的集成
参考资料
本文是「AI Agent 开发实战系列」第 2 篇,系列共 10 篇。