Agent 推理模式 · 深挖版教程 · 第 03 章
审议 · 搜索 · 前沿归位
前两章建立了两根轴——花哪种算力、接不接外部真值。这一章先讲花算力最猛的一种:树搜索(ToT);再用这两根轴,把一批前沿变体一次性归位。
读这章时,心里装着这四句
- ToT = 生成 + 评估 + 搜索三件套:LM 生成候选 thought、给状态打分、再用 BFS/DFS 在树上搜索带回溯。
- 三件套里,评估器才是真难点。生成和搜索都现成;没有一个校准好的状态价值信号,搜索只是把算力账单翻几倍。
- 前沿变体不是新物种,只是两轴上的新坐标——GoT 是 ToT 加合并、Self-Refine 是 Reflexion 去掉外部 evaluator、LATS 是三个老东西缝在一起。
- 本章真正想交给读者的能力:学会自己给新模式归位。下次冒出一个新缩写,问两句就落格子,不必等论文精读。
3.1Tree of Thoughts:把推理摊成一棵可搜索的树
Tree of Thoughts(思维树,Yao 2023)把一条 CoT 链拆成一棵树:每个节点是一步 thought,LM 既生成候选下一步、又给每个候选的状态打分(sure / maybe / impossible),再用 BFS 或 DFS 在树上搜索,走得通就深入、走不通就回溯。
第 01 章的 Self-Consistency 已经在花「并行采样」的算力:同一道题独立跑 N 条 CoT,最后多数投票。但那 N 条链彼此不通气——每条都从头硬走到底,错在第二步的链不会借第三条链的中间结论自救。Tree of Thoughts(下称 ToT)把这笔算力换一种花法:不再让链各跑各的,而是让它们共享同一棵探索树,在每一步岔口主动比较、剪枝、回溯。算力从「并行采样」沿横轴右移到了「树搜索」。
一条 CoT 链是贪心的:第一步选了哪个方向,后面只能将错就错,没有「这条死路,退回上一个岔口换一条」的机制。对于解空间需要试探、且早期决策错了后期无法挽回的题(排程、找证明、数字凑数),贪心一条链的成功率被早期的运气死死压住。树把「在岔口可以反悔」这件事变成了一等公民。
三件套:生成器、评估器、搜索控制
ToT 的论文把一个具体方法拆成四个可替换组件:怎样把问题切成「步」(thought 的粒度)、怎样生成一步的若干候选、怎样评估一个中间状态的价值、用哪种搜索算法(BFS/DFS)调度这一切。落到工程上,前三件最吃重:
- 生成器(propose):给定到目前为止的部分解,让 LM 吐出 b 个候选的下一步 thought。例如 Game of 24 里,当前剩
4 9 10,候选可以是4 + 9 = 13(剩 13 10)、10 - 4 = 6(剩 6 9)…… - 评估器(value):对每个候选状态,让 LM 判断「从这里出发还有没有希望凑出 24」,输出一个离散标签——
sure(基本能成)/maybe(说不准)/impossible(没戏)。这一步把模型当成状态价值函数用。 - 搜索控制(search):BFS 每层只保留评估最高的 b 个状态(beam width,论文里 Game of 24 取 b=5),逐层加深;DFS 则一条路走到底,撞墙就回溯到最近的岔口换分支。
分数最有名的一组来自 Game of 24(给四个数,用加减乘除凑出 24):GPT-4 直接写一条 CoT 只有 4% 通过率,换成 ToT 的 BFS(breadth b=5)冲到 74%。提升近 20 倍,代价是每个节点都要额外掏 LM 调用——生成 b 个候选一次、评估 b 个状态又一次,逐层累乘。
机制:深一层看,赌注全压在评估器上
表面上 ToT 卖点是「探索推理空间、不吊死在一条链上」。但把搜索算法换成最朴素的,分数也不会塌——BFS 和 DFS 之间差别有限。真正决定成败的是那个状态评估器。搜索算法只负责「按分数决定先扩展谁、剪掉谁」;分数本身全靠 LM 当价值函数打出来。评估器要是没校准——把死路标成 sure、把活路标成 impossible——搜索会满怀信心地一头扎进死胡同,或者把唯一的活路提前剪掉。
ToT 不是「搜索算法救了 LM」,而是「LM 给自己的中间状态打分,搜索只是照分行事」。能不能校准这个分,决定 ToT 是神器还是昂贵的随机游走。这也是它在生产里很少真上的原因:Game of 24 这种有清晰「离 24 还差多远」信号的题,评估器好写;客服、写代码、查资料这类没有干净状态价值信号的任务,评估器无从校准,搜索退化成纯烧 token。
这正好接上第 01、02 章的纵轴:ToT 默认是内部闭环的——生成靠 LM、评估也靠 LM,整棵树没有一个节点去碰外部真值。所以它继承了闭环的老毛病:评估器幻觉,整棵搜索树就跟着幻觉,而且因为搜索「显得很有章法」,错得比一条裸 CoT 更有迷惑性。(§3.2 的 LATS 正是冲这条软肋去的——把 ToT 的节点接上真实工具反馈。)
场景走查:用 Game of 24 走一棵小树
题目:用 4 5 6 10 各一次,加减乘除凑 24。跟着 ToT 的三件套走一遍(b 这里取 2,方便手画)。
走查复盘四步:根节点生成两个候选(5×6 与 10−6);评估器打分,把 5×6=30 标成 impossible 直接剪掉,10−6 标成 maybe 保留;在保留的分支上继续展开,4+4=8 走成死路触发回溯,换到 4×5=20 命中。全程没碰任何外部真值——能不能凑成 24,完全押在评估器的离散标签上。把评估器换成一个乱标的,这棵树会同样「井井有条」地走进死胡同。
把 BFS 的 beam width 从 b=5 调到 b=1,ToT 退化成什么?分数会更接近 4% 还是 74%?
参考答案
b=1 时每层只保留评估最高的 1 个状态、不留备选、无从回溯——ToT 退化成一条由评估器逐步贪心选出的 CoT 链。它比裸 CoT 多了「每步挑评估最高的候选」,但丢了搜索的命门(保留多个分支以便剪枝/回溯)。分数会大幅滑向 4% 那一端:一旦评估器在某一步选错且没有备选分支,整条链就废了,正是贪心一条链的老问题。这也反证了 ToT 的收益主要来自「保留多个分支 + 回溯」,而非「让 LM 多打一次分」。
有人把 ToT 直接搬去做「多步联网查资料的客服 agent」,期待复刻 Game of 24 的暴涨。结果几乎没提升、还慢了好几倍。最容易卡在三件套的哪一件?
参考答案
卡在评估器。Game of 24 有一个干净的状态价值信号——「当前剩的数离凑出 24 还差多远」LM 估得相当准。客服查资料任务里,「当前这个中间状态离解决用户问题还差多远」没有这种清晰可估的信号,评估器给不出校准的分,搜索就退化成「按一堆噪声分数瞎扩展」——又慢(每节点多次 LM 调用)又不准。结论:ToT 的适用前提是存在可被 LM 校准评估的中间状态价值;没有它,别上 ToT。要接外部真值纠错,看 §3.2 的 LATS。
ToT 把「推理结构」从一条链升成一棵树,这在控制流视角下有一套对应的工程落地(搜索循环怎么写、节点状态怎么存、回溯怎么实现)。那一面在 agent-planning · Tree of Thoughts 讲;本章只关心它在两轴上的落点(内部闭环 / 树搜索)和「评估器是命门」这条机制结论。
3.2前沿归位:一批新名字,都是已知点的邻居
本章的高潮不是再学一个复杂模式,而是反过来:把一堆 2022–2024 的前沿变体逐个拆成「一句话机制 + 落在两轴哪个格子」,证明它们没有一个是新物种,全是 CoT / Self-Consistency / ToT / ReAct / Reflexion 这几个已知点在两轴上挪了挪位置。
遇到新缩写不必慌。每个前沿方法,按下面两个问题各拆一刀,就归位了:它把算力花在横轴哪一档(串行 token / 并行采样 / 树或图搜索 / 工具往返 / 重试回合)?它的循环接不接外部真值(内部闭环 / 接地)?下面六个,逐个走一遍——每个都点名「它只是某个已知点的什么变体」。
Self-Refine(Madaan 2023)
一句话机制:同一个模型,先生成一版答案,再自我批评(指出自己的毛病),然后修订,如此迭代几轮,全程不引入任何外部信号。格子:内部闭环 / 重试回合。它就是把第 02 章的 Reflexion 去掉外部 evaluator——Reflexion 靠环境给的成功/失败信号驱动反思,Self-Refine 把这个信号也换成模型自己的批评。代价随之而来:没有外部真值,模型自评「我这版挺好」时,你拿不到任何独立校验,它可以一边自我表扬一边错。适用于有内在质量维度、模型自己看得出好坏的任务(润色文字、补全代码风格),不适用于需要事实正确性的任务。
Least-to-Most(Zhou 2022)
一句话机制(least-to-most,从最简到最繁):先让 LM 把难题显式拆成一串由易到难、有依赖顺序的子问题,再顺序求解,把前一个子问题的答案拼进后一个的 prompt 里。格子:内部闭环 / 串行 token(带显式分解)。它仍在横轴最左端花「串行 token」的算力,和 CoT 同档;区别只是把 CoT 隐式的「一步步想」升级成显式的「先列子问题清单、再按清单逐项解」,让早期的简单结论成为后期难题的脚手架。和第 02 章 Plan-and-Execute 的差别:Least-to-Most 全程不接工具、不碰外部真值,纯靠链式 prompt;Plan-Execute 的每个子任务通常要落到真实执行(接地)。
Graph of Thoughts(Besta 2023)
一句话机制:把 ToT 的树放宽成任意有向无环图(DAG)——思维节点之间不止能分叉,还能聚合 / 合并(把多个分支的中间结论汇到一个新节点)、能回环精炼。格子:内部闭环 / 图搜索。它和 ToT 同在「内部闭环」这一排、同在搜索这一档,唯一的增量是给 ToT 加了「合并」这一步操作:树只能从一个父节点往下分叉,图能把几个独立推出的部分解拼成一个更完整的解(典型如排序任务里把分段排好的子列表归并)。代价和 ToT 同源且更重——多出的合并操作又是若干次 LM 调用,评估器依旧是命门。
ReWOO(Xu 2023)
一句话机制(Reasoning WithOut Observation,推理时先不看观察结果):把 ReAct「想一步→调一次工具→看结果→再想」的交错循环,改成三段式——Planner 一次性把所有工具调用规划成一张蓝图(用占位符串起依赖),Worker 批量执行这些调用,Solver 拿着所有结果一次性合成答案。格子:接地 / 工具往返(前置批量版)。它和 ReAct 同在「接地」这一排、同花「工具往返」的算力,挪动的是把每一步的 observation 踢出推理循环:ReAct 每轮都把上一步的工具结果塞回 prompt 再推理,token 随步数滚雪球;ReWOO 推理时只看占位符、不看实际 observation,相对交错式 ReAct 省下约 5× 的 token。代价是牺牲了 ReAct 的临场适应——蓝图一旦定死,中途某个工具返回意外结果,没法像 ReAct 那样当场改主意。省 token 背后的控制流细节在 agent-planning · ReWOO。
LATS(Zhou 2023)
一句话机制(Language Agent Tree Search):在 ReAct 式的节点(每个节点是一次「推理 + 行动 + 观察」)上跑蒙特卡洛树搜索(MCTS),带价值评估、带失败后的反思记忆,把推理 + 行动 + 规划 + 反思缝成一套。格子:接地 / 树搜索 + 工具。这是本节最「缝合」的一个,恰好是三个已知点的合体:ToT 的树搜索 + ReAct 的工具接地 + Reflexion 的反思记忆。它正面回应了 §3.1 那条软肋——ToT 的评估器在内部闭环里没真值可校准,LATS 让节点真去调工具、拿真实 observation,给搜索注入外部信号,评估器不再凭空打分。代价是 ToT 的重 × MCTS 的多次 rollout × 工具往返的延迟,三重叠加,算力账单在本章最高。控制流落地见 agent-planning · LATS。
ADaPT(Prasad 2024)
一句话机制(As-Needed Decomposition and Planning,按需分解):先让 LLM 直接执行整个任务,只有当它在某个子任务上失败时,才递归地把那个子任务再拆细,一层层往下,直到拆到能执行为止——分解深度由「执行成不成」临场决定。格子:接地 / 工具 + 重试(自适应分解)。它和 Least-to-Most 都做分解,但有两处关键挪位:分解是接地的(拆不拆由真实执行的成败触发,而非 Least-to-Most 那样事先一次性列好)、且是按需的(简单任务不拆、只在失败处递归深入,省掉对简单子任务无谓的拆解开销)。可以读成「Least-to-Most 的分解 + ReAct 的执行接地 + 失败触发的重试」。
| 名字 | 一句话机制 | 两轴格子 | 只是谁的变体 |
|---|---|---|---|
| Self-Refine | 同模型 生成→自批评→修订,迭代 | 内部闭环 / 重试回合 | Reflexion 去掉外部 evaluator |
| Least-to-Most | 显式拆有序子问题,逐项解、答案前传 | 内部闭环 / 串行 token | CoT 的显式分解版 |
| Graph of Thoughts | 思维成任意 DAG,分支可聚合/合并 | 内部闭环 / 图搜索 | ToT 加「合并」 |
| ReWOO | Planner→Worker→Solver,前置批量调工具 | 接地 / 工具往返 | ReAct 把 observation 踢出循环(省 ~5×) |
| LATS | ReAct 节点上跑 MCTS + 价值 + 反思 | 接地 / 树搜索 + 工具 | ToT + ReAct + Reflexion 三合一 |
| ADaPT | 失败才递归拆子任务,按需深度 | 接地 / 工具 + 重试 | Least-to-Most + ReAct,失败触发 |
把这六个、加上前两章的 CoT / Self-Consistency / ToT / ReAct / Reflexion,全摆进同一张两轴网格——这就是 index 那张概念图的「详细版」:
3.3归位练习:给任意新模式落格子
把上面的方法固化成一个两步动作:拿到任意一个没见过的模式名,先问「它花哪种算力」定横轴一档,再问「它接不接外部真值」定纵轴一排,两刀下去就落进格子——不必等论文精读。
这套两问法之所以管用,是因为它不依赖记住每篇论文的细节,只依赖两章建立的坐标系。把它当成一个固定流程:
归位两问 · 拿到新名字就跑这两步
- 问一(横轴):它把推理期算力花在哪?多写 token(串行)?跑多条再投票(并行采样)?在树/图上搜索带剪枝回溯?多调几次工具往返?失败了重试整个回合?——选出最主要的一档。
- 问二(纵轴):它的循环里有没有一步去碰外部真值(真实工具/环境/检索结果)?有 = 接地(能纠错);全靠 LM 自产自评 = 内部闭环(会自信地错)。
- 落格子 + 找邻居:两个答案交叉定位一格,再问「这格里我已经认识谁?它比那个老点多挪了什么?」——多半就是某个已知点加一步操作。
Worked 例子:现场归位「Self-Discover」
拿一个本章正文没展开的新名字试刀——Self-Discover(Zhou 2024)。它的机制:让 LLM 在解题前,先从一组通用推理模块(「分解问题」「换个角度」「列子步骤」……)里挑选并组合出一个针对该任务的推理结构,再按这个自拟结构去解题。跑两问:
- 问一(横轴):它没搜索树、没调工具、没重试回合,就是先生成一个结构、再顺着结构写一条加长的推理——算力花在串行 token。
- 问二(纵轴):选模块、组结构、解题,全程 LM 自己干,没有一步碰外部真值——内部闭环。
- 落格子:内部闭环 / 串行 token,紧挨着 CoT 和 Least-to-Most。找邻居:它就是 Least-to-Most 的近亲——都在解题前先搭一个显式结构,区别是 Least-to-Most 搭的是「有序子问题清单」,Self-Discover 搭的是「从模块库组合出的推理骨架」。新名字,老坐标。
落完格子,代价和适用场景几乎自动浮现:落在「内部闭环」=没有外部纠错、事实类任务慎用;落在横轴越右(搜索/工具/重试)=越贵越慢、要先确认任务值这个价;落在「搜索」一档=先问「这任务有没有可被 LM 校准评估的中间状态」,没有就别上。归位不只是贴标签,是把一个陌生名字直接翻译成「该不该用、代价多大」。
轮到读者试刀。Reflexion 之上有人加了一个变体「Retroformer」:保留 Reflexion 的「失败后写一段反思塞进记忆、下一回合带着反思重试」,但额外用强化学习训练一个专门生成反思文字的小模型,让反思质量随回合越来越好。跑两问,它落哪一格?紧挨着谁?
参考答案
问一(横轴):核心仍是「失败→反思→带着反思重试整个任务」,花的是重试回合的算力(训练那个小模型是离线的,不改变在线推理时算力花在哪一档)。问二(纵轴):重试由环境给的真实成功/失败信号触发——接地。落格子:接地 / 重试回合,和 Reflexion 同一格。找邻居:它就是 Reflexion 本身,只是把「反思文字怎么来」从『模型即兴写』升级成『一个被 RL 专门训过的反思生成器写』——同一格里的小改良,不是新坐标。这正印证本章主旨:新缩写大多是已知点上挪了一步操作,归位两问就能看穿。
下面给出归位时常用的伪代码骨架——把「两问」写成一个可套用的判别函数,纯讲解用,帮助固化流程:
# 讲解用伪代码,未本地验证
# 把「归位两问」固化成一个判别骨架:输入对某模式的两点观察,输出它在两轴上的格子 + 最近邻
COMPUTE_AXIS = ["serial_token", "parallel_sampling", "tree_or_graph_search",
"tool_roundtrip", "retry_episode"] # 横轴:从左到右越来越贵
# 已知点:(算力档, 是否接地) -> 名字,用来给新模式找最近邻
KNOWN_POINTS = {
("serial_token", False): "CoT / Least-to-Most",
("parallel_sampling", False): "Self-Consistency",
("tree_or_graph_search", False): "ToT / GoT",
("retry_episode", False): "Self-Refine",
("tool_roundtrip", True): "ReAct / ReWOO",
("tree_or_graph_search", True): "LATS",
("retry_episode", True): "Reflexion / ADaPT",
}
def place(name, main_compute, touches_external_truth):
"""两问归位:
main_compute -> 问一答案,取自 COMPUTE_AXIS
touches_external_truth -> 问二答案,循环里有没有一步碰真实工具/环境/检索
"""
assert main_compute in COMPUTE_AXIS, "问一:先定它主要花哪种算力"
grounded = bool(touches_external_truth) # 问二:定纵轴
grid_cell = ("grounded" if grounded else "internal_loop", main_compute)
neighbor = KNOWN_POINTS.get((main_compute, grounded), "(边角格,自己判断最近邻)")
# 落完格子,代价/适用几乎自动浮现:
caveats = []
if not grounded:
caveats.append("无外部纠错 -> 事实类任务慎用,会自信地错")
if main_compute in ("tree_or_graph_search", "tool_roundtrip", "retry_episode"):
caveats.append("横轴偏右 -> 更贵更慢,先确认任务值这个价")
if main_compute == "tree_or_graph_search":
caveats.append("搜索档 -> 先问有没有可被 LM 校准评估的中间状态价值")
return {"name": name, "cell": grid_cell, "nearest_known": neighbor, "caveats": caveats}
# 现场归位 Self-Discover:先组推理结构再解题,不搜索/不调工具/不重试,全程 LM 自产
print(place("Self-Discover", main_compute="serial_token", touches_external_truth=False))
# -> cell=('internal_loop','serial_token'), nearest_known='CoT / Least-to-Most'
# caveats=['无外部纠错 -> 事实类任务慎用,会自信地错']
·自测 · 合上教程能讲出来吗
四道原子题,先自己答,再翻。最后一道是归位题——拿一个正文没出现的新模式现场落格子。
-
ToT 的三件套是哪三件?其中哪一件决定成败、为什么?
参考答案
三件套 = 生成器(LM 吐 b 个候选下一步 thought)+ 评估器(LM 给中间状态打 sure/maybe/impossible)+ 搜索控制(BFS/DFS 按分数扩展、剪枝、回溯)。决定成败的是评估器:搜索算法只是照评估器给的分行事,分数没校准(把死路标 sure、活路标 impossible),搜索会自信地扎进死胡同或剪掉唯一活路。Game of 24 的 4%→74% 提升,靠的是有一个 LM 估得准的状态价值信号;没有这种信号的任务(客服、查资料)评估器无从校准,ToT 退化成昂贵的随机游走。
-
Self-Refine 和 Reflexion 只差一样东西,是什么?这一差导致 Self-Refine 不能用在什么任务上?
参考答案
差的是外部 evaluator / 真值信号。Reflexion 靠环境给的真实成功/失败信号驱动反思(接地);Self-Refine 把这个信号也换成模型自己的批评,整个循环退回内部闭环。后果:模型自评「这版挺好」时没有任何独立校验,可以一边自我表扬一边事实错误。所以 Self-Refine 不能用在需要事实正确性、模型自己看不出对错的任务上(如查证某事实、算一个有唯一正确答案的结果);适用于有内在质量维度、模型自己判得出好坏的任务(润色文字、改代码风格)。
-
LATS 被称作「三合一」,是哪三个已知点的合体?它合体是为了补 ToT 的哪条软肋?
参考答案
三合一 = ToT 的树搜索 + ReAct 的工具接地 + Reflexion 的反思记忆(在 ReAct 式节点上跑 MCTS,带价值评估与失败反思)。它补的是 ToT 最致命的软肋:ToT 是内部闭环,评估器在没有外部真值的情况下凭空给中间状态打分、无从校准。LATS 让每个节点真去调工具、拿真实 observation,把外部信号注入搜索,评估器不再纯凭空打分。代价是 ToT 的重 × MCTS 多次 rollout × 工具往返延迟三重叠加,算力账单在本章诸法里最高。
-
归位题:有个新模式叫 「Tree-of-Code」——把 ToT 的每个 thought 节点换成「写一段小代码并真的运行它,用运行结果(报错 / 输出)作为这个节点的评估分」,再在这些节点上做树搜索。跑归位两问:它落哪一格?紧挨着谁?比那个邻居多挪了什么?
参考答案
问一(横轴):核心仍是在节点上做树搜索带剪枝回溯——树搜索档。问二(纵轴):节点评估不再靠 LM 凭空打分,而是真去运行代码、拿运行结果当分数——这一步碰了外部真值(解释器就是真值来源),接地。落格子:接地 / 树搜索,正是 LATS 那一格。多挪了什么:和纯 ToT(内部闭环 / 树搜索)相比,它把评估器从「LM 自评」换成「代码执行结果」,精准修掉了 ToT「评估器无从校准」的命门——这正是 LATS 的思路(给树搜索注入外部信号),只是真值来源换成了代码解释器而非通用工具/环境。结论:又一个已知点的邻居,归位两问就看穿了。
设计一个让 ToT 评估器必然失效、从而把 ToT 拖成纯烧 token 的任务
本章反复强调:ToT 成败全押在状态评估器能否校准。反过来设计——构造一个任务,使得「从某个中间状态出发还有没有希望成功」在原理上无法被 LM 估准,于是无论搜索算法多聪明,ToT 都只会比裸 CoT 更贵而不更准。给出三点论证:(1)这个任务的中间状态价值为什么不可估(提示:想想价值只在最后一步才突然揭晓、或前期状态与最终成败无单调关系的任务,如某些密码学/校验和/需要全局信息才能判局部的问题);(2)评估器在这种任务上会怎样系统性犯错(乱标 sure/impossible),把搜索引向哪里;(3)既然 ToT 在这类任务上注定退化,按本章的两轴,这类任务应该换成哪一格的模式,理由是什么(提示:能不能把「最后才揭晓的真值」提前引进循环——往「接地」那一排挪)。