Agent 推理模式 · 深挖版教程 · 第 2 章

接地:从「想」到「做」

上一章建立了「推理即算力」,也留下一个尾巴——闭环 CoT 没有外部真值校验、会一脸自信地错。这一章就是答案:把每一步推理钉在外部反馈上。纵轴(接地 grounding)的下半场从这里开始。

定位:纵轴下半 · grounding | 主角:ReAct · Plan-and-Execute · Reflexion | 代码状态:示例为讲解用伪代码,标注"未本地验证"

本章把这四件事钉清楚

  • 闭环推理(CoT / ToT)为什么会自信地幻觉——错误进了链条就顺着往下传,没有任何一步回头核对真值
  • ReAct 怎么用 observation(把真实结果喂回推理的那一步)接地:交错 Thought → Action → Observation,让下一步推理以真值为条件
  • 前置规划(Plan-and-Execute)vs 交错(ReAct):计划越前置越省调用,但世界中途变了,计划就过时
  • Reflexion 怎么跨「回合(episode,一次完整尝试)」接地:把一次失败的口头复盘写进记忆,下次重试前置进上下文

横轴这一章往右移了两格:从上一章的串行 token、并行采样,走到工具往返(ReAct)与重试回合(Reflexion)。但本章真正的主线是纵轴——所有这三种模式都在做同一件第 1 章模式做不到的事:让推理循环接上外部真值。同一句贯穿全教程的话依旧成立:每种模式都用推理期算力换可靠性;区别只在算力花在哪、循环接不接外部真值。第 1 章的 CoT 把算力花在串行 token 上、却把循环闷在模型内部;这一章把循环打开,接到世界上。

2.1为什么闭环会幻觉

CoT 和 ToT 生成的只是「看起来对」的文本——没有任何一步去和真实世界核对,错误一旦进入链条就顺着往下传。

第 1 章末尾的 faithfulness 讨论已经埋下伏笔:一段 CoT 链条读起来步步成理,不等于每一步都成立。把那个观察推到极限,就是闭环推理的根本风险。一次 CoT 调用,从头到尾发生在模型内部——输入 token 进去,中间 token 一路自回归地生成出来,输出 token 收尾。整条链上,没有任何一个环节把中间结论拿去和外部真值对照。模型写下"《泰坦尼克号》的导演是斯皮尔伯格"时,它不会、也无从去查一下;它只是按训练分布,生成了一个高概率、读起来通顺的下一句。

底层机制 · 错误传播 error propagation

自回归生成的每一步,都把已经写出的全部 token 当成既定前提。这意味着链条第 3 步如果错了,第 4、5、6 步不是去纠正它,而是以这个错误为条件继续往下推——错误被当成事实,顺着链条放大。这就是 error propagation:闭环里没有任何反向校验的力量,一个早期的小错会被后续每一步当成地基,盖出一座自洽却整体错误的推理楼。链条越长,越自洽,也越危险——因为它读起来越像真的。

这正是纵轴上半(内部闭环)的代价,也是引入 grounding 的全部动机。把更多算力花在更长的链条、更宽的树上(横轴右移),只会让这座楼盖得更高更精致——但地基若错,楼越高摔得越狠。要根治,得换一根轴:不在「想得更多」上加码,而是给循环装一个能从外部纠错的接口。下一节的 ReAct 就是这个接口最直接的形态。

场景走查:一道多跳事实题

题目:「莱昂纳多·迪卡普里奥出生那年,拿下奥斯卡最佳影片的电影,它的导演是谁?」这是一道两跳题——先定位「出生年」,再查「那年的最佳影片及其导演」。纯 CoT 怎么走:

纯 CoT(闭环,无核对)

「迪卡普里奥出生于 1974 年。1974 年的奥斯卡最佳影片是《教父 2》,导演是弗朗西斯·科波拉。」——读起来天衣无缝,但两处都可能是编的:出生年若记成 1974(实际 1974-11-11 出生,奥斯卡颁的是上一年度影片,时间错位极易混淆),最佳影片归属哪一届更容易张冠李戴。模型不会停下来查,它只是顺着高概率往下写,把一个似是而非的导演自信地交了出来。

问题不在模型"不够聪明",而在这条链结构上没有核对的入口。下一节同一道题,ReAct 会先 search、再 observe 到真实结果、然后才落笔——区别就是那一根从外部回流的线。

2.2ReAct:用 observation 接地

