一、概念
ReAct智能体框架(Reasoning and Acting Agent Framework)是一种结合推理(Reasoning)与行动(Acting)的智能体架构,可以通过动态环境感知、多步决策和反馈调整,实现复杂任务的自动化解决。其名称中的“React”强调智能体需在动态环境中快速响应(React)环境变化,并通过推理-行动循环持续优化策略。
React智能体的核心逻辑遵循“感知 ➔ 推理 ➔ 行动 ➔ 观察”的循环过程,具体流程如下:
步骤 | 功能 | 实现方法 |
---|---|---|
感知(Perception) | 获取环境状态信息 | 从传感器、数据库或外部接口(如API)读取数据 |
推理(Reasoning) | 分析信息并生成策略 | 基于规则引擎、知识图谱或大语言模型(LLM)生成多步计划 |
行动(Acting) | 执行决策动作 | 调用API、发送控制指令或模拟用户操作(如点击按钮) |
观察(Observation) | 获取行动后的反馈 | 捕捉环境变化(如页面更新、API响应)并更新内部状态 |
二、示例
假设任务为“查找爱因斯坦的出生地”,可能的输出如下:
3 问题:爱因斯坦的出生地是哪里?
Thought 1: 需要先访问维基百科首页
Action 1: click[Wikipedia首页]
Observation 1: 已进入维基百科主页
Thought 2: 应该搜索爱因斯坦的词条
Action 2: search[Albert Einstein]
Observation 2: 爱因斯坦词条页面加载完成
Thought 3: 在“早年生活”部分查找出生地
Action 3: click[早年生活]
Observation 3: 显示“出生于德意志帝国符腾堡王国乌尔姆”
Action 4: finish[]
Observation 4: 任务完成
关键步骤解析
-
初始化环境
env.reset(idx=3)
返回问题“爱因斯坦的出生地是哪里?”,并重置为初始状态(如打开浏览器)。 -
多步交互
-
第1步:LLM生成
Thought 1
(访问维基百科),执行click[Wikipedia首页]
动作,环境返回页面加载成功的观察结果。 -
第2步:LLM生成
Thought 2
(搜索“爱因斯坦” ),执行search[爱因斯坦]
动作,环境返回搜索爱因斯坦后的页面加载成功的观察结果。 -
第3步:LLM定位到词条中的“早年生活”部分,找到出生地信息。
-
第4步:强制
finish[]
提交答案。
-
在爱因斯坦的示例中,代码的每一步思考(Thought)均基于之前的所有观察(Observation)生成,而非一次性生成所有步骤。以下是具体逻辑:
1. 生成流程:逐步迭代
代码通过循环(for i in range(1, 8)
)逐轮生成 Thought
和 Action
,并在每轮结束后将 Observation
添加到上下文中。
这意味着:
-
Thought 2
的生成会参考Observation 1
。 -
Thought 3
的生成会参考Observation 1
和Observation 2
,依此类推。
2. 观察(Observation)的核心作用
作用 | 说明 | 示例 |
---|---|---|
动态更新上下文 | 将环境反馈加入提示(prompt += step_str ),为后续步骤提供最新信息 | 第1步的 Observation 1 包含“维基百科主页加载完成”,LLM在第2步中可据此知道当前处于主页,需执行搜索动作 |
支持状态感知 | 帮助LLM理解当前环境状态,避免重复或无效操作 | 若 Observation 3 显示“早年生活”部分未找到,LLM可能生成“尝试其他章节”的思考 |
终止条件判断 | 根据观察结果提前结束任务(如 done=True ) | 当 Observation 4 包含答案时,直接触发 finish[] |
3. 爱因斯坦示例的完整流程
假设任务为“查找爱因斯坦的出生地”,代码运行步骤如下:
步骤 | 生成逻辑 | 上下文内容 | 观察反馈的作用 |
---|---|---|---|
第1步 | 初始提示 + 问题 | 问题:爱因斯坦的出生地是哪里? | 无历史观察 |
第2步 | 初始提示 + 问题 + Thought 1 + Action 1 + Observation 1 | Observation 1: 已进入维基百科主页 | 告知LLM当前处于主页,需执行搜索动作 |
第3步 | 初始提示 + 问题 + 前两轮记录 + Observation 2 | Observation 2: 爱因斯坦词条页面加载完成 | 告知LLM已进入词条页面,需定位具体章节 |
第4步 | 初始提示 + 问题 + 前三轮记录 + Observation 3 | Observation 3: 显示‘出生于乌尔姆’ | 触发LLM生成终止动作 |
三、代码展示
def webthink(idx=None, prompt=webthink_prompt, to_print=True):
# 初始化环境并获取问题
# env.reset() 返回问题字符串,并重置环境状态
question = env.reset(idx=idx)
# 调试模式:打印当前问题索引和内容
if to_print:
print(f"[问题 {idx}]", question)
# 将当前问题拼接到提示词模板
# 示例:webthink_prompt + "爱因斯坦的出生地是哪里?\n"
prompt += question + "\n"
# 初始化统计指标
n_calls = 0 # 总LLM调用次数
n_badcalls = 0 # 格式错误的调用次数
max_retries = 2 # 单步最大重试次数
# 主循环:最多进行7轮思考-动作迭代
for step in range(1, 8):
valid_response = False # 标记当前步骤是否生成有效响应
thought_action = "" # 保存LLM生成的原始响应
# 重试机制:允许每个步骤重试生成符合格式的响应
for retry in range(max_retries + 1):
n_calls += 1 # 每次调用LLM计数
# 构建当前步骤的提示词
# 示例:prompt + "Thought 1:"
current_prompt = prompt + f"Thought {step}:"
# 调用LLM生成响应
# stop参数确保LLM在生成Action或Observation时停止
thought_action = llm(
current_prompt,
stop=[f"\nAction {step}:", f"\nObservation {step}:"]
).strip()
# 验证响应是否包含必要的Action标签
if f"\nAction {step}: " in thought_action:
valid_response = True
break # 格式正确则退出重试循环
else:
n_badcalls += 1
if to_print:
print(f"[步骤{step} 重试{retry+1}] 响应缺少Action标签")
# 最终失败处理:强制终止任务
if not valid_response:
# 提取第一行作为Thought,设置默认动作
thought = thought_action.split('\n')[0]
action = "finish[]" # 安全终止指令
if to_print:
print(f"[警告] 步骤{step}无法生成有效动作,强制终止")
break # 退出主循环
# 正常解析逻辑
try:
# 分割Thought和Action部分
# 示例输入:"需要访问维基百科\nAction 1: click[主页]"
# 输出:thought="需要访问维基百科", action="click[主页]"
thought, action = thought_action.split(f"\nAction {step}: ")
except:
# 次级错误处理:重新生成Action部分
thought = thought_action.split('\n')[0]
action = llm(
prompt + f"Thought {step}: {thought}\nAction {step}:",
stop=["\n"]
).strip()
n_calls += 1 # 额外调用计数
# 动作格式化:首字母小写处理
# 示例:将"Click[主页]"转为"click[主页]"
formatted_action = action[0].lower() + action[1:]
# 执行动作并获取环境反馈
# env.step()返回四元组:(obs, reward, done, info)
obs, r, done, info = env.step(formatted_action)
# 清理观察结果:移除换行符避免干扰后续提示
obs = obs.replace('\n', ' ')
# 构建当前步骤完整记录
step_str = (
f"Thought {step}: {thought}\n"
f"Action {step}: {action}\n"
f"Observation {step}: {obs}\n"
)
prompt += step_str # 将当前步骤追加到上下文
# 调试模式:打印当前步骤详情
if to_print:
print(step_str)
# 检查终止条件
if done:
break # 提前退出循环
# 未完成时的强制终止处理
if not done:
# 执行预设的终止动作(如提交答案)
obs, r, done, info = env.step("finish[]")
if to_print:
print("[超时终止]", info, '\n')
# 汇总统计信息
info.update({
'n_calls': n_calls, # 总调用次数
'n_badcalls': n_badcalls, # 格式错误次数
'traj': prompt # 完整交互轨迹(用于复现)
})
return info
调用示例及输出
场景描述
任务:查找爱因斯坦的出生地
环境预设动作:
-
click[xxx]
:模拟点击页面元素 -
search[xxx]
:在搜索栏执行搜索 -
finish[]
:提交最终答案
代码调用
# 初始化环境和LLM
env = WikiEnv() # 假设已实现维基百科导航环境
llm = GPT4() # 假设已封装GPT-4接口
# 定义提示词模板
webthink_prompt = """你是一个网页导航助手,请按以下格式操作:
1. 用Thought描述思考过程
2. 用Action执行具体动作(支持click[...]/search[...]/finish[])
---
当前任务:"""
# 执行任务(处理索引为3的问题)
result = webthink(
idx=3,
prompt=webthink_prompt,
to_print=True
)
# 打印统计结果
print("\n==== 任务统计 ====")
print(f"总耗时:{result['n_calls']*2}s") # 假设每次LLM调用耗时2秒
print(f"成功步骤:{result['traj'].count('Observation')}")
print(f"格式化错误率:{result['n_badcalls']/result['n_calls']:.1%}")
输出日志
[问题 3] 爱因斯坦的出生地是哪里?
Thought 1: 需要访问维基百科首页
Action 1: click[Wikipedia首页]
Observation 1: 已进入维基百科主页
Thought 2: 应搜索爱因斯坦的词条
Action 2: search[Albert Einstein]
Observation 2: 爱因斯坦词条页面加载完成
Thought 3: 需要查看"早年生活"部分
Action 3: click[早年生活]
Observation 3: 显示"出生于德意志帝国符腾堡王国乌尔姆"
[超时终止] {'answer': '乌尔姆'}
==== 任务统计 ====
总耗时:8s
成功步骤:3
格式化错误率:0.0%
关键机制图解
+-------------------+
| 初始化环境 |
| 获取问题 |
+-------------------+
|
v
+-------------------+
| 生成Thought i |
| 和Action i |←[重试机制]
+-------------------+
|
v
+-------------------+
| 执行动作 |→[错误处理]
| 获取Observation |
+-------------------+
|
v
+-------------------+
| 更新上下文 |
| 检查终止条件 |→[循环继续/终止]
+-------------------+
四、应用
场景 | 案例 | React智能体的作用 |
---|---|---|
自动化测试 | 网页功能验证 | 模拟用户操作路径,自动发现并报告异常 |
智能助手 | 多步骤任务执行(如订机票+酒店) | 分解任务为子动作,依次执行并协调结果 |
游戏AI | 策略类游戏 | 实时分析战场态势,动态调整单位部署 |
工业控制 | 生产线故障诊断 | 结合传感器数据推理故障原因,触发维修指令 |
五、挑战
-
上下文长度限制:长程任务可能导致提示(Prompt)过长,需结合检索增强(RAG)或记忆压缩技术。
-
推理效率:LLM的延迟较高,需优化轻量化模型或缓存机制。
-
安全性与可解释性:确保动作符合伦理约束,并提供决策依据的可追溯性。