1. ReAct模式的定义
ReAct模式是智能体系统设计中一种经典的设计模式。它由普林斯顿大学和谷歌研究院的研究者们在2023年发表的ICLR论文《React: Synergizing Reasoning And Acting In Language Models》[1]中提出。其核心思想是模仿人类解决问题的方式,将推理 (Reasoning) 与行动 (Acting) 显式地结合起来,形成一个"思考-行动-观察"的循环,以迭代的方式逐步逼近问题的完整解决方案。
2. 核心价值
在ReAct出现之前,智能体系统的设计主要集中在纯思考型或纯行动型智能体上。然而,这两种设计模式都有各自的局限性。纯思考型智能体,如思维链 (Chain-of-Thought)在解决问题时能引导模型进行复杂的逻辑推理,但无法与外部环境进行交互,容易产生事实幻觉;而纯行动型智能体,由模型直接输出要执行的动作,缺乏规划和纠错能力,容易在复杂场景中出错。
ReAct模式解决了纯思考型和纯行动型智能体设计的局限性。它的巧妙之处在于,它认识到思考与行动是相辅相成的。思考指导行动,而行动的结果又反过来修正思考。通过将推理与行动显式地结合起来,ReAct智能体能够在解决问题的过程中进行迭代思考和决策,同时与外部环境进行交互。这使得ReAct智能体在需要复杂推理和决策的场景中表现出色,如问题求解、任务执行等。
3. 实现机制
3.1 核心流程
ReAct模式的核心是实现 "思考 - 行动 - 观察"循环(简称TAO循环),具体流程如下:
思考(Thought):
- 分析当前任务和已有信息,制定下一步行动计划
- 将复杂任务分解为可执行的子目标
- 生成自然语言推理轨迹,类似 CoT(思维链)
行动(Action):
- 根据思考结果调用外部工具(搜索、API、计算等)
- 格式化为 "Action: 工具名 (参数)" 的指令
- 执行具体操作,获取外部环境反馈
观察(Observation):
- 获取工具执行结果
- 分析结果,提取关键信息
- 更新上下文,为下一轮思考提供依据
循环与终止:
- 基于观察结果重新思考,调整策略,重复循环
- 当信息足够回答问题或达到最大迭代次数时,输出最终答案
3.2 提示词设计
ReAct模式依赖精心设计的提示词引导模型遵循 TAO 循环,典型结构如下:
plaintext
你是一个ReAct智能体,需严格按照以下格式解决问题:
问题: {用户查询}
可用工具: {工具描述列表}
历史记录: {思考-行动-观察循环记录}
请按照以下格式回复:
思考: 分析当前情况,规划下一步
行动: 工具名(参数) # 如果需要调用工具
# 或
答案: 最终答案 # 如果已有足够信息
4. 应用场景
ReAct模式适用于需要与外部世界交互或依赖动态信息的任务,典型场景包括:
- 需要外部知识的任务:如查询实时信息(天气、新闻、股价)、搜索专业领域知识等;
- 需要精确计算的任务:通过计算器工具避免 LLM 的计算错误;
- 需要与 API 交互的任务:如操作数据库、调用服务接口完成特定功能;
- 时效性要求较高的问题:例如 "华为最新手机型号及卖点" 这类依赖实时更新信息的问题。
4.1 复杂问答系统
多跳问答:如 HotpotQA,需从多个信息源获取信息并综合分析
plaintext
问题:"贝多芬与莫扎特谁的音乐生涯更长?"
ReAct流程:思考→搜索两人生平→观察生卒年份→计算职业生涯长度→比较→输出答案
4.2 知识密集型研究
- 学术研究辅助:文献检索、数据分析、论文撰写
- 市场分析:"分析 2024 年全球 AI 芯片市场格局及趋势"(需调用搜索、数据可视化工具)
4.3 交互式任务执行
- 客户服务自动化:查询订单、解决投诉、提供建议
- 数据分析报告生成:连接数据库、执行分析、生成可视化
- 网页操作自动化:表单填写、数据提取、导航
4.4 其他领域
- 代码开发辅助:调试、代码生成、文档编写
- 智能测试:生成测试用例、执行测试、分析结果
- 智能购物助手:产品比较、价格监控、推荐
5. 优势与挑战
5.1 优势
任何技术和范式都有其优势和局限。ReAct模式作为一种新兴的智能体设计模式,其优势主要体现在以下几个方面:
- 推理与行动的协同能力:ReAct模式将推理与行动显式地结合起来,使得智能体能够在解决问题的过程中进行迭代思考和决策,同时与外部环境进行交互。这使得ReAct智能体在需要复杂推理和决策的场景中表现出色,如问题求解、任务执行等。
- 动态规划和纠错能力:与一次性生成完整计划的范式不同,ReAct 是"走一步,看一步"。它根据每一步从外部世界获得的 Observation 来动态调整后续的 Thought 和 Action。如果上一步的搜索结果不理想,它可以在下一步中修正搜索词,重新尝试。
- 高可解释性:由于ReAct模式将推理与行动显式地结合起来,使得模型的决策过程更加透明和可解释。这为用户提供了理解模型决策的依据,同时也为模型的调试和优化提供了便利。
5.2 挑战
- 对LLM自身能力的强依赖:ReAct模式的成功与否,高度依赖于底层 LLM 的综合能力。如果 LLM 的逻辑推理能力、指令遵循能力或格式化输出能力不足,就很容易在 Thought 环节产生错误的规划,或者在 Action 环节生成不符合格式的指令,导致整个流程中断。
- 执行效率问题:由于其循序渐进的特性,完成一个任务通常需要多次调用 LLM。每一次调用都伴随着网络延迟和计算成本。对于需要很多步骤的复杂任务,这种串行的"思考-行动"循环可能会导致较高的总耗时和费用。
- 提示词的脆弱性:整个机制的稳定运行建立在一个精心设计的提示词模板之上。模板中的任何微小变动,甚至是用词的差异,都可能影响 LLM 的行为。此外,并非所有模型都能持续稳定地遵循预设的格式,这增加了在实际应用中的不确定性。
- 可能陷入局部最优:步进式的决策模式意味着智能体缺乏一个全局的、长远的规划。它可能会因为眼前的 Observation 而选择一个看似正确但长远来看并非最优的路径,甚至在某些情况下陷入"原地打转"的循环中。
6. 代码示例
以下是一个简单的 ReAct 模式代码示例,展示了如何使用 LangChain 实现一个基本的智能体,该智能体能够根据用户输入的问题,规划问题解决步骤和工具调用,得到最终答案。
测试问题:"贝多芬与莫扎特谁的音乐生涯更长?长多少天?"
python
import os
from dotenv import load_dotenv
from datetime import datetime
from langchain.agents import create_agent
from langchain.agents.middleware.tool_call_limit import ToolCallLimitMiddleware
from langchain_tavily import TavilySearch
from langchain.chat_models import init_chat_model
from langgraph.checkpoint.memory import InMemorySaver
from langchain.tools import tool
# 获取当前日期用于提示词背景信息
def get_today_str() -> str:
"""Get current date in a human-readable format.
Returns:
Current date formatted as 'Day Mon DD, YYYY' (e.g., 'Mon Jan 01, 2023')
"""
try:
# Try Linux/Unix format first
return datetime.now().strftime("%a %b %-d, %Y")
except ValueError:
# Fall back to Windows format if Linux format fails
return datetime.now().strftime("%a %b %#d, %Y")
# 生成系统提示词
def get_system_prompt(tools):
# ReAct 提示词模板
REACT_PROMPT_TEMPLATE = """
你是一个有能力调用外部搜索工具的ReAct智能体,你的任务是回答用户的问题。
作为背景信息,今天日期是: {today_str}
可用工具如下:
{tools}
<工作流程>
1. 分析用户的问题,思考解决问题的步骤,确定是否需要调用外部工具, 调用reasoning_record工具记录思考过程。
2. 如果需要,调用外部工具并获取结果。
3. 根据工具的输出结果,判断问题是否得到全面回答, 调用reasoning_record工具记录思考过程。
4. 如果回答问题不全面,思考缺少的信息,继续调用外部工具获取相关信息。
5. 重复以上步骤,直到问题得到全面回答或确定无法回答。
</工作流程>
<要求>
1. 每次调用外部搜索工具后,都要调用reasoning_record工具记录思考过程。
2. 外部搜索工具的调用次数不能超过5次。
3. 如果问题无法回答,要调用reasoning_record工具记录思考过程。
4. 必须使用和用户一致的语言回答问题。
</要求>
"""
return REACT_PROMPT_TEMPLATE.format(tools=tools, today_str=get_today_str())
# 获取对话模型
def get_model():
model = init_chat_model(
api_key = os.getenv("model_api_key"),
base_url = os.getenv("model_api_base"),
model = "Qwen/Qwen3-8B",
model_provider = "openai",
temperature = 0.7,
)
return model
# 定义记录思考过程的工具
@tool
def reasoning_record(thought: str):
"""Record the thought process of the agent."""
return thought
# 定义计算器工具
@tool
def date_calculator(start_date: str, end_date: str) -> int:
"""计算两个日期之间的天数差,用于计算两个事件之间的时间长度"""
try:
start = datetime.strptime(start_date, "%Y-%m-%d")
end = datetime.strptime(end_date, "%Y-%m-%d")
return (end - start).days
except ValueError as e:
return f"日期格式错误: {e}"
# 获取可用工具
def get_tools():
tools = [reasoning_record, date_calculator]
"""Search the web using Tavily API."""
if not os.getenv("TAVILY_API_KEY"):
raise ValueError("TAVILY_API_KEY not found in environment variables")
tavily_search = TavilySearch(max_results=3, topic="general") # topic 可以是 general, news, finance
tools.append(tavily_search)
return tools
# 获取中间件
def get_middlewares():
middlewares = []
max_threads=20
max_tool_call_iterations=5
limit_middleware = ToolCallLimitMiddleware(
tool_name="tavily_search",
thread_limit=max_threads,
run_limit=max_tool_call_iterations,
exit_behavior="end", # default
)
middlewares.append(limit_middleware)
return middlewares
# 获取智能体
def get_agent(llm, tools, system_prompt, middlewares):
agent = create_agent(
model = llm,
tools = tools,
system_prompt = system_prompt,
checkpointer=InMemorySaver(),
middleware = middlewares,
)
return agent
# 主程序:ReAct智能体调用示例
if __name__ == "__main__":
load_dotenv()
llm = get_model()
tools = get_tools()
system_prompt = get_system_prompt(tools)
middlewares = get_middlewares()
react_agent = get_agent(llm, tools, system_prompt, middlewares)
config={"configurable": {"thread_id": "1"}}
query = {"messages": [{"role": "user", "content": "比较贝多芬与莫扎特谁的音乐生涯更长?长多少天?"}]}
for chunk in react_agent.stream(query, config, stream_mode="values"):
chunk['messages'][-1].pretty_print()
参考文献
1\] [React: Synergizing Reasoning And Acting In Language Models](https://link.juejin.cn?target=https%3A%2F%2Farxiv.org%2Fpdf%2F2210.03629 "https://arxiv.org/pdf/2210.03629")