交错 Thought → Action → Observation:Action 调用工具或环境,Observation 把真实结果喂回推理,于是下一步「想」是以真值为条件,而不是只凭先验。

ReAct(Yao et al. 2022,Reasoning + Acting,直译「边推理边行动」)的结构朴素到一句话能讲完:让模型在一个循环里交替产出三种东西——Thought(一句自然语言推理,决定下一步干什么)、Action(一个结构化的工具调用,比如 search[1974 奥斯卡最佳影片])、Observation(外部代码执行那个 Action 后,把真实返回结果作为文本贴回上下文)。然后基于这条新到手的真值,模型产出下一个 Thought,循环继续,直到某个 Action 是 finish[答案]。

它同时解决了两个方向的病。纯推理(CoT)的病是幻觉、且无法更新事实——模型只能调用训练时见过的世界,查不了今天的航班、也核对不了一个具体年份。纯行动(一个只会调工具、不会推理的策略)的病是无法规划——它能执行单步动作,却串不起「先查 A、根据 A 的结果决定查 B」这种依赖链。ReAct 把两者缝在一起:Thought 负责规划与决策,Action/Observation 负责落地与核对。

CoT:内部闭环 模型内部 想 再想 无核对 ReAct:接地闭环 Thought(想) Action(做) Observation(看) 外部工具 / 世界 Wikipedia API… 真值回流 下一轮
图 2.1左边的 CoT 在模型内部空转、没有任何核对入口;右边的 ReAct 多了一根从框外回流的红线——Observation 把真实外部状态注入上下文。注意:接地的全部要害,就是那根从「外部工具 / 世界」流回 Observation 的红线;没有它,ReAct 退化成普通 CoT。
底层机制 · 深一层:Observation 改变了下一步的条件分布

关键不在「模型会调工具」这件表面事,而在 Observation 把真实外部状态写进了上下文。自回归生成永远是「给定上文,预测下一个 token」。CoT 的上文全由模型自己生成,所以下一步只能以模型的先验为条件——它编出的那个导演,就是先验里最顺的猜测。ReAct 在上文里插入了一段模型自己生不出来的真值("搜索结果:1974 年最佳影片是《骗中骗》"),于是下一个 Thought 是以这段真值为条件生成的。这一步条件的替换,正是消除 CoT 自信幻觉与错误传播的机制本身:错误不再能无阻力地往下传,因为每隔一步就有一段外部真值把链条钉回地面。

ReAct 还便宜得惊人:原始论文靠 1–2 个 in-context 例子(few-shot 演示几轮 Thought/Action/Observation 长什么样)就能让模型学会这套交错格式,无需微调。它在 PaLM-540B 与 GPT-3 上验证。决策类任务的提升尤其大:ALFWorld(文字版具身家务任务)上 ReAct 比 imitation / RL 基线绝对高出 34%;WebShop(模拟网购)上高出约 10%。这些任务的共同点是——你必须真的去环境里看一眼当前状态,纯靠脑补走不通。

别漏 · 一个被广泛误记的细节

「ReAct 在所有任务上都吊打 CoT」是错的。在知识密集型问答 HotpotQA 上,单纯 ReAct 可能不如单纯 CoT——因为 ReAct 受限于检索质量,搜不到好结果时反而被带偏,而 CoT 能放开调用模型的内部知识。论文里那个著名的胜绩,是 ReAct + CoT-Self-Consistency 合用(ReAct 搜不动时回退到 CoT 投票,两者互补),常被以讹传讹成"纯 ReAct 赢了"。记准这一点,面试里能一句话戳破对方的复述式理解。

场景走查:同一道多跳题,ReAct 怎么走

回到 §2.1 那道题。ReAct 不再一口气编出导演,而是把它拆成可核对的几轮。逐步标注每一段对应什么:

react_trace.txttrace
Thought 1:  要先确定迪卡普里奥的出生年份。            ← 规划:决定先查哪一跳
Action 1:   search["Leonardo DiCaprio 出生年份"]      ← 行动:发出结构化工具调用
Observation 1: 莱昂纳多·迪卡普里奥,1974-11-11 出生。  ← 接地:真实结果回流(模型自己生不出这条)
Thought 2:  1974 年出生。下一步查 1974 年度奥斯卡最佳影片。
Action 2:   search["1974 年度 奥斯卡 最佳影片"]
Observation 2: 第 47 届(表彰 1974 年影片)最佳影片为《教父 2》。  ← 接地:纠正了"届/年份"错位
Thought 3:  《教父 2》的导演需要确认。
Action 3:   search["教父 2 导演"]
Observation 3: 《教父 2》导演为弗朗西斯·福特·科波拉。
Thought 4:  三跳齐了,可以作答。
Action 4:   finish["弗朗西斯·福特·科波拉"]            ← 收尾:每一跳都被外部真值钉过

