从零构建 ReAct 智能体:让大模型学会”边想边做”

本文改编自《Hello Agents》第四章”智能体经典范式构建”中的 ReAct 部分。在保留核心技术细节的基础上,对内容进行了重新组织和润色,使其更适合作为一篇独立的技术博客来阅读。

为什么需要 ReAct?

大语言模型(LLM)已经很强大了,但它有两个绕不开的短板:知识有截止日期,且无法主动与外部世界交互。你问它”华为最新款手机是什么”,它要么承认不知道,要么凭记忆编一个——也就是我们常说的”幻觉”。

在 ReAct 诞生之前,业界尝试过两条路线:

  • 纯思考型(如 Chain-of-Thought 思维链):让模型一步步推理,逻辑能力强,但”闭门造车”,无法获取外部信息,容易产生事实性幻觉。
  • 纯行动型:模型直接输出要执行的动作,但没有规划,也没有纠错能力,像一个没有大脑的执行器。

2022 年,Shunyu Yao 提出了 ReAct (Reason + Act) 范式[1],核心思想非常朴素却有效:模仿人类解决问题的方式,把”思考”和”行动”显式地结合起来。

人类解决陌生问题时的典型过程是这样的:先想一下”我需要查什么资料” → 去查 → 看到结果后再想”这些信息够不够” → 不够就再查 → 够了就总结答案。ReAct 就是把这套流程固化成了智能体的运行机制。


ReAct 的工作流程

ReAct 通过提示工程引导 LLM,使其每一步输出遵循一个固定的轨迹:

阶段 含义 举例
Thought (思考) 智能体的”内心独白”,分析现状、拆解任务、规划下一步 “我需要搜索华为最新手机的信息”
Action (行动) 决定采取的具体动作,通常是调用一个外部工具 Search['华为最新款手机']
Observation (观察) 执行 Action 后从工具返回的结果 搜索结果的摘要

智能体不断重复 Thought → Action → Observation 的循环,将每一步的观察结果追加到上下文中,形成一个不断增长的历史记录,直到它在 Thought 中判断已经找到了最终答案。

这个过程可以形式化地表达为:在每个时间步 $t$,模型 $\pi$ 根据问题 $q$ 和历史轨迹生成思考 $th_t$ 和行动 $a_t$:

$$
(th_t, a_t) = \pi(q, (a_1,o_1),\ldots,(a_{t-1},o_{t-1}))
$$

随后环境中的工具 $T$ 执行行动并返回观察:

$$
o_t = T(a_t)
$$

推理使得行动更具目的性,而行动则为推理提供了事实依据。 这就是 ReAct 的核心协同效应。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
flowchart TD
Q([用户问题 q]) --> T1

subgraph 循环 ["Thought → Action → Observation 循环"]
T1["Thought t₁<br/>分析问题,决定搜索"] --> A1
A1["Action a₁<br/>Search[华为最新手机]"] --> O1
O1["Observation<br/>搜索结果返回"] --> T2
T2["Thought t₂<br/>分析结果,决定是否足够"] --> Check
end

Check{"信息是否足够?"}
Check -->|是| F(["Finish[答案]"])
Check -->|否| T1

style Q fill:#e1f5fe,stroke:#0288d1
style F fill:#e8f5e9,stroke:#388e3c
style Check fill:#fff3e0,stroke:#f57c00

ReAct 特别适合以下场景:

  • 需要外部知识的任务(实时信息、专业领域知识)
  • 需要精确计算的任务(交给计算器工具,避免 LLM 算错)
  • 需要与 API 交互的任务(操作数据库、调用服务)

接下来,我们亲手从零实现一个 ReAct 智能体,让它回答”华为最新的手机是哪一款?它的主要卖点是什么?”这个 LLM 自身知识库无法直接回答的实时性问题。


环境准备

安装依赖

1
pip install openai python-dotenv google-search-results

配置 API 密钥

在项目根目录创建 .env 文件:

1
2
3
4
5
# .env file
LLM_API_KEY="YOUR-API-KEY"
LLM_MODEL_ID="YOUR-MODEL"
LLM_BASE_URL="YOUR-URL"
SERPAPI_API_KEY="YOUR_SERPAPI_API_KEY"

