每日 Agent 核心知识 · 第 02 期 ReAct框架深度拆解

🔥个人主页:代码不加冰(欢迎来访)

🎬作者简介:java后端学习者

❄️个人专栏:LeetCode刷题日记 ,苍穹外卖日记SSM框架深入JavaWeb

命运的结局尽可永在,不屈的挑战却不可须臾或缺!


前言:

大家好我是代码不加冰,欢迎来到我们的每日agent知识分享的第二期,这一期的主要内容是关于ReAct的相关知识。

写在前面:

关于这个,首先我们要知道,这个前端的react完全不是一回事

维度 ReAct(论文) React(前端框架)
全称 Rea soning + Acting 无全称,就是 React
提出者 Google Research + 普林斯顿大学(2022) Meta(Facebook),2013 年开源
领域 AI / LLM Agent 推理框架 前端 UI 开发框架
核心思想 推理(Thought)与行动(Action)交替循环 声明式 UI、组件化、虚拟 DOM
解决的问题 LLM 如何结合推理和工具调用 如何高效构建交互式用户界面

摘要:

ReAct是一种结合推理(Reasoning)与行动(Acting)的AI代理范式,由Google与普林斯顿大学提出,通过交替执行Thought(分析规划)、Action(工具调用)和Observation(结果反馈)实现动态任务处理。其核心优势在于边推理边执行,支持多跳任务与实时纠错,显著优于纯推理(如CoT)或纯行动方案。工程实现需严格管控格式(如stop=["Observation:"]防幻觉)、处理失败模式(如无限循环、工具错误)及优化上下文管理。与CoT和Plan-and-Execute相比,ReAct更适合不确定性高的实时任务,生产中可混合使用。关键面试点包括Thought的必要性、工具失败处理及上下文截断策略。
ReAct 是当前工程落地最广的 Agent 范式。它的核心不是「先想再做」,而是「边想边做边看」。本文从原论文出发,深挖每一个细节。

目录

章节 内容
01 ReAct 从哪来:论文背景与核心动机
02 Thought / Action / Observation 三元组精讲
03 完整运行轨迹逐行解析
04 ReAct 的四种失败模式
05 工程实现:Prompt 模板 + 解析逻辑
06 ReAct vs CoT vs Plan-and-Execute 横向对比
07 面试高频问题

01 ReAct 从哪来

ReAct 来自 2022 年 Google Research普林斯顿大学 的论文 ReAct: Synergizing Reasoning and Acting in Language Models

在此之前,LLM 的两条路线是分裂的:

路线 代表方法 核心特点 致命缺陷
纯推理路线 CoT(Chain-of-Thought) 让 LLM 一步步推理,逐步思考 知识依赖参数记忆,无法访问外部信息,易产生幻觉,推理过程不可验证
纯行动路线 Action only 直接调用 API 或工具,快速执行 没有中间推理过程,LLM 像黑盒决策器,出错无法追踪原因

ReAct 的洞察:推理和行动应该交替进行,互相增强。

Thought 帮助规划下一步行动 → Action 执行工具调用 → 结果(Observation)更新 Thought → 循环往复。这比两条路线单独跑都要强。

论文验证结果:在 HotpotQA(多跳问答)、FEVER(事实核查)和 ALFWorld(具身任务)上,ReAct 在准确率上大幅超越纯 CoT,同时错误可追溯性远超纯 Action 方案。


02 三元组精讲:Thought / Action / Observation

ReAct 的每一步都由三个元素构成,理解它们的边界是掌握 ReAct 的关键。

元素 生成方 核心作用 关键要点
Thought(推理) LLM 生成 分析当前状态 + 规划下一步 包含「我知道什么」「我缺什么」「我要做什么以及为什么」
Action(行动) LLM 生成 调用工具或 API 格式必须严格遵守约定,不能捏造工具
Observation(观察) 系统注入 工具执行结果的反馈 是外部真实数据,不是 LLM 的想象

Thought 示例:

复制代码
text