对照纯 CoT 那条「一口气编完」的链:ReAct 把同一道题切成「想要查什么 → 真的去查 → 拿真值校准下一步」的节拍。每个 Observation 都是一次纠错机会——哪怕 Thought 2 的年份记岔了,Observation 2 也会把它拽回正轨。这就是接地与闭环最直观的差别。

至于 Action 在工程上究竟怎么变成一次真实的 tool_use 调用、结果又怎么被解析回 Observation——那是工具协议层的事,本教程不展开,详见 tool-use · Agent Loop。而 ReAct 作为一种控制流在「提前承诺 ↔ 临场适应」轴上的定位与机制细节,规划视角讲得更细:agent-planning · ReAct。本节只关心一件事——它为什么能消除幻觉(答案就是那根真值回流线)。

先想再翻 · Predict

把上面 trace 里所有 Observation 行删掉、只留 Thought 和 Action,让模型照着往下生成——它还算不算 ReAct?输出质量会怎样?

翻看答案

不算了,它退化成了纯 CoT。Observation 是接地的唯一载体;删掉它,模型的下一个 Thought 又只能以自己生成的上文为条件,真值回流线断了,自信幻觉与错误传播全部回来。更糟的是格式上它还长得像 ReAct(有 Thought 有 Action),容易骗过 review——这正是图 2.1 那根红线被强调的原因:判断一个循环接没接地,看的不是它有没有 Action,而是有没有真值流回来喂给下一步。

2.3Plan-and-Execute(前置)vs ReAct(交错)

ReAct 走一步看一步、交错地接地;Plan 系把规划提到最前面一次出全——更省调用、更便宜,代价是世界中途变了,计划就过时。

ReAct 的接地是交错式的:每个 Action 之后立刻 observe、立刻调整。另一种思路是把「想」整体前置——先一次性想出完整计划,再逐步执行。这条路有两个常被混为一谈的东西,必须拆开讲。

论文:Plan-and-Solve(Wang 2023)

Plan-and-Solve(Wang et al. 2023,arXiv 2305.04091)是一个单次 zero-shot prompt,不是多智能体架构。它把经典的「Let's think step by step」升级成「先理解问题、制定一个分步计划,再按计划逐步求解」——本质是用一句更结构化的指令,引导模型在一次生成里先列计划纲要、再展开。它的进阶版 PS+ 进一步加上「抽取相关变量、算出中间量」的指令,专治算术题里看错数、漏算中间步的毛病。效果:在 GSM8K(小学数学应用题)上,PS+ 用 text-davinci-003 拿到 58.2%,全面超过 Zero-shot-CoT。注意它仍是闭环的——计划和求解都在一次模型生成里完成,没有外部真值;它的接地程度其实和 CoT 同档,"Plan"在这里是一种 prompt 结构,不是工具往返。

工程脚手架:LangChain 的 Plan-and-Execute agent

另一个东西是 LangChain 的「Plan-and-Execute」agent(思路源自 BabyAGI):一个独立的 planner LLM 先产出一张完整的步骤表,然后一个 executor 逐条执行——执行每一步时才可能去调工具、接地。它是工程脚手架,不是 Wang 那篇论文。两者都叫「Plan」,但层级完全不同:论文是「一次生成内先列计划」的提示技巧;LangChain 是「planner 与 executor 分成两个角色」的运行架构。把它们混作一谈,是这一带最常见的认知错位。

ReAct · 交错 想 做 看 想 做 看 想 做 看 每步都重想 Plan-Execute · 前置 一次出全计划 步1·步2·步3 执行 步1 执行 步2 执行 步3 不再回头改计划
图 2.2上排 ReAct 想 / 做 / 看交替、每步都重想(planner 调用多、贵,但意外能立刻被看见);下排 Plan-Execute 先一次性出全计划、再逐步执行。注意:计划越前置,planner 调用越省、越便宜,但执行途中世界若变了,前置的计划越看不见意外、越容易过时。
取舍 · 前置规划买什么、赔什么