SerpApi 提供 Google 搜索的结构化 API,可在 serpapi.com 免费注册获取密钥。

封装基础 LLM 客户端

为了让代码结构清晰且易于复用,先定义一个 LLM 客户端类,封装所有与模型服务交互的细节:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import os
from openai import OpenAI
from dotenv import load_dotenv
from typing import List, Dict

# 加载 .env 文件中的环境变量
load_dotenv()

class HelloAgentsLLM:
"""
LLM 客户端,兼容任何 OpenAI 接口的服务,默认使用流式响应。
"""
def __init__(self, model: str = None, apiKey: str = None,
baseUrl: str = None, timeout: int = None):
self.model = model or os.getenv("LLM_MODEL_ID")
apiKey = apiKey or os.getenv("LLM_API_KEY")
baseUrl = baseUrl or os.getenv("LLM_BASE_URL")
timeout = timeout or int(os.getenv("LLM_TIMEOUT", 60))

if not all([self.model, apiKey, baseUrl]):
raise ValueError("模型ID、API密钥和服务地址必须被提供或在.env文件中定义。")

self.client = OpenAI(api_key=apiKey, base_url=baseUrl, timeout=timeout)

def think(self, messages: List[Dict[str, str]], temperature: float = 0) -> str:
"""调用大语言模型进行思考,并返回其完整响应。"""
print(f"🧠 正在调用 {self.model} 模型...")
try:
response = self.client.chat.completions.create(
model=self.model,
messages=messages,
temperature=temperature,
stream=True,
)

print("✅ 大语言模型响应成功:")
collected_content = []
for chunk in response:
if not chunk.choices:
continue
content = chunk.choices[0].delta.content or ""
print(content, end="", flush=True)
collected_content.append(content)
print()
return "".join(collected_content)

except Exception as e:
print(f"❌ 调用LLM API时发生错误: {e}")
return None

temperature 设为 0 是为了保证输出的确定性,这在需要严格格式解析的 ReAct 场景中尤为重要。


工具的定义与实现

如果说大语言模型是智能体的大脑,那么工具 (Tools) 就是其与外部世界交互的”手和脚”。

工具三要素

一个良好定义的工具应包含三个核心要素:

  1. 名称 (Name):简洁、唯一的标识符,供智能体在 Action 中调用,例如 Search
  2. 描述 (Description):清晰的自然语言描述。这是整个机制中最关键的部分——LLM 会依赖这段描述来判断何时、是否使用某个工具
  3. 执行逻辑 (Execution Logic):真正执行任务的函数

实现搜索工具

我们选用 SerpApi,它能返回 Google 搜索的”答案摘要框”和”知识图谱”等结构化信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
from serpapi import SerpApiClient

def search(query: str) -> str:
"""
基于 SerpApi 的网页搜索引擎工具。
智能解析搜索结果,优先返回直接答案或知识图谱信息。
"""
print(f"🔍 正在执行 [SerpApi] 网页搜索: {query}")
try:
api_key = os.getenv("SERPAPI_API_KEY")
if not api_key:
return "错误:SERPAPI_API_KEY 未在 .env 文件中配置。"

params = {
"engine": "google",
"q": query,
"api_key": api_key,
"gl": "cn",
"hl": "zh-cn",
}

client = SerpApiClient(params)
results = client.get_dict()

# 智能解析:优先寻找最直接的答案
if "answer_box_list" in results:
return "\n".join(results["answer_box_list"])
if "answer_box" in results and "answer" in results["answer_box"]:
return results["answer_box"]["answer"]
if "knowledge_graph" in results and "description" in results["knowledge_graph"]:
return results["knowledge_graph"]["description"]
if "organic_results" in results and results["organic_results"]:
snippets = [
f"[{i+1}] {res.get('title', '')}\n{res.get('snippet', '')}"
for i, res in enumerate(results["organic_results"][:3])
]
return "\n\n".join(snippets)

return f"对不起,没有找到关于 '{query}' 的信息。"

except Exception as e:
return f"搜索时发生错误: {e}"

解析策略的优先级是:answer_box_list → answer_box → knowledge_graph → organic_results(前 3 条)。这种”智能解析”能为 LLM 提供质量更高的信息输入。

