Chapter 03
搜索 & 反思家族:探索多条路 / 从失败中学
上一章的四种范式(ReAct → Plan-and-Execute → ReWOO → LLMCompiler),无论多适应、计划定得多死,都只产出一条计划/轨迹。这一章处理两种"一条路不够"的情形:探索多条候选路径再选最好的一条(搜索,家族②),以及整次失败后把教训学到手再重来(反思,家族③)。
本章你将建立的 schema
- 多计划选择不是新的"计划格式",而是三个动作的合成:生成多个候选 + 评估每个 + 用搜索算法挑
- ToT 的"评估函数"其实是让 LLM 给自己的状态打分(sure/likely/impossible),不是一个训练出来的 critic
- Reflexion 用一段自然语言"梯度"(写成教训)代替权重更新——零微调,靠改 prompt 重塑行为
- 反思发生在 episode 之间(整次重试),ReAct 当场纠错发生在 step 之间(同一次执行内)——回扣第 1 章那条区分
- LATS 用 MCTS 把搜索(②)、反思(③)、ReAct 的行动-观察(①)缝成一个算法——三大家族可以组合
3.1从「一条路」到「多条路」
上一章所有范式都押注一条轨迹;有些任务的最优解藏在岔路里,押单条路会走进死胡同——这时需要同时探索几条、再比较。
第 2 章的四种范式,差别只在"那条计划定得多死、能不能中途重排",但它们始终只维护一条路。这对"路径基本确定、只是步骤多"的任务(订差旅、多跳问答)足够。可一旦任务有多条看似可行的解题路径、且贪心地顺着一条走容易走进死胡同(数字谜题、需要试探性假设的推理、代码里有多种实现思路),单条轨迹就力不从心——错一步,整条轨迹作废,而 ReAct 式的当场纠错只能"改下一步",没法"退回岔路口换一条"。
底层机制(比文档深一层):单条轨迹的范式,本质是在解空间里走一条不回头的路径——即便 ReAct 能每步纠偏,它纠的也只是"当前这条路的下一步往哪拐",从不同时持有两个互斥的候选未来。家族②把规划重新表述成在一棵树(或图)上做搜索:每个节点是一个"想到这里的状态",从一个节点可以展开出多个候选下一步,于是天然出现分叉。一旦有了分叉,就必须回答两个新问题——哪条岔路更有希望(评估)、按什么顺序展开、什么时候砍掉没希望的岔路(搜索策略)。这两个问题,就是 §3.2 ToT 的全部内容。
单条轨迹像走迷宫时一直摸右墙——能走,但撞墙了只能原地换个方向,没法瞬移回上一个路口。多路径搜索像在路口插旗、记下每条岔路、择优探索、走不通就回到插旗处换一条。类比失效在"回退成本":真人回到路口几乎免费,而 agent 的每个节点都是一次(或多次)LLM 调用——分叉越多、探得越深,账单越线性膨胀。这就是搜索家族的核心代价,§3.2 末尾会算这笔账。
"把 24 这个数用 4、9、10、13 四个数和加减乘除算出来"——这种题,为什么 ReAct 的"每步看真实结果再纠下一步"帮不上忙?它缺的到底是哪种能力?
展开答案(先停 10 秒)
这类题没有可供"接地"的外部观察——没有工具会告诉你"(13−9)=4 这一步是对是错",对错只有等你凑完四个数才知道。ReAct 的纠错依赖"看到真实 Observation 再调整",可这里每一步的"真实结果"要到终局才显现。它缺的不是"接地的纠错",而是"在到达终局前,并行持有多个候选算式、给每个估个前景分、砍掉明显凑不出 24 的分支"——这正是搜索(家族②)才有的能力。Game-of-24 也确实是 ToT 论文的主战场。
3.2Tree of Thoughts(ToT):生成 → 评估 → 搜索
ToT = 把"一连串想法"升级成"一棵想法树":每步生成 k 个候选想法,让 LLM 给每个状态打分,再用 BFS/DFS 择优展开、砍掉没希望的分支。
纯思维链(CoT)是一条不回头的想法链——一步推错,整条链跟着错下去(第 1 章已埋)。Tree of Thoughts(Yao et al. 2023)把这条链展开成树:在每个"想到一半的状态"处生成多个候选下一步,并引入一个评估来比较它们、一个搜索来决定先探哪条、砍哪条。它给 LLM 装上了人类解题时天然会做却被 CoT 抹掉的两件事:试探不同思路和走不通就回头。
底层机制(比文档深一层):ToT 把规划拆成可独立替换的三个部件——
- 状态(state):一个节点是
s = [x, z₁…zᵢ],即"原始输入 x + 到目前为止的若干步想法 z"。注意节点存的是累积的部分解,不是单个想法。 - 生成器(generator):从当前状态产出 k 个候选下一步想法——要么用一个 CoT prompt 独立同分布地采样 k 次(想法空间大、彼此差异大时),要么用一个"propose" prompt 一次性顺序列出 k 个不重复候选(想法空间窄时)。
- 状态评估器(state evaluator):让 LLM 给每个状态打分。两种打法——赋值:对单个状态做一小段前瞻推理后贴标签(
sure / likely / impossible);投票:把多个状态摆在一起,让 LLM 投票选最有希望的那个。 - 搜索算法(search):BFS——每一层只保留评估最高的 b 个状态(beam),逐层向下;DFS——沿一条路深入,一旦某状态的评估值低于阈值就剪枝并回溯到上一个岔口换分支。
这里有个该被讲明白的意外:那个"评估器"不是一个训练出来或外挂的 critic——它就是同一个 LLM 在给自己生成的状态打分(贴 sure/likely/impossible,或自己跟自己投票)。换句话说,ToT 的"智能"全部来自"让模型多生成几个候选、再让它自己评判哪个更靠谱"这套提示工程,没有任何额外的模型或参数。就这么朴素的一招,在 Game-of-24 上把准确率从 CoT 的 4% 抬到 74%。
成本怎么算:一次 ToT 大约要 b × 深度 × (生成 + 评估) 次 LLM 调用——Game-of-24 这种约需 100+ 次调用,单题成本约 $0.74,而朴素 CoT 单次约 $0.47。表面看贵了一半,但下面这点才反直觉。
ToT 并不比"无脑多采样 CoT"贵多少。它在 Game-of-24 上用约 5.5k token 拿到 74%;而盲目跑 100 次独立 CoT(指望蒙对一次)大约要 6.7k token——量级相当,准确率却天差地别。结论是反直觉的:把同样的 token 预算花在"有评估、有剪枝的结构化搜索"上,比花在"碰运气重采样"上,既更准、又不更贵。ToT 的贵是"比单次 CoT 贵",不是"比同等算力的暴力采样贵"——这两件事常被混为一谈。
把 ToT 的搜索从 DFS 换成 BFS,对"什么时候发现走错了路"这件事有什么影响?哪一种更省 token?
展开答案(先停 10 秒)
DFS 沿一条路一插到底,靠"评估值低于阈值就回溯"来及时止损——发现错路早,但若评估不准,可能在一条死路上深挖几步才掉头。BFS 逐层保留 top-b,每层都横向比较 b 个状态,更稳健但要同时维护 b 条路、每层都评估,调用数通常更多。一般规律:解的深度浅、分支因子大、想尽早砍掉烂分支,用 DFS 更省;需要全局横向比较、怕过早剪错好分支,用 BFS。Game-of-24(深度固定为 3 步)用的是 BFS(b=5)。
3.3Reflexion:把失败写成下一次的提示
Reflexion = 跑完一次失败的轨迹后,让 LLM 把"哪里错了、下次怎么改"写成一段自然语言教训,存进记忆,拼到下一次同一任务的 prompt 开头——靠改提示而非改权重来变强。
搜索(家族②)解决"一次执行内探索多条路"。但还有一类改进发生在整次执行之间:agent 把一个任务整体做砸了,按理该"吸取教训、下次别再这么干"。强化学习的标准答案是用奖励信号去更新权重——但对一个动辄千亿参数的 LLM,每失败一次就微调一轮,代价高到不可行。Reflexion(Shinn et al. 2023)给出一条绕过权重的路:把"教训"写成自然语言,存进记忆,下次直接喂回模型。
底层机制(比文档深一层):Reflexion 由三个角色构成——
- Actor(执行者):一个跑轨迹的策略,本身就可以是第 2 章的 ReAct,或一个 CoT 策略——注意这里把上一章的 ReAct 当成了一个可插拔的组件复用。它产出一条完整轨迹(短期记忆)。
- Evaluator(评估者):把这条轨迹的结果转成一个稀疏奖励——可以是一个启发式判断,更关键的是可以是外部信号,比如单元测试通过/失败、游戏赢/输。
- Self-Reflection(自我反思 LLM):拿到"奖励 + 那条失败轨迹",生成一段自然语言教训("我假设了文件已存在,但其实没有——下次应先检查"),存进长期记忆。
关键在记忆怎么用:轨迹是短期记忆(只在本次有效),反思则被拼接到下一次同一任务的 prompt 开头,像一道"语义梯度"——它不更新任何一个权重,却通过改写输入上下文重塑了模型这一次的行为。零微调、零权重更新;记忆通常只保留最近 1–3 条反思(防止上下文膨胀)。这就是"用自然语言代替梯度下降"的字面含义。
成本与前提:Reflexion 不是免费的——它需要两个硬前提。其一,任务可重复尝试(同一道题能再做一遍);其二,存在可用的奖励信号(能判出这次到底成没成)。少了任一个,Reflexion 就无米下锅:无法重试,写了教训也没机会用;分不清成败,反思就只能瞎猜"哪里错了"。它最闪光的场景,恰恰是有现成外部判分器的地方——代码任务有单元测试(HumanEval pass@1 达 91%,对比 GPT-4 的 80%),具身决策有环境给的成败信号(ALFWorld 完成 130/134)。
反思像考完试写错题本——把这次错的题、错因、下次对策写下来,下次开考前先翻一遍。类比失效在两处:① 错题本是你主动选择翻不翻,而 Reflexion 的教训是每次都被强制拼进 prompt;② 真人的错题本可以无限积累,Reflexion 的记忆窗口很小(1–3 条)——塞太多反而挤占上下文、稀释重点。它是"带遗忘的错题本"。
Reflexion 和 ReAct 都说"从失败中学",但时间尺度完全不同,别混。ReAct 学在 step 之间:看到这一步的真实 Observation,立刻调整下一步,全程在同一次执行内。Reflexion 学在 episode 之间:整条轨迹跑完、判定失败后,才把教训写进记忆,作用于下一次整体重试。同样四个字"从失败中学"——一个是当场微调走向,一个是赛后复盘重赛。第 1 章 §1.4 的预测题埋的就是这个,这里把它焊死:step 级 ≠ episode 级。
3.4LATS:把搜索、反思、ReAct 缝在一起
LATS = 以蒙特卡洛树搜索(MCTS)为骨架,把 ReAct 的行动-观察、ToT 的分支搜索、Reflexion 的自我反思统一进一个算法——本章三大能力的合体。
到这里,本章两条线(搜索②、反思③)加上第 2 章的行动-观察(①)各自独立。LATS(Language Agent Tree Search, Zhou et al. 2023)回答最后一个问题:三者能不能合成一个算法?能——用 MCTS 当骨架,让一个 agent 同时具备"探多条路、从失败学、与环境接地"三种能力。它是本章最强、也最贵的范式。
底层机制(比文档深一层):MCTS 的四步循环(选择 → 扩展 → 评估/模拟 → 回传)天然就是一个"边探索边记账"的搜索框架,LATS 把三家能力各塞进一步——
- 扩展时用 ReAct 式的行动-观察:每个新节点是一次真实的"行动 + 环境观察",让搜索接地到外部世界(这是纯 ToT 没有的)。
- 分支与回溯继承 ToT:一个节点展开多个候选、用价值评估择优、走不通就回到树上别的节点。
- 回传失败信号时引入 Reflexion:一条路径失败后,生成自然语言反思,注入后续的搜索决策,让"学到的教训"指导下一轮选择。
代价也最直白:MCTS 要反复"模拟—回传",节点数和 LLM 调用量远超前面任何范式。所以 LATS 不是默认选项,只保留给高可靠性、且状态可验证的领域——谜题、博弈、带测试的代码生成。能拿来当裁判的外部信号越硬,LATS 越划算;信号一软,它高昂的搜索成本就换不来对应收益。
3.5跨概念综合 + 备选方案对比
把四章范式放回那条轴边上,选型问题收敛成两问。第一问:单条轨迹(第 2 章)何时就够?当任务的解题路径基本唯一、步骤间依赖在执行前大体可知(订差旅、多跳问答、跑一段确定流程),单条轨迹既省又快,多开几条路只是徒增成本;只有当"贪心顺着一条路走容易走进死胡同、最优解藏在岔路里"时,才值得上 ToT 的树搜索。第二问:反思何时强过单纯重试?如果失败是随机噪声(偶发超时、采样抖动),直接重试(甚至 CoT 自洽采样)更省——反思那一步是白花的 token;只有当失败有可复盘的系统性原因、且存在外部判分器能确认成败时,把教训写下来喂回去,下一次才真的会更好。一句话收口:多路径买的是"广度",反思买的是"跨次的记性",两者都不免费,按任务缺的是哪种能力来取。
| 方法 | 核心动作 | 何时用 | 主要代价 | 学习的时间尺度 |
|---|---|---|---|---|
| 单条轨迹 ReAct / Plan-and-Execute(ch02) |
沿一条路径走到底(ReAct 每步可纠下一步) | 解题路径基本唯一、依赖大体可预知 | 错一步整条作废;无法退回岔口换路 | step 级(仅 ReAct,同一次执行内) |
| Tree of Thoughts | 每步生成 k 个候选 + LLM 自评 + BFS/DFS 搜索剪枝 | 多条候选路径、贪心易走死胡同(谜题、试探性推理) | ~b×深度×(生成+评估) 次调用,比单次 CoT 贵 | 无跨次学习(探索在一次执行内) |
| Reflexion | 失败轨迹 → 自然语言教训 → prepend 到下次 prompt | 可重复尝试 + 有外部判分器(代码测试、环境信号) | 需可重试 + 可用奖励;记忆窗口仅 1–3 条 | episode 级(跨尝试,整体重试) |
| LATS | MCTS 骨架统一 ReAct + ToT + Reflexion | 高可靠性 + 状态可验证(谜题、博弈、带测试的代码) | 最贵:MCTS 反复模拟-回传,调用量最大 | step + episode 兼有(搜索内接地 + 反思跨次) |
§本章 self-check
先合上教程,把答案写在纸上或编辑器里。写完再点开对照——直接点开等于把这一节再读一遍。
- ToT 的"状态评估器"是一个单独训练出来 / 外挂的 critic 吗?它具体怎么给状态打分?
- Reflexion 说它"用自然语言梯度代替权重更新"——这句话里"零权重更新"具体指什么?教训最后是怎么影响下一次行为的?
- ReAct 的当场纠错和 Reflexion 的反思都"从失败中学",但发生在不同的时间尺度。分别是哪个尺度?用一个词区分这两个尺度。
- LATS 把哪三种范式缝进了一个算法?它的骨架是什么?为什么它被限制在"状态可验证"的领域?
答案(先做完再展开)
- 不是。评估器就是同一个 LLM 在给自己生成的状态打分——要么对单个状态做一小段前瞻后贴
sure/likely/impossible标签(赋值),要么把多个状态摆一起让它投票选最优(投票)。没有任何额外训练的模型或参数;它的"评判力"全来自提示工程。 - "零权重更新"指不做任何微调——模型参数一个都不动。教训的影响路径是:失败轨迹 + 奖励 → Self-Reflection 写成自然语言教训 → 存进长期记忆 → 下一次同一任务时拼接到 prompt 开头,靠改写输入上下文(而非改权重)来重塑这一次的行为。记忆通常只留最近 1–3 条。
- ReAct 在 step 之间(同一次执行内,看到 Observation 立刻调整下一步);Reflexion 在 episode 之间(整次失败后写教训、下一次整体重试)。一词区分:step 级 vs episode 级(或"当场微调 vs 赛后重赛")。
- 缝进了 ReAct(行动-观察接地)+ Tree of Thoughts(分支搜索)+ Reflexion(自我反思)。骨架是 MCTS(蒙特卡洛树搜索)。限制在状态可验证领域,是因为 MCTS 的反复模拟-回传调用量极大,只有当存在硬外部判分器(测试、博弈胜负)时,这笔搜索成本才换得回可靠性收益。
给一个"会自我修复的代码 agent"挑范式
需求:「给定一道编程题 + 一套隐藏单元测试,agent 要写出能全部通过的代码」。可以反复提交、每次提交都拿到测试的通过/失败结果。在本章四种方法里,写下你的首选和次选,并说清:单纯让它"失败就重写一遍"为什么往往不够?如果改用 ToT 单打,缺了什么?
提示(卡住再展开)
先核对两个 Reflexion 前提:能反复提交(✓ 可重试)、有单元测试(✓ 硬奖励信号)——这正是 Reflexion 的主场,也是它 HumanEval 91% 的来源,首选它。"失败就盲目重写"为什么不够:模型很可能重复同一个错误,因为它没把"上次为什么挂"显式带进下一次的上下文;Reflexion 的那段自然语言教训正是补上这块。ToT 单打缺什么:它能在一次提交内探索多条实现思路,但不携带跨提交的记忆——这次学到的教训,下次提交时丢得一干二净。想两者兼得(一次内搜索 + 跨次反思 + 测试接地),答案就滑向 LATS 那一格——但要先掂量它的调用成本是否撑得起。