买到:planner 只调一次(而非每步重想),执行阶段可以用更便宜的逻辑甚至更小的模型,总 token 成本与延迟都低;计划还能被人读一遍、审一遍再放行。赔上:计划是在「执行前的世界快照」上做的,一旦执行途中世界变了(某步报错、检索到的事实推翻了前提、外部状态被别人改了),计划就过时(stale),executor 还在照着一张已作废的地图走。ReAct 反过来——每步都重想,贵,但任何意外都在下一个 Thought 里立刻被纳入。世界越稳、步骤越确定,越偏前置;世界越动、越需要临场纠偏,越偏交错。

这条「前置 ↔ 交错」正是规划视角的主轴。Plan-and-Execute 作为一种控制流的完整定位(planner / executor 怎么分、什么时候触发重规划),见 agent-planning · Plan-and-Execute。

2.4Reflexion:跨回合用记忆接地

ReAct 在一次尝试内接地;Reflexion 跨「回合」接地——一次失败后把口头自我批评写进记忆,下次重试前置进上下文,零梯度更新。

ReAct 和 Plan-Execute 的接地都发生在一次尝试(episode,一个回合 = 从开始到给出结果或失败的完整一轮)之内。Reflexion(Shinn et al. 2023,arXiv 2303.11366)把接地的尺度拉长到回合之间:这一回合栽了,不白栽——让模型把"我哪一步错了、下次该怎么改"用自然语言写下来,存进 episodic memory(情节记忆,一段跨回合保留的文字笔记),下一回合重试时把这段反思前置进上下文,相当于带着上次的教训重来。

它有三个角色:Actor(真正去做任务的策略,本身可以就是个 ReAct agent)、Evaluator(判定这一回合成没成、给出信号,可以是单元测试、环境奖励、或一个打分模型)、Self-Reflection(拿到失败信号后,生成一段口头复盘)。循环是:Actor 跑一回合 → Evaluator 判失败 → Self-Reflection 写反思 → 反思进记忆 → Actor 带记忆再跑下一回合。

回合 1:失败 写口头反思 "我漏看了X" episodic memory 情节记忆(持续累积) 前置进上下文 回合 2:带记忆重试 这次避开上次的错 权重不变 口头强化 · 零梯度
图 2.3失败不更新权重,而是变成一段文字存进 episodic memory,下一回合前置回上下文——这就是「口头强化」。注意:接地这次发生在回合之间,载体是记忆里那段自我批评;它要起作用,前提是 Evaluator 能给出"这回合失败了"的可靠信号。
底层机制 · 为什么叫「口头强化」而非强化学习

Reflexion 不更新任何权重。传统强化学习靠梯度把奖励信号回灌进参数;Reflexion 把"奖励"翻译成一段自然语言反思,靠上下文而非参数传递改进——论文称之为 verbal reinforcement(口头强化),零梯度。所以把它归为 RL 其实有点勉强:它没有策略梯度、没有价值函数更新,更像「带长期记忆的自我提示」。这也决定了它的脾气——改进是软的(取决于模型读不读得懂、用不用得上那段反思),不像梯度更新那样硬地刻进权重。

效果相当亮眼:HumanEval(代码生成)上,给 GPT-4 套 Reflexion,pass@1 从 80% 提到 91%(失败用例 → 跑测试 → 反思哪里写错 → 重写);ALFWorld 上达到 97%。它和 ReAct 是叠加关系:Actor 用 ReAct 在回合内接地,Reflexion 在回合间接地,两层接地各管一段。Reflexion 作为「反思家族」的一员、它和 Self-Refine 等的异同,见 agent-planning · Reflexion。

代价 · 三个让 Reflexion 失效的条件

① 没有可靠的 reward / eval 信号就没用。Reflexion 的全部杠杆压在 Evaluator 上——它得能判断"这回合到底成没成"。代码任务有单元测试、游戏有胜负,信号清晰;可一旦任务的成败本身模糊("这篇文案写得好不好"),Evaluator 给不出可信判定,反思就成了无的放矢。② 一条坏反思会把错误固化。反思是模型自己写的,也可能写错;一条误导性的反思被前置进下一回合,会让 Actor 更偏,越改越糟。③ 记忆持续变长。每回合都往 episodic memory 里加,上下文越堆越长,成本上升、且早期反思可能与当前回合不再相关。

先想再翻 · Predict

把 Reflexion 用在一个「开放式写作润色」任务上(没有单元测试、没有客观对错),让它反复反思重写。它多半会怎样?