构建通用工具执行器

当智能体需要使用多种工具时(搜索、计算、数据库查询等),我们需要一个统一的管理器来注册和调度:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from typing import Dict, Any

class ToolExecutor:
"""工具执行器,负责管理和执行工具。"""
def __init__(self):
self.tools: Dict[str, Dict[str, Any]] = {}

def registerTool(self, name: str, description: str, func: callable):
"""向工具箱中注册一个新工具。"""
if name in self.tools:
print(f"警告:工具 '{name}' 已存在,将被覆盖。")
self.tools[name] = {"description": description, "func": func}
print(f"工具 '{name}' 已注册。")

def getTool(self, name: str) -> callable:
"""根据名称获取工具的执行函数。"""
return self.tools.get(name, {}).get("func")

def getAvailableTools(self) -> str:
"""获取所有可用工具的格式化描述字符串。"""
return "\n".join([
f"- {name}: {info['description']}"
for name, info in self.tools.items()
])

验证工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if __name__ == '__main__':
toolExecutor = ToolExecutor()

search_description = "一个网页搜索引擎。当你需要回答关于时事、事实以及在你的知识库中找不到的信息时,应使用此工具。"
toolExecutor.registerTool("Search", search_description, search)

print("\n--- 可用的工具 ---")
print(toolExecutor.getAvailableTools())

print("\n--- 执行 Action: Search['英伟达最新的GPU型号是什么'] ---")
tool_function = toolExecutor.getTool("Search")
observation = tool_function("英伟达最新的GPU型号是什么")
print("--- 观察 (Observation) ---")
print(observation)

运行结果:

1
2
3
4
5
6
7
8
9
10
工具 'Search' 已注册。

--- 可用的工具 ---
- Search: 一个网页搜索引擎。当你需要回答关于时事、事实以及在你的知识库中找不到的信息时,应使用此工具。

--- 执行 Action: Search['英伟达最新的GPU型号是什么'] ---
🔍 正在执行 [SerpApi] 网页搜索: 英伟达最新的GPU型号是什么
--- 观察 (Observation) ---
[1] GeForce RTX 50 系列显卡
GeForce RTX™ 50 系列GPU 搭载NVIDIA Blackwell 架构,为游戏玩家和创作者带来全新玩法...

至此,智能体的”手和脚”已经就绪。


ReAct 智能体的编码实现

现在,我们将 LLM 客户端和工具执行器组装起来,构建一个完整的 ReActAgent 类。为了便于理解,将其拆分为几个关键部分逐一讲解。

第一步:系统提示词设计

提示词是整个 ReAct 机制的基石,它为 LLM 提供了行动的操作指令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# ReAct 提示词模板
REACT_PROMPT_TEMPLATE = """
请注意,你是一个有能力调用外部工具的智能助手。

可用工具如下:
{tools}

请严格按照以下格式进行回应:

Thought: 你的思考过程,用于分析问题、拆解任务和规划下一步行动。
Action: 你决定采取的行动,必须是以下格式之一:
- `{{tool_name}}[{{tool_input}}]`:调用一个可用工具。
- `Finish[最终答案]`:当你认为已经获得最终答案时。
- 当你收集到足够的信息,能够回答用户的最终问题时,你必须在Action:字段后使用 Finish[最终答案] 来输出最终答案。

现在,请开始解决以下问题:
Question: {question}
History: {history}
"""

这个模板定义了智能体与 LLM 交互的规范:

  • 角色定义:”你是一个有能力调用外部工具的智能助手”
  • 工具清单 {tools}:告知 LLM 它有哪些可用的”手和脚”
  • 格式规约 (Thought/Action):最重要的部分,强制 LLM 输出具有结构性,使我们能通过代码精确解析其意图
  • 动态上下文 ({question}/{history}):注入用户的原始问题和不断累积的交互历史

第二步:核心循环

ReActAgent 的核心是一个 while 循环:不断”格式化提示词 → 调用 LLM → 执行动作 → 整合结果”,直到任务完成或达到最大步数限制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import re

class ReActAgent:
def __init__(self, llm_client: HelloAgentsLLM,
tool_executor: ToolExecutor, max_steps: int = 5):
self.llm_client = llm_client
self.tool_executor = tool_executor
self.max_steps = max_steps
self.history = []

