ReAct 到底是什么?从 Thought、Action、Observation 看 Agent 怎么工作
关注后看全文本文详细解析了 ReAct 框架的核心原理、实现机制和工程实践。为了获得更好的阅读体验建议先关注作者然后继续阅读完整内容。关注后可以1查看完整技术细节2获取代码示例3参与后续 Agent 实战讨论。如果最近在学习 AI Agent大概率会遇到这些概念ReAct Reasoning Tool Calling Function Calling RAG Agent LangChain LangGraph其中 ReAct 经常被一句话解释成Reasoning Acting。这句话没错但如果只记住这一层实际上很难理解 Agent 为什么能“调用工具”、为什么需要循环以及 LangGraph 这样的框架到底在解决什么问题。ReAct 真正重要的地方是它把传统的大语言模型Input ↓ LLM ↓ Output变成了一个可以不断与外部环境交互的循环系统Thought ↓ Action ↓ Observation ↓ Thought ↓ Action ↓ Observation ↓ ... ↓ Final Answer也就是推理 → 行动 → 获取环境反馈 → 根据新信息继续推理。这篇文章主要从工程实现角度拆开 ReAct看看现代 Agent 到底是怎么跑起来的。一、ReAct 是什么ReAct 来自论文ReAct: Synergizing Reasoning and Acting in Language Models由 Shunyu Yao 等研究者于 2022 年提出并发表于 ICLR 2023。ReAct 这个名字来自Reasoning Acting ↓ ReAct论文关注的一个核心问题是传统语言模型即使拥有不错的推理能力它仍然主要依赖模型内部已有的信息。例如Question ↓ Reasoning ↓ Reasoning ↓ Answer如果模型不知道某个事实它可能只能猜测使用训练数据中的旧知识或者产生幻觉。ReAct 引入了另一个维度Action。模型在推理过程中可以执行一个外部操作。例如Search Read Lookup Calculate Query Database外部环境执行完成之后再把结果作为Observation返回给模型。于是Reasoning ↓ Action ↓ Environment ↓ Observation ↓ Reasoning形成闭环。二、为什么 Agent 需要 ReAct先看一个普通 LLM。假设用户问Python 最新稳定版本是什么如果只是普通模型User ↓ LLM ↓ Answer模型只能依赖自身知识。但这个问题明显具有时效性。一个 Agent 可以判断我不能仅依靠模型内部知识 应该查询 Python 官方网站。然后执行Action: search_web(Python latest stable release)工具返回Observation: Python 官方页面显示当前稳定版本为 ...模型再根据结果生成回答。整个过程就是Question ↓ Thought ↓ Search Tool ↓ Observation ↓ Answer更复杂的任务则可能重复多轮Question ↓ Thought ↓ Action ↓ Observation ↓ Thought ↓ Action ↓ Observation ↓ Final Answer因此从执行模型来看Agent 最关键的特征之一不是“用了 LLM”而是存在循环。三、ReAct 的三个核心部分ReAct 可以拆成三个阶段Thought Action Observation分别来看。四、Thought决定下一步应该做什么Thought 本质上承担的是根据当前状态判断下一步操作。假设任务是核查论文 A 中报告的样本量是否正确。当前系统中只有review_value 120 paper_title Study A模型可能判断目前只有综述中的数字 还没有源论文证据。 下一步需要获取论文全文。然后进入 Action。从系统角度可以写成Current State ↓ LLM ↓ Next Decision这里的 State 非常重要。例如state { question: ..., review_value: 120, paper: None, evidence: [], result: None }每一次 ReAct Loop模型实际上都在读取 State ↓ 判断缺什么 ↓ 决定下一步 Action五、Action让模型真正执行外部操作Action 是 ReAct 从“聊天模型”变成 Agent 的关键。因为大模型本身不会真正搜索网页查询数据库读取文件系统执行 Python调 API。这些能力来自Tools。例如def search_paper(title: str): ... def read_pdf(path: str): ... def query_database(sql: str): ... def calculator(expression: str): ...Agent 并不是直接执行这些 Python 函数。通常模型首先输出一个结构化 Tool Call{ name: search_paper, arguments: { title: Study A } }Agent Runtime 再负责LLM ↓ Tool Call ↓ Tool Executor ↓ 真实函数因此 Tool Calling 本质上可以理解成让模型负责“选择动作”让程序负责“真正执行动作”。模型负责调用哪个 Tool 参数是什么Runtime 负责解析 Tool Call 执行函数 捕获异常 返回结果六、Observation把现实世界反馈给模型Action 执行之后会产生结果。例如Action: search_paper(Study A)实际程序执行搜索。返回{ doi: ..., pdf_url: ..., title: ... }这个结果会重新进入模型上下文。在 ReAct 中这就是Observation于是Thought: 需要获取论文。 Action: search_paper() Observation: 找到了 PDF。模型接下来可能继续判断Thought: 现在已经有全文 下一步应该查找 participant / sample size。然后Action: search_pdf( keywords[participant, sample, patients] )工具返回Observation: 120 patients were initially recruited.模型继续判断。七、Observation 为什么如此重要如果没有 Observation整个 Agent 实际上就是LLM ↓ 自己想 ↓ 自己想 ↓ 自己想 ↓ 回答ReAct 则增加了LLM ↓ Environment ↓ New Evidence ↓ LLM也就是说模型可以根据外部世界的新事实修改原来的判断。例如第一次查到120 patients were recruited.模型不能马上结束。因为可能还有20 patients were excluded.于是Thought: 120 是 recruited population 还不能确认是否为最终分析样本。再次调用工具Action: search_pdf(excluded final analysis)返回Observation: 100 participants entered final analysis.这时 Agent 才知道recruited 120 analysed 100因此真正重要的并不是模型“多想了一次”。而是模型根据新证据更新了 State。八、一个完整的 ReAct 执行案例假设需要验证系统综述声称 Study A sample size 120Agent 可以执行以下过程。第一步寻找源论文Thought: 目前只有综述中的数据 需要先获得对应源论文。Action: search_paper(Study A)Observation: 找到 DOI 和 PDF。State 更新state[paper] pdf第二步寻找样本量Thought: 需要从全文中找到 participant/sample size 描述。Action: search_pdf([ participants, patients, sample ])Observation: 120 patients were initially recruited.Statestate[evidence].append({ population: recruited, n: 120 })第三步检查排除人数Thought: initially recruited 不一定等于 analysed sample 还需要检查 exclusion。Action: search_pdf([ excluded, final analysis, completed ])Observation: 20 participants were excluded. 100 participants entered final analysis.Statestate[evidence].append({ population: analysed, n: 100 })第四步生成判断此时状态已经变成{ review_value: 120, source: { recruited: 120, analysed: 100 } }于是Thought: 数值 120 可以在源论文中找到 但它对应 recruited population 不是 final analysed population。最终Final: PARTIAL MATCH Review: 120 Recruited: 120 Analysed: 100完整路径Review Claim ↓ Search Paper ↓ Observation ↓ Search Evidence ↓ Observation ↓ Check Exclusion ↓ Observation ↓ Compare ↓ Final Verdict这就是典型 ReAct。九、ReAct 和 Chain-of-Thought 有什么区别这两个概念非常容易混淆。Chain-of-Thought典型结构Question ↓ Reasoning ↓ Reasoning ↓ Reasoning ↓ Answer重点是通过中间推理过程提升复杂问题求解能力。模型仍然主要运行在自己的上下文内部。ReAct结构Question ↓ Reasoning ↓ Action ↓ Environment ↓ Observation ↓ Reasoning区别就在External Environment也就是说CoT Reasoning ReAct Reasoning External Action Feedback因此可以简单理解CoT 是“继续想”。ReAct 是“想一下 → 去做 → 看结果 → 再想”。十、ReAct 和 Tool Calling 是什么关系这也是很容易混淆的一点。Tool Calling 是一种模型调用外部函数的技术能力。比如模型输出{ name: get_weather, arguments: { city: Shenzhen } }这只是一次LLM ↓ Tool ↓ Result但 ReAct 更强调Reason ↓ Tool ↓ Observation ↓ Reason Again所以Tool Calling可以看作 ReAct Agent 的基础能力之一。但Tool Calling ≠ ReAct只有形成Decision → Action → Observation → New Decision的循环才更接近完整的 Agent 执行机制。十一、ReAct 和 RAG 有什么区别RAG 的典型流程Question ↓ Retriever ↓ Documents ↓ LLM ↓ Answer搜索动作通常是程序提前规定的。例如docs retriever.invoke(question) answer llm.invoke(question docs)无论问题是什么先检索 再回答ReAct 不同。模型可以决定是否需要搜索 搜索什么 第一次搜索够不够 需不需要换关键词 需要搜索数据库还是网页 什么时候停止例如┌── Vector DB │ LLM ────┼── Web Search │ ├── SQL │ └── PDF Reader所以 RAG 完全可以作为ReAct Agent ↓ Retriever Tool中的一个 Tool。因此RAG 解决“怎么找到相关知识”。ReAct 解决“下一步应该做什么”。十二、ReAct 和 Workflow 有什么区别假设有固定流程Load PDF ↓ Parse ↓ Extract ↓ Audit ↓ Report这属于典型 Workflow。程序员已经提前决定了A → B → C → DLLM 即使参与某个节点也不会改变整个流程。ReAct Agent 更像┌── Search │ Current State → LLM ── Read PDF │ ├── Database │ └── FinishLLM 会根据 State 动态选择下一条路径。所以Workflow 控制流由代码决定而Agent 部分控制流由模型决定实际生产系统中通常不会二选一而是Workflow Agent例如固定流程 ↓ Collector Agent ↓ 固定校验 ↓ Auditor Agent ↓ 条件路由十三、一个最简单的 ReAct Agent Loop如果不使用任何 Agent FrameworkReAct Runtime 可以简化成这样state [] for step in range(MAX_STEPS): response model.invoke(state) if response.type tool_call: tool_name response.tool_name arguments response.arguments observation tools[tool_name](**arguments) state.append({ role: tool, content: observation }) elif response.type final: return response.content raise RuntimeError(Agent exceeded max steps)核心其实只有while True: model(state) ↓ action ↓ execute tool ↓ observation ↓ update state然后继续下一轮。所以可以把 Agent 的底层逻辑理解成LLM Tool Executor State Loop十四、真正的 Agent Runtime 还需要什么上面的代码只能算 Demo。真正上线至少还需要解决几个问题。1. Stop ConditionAgent 什么时候结束不能简单while True:至少需要Final Answer Max Iterations Timeout Token Limit Cost Limit否则 Agent 很容易无限循环。2. Tool Error如果search_web()报错怎么办需要Retry Fallback Error Observation例如try: result tool(**args) except Exception as e: result { error: str(e) }然后把错误重新作为 Observation 交给 Agent。3. Invalid Tool Call模型可能生成不存在的 tool 错误参数 错误类型所以 Tool Schema 必须进行严格验证。例如class SearchInput(BaseModel): query: str top_k: int 54. State Management复杂 Agent 不可能只靠一串 messages。通常还需要{ messages: [], documents: [], evidence: [], tool_results: [], retry_count: 0, status: running }因此随着系统复杂度上升Agent 问题最终会变成 State Management 问题。十五、为什么后来又有 LangChain如果自己实现 ReAct需要处理Model API Prompt Tool Schema Tool Executor Structured Output Retry State Agent Loop Error Handling StreamingLangChain 等框架就是把这些高频组件抽象出来。例如Model Tools Agent框架负责处理调用模型 ↓ 发现 Tool Call ↓ 执行 Tool ↓ 把 Observation 返回模型 ↓ 继续 Loop因此 LangChain 的价值并不是“让模型突然拥有 Agent 能力。”而更多是提供 Agent Harness 和常见工程组件。十六、为什么又会出现 LangGraph普通 ReActLLM ↕ Tools还比较容易管理。但如果系统开始出现Collector ↓ Auditor ↓ PASS? ↙ ↘ NO YES ↓ ↓ Collector Finish就需要处理循环 条件分支 状态持久化 人工介入 失败恢复 多 Agent 并行 Checkpoint此时简单while True就会越来越难维护。于是可以把流程显式建模成Node Edge State例如┌─────────────┐ │ ↓ Collector → Auditor → Need More Evidence? │ No│ ↓ Judge ↓ END这就是 LangGraph 这一类 Agent Runtime / Graph Orchestration 的价值。十七、ReAct Agent 最大的问题不可控性ReAct 的优势是动态决策但这同时也是它最大的风险。如果给 AgentSearch Read File Write File Run Code Database Email模型拥有非常大的决策空间。于是可能出现错误 Tool ↓ 错误 Observation ↓ 错误 Reasoning ↓ 错误 Action ↓ 错误进一步放大因此生产系统通常会限制 Agent 的自由度。例如系统控制 必须先 Search ↓ Agent 控制 Search 什么 ↓ 系统控制 必须进入 Audit ↓ Agent 控制 Evidence 是否足够 ↓ 系统控制 失败最多重试两次所以真正成熟的 Agent 通常不是100% Agent而是Deterministic Workflow Agentic Decision十八、什么时候不应该使用 ReAct并不是所有 LLM 应用都应该做 Agent。例如输入文章 ↓ 生成摘要一次 LLM 调用足够。如果是问题 ↓ 固定 RAG ↓ Answer普通 RAG Pipeline 足够。如果是A ↓ B ↓ C流程完全确定。Workflow 更简单、更便宜、更稳定。ReAct 更适合下一步无法完全提前确定 需要访问外部环境 需要根据执行结果动态修改计划例如Web Research AgentCoding Agent数据分析 AgentSQL Agent自动故障排查文献审计多工具复杂任务。一个简单判断如果你在写代码之前已经知道 Agent 每一步应该怎么走大概率先用 Workflow而不是 ReAct。十九、ReAct 的工程成本ReAct 带来了 Agent 能力也带来了额外成本。Token 成本普通LLM × 1AgentLLM ↓ Tool ↓ LLM ↓ Tool ↓ LLM一次任务可能需要很多次模型调用。延迟每一个 Action 都可能涉及网络请求 模型请求 数据库请求 搜索 文件解析所以复杂 Agent 延迟明显更高。错误传播Agent 可能Search 错 ↓ Observation 错 ↓ Reasoning 错 ↓ 下一次 Action 错错误具有连锁效应。Debug 难度传统程序Input → Function → OutputAgentState ↓ LLM Decision ↓ Tool ↓ Observation ↓ New State ↓ LLM Decision ...因此必须记录State Tool Call Tool Result Routing Retry Final Output也就是Tracing / Observability。二十、理解 ReAct 后再看 Agent 就会简单很多可以把最基础的 Agent 写成┌─────────────────┐ │ │ ↓ │ Input → LLM / Decision → Action ↑ │ │ ↓ └── Observation ← Tool │ ↓ Final Answer里面其实只有几个核心概念Goal State Decision Action Observation Loop Stop Condition而 LangChain、LangGraph、AutoGen 等框架本质上都在不同层次解决这些问题如何定义 Agent 如何调用 Tool 如何保存 State 如何控制 Loop 如何做 Routing 如何处理异常 如何调试执行过程理解底层 ReAct Loop 之后再学习任何 Agent Framework 都会容易很多。结语ReAct 最重要的贡献并不仅仅是提出了Thought Action Observation三个步骤。它真正改变的是 LLM 的执行模式。传统 LLMInput ↓ Generate ↓ OutputReActGoal ↓ Reason ↓ Act ↓ Observe ↓ Update State ↓ Reason Again ↓ ... ↓ Complete也就是说大语言模型第一次真正开始参与软件系统的控制循环。所以一个 Agent 不能简单理解成LLM Tools。更完整的表达应该是Agent LLM Tools State Environment Control Loop Stop Condition而 ReAct正是理解这个 Agent Loop 最经典、也最基础的起点之一。参考资料Shunyu Yao et al.,ReAct: Synergizing Reasoning and Acting in Language Models, ICLR 2023[2210.03629] ReAct: Synergizing Reasoning and Acting in Language ModelsGoogle Research,ReAct: Synergizing Reasoning and Acting in Language Modelshttps://research.google/blog/react-synergizing-reasoning-and-acting-in-language-models/LangChain 官方文档AgentsAgents - Docs by LangChainLangGraph 官方文档Workflows and AgentsWorkflows and agents - Docs by LangChainLangGraph 官方文档OverviewLangGraph overview - Docs by LangChain