翻看答案

多半原地打转甚至变差。问题出在 Evaluator:开放式写作没有客观成败信号,所谓"评估"只能是模型对自己的主观打分,而模型既当运动员又当裁判,反思就失去了外部真值的锚——这恰恰违背了本章主线(接地需要外部真值)。更隐蔽的是代价②:一条主观且可能错误的反思被当成改进前置进去,会把输出往一个并不更好的方向越推越远。要救,得给它一个外部的、可信的评估信号(见本章末 challenge)。

把三种接地放一起看

本章三种模式 · 接地发生在哪、靠什么、什么时候失效
模式算力花在哪(横轴)接地发生在哪 / 靠什么主要代价
ReAct 工具往返 一次尝试内 · 每步的 Observation 把真值喂回推理 受检索质量牵制;HotpotQA 上单用可能不如 CoT
Plan-Execute 前置规划 + 逐步执行 计划前置、执行时才接地 · planner / executor 分离 世界中途变了计划就过时(stale)
Reflexion 重试回合 回合之间 · 口头反思存入 episodic memory 再前置 没有可靠 eval 信号即失效;坏反思会固化错误;记忆变长

三者沿横轴依次右移(工具往返 → 前置规划 → 重试回合),但都坐在纵轴下半——都接了外部真值,只是接地的时间尺度不同:ReAct 在步与步之间,Plan-Execute 在计划与执行之间,Reflexion 在回合与回合之间。这正是本章的骨架。

自测 & 辨析

合上本页,先自己作答,再展开核对。第 4 题是设计层。

  1. 一段 agent trace 里有 Thought 和 Action,但每个 Action 后面跟的"Observation"其实是模型自己续写出来的、没真去调工具。它接地了吗?
    翻看答案

    没有。接地的唯一判据是有没有外部真值流回上下文,而不是格式上有没有 Action / Observation 字样。模型自己续写的 Observation 仍来自先验,真值回流线是断的——它本质还是 CoT,只是穿了件 ReAct 的外衣(呼应图 2.1 和 §2.2 的 Predict)。

  2. 为什么说「ReAct 在所有任务上都强于 CoT」是错的?举出反例任务。
    翻看答案

    反例是 HotpotQA:单纯 ReAct 受检索质量牵制,搜不到好结果时被带偏,可能不如能放开调用内部知识的单纯 CoT。论文里的胜绩是 ReAct + CoT-Self-Consistency 合用,不是纯 ReAct。

  3. Reflexion 跨回合改进,却说它「不是真正的强化学习」,为什么?
    翻看答案

    因为它零梯度、不更新权重。它把失败翻译成一段自然语言反思,靠上下文而非参数传递改进——论文称 verbal reinforcement(口头强化)。没有策略梯度、没有价值函数更新,所以归到 RL 名下有点勉强。

  4. (设计层)一个客服 agent 要处理「按订单号查物流、若延误则按公司政策给补偿方案」。该用 ReAct 还是 Plan-Execute?说清为什么不选另一个。
    翻看答案

    偏 ReAct(交错)。关键在「若延误则…」——补偿这一步依赖物流查询的真实结果,执行前根本不知道走不走得到补偿分支,世界状态(物流是否延误)必须查了才知道。Plan-Execute 的前置计划在这里会过时:它得在没看到物流结果前就把整张步骤表定死,要么把补偿分支硬编进去(可能根本用不上)、要么漏掉(真延误时没法临场补)。ReAct 每步重想的代价(多几次 planner 调用)在这种状态依赖、分支由真值决定的任务里完全值得。反过来,若任务是「固定地把一张表的每行翻译并写回」——步骤确定、世界不变——那才轮到 Plan-Execute 省调用的优势。

刚好够不着 · Challenge

给一个没有可靠 eval 信号的任务,说明 Reflexion 为什么会失效、怎么补

任务:让 agent 反复打磨一封「劝退信」,要求"既坚定又不失礼貌"。这里没有单元测试、没有客观胜负。(a) 用本章的机制说清:Reflexion 在这个任务上为什么多半原地打转甚至变差(提示:Evaluator 这一环断在哪、坏反思怎么固化)。(b) 不改 Reflexion 的核心循环,只动 Evaluator,给出至少一种把"模糊主观判断"重新接上外部真值的办法——让那段反思重新有锚可依。想清楚后,对照 §2.4 的 Predict 与代价清单检验自己的答案是否抓住了"接地需要外部真值"这条主线。