def run(self, question: str):
"""运行 ReAct 智能体来回答一个问题。"""
self.history = [] # 每次运行时重置历史记录
current_step = 0

while current_step < self.max_steps:
current_step += 1
print(f"--- 第 {current_step} 步 ---")

# 1. 格式化提示词
tools_desc = self.tool_executor.getAvailableTools()
history_str = "\n".join(self.history)
prompt = REACT_PROMPT_TEMPLATE.format(
tools=tools_desc,
question=question,
history=history_str
)

# 2. 调用 LLM 进行思考
messages = [{"role": "user", "content": prompt}]
response_text = self.llm_client.think(messages=messages)

if not response_text:
print("错误:LLM未能返回有效响应。")
break

# 3. 解析 LLM 的输出
thought, action = self._parse_output(response_text)

if thought:
print(f"🤔 思考: {thought}")

if not action:
print("警告:未能解析出有效的Action,流程终止。")
break

# 4. 执行 Action
if action.startswith("Finish"):
final_answer = re.match(r"Finish\[(.*)\]", action, re.DOTALL).group(1)
print(f"🎉 最终答案: {final_answer}")
return final_answer

tool_name, tool_input = self._parse_action(action)
if not tool_name or not tool_input:
print(f"警告:无法解析Action: {action}")
continue

print(f"🎬 行动: {tool_name}[{tool_input}]")

tool_function = self.tool_executor.getTool(tool_name)
if not tool_function:
observation = f"错误:未找到名为 '{tool_name}' 的工具。"
else:
observation = tool_function(tool_input)

# 5. 整合观测结果
print(f"👀 观察: {observation}")

# 将本轮的 Action 和 Observation 添加到历史记录中
self.history.append(f"Action: {action}")
self.history.append(f"Observation: {observation}")

# 循环结束
print("已达到最大步数,流程终止。")
return None

max_steps 参数是一个重要的安全阀,防止智能体陷入无限循环而耗尽资源。

第三步:输出解析器

LLM 返回的是纯文本,我们需要从中精确提取出 Thought 和 Action。这里使用正则表达式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def _parse_output(self, text: str):
"""解析 LLM 的输出,提取 Thought 和 Action。"""
thought_match = re.search(r"Thought:\s*(.*?)(?=\nAction:|$)", text, re.DOTALL)
action_match = re.search(r"Action:\s*(.*?)$", text, re.DOTALL)
thought = thought_match.group(1).strip() if thought_match else None
action = action_match.group(1).strip() if action_match else None
return thought, action

def _parse_action(self, action_text: str):
"""解析 Action 字符串,提取工具名称和输入。"""
match = re.match(r"(\w+)\[(.*)\]", action_text, re.DOTALL)
if match:
return match.group(1), match.group(2)
return None, None
  • _parse_output:从完整响应中分离出 Thought 和 Action 两个部分
  • _parse_action:从 Search[华为最新手机] 中提取工具名 Search 和输入 华为最新手机

核心循环的完整流程

把上面的代码组合起来,ReAct 的运行逻辑可以总结为这张流程图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
flowchart TD
Start([开始]) --> Format["格式化提示词<br/>注入 tools / question / history"]
Format --> CallLLM["调用 LLM"]
CallLLM --> Parse["解析 Thought + Action"]
Parse --> CheckFinish{"Action 是 Finish?"}

CheckFinish -->|是| Answer([输出最终答案,结束])
CheckFinish -->|否| ParseTool["解析工具名和输入"]
ParseTool --> ExecTool["执行工具得到 Observation"]
ExecTool --> Append["将 Action + Observation 追加到 history"]
Append --> StepCheck{"已达到 max_steps?"}
StepCheck -->|否| Format
StepCheck -->|是| Stop([流程终止])

style Start fill:#e1f5fe,stroke:#0288d1
style Answer fill:#e8f5e9,stroke:#388e3c
style Stop fill:#ffebee,stroke:#d32f2f
style CheckFinish fill:#fff3e0,stroke:#f57c00
style StepCheck fill:#fff3e0,stroke:#f57c00