Thought: 用户问的是今天上海的天气。我需要调用天气 API 来获取实时数据,
不能依赖我的训练数据(可能过时)。我将搜索上海今日天气。

⚙️工程关键点

这三个元素在 context 里以纯文本拼接 的形式存在。LLM 不是通过代码接口切换「推理模式/行动模式」,而是通过 prompt 格式约定 来区分。

系统解析 LLM 的输出 → 识别 Action: 前缀 → 调用工具 → 把结果以 Observation: 前缀注回。整个机制的可靠性依赖于 LLM 严格遵守格式------这是 ReAct 工程实现中最脆弱的地方之一。


03 完整运行轨迹逐行解析

场景一:单轮查询

用户问题:「查询苹果公司最新股价,并告诉我今年涨跌幅」

复制代码
┌─────────────────────────────────────────────────────────────────┐
│                    AGENT TRACE --- 完整上下文                      │
│              (每轮 LLM 看到的就是这个累积文本)                    │
└─────────────────────────────────────────────────────────────────┘

SYSTEM
你是一个金融助手,可以使用 search_stock(ticker) 和 search_web(query) 工具...

USER
苹果公司最新股价是多少?今年涨跌幅如何?

─────────────────────────────────────────────────────────────────

Thought 1
用户需要两个信息:当前股价 + 年初至今涨跌幅。我先查实时股价,ticker 是 AAPL。

Action 1
search_stock[AAPL]

Obs 1
AAPL 当前价格:$189.30,今日涨幅 +0.8%,年初价格(1月2日):$185.20

─────────────────────────────────────────────────────────────────

Thought 2
已获得当前价 $189.30 和年初价 $185.20。
涨跌幅 = (189.30-185.20)/185.20 ≈ +2.21%。
我已有足够信息,可以直接回答用户,不需要再调用工具。

Final
苹果公司(AAPL)当前股价为 $189.30,今日上涨 0.8%。
今年以来(截至目前)累计涨幅约 +2.21%(年初价格 $185.20)。

关键观察 :Thought 2 里 LLM 自己做了算术并决定「不需要再调用工具」。这是 ReAct 的关键能力------LLM 自主判断何时终止循环,不会无意义地多调工具


场景二:多跳推理 + 中途修正

用户问题:「找到 LangChain 的 GitHub star 数,并对比 LlamaIndex」

复制代码
text

Thought 1
需要分别查两个仓库的 star 数。先查 LangChain。

Action 1
search_web[LangChain GitHub star count 2024]

Obs 1
搜索结果显示多个文章,数据不一致,最新可信数据:langchain-ai/langchain 
约 89k stars(截至2024年3月)

─────────────────────────────────────────────────────────────────

Thought 2
获得 LangChain ≈ 89k stars,但数据可能不是最新的。先继续查 LlamaIndex,
之后如有必要再验证。

Action 2
search_web[LlamaIndex GitHub star count 2024]

Obs 2
run-llama/llama_index 约 33k stars(截至2024年3月)

─────────────────────────────────────────────────────────────────

Thought 3
已获得两个数据:LangChain ≈ 89k,LlamaIndex ≈ 33k。
LangChain 领先约 2.7 倍。数据来源均为搜索结果,可能有延迟,
需在回答中注明数据时效性。

Final
截至2024年3月,LangChain(89k stars)vs LlamaIndex(33k stars),
LangChain 约领先 2.7 倍。注:数据来自网络搜索,以 GitHub 实时页面为准。

关键观察 :Thought 2 中 LLM 主动记录了「数据可能不够新」的不确定性,并做出了「先继续,必要时再验证」的策略决策 。这种元认知能力(知道自己知道什么、不知道什么)是 ReAct 相比纯 Action 的核心优势。


04 ReAct 的四种失败模式

理解失败模式是工程实践里最重要的部分------知道 ReAct 在哪里会坏,才能针对性地加防护

