深入理解 ReAct 模式:让大模型学会推理与行动
6135 字
31 分钟
深入理解 ReAct 模式:让大模型学会推理与行动
1. ReAct 的引入
1.1 传统 LLM 的局限
传统的大语言模型本质上是一个封闭的对话系统:
- 只能基于训练数据:无法获取实时信息
- 无法执行操作:不能调用工具、操作外部系统
- 推理受限:无法与外部世界交互验证
- 无法长期记忆:每次对话独立,无持久化
这就像一个博学但孤立的天才:
- 知道很多,但不能查询最新信息
- 能推理,但不能验证
- 能说话,但不能行动
1.2 ReAct 的诞生
ReAct (Reasoning + Acting) 由 Shunyu Yao 等人在 2022 年 10 月提出 (ICLR 2023):
将”思考”和”行动”统一在一个框架中,让 LLM 在推理的同时执行操作,与外部世界交互。
核心洞察:
传统 CoT: 思考 → 答案(无行动)
ReAct: 思考 + 行动 → 观察 → 思考 → ... → 答案
↓ ↓ Reason 决定 Act 执行操作 下一步怎么办 并获取反馈1.3 ReAct 的革命性意义
ReAct 标志着 LLM 从纯推理模型进化为Agent 系统:
| 维度 | 纯推理模型 | ReAct Agent |
|---|---|---|
| 推理 | ✓ | ✓ |
| 工具使用 | ✗ | ✓ |
| 外部交互 | ✗ | ✓ |
| 错误恢复 | 弱 | 强 |
| 长期任务 | ✗ | ✓ |
| 可验证性 | 低 | 高 |
1.4 ReAct 发展简史
2022.10: ReAct (Yao et al.) │ 首次提出 Thought-Action-Observation 循环 │2023: AutoGPT / BabyAGI 兴起 │ 基于 ReAct 思路的自主 Agent │2023.06: Toolformer (Schick et al.) │ 让 LLM 学会自动调用工具 │2023.07: LangChain 集成 ReAct │ 主流框架支持 │2023.11: OpenAI Function Calling │ 原生工具调用能力 │2024: Agent 系统爆发 │ GPTs, Claude Tools, Gemini Function Call │2024-2025: ReAct 的局限反思 │ Plan-and-Execute, Reflexion, ... │ ReAct 仍是最经典的基础模式2. ReAct 的核心思想
2.1 人类认知的类比
人类解决问题的过程:
遇到问题:我的航班延误了,需要改签
思考 1: 我需要先了解我的预订信息行动 1: 查看我的预订 (Search Booking)观察 1: 订单 #12345, 7:00 出发
思考 2: 我需要知道航班状态行动 2: 查询航班状态 (Check Flight Status)观察 2: 航班已取消
思考 3: 我需要找到替代航班行动 3: 搜索替代航班 (Search Alternatives)观察 3: 有 3 个选择: 9:00, 14:00, 18:00
思考 4: 让我选 14:00 的,因为时间合适行动 4: 改签航班 (Reschedule Flight)观察 4: 改签成功
思考 5: 任务完成,告知用户最终答案: 航班已成功改签至 14:002.2 Thought-Action-Observation 循环
ReAct 的核心是三个元素的循环:
┌─────────────────────────────────────────────────────────────────┐│ ReAct 循环 │├─────────────────────────────────────────────────────────────────┤│ ││ Thought (思考) ││ "我需要做什么?" ││ │ ││ ▼ ││ Action (行动) ││ "执行具体的工具调用" ││ │ ││ ▼ ││ Observation (观察) ││ "我得到了什么结果?" ││ │ ││ ▼ ││ Thought (再次思考) ││ "根据结果,下一步怎么做?" ││ │ ││ ▼ ││ ... 循环直到得出答案 ... ││ │└─────────────────────────────────────────────────────────────────┘2.3 ReAct 的优势
2.3.1 透明性
每个 Thought 都展示了 LLM 的”思维过程”:
- 可审计:可以追踪决策路径
- 可调试:错误容易定位
- 可解释:用户能理解 AI 在想什么
2.3.2 可验证
通过实际执行行动来验证推理:
- 不只靠 LLM “想”,还要靠工具”验证”
- 减少幻觉和错误
2.3.3 可扩展
通过添加新工具扩展能力:
- 添加搜索引擎 → 联网能力
- 添加数据库 → 业务数据访问
- 添加 API → 各种服务
2.4 ReAct 的数学形式
ReAct 可以形式化为一个部分可观察的马尔可夫决策过程 (POMDP):
- 状态 (State) :历史 Thought-Action-Observation 序列
- 动作 (Action) :选择下一步 Thought 或工具调用
- 观测 (Observation) :工具执行结果
- 奖励 (Reward) :任务完成度
策略 由 LLM 参数化。
3. ReAct 的核心机制
3.1 Prompt 模板
经典的 ReAct Prompt 模板:
REACT_PROMPT_TEMPLATE = """回答以下问题,请使用 Thought-Action-Observation 的方式一步步推理。
可用工具:{tool_descriptions}
格式要求:Thought: [你对当前情况的分析]Action: [工具名称][工具输入]Observation: [工具返回的结果]... (重复 Thought/Action/Observation 直到有足够信息)Thought: 我现在可以给出最终答案了Final Answer: [你的最终答案]
开始!
问题:{question}历史:{history}Thought:"""3.2 Thought 的类型
ReAct 中有不同类型的 Thought:
class ThoughtType: """Thought 的类型分类"""
PLANNING = "planning" # 规划:分解任务 INFORMATION_SEEKING = "info" # 信息寻求:寻找信息 REASONING = "reasoning" # 推理:逻辑推导 CALCULATION = "calculation" # 计算:数学运算 VERIFICATION = "verification" # 验证:检查正确性 RE_REVISION = "revision" # 修正:纠正错误 FINAL_ANSWER = "final" # 最终答案示例:
Thought 1: 我需要先了解用户的预订信息类型: INFORMATION_SEEKING
Thought 2: 用户预订在 7:00,因此需要查询这个时间的航班类型: PLANNING
Thought 3: 7:00 的航班是 AA123,让我计算是否会延误类型: CALCULATION
Thought 4: 根据观察,航班延误 2 小时,我需要找替代方案类型: REASONING
Thought 5: 让我验证一下这个替代航班是否还有座位类型: VERIFICATION
Thought 6: 现有 5 个座位,可以预订,给用户最终建议类型: FINAL_ANSWER3.3 Action 的格式
Action 通常采用解析友好的格式:
方法 1: 字符串解析(传统 ReAct)Action: Search[北京今天的天气]
方法 2: JSON 解析(现代)Action: { "tool": "Search", "input": "北京今天的天气"}
方法 3: Function Calling(OpenAI 风格)Action: <invoke name="Search"><parameter name="query">北京今天的天气</parameter></invoke>3.4 Observation 的处理
Observation 是工具的执行结果:
方法 1: 字符串结果Observation: 今天北京晴天,温度 25°C
方法 2: JSON 结果Observation: {"weather": "sunny", "temperature": 25}
方法 3: 结构化数据Observation:{ "status": "success", "data": [1, 2, 3], "message": "查询成功"}3.5 完成检测
检测 ReAct 循环何时终止:
def is_finished(response): """ 检测 LLM 是否给出最终答案 """ indicators = [ "Final Answer:", "Finish[", "答案:", "<final_answer>", "<invoke name=\"Finish\">", ]
for indicator in indicators: if indicator in response: return True
return False4. 工具系统设计
4.1 工具的定义
from typing import Callable, Dict, Anyfrom dataclasses import dataclass
@dataclassclass Tool: """工具定义""" name: str description: str func: Callable parameters: Dict[str, Any] = None
def to_schema(self): """转换为 OpenAI function calling 格式""" return { "type": "function", "function": { "name": self.name, "description": self.description, "parameters": self.parameters } }
def execute(self, **kwargs): """执行工具""" try: result = self.func(**kwargs) return f"Observation: {result}" except Exception as e: return f"Observation: Error - {str(e)}"4.2 工具注册表
class ToolRegistry: """工具注册表"""
def __init__(self): self.tools: Dict[str, Tool] = {}
def register(self, tool: Tool): """注册工具""" self.tools[tool.name] = tool
def get(self, name: str) -> Tool: """获取工具""" return self.tools.get(name)
def to_prompt(self) -> str: """生成工具描述(用于 prompt)""" descriptions = [] for tool in self.tools.values(): desc = f"- {tool.name}: {tool.description}" if tool.parameters: params = ", ".join( f"{k}: {v.get('type', 'string')}" for k, v in tool.parameters.get("properties", {}).items() ) desc += f"\n 参数: {params}" descriptions.append(desc) return "\n".join(descriptions)
def to_openai_schema(self): """生成 OpenAI function calling schema""" return [tool.to_schema() for tool in self.tools.values()]4.3 常用工具示例
# 1. 搜索工具def search(query: str) -> str: """搜索信息""" # 实际实现可能调用 Google API return f"搜索结果: {query} 的相关信息..."
# 2. 计算工具def calculator(expression: str) -> str: """计算数学表达式""" try: result = eval(expression) return f"计算结果: {result}" except Exception as e: return f"计算错误: {e}"
# 3. 数据库查询工具def query_database(sql: str) -> str: """查询数据库""" # 实际执行 SQL return f"查询结果: ..."
# 4. 文件读取工具def read_file(path: str) -> str: """读取文件内容""" with open(path, 'r', encoding='utf-8') as f: return f.read()
# 5. API 调用工具def call_api(endpoint: str, params: dict) -> str: """调用 HTTP API""" import requests response = requests.get(endpoint, params=params) return response.text
# 注册工具registry = ToolRegistry()registry.register(Tool( name="Search", description="搜索网络信息", func=search, parameters={ "type": "object", "properties": { "query": {"type": "string", "description": "搜索关键词"} }, "required": ["query"] }))registry.register(Tool( name="Calculator", description="计算数学表达式", func=calculator, parameters={ "type": "object", "properties": { "expression": {"type": "string", "description": "数学表达式"} }, "required": ["expression"] }))5. 完整 ReAct Agent 实现
5.1 基础实现
import refrom typing import List, Tuplefrom openai import OpenAI
class ReActAgent: """ 完整的 ReAct Agent 实现 """ def __init__(self, llm, tools: ToolRegistry, max_steps=10, max_context=5): self.llm = llm self.tools = tools self.max_steps = max_steps self.max_context = max_context
def run(self, question: str) -> str: """执行 ReAct 循环""" history = []
for step in range(self.max_steps): # 1. 构建 prompt prompt = self._build_prompt(question, history)
# 2. 调用 LLM response = self.llm.generate(prompt)
# 3. 解析响应 thought, action = self._parse_response(response)
# 4. 检查是否完成 if self._is_finished(response): return self._extract_final_answer(response)
# 5. 检查是否有有效行动 if action is None: # LLM 没有产生有效行动,反馈给 LLM history.append({ 'thought': thought, 'action': None, 'observation': '请选择一个有效的工具或给出最终答案。' }) continue
# 6. 执行工具 tool_name, tool_input = action observation = self._execute_tool(tool_name, tool_input)
# 7. 记录历史 history.append({ 'thought': thought, 'action': action, 'observation': observation })
# 达到最大步数 return "达到最大步数,无法完成任务。"
def _build_prompt(self, question, history): """构建 prompt""" tools_desc = self.tools.to_prompt()
history_str = "" for h in history[-self.max_context:]: history_str += f"\nThought: {h['thought']}" if h['action']: history_str += f"\nAction: {h['action'][0]}[{h['action'][1]}]" history_str += f"\nObservation: {h['observation']}" elif h.get('observation'): history_str += f"\nObservation: {h['observation']}"
return REACT_PROMPT_TEMPLATE.format( tool_descriptions=tools_desc, question=question, history=history_str )
def _parse_response(self, response): """解析 Thought 和 Action""" thought_match = re.search(r'Thought:\s*(.+?)(?=Action:|$)', response, re.DOTALL) action_match = re.search(r'Action:\s*(\w+)\[(.+?)\]', response)
thought = thought_match.group(1).strip() if thought_match else ""
action = None if action_match: tool_name = action_match.group(1) tool_input = action_match.group(2) action = (tool_name, tool_input)
return thought, action
def _is_finished(self, response): """检测是否完成""" return "Final Answer:" in response or "Finish[" in response
def _extract_final_answer(self, response): """提取最终答案""" match = re.search(r'Final Answer:\s*(.+)', response, re.DOTALL) if match: return match.group(1).strip() return response
def _execute_tool(self, name, input_str): """执行工具""" tool = self.tools.get(name) if not tool: return f"错误:未知工具 {name}"
try: # 简单的字符串输入处理 # 实际实现可能需要更复杂的参数解析 return tool.execute(input=input_str) except Exception as e: return f"执行错误:{e}"5.2 使用 OpenAI Function Calling
class ReActAgentFunctionCall(ReActAgent): """ 使用 OpenAI Function Calling 的 ReAct Agent """ def __init__(self, llm, tools: ToolRegistry, max_steps=10): super().__init__(llm, tools, max_steps) self.client = OpenAI()
def run(self, question: str) -> str: """使用 Function Calling 运行""" messages = [{"role": "user", "content": question}] tool_schemas = self.tools.to_openai_schema()
for step in range(self.max_steps): # 1. 调用 LLM response = self.client.chat.completions.create( model="gpt-4", messages=messages, tools=tool_schemas, )
message = response.choices[0].message messages.append(message)
# 2. 检查是否有工具调用 if message.tool_calls: for tool_call in message.tool_calls: tool_name = tool_call.function.name tool_args = json.loads(tool_call.function.arguments)
# 执行工具 tool = self.tools.get(tool_name) result = tool.execute(**tool_args)
# 添加结果 messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": str(result) }) else: # 没有工具调用,返回最终答案 return message.content
return "达到最大步数"6. ReAct 的实例应用
6.1 智能客服 Agent
class CustomerServiceAgent(ReActAgent): """ 智能客服 Agent 工具集:订单查询、退款、推荐等 """ def __init__(self, llm): tools = ToolRegistry() tools.register(Tool( name="query_order", description="查询订单状态", func=self._query_order, parameters={ "type": "object", "properties": { "order_id": {"type": "string"} } } )) tools.register(Tool( name="process_refund", description="处理退款", func=self._process_refund, parameters={ "type": "object", "properties": { "order_id": {"type": "string"}, "reason": {"type": "string"} } } )) tools.register(Tool( name="recommend_product", description="推荐相关商品", func=self._recommend_product, parameters={ "type": "object", "properties": { "category": {"type": "string"} } } ))
super().__init__(llm, tools)
def _query_order(self, order_id: str) -> str: # 实际查询订单 return f"订单 {order_id}: 已发货,预计明天送达"
def _process_refund(self, order_id: str, reason: str) -> str: return f"订单 {order_id} 的退款已处理,原因:{reason}"
def _recommend_product(self, category: str) -> str: return f"在 {category} 类别下,推荐: 商品A, 商品B, 商品C"6.2 研究助手 Agent
class ResearchAgent(ReActAgent): """ 学术研究助手 工具集:论文搜索、摘要、引用查询等 """ def __init__(self, llm): tools = ToolRegistry()
# 注册学术工具 tools.register(Tool( name="arxiv_search", description="在 arXiv 搜索论文", func=self._arxiv_search )) tools.register(Tool( name="summarize_paper", description="总结论文内容", func=self._summarize_paper )) tools.register(Tool( name="find_citations", description="查找引用", func=self._find_citations )) tools.register(Tool( name="compare_methods", description="比较方法", func=self._compare_methods ))
super().__init__(llm, tools)
def research_question(self, question: str) -> str: return self.run(question)6.3 旅行规划 Agent
# 完整的 ReAct 旅行规划示例
travel_tools = ToolRegistry()
# 注册工具travel_tools.register(Tool( name="SearchFlights", description="搜索航班", func=lambda origin, destination, date: f"{origin} 到 {destination} ({date}): 直飞 ¥1500, 9:00-11:00"))
travel_tools.register(Tool( name="SearchHotels", description="搜索酒店", func=lambda city, checkin, nights: f"{city} 酒店 ({checkin}, {nights}晚): A酒店 ¥500/晚, B酒店 ¥800/晚"))
travel_tools.register(Tool( name="GetWeather", description="查询天气", func=lambda city, date: f"{city} ({date}): 晴 25°C"))
travel_tools.register(Tool( name="BookFlight", description="预订航班", func=lambda flight_id: f"已预订航班 {flight_id}"))
# 运行agent = ReActAgent(llm, travel_tools)result = agent.run("""我想去北京旅行 3 天,从上海出发,明天出发。帮我安排:1. 合适的航班2. 性价比高的酒店3. 查看当时的天气""")
# ReAct 执行的可能轨迹:# Thought: 需要先搜索航班# Action: SearchFlights[上海, 北京, 明天]# Observation: 直飞 ¥1500, 9:00-11:00## Thought: 现在搜索酒店# Action: SearchHotels[北京, 2026-07-17, 3]# Observation: A酒店 ¥500/晚## Thought: 查看天气# Action: GetWeather[北京, 2026-07-17]# Observation: 晴 25°C## Thought: 天气好,选择经济型酒店,给出总结# Final Answer: 推荐方案...7. ReAct 的改进方法
7.1 Reflexion: 自我反思的 ReAct
Reflexion 在 ReAct 基础上添加反思机制:
class ReflexionAgent(ReActAgent): """ Reflexion: 反思增强的 ReAct """ def __init__(self, llm, tools, max_reflections=3): super().__init__(llm, tools) self.max_reflections = max_reflections self.reflections = []
def run(self, question: str) -> str: """带反思的运行""" for attempt in range(self.max_reflections + 1): # 1. 执行 ReAct result = super().run(question)
# 2. 评估结果 evaluation = self._evaluate_result(question, result)
# 3. 如果成功,返回 if evaluation['success']: return result
# 4. 否则,反思 if attempt < self.max_reflections: reflection = self._reflect(question, result, evaluation) self.reflections.append(reflection)
return result
def _reflect(self, question, result, evaluation): """反思失败原因""" prompt = f""" 问题:{question} 结果:{result} 评估:{evaluation['reason']}
请反思: 1. 哪里出错了? 2. 如何改进? 3. 下次应该采取什么不同的策略?
反思: """ return self.llm.generate(prompt)
def _build_prompt(self, question, history): """构建带反思历史的 prompt""" base_prompt = super()._build_prompt(question, history)
if self.reflections: reflection_text = "\n\n之前的反思:\n" + "\n".join(self.reflections) return base_prompt + reflection_text return base_prompt7.2 ReAct + 计划 (Plan-and-Execute)
先制定计划,再执行:
class PlanAndExecuteAgent: """ Plan-and-Execute: 先计划后执行 """ def __init__(self, llm, tools): self.llm = llm self.tools = tools
def run(self, question: str) -> str: # 1. 计划阶段 plan = self._create_plan(question)
# 2. 执行阶段 results = [] for step in plan: result = self._execute_step(step, results) results.append(result)
# 3. 总结 return self._summarize(question, plan, results)
def _create_plan(self, question): """创建执行计划""" prompt = f""" 问题:{question}
请创建一个详细的执行计划,列出所有需要完成的步骤。 每个步骤应该是一个可以执行的操作。
计划: """ plan_text = self.llm.generate(prompt)
# 解析计划 steps = [] for line in plan_text.split('\n'): if re.match(r'\d+\.', line.strip()): steps.append(line.strip())
return steps7.3 多 Agent 协作
class MultiAgentReAct: """ 多 Agent 协作系统 """ def __init__(self, llm, agents: Dict[str, ReActAgent]): self.llm = llm self.agents = agents self.supervisor = ReActAgent(llm, self._supervisor_tools())
def run(self, question: str) -> str: """多 Agent 协作""" # 1. Supervisor 决定分配 decision = self._assign_agent(question) agent_name = decision['agent']
# 2. 调用专门的 Agent agent = self.agents[agent_name] result = agent.run(decision['subtask'])
# 3. Supervisor 整合结果 final = self._integrate(question, result)
return final7.4 ReWOO: 减少 token 消耗
ReWOO 通过预先生成所有工具调用减少重复:
class ReWOOAgent: """ ReWOO: Reason WithOut Observation 一次性规划,减少观察次数 """ def __init__(self, llm, tools): self.llm = llm self.tools = tools
def run(self, question): # 1. 规划阶段:一次性生成所有步骤 plan = self._create_full_plan(question)
# 2. 执行阶段:执行所有工具调用 evidence = {} for step in plan.steps: result = self._execute_tool_call(step) evidence[step.id] = result
# 3. 总结阶段:基于所有证据回答 return self._synthesize(plan, evidence, question)8. 错误处理与恢复
8.1 常见错误类型
class ReActErrorHandler: """ ReAct 错误处理器 """
ERROR_TYPES = { 'invalid_action': 'LLM 生成了无效的工具调用', 'tool_failure': '工具执行失败', 'no_progress': '多次循环无进展', 'max_steps': '达到最大步数', 'context_overflow': '上下文超过限制', 'infinite_loop': '陷入循环' }
def handle_error(self, history, error_type): """处理错误""" if error_type == 'invalid_action': return self._handle_invalid_action(history) elif error_type == 'tool_failure': return self._handle_tool_failure(history) elif error_type == 'infinite_loop': return self._handle_infinite_loop(history) # ...
def _handle_infinite_loop(self, history): """处理循环""" # 检测最近的步骤是否重复 if len(history) >= 3: last_three = history[-3:] if (last_three[0]['thought'] == last_three[2]['thought'] and last_three[0]['action'] == last_three[2]['action']): # 强制给予反馈 return "警告:检测到循环,请尝试不同的方法。" return None8.2 健壮性增强
class RobustReActAgent(ReActAgent): """ 健壮的 ReAct Agent """ def __init__(self, *args, max_retries=3, **kwargs): super().__init__(*args, **kwargs) self.max_retries = max_retries self.visited_states = set()
def run(self, question): """带错误处理的运行""" for attempt in range(self.max_retries): try: return super().run(question) except Exception as e: if attempt < self.max_retries - 1: print(f"出错:{e}, 重试...") self.visited_states.clear() else: return f"多次失败: {e}"8.3 循环检测
class LoopDetector: """循环检测器"""
def __init__(self, threshold=3): self.threshold = threshold self.recent_states = []
def record(self, thought, action): """记录状态""" state = (thought[:100], action) # 截断以处理长 thought self.recent_states.append(state)
if len(self.recent_states) > self.threshold: self.recent_states.pop(0)
def is_looping(self): """检测是否循环""" if len(self.recent_states) < self.threshold: return False
# 检查是否有重复 states = self.recent_states[-self.threshold:] return len(set(map(str, states))) < len(states)9. 评估 ReAct Agent
9.1 评估指标
class ReActEvaluator: """ ReAct Agent 评估器 """
METRICS = [ 'task_success_rate', # 任务成功率 'average_steps', # 平均步数 'tool_accuracy', # 工具调用准确率 'thought_quality', # 思考质量 'efficiency', # 效率 'hallucination_rate', # 幻觉率 'recovery_rate', # 错误恢复率 ]
def evaluate(self, agent, dataset): """评估 Agent""" results = { 'success_count': 0, 'total_count': 0, 'total_steps': 0, 'tool_calls': [], 'thoughts': [], }
for item in dataset: result = agent.run(item.question) metrics = self._compute_metrics(result, item.expected) self._update_results(results, metrics)
# 汇总 total = results['total_count'] return { 'task_success_rate': results['success_count'] / total, 'average_steps': results['total_steps'] / total, # ... 其他指标 }
def _compute_metrics(self, result, expected): """计算单个样本的指标""" return { 'success': self._check_success(result, expected), 'num_steps': self._count_steps(result), }9.2 基准测试
class ReActBenchmark: """ReAct 标准基准"""
BENCHMARKS = { 'HotpotQA': '多跳问答', 'FEVER': '事实核查', 'AlfWorld': '文本游戏', 'WebShop': '网页购物', 'Mind2Web': '网页操作', 'SWE-bench': '代码任务', }
def run_benchmark(self, agent, benchmark_name): """在基准上评估""" if benchmark_name == 'HotpotQA': return self._eval_hotpotqa(agent) elif benchmark_name == 'FEVER': return self._eval_fever(agent) # ...9.3 性能基准结果
| Agent | HotpotQA | FEVER | AlfWorld | 平均步数 |
|---|---|---|---|---|
| Direct | 28.5% | 56.2% | 12% | 1 |
| CoT | 35.7% | 62.4% | 18% | 1 |
| ReAct (GPT-3.5) | 42.8% | 68.9% | 35% | 3.5 |
| ReAct (GPT-4) | 56.4% | 78.2% | 62% | 4.2 |
| ReAct+Reflexion | 61.3% | 81.5% | 71% | 5.8 |
| Plan-and-Execute | 58.2% | 79.8% | 68% | 4.0 |
10. ReAct 的局限与挑战
10.1 主要局限
| 局限 | 描述 | 缓解方法 |
|---|---|---|
| Token 消耗大 | 每步都生成 Thought+Action | ReWOO, 批处理 |
| 容易陷入循环 | 重复相同的 Thought | 循环检测 |
| Thought 质量依赖 LLM | LLM 弱则 Thought 差 | 用更强的 LLM |
| 工具错误敏感 | 工具失败易导致整体失败 | 容错处理 |
| 步骤多时上下文膨胀 | 历史过长 | 总结、截断 |
| 难以并行 | 顺序执行 | 部分并行化 |
10.2 与其他模式的对比
| 模式 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|
| 直接回答 | 快、便宜 | 无推理 | 简单任务 |
| CoT | 推理强 | 不能交互 | 推理任务 |
| ReAct | 可交互 | 慢、贵 | Agent 任务 |
| Plan-Execute | 效率高 | 计划失误难恢复 | 复杂多步 |
| Reflexion | 自学习 | 反思也可能错 | 反复尝试 |
| Tree of Thoughts | 全局搜索 | 计算成本高 | 复杂搜索 |
10.3 工程挑战
# 1. Prompt 工程的挑战challenges = { 'parsing': 'Action 解析容易失败', 'context_management': '上下文管理困难', 'tool_design': '工具设计影响效果', 'hallucination': 'LLM 幻觉', 'latency': '延迟较高', 'cost': 'Token 成本'}
# 解决方案solutions = { 'parsing': '使用 Function Calling 替代字符串解析', 'context_management': '定期总结、滑动窗口', 'tool_design': '明确的工具描述、清晰的参数', 'hallucination': '强制基于 Observation 推理', 'latency': '并行执行、缓存', 'cost': 'ReWOO、模型选择',}11. 性能优化
11.1 减少 Token 消耗
class TokenEfficientReAct(ReActAgent): """Token 高效的 ReAct"""
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.history_summary = ""
def _build_prompt(self, question, history): """使用总结而非完整历史""" # 总结历史而不是直接使用 if len(history) > 5: self.history_summary = self._summarize_history(history) history_text = self.history_summary else: history_text = self._format_history(history)
return super()._build_prompt(question_with_replaced_history=history_text)
def _summarize_history(self, history): """总结历史""" history_text = self._format_history(history) prompt = f"总结以下 agent 的执行历史:\n{history_text}\n总结:" return self.llm.generate(prompt)11.2 并行化
class ParallelReAct(ReActAgent): """支持并行工具调用的 ReAct"""
def _execute_tool(self, name, input_str): # 如果是独立的工具调用,可以并行执行 return super()._execute_tool(name, input_str)
def run(self, question): history = [] for step in range(self.max_steps): response = self.llm.generate(self._build_prompt(question, history)) thought, actions = self._parse_multi_actions(response)
if self._is_finished(response): return self._extract_final_answer(response)
# 并行执行多个独立的 actions if len(actions) > 1: observations = self._parallel_execute(actions) for action, obs in zip(actions, observations): history.append({ 'thought': thought, 'action': action, 'observation': obs }) elif actions: observation = self._execute_tool(*actions[0]) history.append({ 'thought': thought, 'action': actions[0], 'observation': observation })11.3 缓存
import hashlib
class CachedReActAgent(ReActAgent): """带缓存的 ReAct"""
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.cache = {}
def _execute_tool(self, name, input_str): """带缓存的工具执行""" cache_key = hashlib.md5(f"{name}:{input_str}".encode()).hexdigest()
if cache_key in self.cache: return self.cache[cache_key]
result = super()._execute_tool(name, input_str) self.cache[cache_key] = result return result12. 实际部署考虑
12.1 安全考虑
class SafeReActAgent(ReActAgent): """安全的 ReAct Agent"""
def __init__(self, *args, sensitive_tools=None, **kwargs): super().__init__(*args, **kwargs) self.sensitive_tools = sensitive_tools or set() self.require_confirmation = True
def _execute_tool(self, name, input_str): """带安全检查的执行""" # 1. 检查是否敏感工具 if name in self.sensitive_tools: if self.require_confirmation: if not self._confirm_action(name, input_str): return "用户拒绝执行此操作"
# 2. 检查参数 if not self._validate_input(name, input_str): return "参数验证失败"
# 3. 执行 return super()._execute_tool(name, input_str)
def _validate_input(self, name, input_str): """验证输入,防止注入""" # SQL 注入检查 if 'sql' in name.lower(): dangerous = ['DROP', 'DELETE', 'UPDATE', 'INSERT'] if any(kw in input_str.upper() for kw in dangerous): return False
# 命令注入检查 if 'shell' in name.lower() or 'exec' in name.lower(): dangerous = [';', '|', '&', '`', '$'] if any(c in input_str for c in dangerous): return False
return True12.2 监控与日志
class MonitoredReActAgent(ReActAgent): """带监控的 ReAct"""
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.metrics = []
def run(self, question): """带监控的运行""" import time start_time = time.time()
result = super().run(question)
# 记录指标 self.metrics.append({ 'timestamp': time.time(), 'duration': time.time() - start_time, 'num_steps': len(self.history), 'success': self._is_success(result), 'question_length': len(question), 'result_length': len(result), })
return result13. 与现代 Agent 框架的集成
13.1 LangChain 集成
from langchain.agents import initialize_agent, AgentTypefrom langchain.tools import Tool as LangChainTool
# 在 LangChain 中使用 ReActagent = initialize_agent( tools=langchain_tools, llm=llm, agent=AgentType.REACT_DOCSTORE, verbose=True)
result = agent.run("北京的人口是多少?")13.2 LlamaIndex 集成
from llama_index.agent import ReActAgentfrom llama_index.llms import OpenAI
# LlamaIndex 的 ReAct Agentagent = ReActAgent.from_tools( tools=[search_tool, query_tool], llm=OpenAI(model="gpt-4"), verbose=True)
response = agent.chat("2024 年诺贝尔物理学奖获得者是谁?")13.3 自研 Agent 框架
class CustomAgentFramework: """自研 Agent 框架"""
def __init__(self, llm, tools): self.react_agent = ReActAgent(llm, tools) self.reflection_agent = ReflexionAgent(llm, tools)
def run(self, question, mode='auto'): """根据复杂度选择模式""" if mode == 'auto': complexity = self._estimate_complexity(question) if complexity > 0.7: return self.reflection_agent.run(question) else: return self.react_agent.run(question) elif mode == 'react': return self.react_agent.run(question) elif mode == 'reflection': return self.reflection_agent.run(question)14. 核心概念总结
14.1 ReAct 的三大组件
REACT_COMPONENTS = """1. Thought (思考): - 分析当前状态 - 制定下一步策略 - 类型:规划、信息寻求、推理、计算、验证、修正
2. Action (行动): - 调用外部工具 - 格式:工具名[输入] 或 JSON - 类型:API 调用、查询、计算、操作
3. Observation (观察): - 工具返回的结果 - 信息来源:工具执行结果 - 用于下一步的 Thought"""14.2 ReAct 的核心循环
Thought_n → Action_n → Observation_n → Thought_{n+1} → Action_{n+1} → ... │ │ │ │ │ │ │ ▼ ▼ │ [更新历史] [输出结果] │ └─ "我应该做什么?"14.3 关键设计选择
| 设计选择 | 传统 ReAct | 现代 ReAct |
|---|---|---|
| Action 格式 | 字符串解析 | Function Calling |
| Prompt 模板 | 手工 | 自动生成 |
| 工具描述 | 简短 | 结构化 |
| 错误处理 | 无 | 完善 |
| 上下文管理 | 完整保留 | 总结/截断 |
15. 实战建议
15.1 设计 Agent 时的最佳实践
BEST_PRACTICES = """1. 工具设计: - 工具数量适中(5-15 个) - 每个工具职责单一 - 提供清晰的描述 - 验证所有输入
2. Prompt 设计: - 清晰的任务描述 - 详细的工具说明 - 完整的格式示例 - 错误处理提示
3. 错误处理: - 检测循环 - 处理无效 Action - 容错工具失败 - 设置最大步数
4. 性能优化: - 缓存重复结果 - 并行独立调用 - 总结历史记录 - 减少冗余 token
5. 安全考虑: - 验证输入 - 限制敏感操作 - 用户确认机制 - 审计日志"""15.2 调试技巧
DEBUGGING_TIPS = """1. 启用 verbose 模式: 显示每个 Thought/Action/Observation2. 记录所有历史: 便于回溯分析3. 单独测试每个工具: 确认工具正常4. 使用简单任务测试: 排除 bug5. 对比不同 LLM: 区分 LLM 和 Agent 框架问题6. 监控循环: 检测重复模式"""15.3 何时不使用 ReAct
def should_use_react(task): """判断是否应该使用 ReAct""" use_react = True
# 简单的问答不需要 if is_simple_question(task): use_react = False
# 数学计算可能用其他工具更合适 if is_pure_math(task): use_react = False # 直接用计算工具
# 需要实时信息才用 if not need_real_time(task) and not need_tools(task): use_react = False
return use_react16. 总结与展望
ReAct 的核心价值
- 统一推理与行动:打破推理模型的封闭性
- 可扩展 Agent 系统:通过工具扩展能力边界
- 透明可调试:Thought 提供决策透明度
- 实用性强:解决真实世界问题
ReAct 的局限与替代方案
| 问题 | 替代方案 |
|---|---|
| Token 消耗大 | ReWOO, Plan-and-Execute |
| 容易循环 | Reflexion, Plan-and-Execute |
| 上下文膨胀 | Memory 管理 + RAG |
| 错误传播 | 自适应重试 + 验证 |
| 长链效率低 | 多 Agent 协作 |
未来发展方向
- 更智能的 ReAct:自动选择何时推理、何时行动
- 多模态 ReAct:视觉、语音的统一
- 协作 ReAct:多个 Agent 协同解决复杂问题
- 持久化 ReAct:长期记忆和状态管理
- 自我改进的 ReAct:从历史经验学习
学习建议
- 理论:理解 Thought-Action-Observation 循环
- 实践:用 LangChain 或自己实现一个简单 Agent
- 进阶:研究 Reflexion、Plan-and-Execute 等改进
- 应用:将 ReAct 应用到自己的领域
- 前沿:关注 LLM Agent 领域的最新研究
ReAct 是 LLM Agent 时代的奠基石。掌握 ReAct 模式是理解现代 Agent 系统(GPTs、Claude Tools 等)的关键。无论技术如何演进,Thought-Action-Observation 的循环思想仍然是 Agent 系统设计的核心范式。
参考资料
- Yao, S., et al. (2022). “ReAct: Synergizing Reasoning and Acting in Language Models.” ICLR 2023.
- Wei, J., et al. (2022). “Chain-of-Thought Prompting Elicits Reasoning in Large Language Models.” NeurIPS.
- Shinn, N., et al. (2023). “Reflexion: Language Agents with Verbal Reinforcement Learning.” NeurIPS.
- Schick, T., et al. (2023). “Toolformer: Language Models Can Teach Themselves to Use Tools.” NeurIPS.
- Paranjape, B., et al. (2023). “ART: Automatic multi-step reasoning and tool-use for language models.” arXiv.
- Khot, T., et al. (2022). “Decomposed Prompting: A Modular Approach for Solving Complex Tasks.” ICLR.
- Press, O., et al. (2022). “Measuring and Narrowing the Compositionality Gap in Language Models.” EMNLP.
- Besta, M., et al. (2023). “Graph of Thoughts: Solving Elaborate Problems with Large Language Models.” AAAI.
- Chase, H. (2022). “LangChain: Building applications with LLMs through composability.” GitHub.
- Liu, J., et al. (2023). “ReWOO: Decoupling Reasoning from Observations for Efficient Augmented Language Models.” arXiv.
- Weng, L. (2023). “LLM-powered Autonomous Agents.” Lil’Log Blog.
- Wei, J., et al. (2024). “Simple Synthetic Data Reduces Sycophancy in Large Language Models.” arXiv.
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!
深入理解 ReAct 模式:让大模型学会推理与行动
https://aiattnstudio.link/posts/react-pattern/