将 Observation 追加到 self.history 是形成闭环的关键——智能体在下一轮生成提示词时,就能”看到”上一步行动的结果,并据此进行新一轮的思考和规划。


运行实例与分析

将所有组件组装起来,向智能体提问”华为最新的手机是哪一款?它的主要卖点是什么?”:

1
2
3
4
5
6
7
8
9
10
11
12
if __name__ == '__main__':
# 初始化 LLM 客户端
llm_client = HelloAgentsLLM()

# 初始化工具执行器并注册搜索工具
tool_executor = ToolExecutor()
search_description = "一个网页搜索引擎。当你需要回答关于时事、事实以及在你的知识库中找不到的信息时,应使用此工具。"
tool_executor.registerTool("Search", search_description, search)

# 创建并运行 ReAct 智能体
agent = ReActAgent(llm_client, tool_executor, max_steps=5)
agent.run("华为最新的手机是哪一款?它的主要卖点是什么?")

运行记录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
工具 'Search' 已注册。

--- 第 1 步 ---
🧠 正在调用 xxxxxx 模型...
✅ 大语言模型响应成功:
Thought: 要回答这个问题,我需要查找华为最新发布的手机型号及其主要特点。这些信息可能在我的现有知识库之外,因此需要使用搜索引擎来获取最新数据。
Action: Search[华为最新手机型号及主要卖点]
🤔 思考: 要回答这个问题,我需要查找华为最新发布的手机型号及其主要特点。这些信息可能在我的现有知识库之外,因此需要使用搜索引擎来获取最新数据。
🎬 行动: Search[华为最新手机型号及主要卖点]
🔍 正在执行 [SerpApi] 网页搜索: 华为最新手机型号及主要卖点
👀 观察: [1] 华为手机- 华为官网
智能手机 ; Mate 系列. 非凡旗舰 · HUAWEI Mate XTs. 非凡大师 ; Pura 系列. 先锋影像 · HUAWEI Pura 80 Pro+ ; Pocket 系列. 美学新篇. HUAWEI Pocket 2 ; nova 系列. 专业人像.

[2] 2025年华为手机哪一款性价比高?华为手机推荐与市场分析 ...
现在华为手机最大的卖点只剩下鸿蒙HarmonyOS系统...

[3] 2025年华为新款手机哪个性价比高?10款华为新款手机推荐
选华为主要还是要推荐高端手机,Mate 70和Pura 70系列是最新发布的旗舰机型...

--- 第 2 步 ---
🧠 正在调用 xxxxxx 模型...
✅ 大语言模型响应成功:
Thought: 根据搜索结果,华为最新发布的旗舰机型包括Mate 70和Pura 80 Pro+。从提供的链接来看,Mate 70系列和Pura 80 Pro+都是近期发布的产品,但具体哪一个是"最新"还需要进一步确认。同时,我可以从这些信息中提取出它们的主要卖点。
Action: Finish[根据最新信息,华为的最新手机可能是HUAWEI Pura 80 Pro+或HUAWEI Mate 70。其中,HUAWEI Mate 70的主要卖点包括顶级的拍照配置,全焦段覆盖,适合专业摄影,做工出色,并且具有良好的户外抗摔性能。而HUAWEI Pura 80 Pro+则强调了先锋影像技术。]
🎉 最终答案: 根据最新信息,华为的最新手机可能是HUAWEI Pura 80 Pro+或HUAWEI Mate 70。其中,HUAWEI Mate 70的主要卖点包括顶级的拍照配置,全焦段覆盖,适合专业摄影,做工出色,并且具有良好的户外抗摔性能。而HUAWEI Pura 80 Pro+则强调了先锋影像技术。

从输出可以看到智能体清晰的思考链条:

  1. 第 1 步:意识到自身知识不足 → 调用 Search 工具
  2. 第 2 步:基于搜索结果进行推理和总结 → 判断信息足够 → Finish 输出答案

仅用两步就完成了任务。这正是 ReAct 的魅力所在——推理指导行动,行动反馈推理。

由于模型知识和互联网信息会不断更新,你运行的结果可能与此不完全相同。


ReAct 的特点、局限性与调试技巧

亲手实现之后,我们来客观审视一下 ReAct 范式的优劣。

主要特点

1. 高可解释性