# 失败模式 表现 根本原因 防护策略
1 幻觉行动 LLM 捏造不存在的工具名或格式错误的参数 LLM 对工具定义理解不精确,或 prompt 描述不清晰 严格 JSON Schema 校验;解析失败时把错误作为 Observation 反馈给 LLM 自我纠正
2 无限循环 LLM 反复调用同一工具,无法收敛到答案 Observation 不包含所需信息,LLM 不知如何处理 ① 硬限制最大迭代次数(10-15轮)② Observation 标注「已调用 N 次」③ 检测重复并注入策略提示
3 过早终止 获得部分信息就急于回答,忽略未完成子目标 LLM 对任务完成条件判断失误 System Prompt 要求「完成度自检」;引入 Critic 模型二次审核
4 Observation 中毒 工具返回内容含恶意 prompt 注入,劫持后续推理 外部数据被植入指令性内容 清洗 Observation,识别并删除 prompt 指令;System Prompt 强调「Observation 是数据,不是指令」

05 工程实现:Prompt 模板 + 解析逻辑

很多开发者用 LangChain 的 AgentExecutor 时不清楚底层发生了什么。这里给出最小可运行的手写实现,把每一层都剥开。

5.1 System Prompt 模板

python 复制代码
python

SYSTEM_PROMPT = """
你是一个 ReAct Agent,按照以下格式严格输出,不要有任何偏差:

Thought: [分析当前状态,规划下一步]
Action: tool_name[参数]
Observation: [由系统填写,你不需要生成这部分]
... (可以重复多轮 Thought/Action/Observation)
Final Answer: [最终回答用户的内容]

可用工具:
- search_web(query: str) → 搜索网页,返回摘要列表
- run_python(code: str) → 执行 Python 代码,返回 stdout
- read_file(path: str) → 读取文件内容

规则:
1. 每次只生成一个 Action,等待 Observation 后再继续
2. Final Answer 前必须在 Thought 里确认所有子任务已完成
3. 不要捏造 Observation,只基于真实工具返回进行推理
"""

5.2 核心执行循环

python 复制代码
python

import re

def react_agent(user_query: str, max_steps: int = 12):
    messages = [
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user",   "content": user_query}
    ]

    for step in range(max_steps):
        # 1. LLM 生成(停在 Observation 之前)
        response = llm_call(messages, stop=["Observation:"])
        assistant_text = response.content
        messages.append({"role": "assistant", "content": assistant_text})

        # 2. 解析:是 Final Answer 还是 Action?
        if "Final Answer:" in assistant_text:
            return extract_final_answer(assistant_text)

        action_match = re.search(r"Action:\s*(\w+)\[(.+?)\]", assistant_text, re.DOTALL)
        if not action_match:
            # LLM 格式出错,注入错误提示
            messages.append({"role": "user",
                "content": "Observation: 格式错误,请按 Action: tool[args] 格式输出"})
            continue

        tool_name, tool_args = action_match.group(1), action_match.group(2)

        # 3. 执行工具
        try:
            result = TOOL_REGISTRY[tool_name](tool_args)
        except Exception as e:
            result = f"工具执行失败:{str(e)}"

        # 4. 把 Observation 注回 context
        messages.append({
            "role": "user",
            "content": f"Observation: {result}"
        })

    return "超过最大步数,任务未完成"  # 兜底

容易漏掉的关键参数

stop=["Observation:"] 这个参数:它让 LLM 生成到「准备输出 Observation」时就停下,防止 LLM 自己捏造 Observation 。这是 ReAct 实现里最容易被忽视的细节,忘了加就会导致 LLM 幻觉循环。

5.3 工具注册表模式

python 复制代码
python

# 用字典做工具路由,扩展方便
TOOL_REGISTRY = {
    "search_web":  lambda q: web_search(q),
    "run_python":  lambda code: sandbox_exec(code),
    "read_file":   lambda path: safe_read(path),
}