ReAct 最大的优点之一就是透明。通过 Thought 链,我们可以清晰地看到智能体每一步的”心路历程”——它为什么选择这个工具,下一步打算做什么。这对于理解、信任和调试智能体的行为至关重要。

2. 动态规划与纠错能力

与一次性生成完整计划的范式不同,ReAct 是”走一步,看一步”。它根据每一步从外部世界获得的 Observation 来动态调整后续的 Thought 和 Action。如果上一步的搜索结果不理想,它可以在下一步中修正搜索词,重新尝试。

3. 工具协同能力

ReAct 天然地将 LLM 的推理能力与外部工具的执行能力结合起来。LLM 负责运筹帷幄(规划和推理),工具负责解决具体问题(搜索、计算),二者协同工作,突破了单一 LLM 在知识时效性、计算准确性等方面的固有局限。

固有局限

1. 对 LLM 自身能力的强依赖

ReAct 流程的成功与否,高度依赖于底层 LLM 的综合能力。如果 LLM 的逻辑推理能力、指令遵循能力或格式化输出能力不足,就很容易在 Thought 环节产生错误的规划,或者在 Action 环节生成不符合格式的指令,导致整个流程中断。

2. 执行效率问题

由于循序渐进的特性,完成一个任务通常需要多次调用 LLM。每一次调用都伴随着网络延迟和计算成本。对于步骤较多的复杂任务,这种串行的”思考-行动”循环可能导致较高的总耗时和费用。

3. 提示词的脆弱性

整个机制的稳定运行建立在一个精心设计的提示词模板之上。模板中的任何微小变动,甚至是用词的差异,都可能影响 LLM 的行为。此外,并非所有模型都能持续稳定地遵循预设的格式,这增加了实际应用中的不确定性。

4. 可能陷入局部最优

步进式的决策模式意味着智能体缺乏全局的、长远的规划。它可能会因为眼前的 Observation 而选择一个看似正确但长远来看并非最优的路径,甚至在某些情况下陷入”原地打转”的循环中。

调试技巧

当你构建的 ReAct 智能体行为不符合预期时,可以从以下几个方面入手:

  • 检查完整的提示词:在每次调用 LLM 之前,将最终格式化好的、包含所有历史记录的完整提示词打印出来。这是追溯 LLM 决策源头的最直接方式。
  • 分析原始输出:当输出解析失败时(例如正则表达式没有匹配到 Action),务必将 LLM 返回的原始、未经处理的文本打印出来。这能帮助你判断是 LLM 没有遵循格式,还是你的解析逻辑有误。
  • 验证工具的输入与输出:检查智能体生成的 tool_input 是否是工具函数所期望的格式,同时也要确保工具返回的 observation 格式是智能体可以理解和处理的。
  • 调整提示词中的示例 (Few-shot Prompting):如果模型频繁出错,可以在提示词中加入一两个完整的”Thought-Action-Observation”成功案例,通过示例来引导模型更好地遵循指令。
  • 尝试不同的模型或参数:更换一个能力更强的模型,或者调整 temperature 参数(通常设为 0 以保证输出的确定性),有时能直接解决问题。

总结

ReAct 的核心其实就四个字:边想边做。

它用一种非常符合直觉的方式,把大语言模型的推理能力和外部工具的执行能力串联起来:

  • LLM 负责”想”——分析问题、规划动作、解读结果
  • 工具负责”做”——搜索、计算、调用 API
  • Observation 把两者粘合在一起,形成闭环

这个范式的代码实现并不复杂(核心就是一个 while 循环加正则解析),但它揭示了一个重要的设计思想:智能体的力量不在于 LLM 本身有多强,而在于它能否有效地与外部世界交互。

当然,ReAct 也有它的局限——串行执行效率低、依赖提示词稳定性、缺乏全局规划。这些正是后续范式(如 Plan-and-Solve 的”先规划后执行”、Reflection 的”自我反思”)试图解决的问题。理解了 ReAct 的长处和短板,你才能在实际场景中做出正确的技术选型。

参考文献

[1] Yao, S., Zhao, J., Yu, D., et al. “ReAct: Synergizing Reasoning and Acting in Language Models.” arXiv preprint arXiv:2210.03629, 2022.