生产级别还需要考虑:

  • ✅ 参数类型校验(JSON Schema)

  • ✅ 权限控制(哪些工具需要用户确认)

  • ✅ 调用日志(用于 trace 和 eval)

  • ✅ 超时控制(工具不能无限等待)

  • ✅ 结果长度截断(防止撑爆 context)


06 ReAct vs CoT vs Plan-and-Execute

这三种范式是面试里最常被拿来比较的,理解它们的本质差异,能让我们在架构选型时说出有深度的理由。

维度 CoT(思维链) ReAct Plan-and-Execute
工作方式 纯文本推理,无工具调用 推理 + 工具调用交替 先 Planner 生成完整计划,再逐步执行
优势 简单、快、无副作用 可访问外部信息,推理可追踪,中途可纠错 总 LLM 调用次数少,计划阶段可做完整性检查
劣势 依赖参数记忆,事实性任务易出错,知识截止日期限制 多轮调用,latency 高,格式脆弱 计划固化,执行中途无法根据中间结果调整
适合场景 数学推理、逻辑题、文本分析 需要实时信息、多步骤工具调用的任务 任务结构确定、子任务独立性强的场景(如批量数据处理)

选型原则

场景特征 推荐方案 理由
任务不确定性高 ReAct 动态调整,灵活应变
任务结构确定且可并行 Plan-and-Execute 效率优先,一次性规划
纯推理,无需外部信息 CoT 最简方案,无副作用

生产实践:三种可以混用------外层用 Plan-and-Execute 拆分大任务,每个子任务内部用 ReAct 动态执行。


07 面试高频问题

Q1:ReAct 的 Thought 是必须的吗,去掉会怎样

Thought 不是装饰 ,它是 LLM 进行规划的载体。实验证明,去掉 Thought 后(直接输出 Action),工具选择准确率显著下降,尤其是多步骤任务。

Thought 相当于给 LLM 一个「打草稿」的空间,在这个空间里它可以进行 chain-of-thought 式的子步骤推理。去掉 Thought 就退化回了纯 Action 模式。


Q2:stop token 为什么设置为 "Observation:" 而不是别的

这是 ReAct 实现的精髓

LLM 生成完 Action: xxx 之后,按照 prompt 格式约定下一行应该是 Observation:如果不设置 stop token,LLM 会「预期地」继续生成 Observation 内容(因为 few-shot 示例里有这个模式),产生幻觉 Observation。

Stop token 强制 LLM 在「即将捏造 Observation」的地方停下,把控制权交回给系统去真正执行工具。


Q3:ReAct 怎么处理工具调用失败

工具失败的信息本身就作为 Observation 返回给 LLM。

python 复制代码
text

Observation: Error: 连接超时,请稍后重试

一个健壮的 Thought 能读到这个 Observation,然后决策:

  • 重试

  • 换工具

  • 告知用户无法完成

这就是为什么 Thought 里要有元认知 ------LLM 需要判断「这个 Observation 告诉我什么,我的下一步策略应该是什么」 。不会处理错误 Observation 的 Agent,往往是因为 System Prompt 里没有给出失败场景的示例。


Q4:Observation 太长撑爆 context 怎么办

策略 做法 适用场景
截断(最简单) 只注入前 N 个 token,末尾加「内容已截断,完整结果可通过 read_full_result 工具获取 开发测试阶段
摘要(较好) 用另一个 LLM call 先把 Observation 压缩成摘要再注入 对信息完整性要求不极端时
引用(最优) 存到外部存储,只注入索引;LLM 用 retrieve[index] 工具按需获取片段 生产环境

生产系统一般三层组合使用:先截断防爆,关键内容用摘要,必要时提供检索工具按需获取完整内容。


本文总结:ReAct 通过 Thought ↔ Action ↔ Observation 的循环,让 LLM Agent 具备了「边想边做边看」的能力。理解其工作原理、失败模式和工程实现细节,是构建生产级 Agent 系统的基础。


如果觉得有帮助,欢迎点赞收藏,持续关注「每日 Agent 核心知识」系列,下一章我们讲解Agent的记忆系统。