当前位置: 首页 > news >正文

从零学习 Agentic RL(四)—— 超越 ReAct 的线性束缚:深入解析 Tree-of-Thoughts (ToT)

从零学习 Agentic RL(四)—— 超越 ReAct 的线性束缚:深入解析 Tree-of-Thoughts (ToT)

摘要:

本文是“从零学习 Agentic RL”专栏的第四篇。在(三)中,我们实现了 ReAct 框架,它通过 T-A-O (思考-行动-观察) 循环赋予了 LLM 执行能力。然而,ReAct 的线性思考链在面对复杂规划或需要探索的任务时(例如数学难题、棋局)显得十分脆弱,一旦某一步思考出错,整个任务便会失败。

为解决此问题,本文将深入探讨 Tree-of-Thoughts (ToT) 框架。ToT 将 Agent 的思考过程从一条“链”扩展为一棵“树”,允许 Agent 同时探索多条推理路径评估(剪枝)不同分支,并进行回溯 (Backtracking)。本文将包含 ToT 的核心原理、与 ReAct 的详细对比表格、一个简化的 ToT 框架 Python 实战(实现 BFS 搜索算法),以及 PPO 如何优化 ToT 的进阶讨论和相关面试问题

文章目录

  • 从零学习 Agentic RL(四)—— 超越 ReAct 的线性束缚:深入解析 Tree-of-Thoughts (ToT)
    • 🦈 一、前言:ReAct 的“线性”困境
    • 二、[原理] 从“链”到“树”:ToT 的核心思想
      • 2.1 ToT 的四大核心组件
    • 三、[实战] 从零实现一个简化的 ToT 框架
      • 3.1 步骤 1:定义“评估器” (Evaluator)
      • 3.2 步骤 2:定义“生成器” (Generator)
      • 3.3 步骤 3:定义“搜索算法” (BFS Executor)
      • 3.4 运行与分析
    • 四、[进阶] ToT, PPO 与 ReAct 的“大一统”
      • 4.1 ToT vs ReAct:成本与收益的权衡
      • 4.2 PPO 如何优化 ToT?
    • 五、🧠 专栏面试问题角 🧠
    • 六、总结与参考链接

🦈 一、前言:ReAct 的“线性”困境

在上一篇文章中,我们构建的 ReAct Agent 已经可以解决“苹果 CEO 家乡”这类多步查询任务。其工作流是一个单线程的 T-A-O 循环

Task -> Thought 1 -> Action 1 -> Observation 1 -> Thought 2 -> ... -> Finish

这个模式的致命弱点在于:它是一条“单行道”,无法“掉头”或“探索岔路”。

想象一下,如果 Agent 在 Thought 2 这一步做出了一个次优甚至错误的决策(例如,错误地搜索了一个不相关的人名),ReAct 框架没有原生的机制去回溯 (Backtrack)Thought 1 并尝试另一条路径。它只能“硬着头皮”在错误的基础上继续下去,导致任务最终失败。

这种“线性”的特性,使得 ReAct 在处理以下任务时力不从心:

  • 复杂规划:例如需要多步权衡的旅行规划。
  • 数学与逻辑:例如“24点游戏”或逻辑谜题,第一个思路很可能是错的。
  • 探索性任务:例如“写一个有创意的押韵短诗”,需要尝试多种措辞。

``

(图 1:ReAct 的线性思考链及其"死胡同"困境)

为了解决这个问题,研究者们提出了 Tree-of-Thoughts (ToT),其核心思想是:与其“一条路走到黑”,不如“广撒网,多探索”

二、[原理] 从“链”到“树”:ToT 的核心思想

ToT 框架(源自论文 Tree of Thoughts: Deliberate Problem Solving with Large Language Models)将 LLM 的问题解决过程,从一个“序列 (Sequence)”建模为一个“树 (Tree)”。

  • ReAct 是链 (Chain)State0→State1→State2→...State_0 \rightarrow State_1 \rightarrow State_2 \rightarrow ...State0State1State2...
  • ToT 是树 (Tree):在任何一个 StateStateState 节点,都可以分岔出 NNN 个可能的下一步 StateStateState

``

(图 2:从“链式思考” (左) 到“树状思考” (右) 的演变)

2.1 ToT 的四大核心组件

ToT 框架的实现,依赖于四个关键组件的协同工作:

  1. 分解 (Decomposition)
    • 作用:将一个复杂的大任务,分解为 KKK 个有序的“思考步骤”(即树的 KKK 层深度)。
    • 类比:ReAct 的 T-A-O 循环是隐式的、一步一步的分解。ToT 则是有意识地将问题规划为多个阶段。
  2. 生成 (Generation)
    • 作用:在树的任何一个节点(一个部分思考),调用 LLM 生成 NNN不同的、可能的“下一步思考”(即树的 NNN 个分支)。
    • 实现:通过修改 Prompt,例如 “Based on the current plan, propose 3 different next steps.”
  3. 评估 (Evaluation)
    • 作用这是 ToT 的灵魂。你需要一个“评估器 (Evaluator)”来判断 NNN 个新生成的“思考分支”中,哪一个“更靠谱”。
    • 实现:评估器可以是:
      • 启发式 (Heuristic):一个简单的、基于规则的函数(例如,在24点游戏中,“计算结果是否更接近24”)。
      • LLM 自我评估:调用 LLM,让它自己给这 NNN 个分支打分(例如 “Rate these 3 thoughts from 1-10 on their likelihood of success.”)。
      • 价值函数 (Value Function):(剧透) 这就是 PPO 的 Critic 可以发挥作用的地方!
  4. 搜索 (Search)
    • 作用:有了 NNN 个分支和它们的“评估分数”,你需要一个“搜索算法”来决定接下来探索哪条分支
    • 实现:可以是:
      • 广度优先搜索 (BFS):一层一层地探索所有分支。
      • 深度优先搜索 (DFS):先沿着一条“最有希望”的分支一路走到底。
      • A* 搜索:更高级的启发式搜索。

三、[实战] 从零实现一个简化的 ToT 框架

我们来手写一个 ToT Agent。为了聚焦核心原理(生成、评估、搜索),我们选择一个简单的逻辑谜题,而不是依赖外部 API。

  • 目标任务:一个简单的“物品分配”谜题。
    • 已知:有3个盒子 (A, B, C) 和 3 个物品 (钥匙, 硬币, 钻石)。
    • 线索 1:盒子 A 里不是钥匙。
    • 线索 2:盒子 C 里是钻石。
    • 求解:A, B, C 分别是什么?

一个 ReAct Agent 可能会“猜” A 是硬币,然后一条路走下去。但 ToT 可以同时探索 A 是硬币和 A 是钻石(虽然线索2马上会否定后者)的路径。

3.1 步骤 1:定义“评估器” (Evaluator)

我们的“评估器”是一个启发式函数,它负责检查一个“部分解”是否与线索冲突。

# 代码块 1: 定义评估器 (Heuristic Evaluator)# 谜题的线索 (我们的“环境”)
CLUES = {"clue1": "A is not 钥匙","clue2": "C is 钻石"
}def evaluate_thought(solution: dict) -> str:"""评估一个“部分解”(thought) 是否有效。Args:solution (dict): e.g., {'A': '硬币', 'B': '?', 'C': '钻石'}Returns:str: 'valid' (有效), 'invalid' (无效/冲突), 'complete' (完整且有效)"""# 检查线索 1if solution.get('A') == '钥匙':return 'invalid'# 检查线索 2if solution.get('C') and solution.get('C') != '钻石':return 'invalid'if solution.get('C') == '钻石' and (solution.get('A') == '钻石' or solution.get('B') == '钻石'):return 'invalid' # 物品不能重复# 检查物品是否重复items = [v for v in solution.values() if v != '?']if len(items) != len(set(items)):return 'invalid' # 发现了重复物品# 检查是否完成if all(v != '?' for v in solution.values()):# 确保所有物品都用上了if set(items) == {'钥匙', '硬币', '钻石'}:return 'complete'else:return 'invalid' # 物品不全# 如果没有冲突,且未完成,则为有效的部分解return 'valid'print("[System] 评估器 (Evaluator) 已定义。")

3.2 步骤 2:定义“生成器” (Generator)

我们的“生成器”模拟 LLM,它在当前状态下,生成所有可能的下一步“思考”。

# 代码块 2: 定义“思考”生成器 (Thought Generator)ALL_ITEMS = ['钥匙', '硬币', '钻石']def generate_thoughts(current_solution: dict, all_items: list) -> list[dict]:"""在当前解的基础上,生成所有可能的下一步“思考” (新解)"""thoughts = []# 找到第一个未分配的盒子box_to_fill = Nonefor box in ['A', 'B', 'C']:if current_solution[box] == '?':box_to_fill = boxbreakif box_to_fill is None: # 已经填满了return []# 尝试所有可能的物品for item in all_items:new_solution = current_solution.copy()new_solution[box_to_fill] = itemthoughts.append(new_solution)return thoughtsprint("[System] 生成器 (Generator) 已定义。")

3.3 步骤 3:定义“搜索算法” (BFS Executor)

这是 ToT 的“执行器”。我们使用广度优先搜索 (BFS),它会一层一层地探索所有可能的分支。

# 代码块 3: ToT 执行器,使用广度优先搜索 (BFS)from collections import dequedef run_tot_executor(initial_task: dict, max_steps: int = 10):"""ToT 的主执行器,使用 BFS 搜索算法。"""# 搜索队列,每个元素是一个 (solution, path_str) 元组# path_str 用于追踪思考路径queue = deque([(initial_task, "Start")])# 记录已访问过的状态,防止循环visited = set()step = 0while queue and step < max_steps:step += 1current_solution, current_path = queue.popleft()# 1. 评估当前“思考”status = evaluate_thought(current_solution)# 打印搜索轨迹print(f"--- Step {step} ---")print(f"  [Exploring] {current_path}")print(f"  [Solution]  {current_solution}")print(f"  [Status]    {status.upper()}")# -----------------------------------# 2. 检查状态# -----------------------------------if status == 'complete':print(f"\n======= 任务成功 (Task Complete) =======\n")print(f"最终解: {current_solution}")print(f"思考路径: {current_path}")return current_solutionif status == 'invalid':print("  [Pruning]   此分支无效,剪枝。")continue # 剪枝,不再探索此路径# -----------------------------------# 3. 生成下一步“思考”# -----------------------------------# 将 solution 转换为不可变类型 (tuple) 以便存入 setsolution_tuple = tuple(sorted(current_solution.items()))if solution_tuple in visited:print("  [Pruning]   已访问,跳过。")continuevisited.add(solution_tuple)# 这是一个 'valid' 的部分解,继续生成分支next_thoughts = generate_thoughts(current_solution, ALL_ITEMS)if not next_thoughts:print("  [Info]      无更多分支。")for thought in next_thoughts:# 将新分支加入队列new_path = f"{current_path} -> {thought}"queue.append((thought, new_path))print(f"\n======= 任务失败 (Task Failed) =======\n在 {max_steps} 步内未找到解。")# --- 运行我们的 ToT Agent ---
initial_state = {'A': '?', 'B': '?', 'C': '?'}
run_tot_executor(initial_state)

3.4 运行与分析

当你运行 run_tot_executor 时,你会在控制台看到一个清晰的“搜索树”:

[System] 评估器 (Evaluator) 已定义。
[System] 生成器 (Generator) 已定义。
--- Step 1 ---[Exploring] Start[Solution]  {'A': '?', 'B': '?', 'C': '?'}[Status]    VALID
--- Step 2 ---[Exploring] Start -> {'A': '钥匙', 'B': '?', 'C': '?'}[Solution]  {'A': '钥匙', 'B': '?', 'C': '?'}[Status]    INVALID[Pruning]   此分支无效,剪枝。
--- Step 3 ---[Exploring] Start -> {'A': '硬币', 'B': '?', 'C': '?'}[Solution]  {'A': '硬币', 'B': '?', 'C': '?'}[Status]    VALID
--- Step 4 ---[Exploring] Start -> {'A': '钻石', 'B': '?', 'C': '?'}[Solution]  {'A': '钻石', 'B': '?', 'C': '?'}[Status]    VALID
... (BFS 会继续探索 Step 34 的分支) ...
... (例如,探索 Step 3 的分支: 'A': '硬币', 'B': '钥匙', 'C': '?') ...
... (它会探索到 {'A': '硬币', 'B': '钥匙', 'C': '钻石'}) ...
--- Step X ---[Exploring] Start -> {'A': '硬币', 'B': '?', 'C': '?'} -> {'A': '硬币', 'B': '钥匙', 'C': '?'} -> {'A': '硬币', 'B': '钥匙', 'C': '钻石'}[Solution]  {'A': '硬币', 'B': '钥匙', 'C': '钻石'}[Status]    COMPLETE======= 任务成功 (Task Complete) =======最终解: {'A': '硬币', 'B': '钥匙', 'C': '钻石'}
...

分析

  • Step 2,Agent 探索了“A 是钥匙”的路径。我们的“评估器”立刻发现这违反了线索1,判为 INVALIDToT 框架便自动“剪枝”了这条路径
  • ReAct 如果第一步猜了“A 是钥匙”,它就会卡死。
  • ToT 则会继续探索 Step 3 (“A 是硬币”) 和 Step 4 (“A 是钻石”) 的路径,最终找到正确答案。

``

(图 3:本实战的 ToT-BFS 搜索树简图)

四、[进阶] ToT, PPO 与 ReAct 的“大一统”

我们已经掌握了 ReAct 和 ToT。那么在 Agentic RL 的大框架下,它们是什么关系?

4.1 ToT vs ReAct:成本与收益的权衡

ToT 并不总是优于 ReAct。它是一种“用计算换准确率”的策略。

表格 1:ReAct 与 ToT 的关键权衡

特性ReAct (链式)Tree-of-Thoughts (ToT) (树状)
思考模式线性,单路径并行,多路径,可回溯
适用任务简单查询、直接任务、事实获取复杂规划、数学、逻辑、探索性任务
主要弱点脆弱,一步错则全错成本极高(计算量呈指数增长)
LLM 调用成本 (任务 LLL≈\approx LLL 次 LLM 调用)极高 ( LLL 步, NNN 分支 ≈\approx O(NL)O(N^L)O(NL) 次调用)
实现复杂度简单 (一个循环)复杂 (需实现搜索算法、评估器)

4.2 PPO 如何优化 ToT?

这再次把我们专栏的(一)、(二)、(四)篇串联了起来。

在我们的“手写实战”中,“生成器”和“评估器”都是基于规则的 (Rule-based)。但在真实世界中,问题是开放的,我们必须用 PPO 来“训练”这两个组件。

  1. 训练“生成器” (Policy Network)
    • 目标:PPO 可以训练“生成器” LLM,使其从一开始就倾向于生成“更有希望”的分支
    • 方法:在 PPO 中,LLM Generator 就是策略 (Policy)。如果一条分支最终导向了“成功”(高 Reward),PPO 就会增加生成这条分支(这个 Thought)的概率。
  2. 训练“评估器” (Value Network)
    • 目标:PPO 可以训练“评估器” LLM,使其能准确预测一个“部分解 (Thought)”的未来潜在价值
    • 方法:这完美对应 PPO 中的 Critic (Value Function)
    • 在专栏(一)中,Critic V(s)V(s)V(s) 预测的是游戏状态 sss 的未来总回报。
    • 在这里,Critic V(thought)V(\text{thought})V(thought) 预测的就是这个“思考” thoughtthoughtthought 未来的成功概率。
    • 有了一个 PPO 训练的强大 Critic,ToT 的“搜索算法”就可以更智能:优先探索那些 KaTeX parse error: Unexpected end of input in a macro argument, expected '}' at end of input: …(\text{thought) 分数更高的分支

五、🧠 专栏面试问题角 🧠

Q1:ToT (Tree-of-Thoughts) 相比 ReAct,核心解决了什么问题?

A1:ToT 核心解决了 ReAct 的**“线性思考”和“脆弱性”问题。ReAct 无法从错误的决策中回溯,而 ToT 通过引入多路径探索**、评估和搜索机制,允许 Agent 在一个思考节点上生成多个可能的下一步,并评估它们的好坏,然后选择最优路径或进行回溯,极大地提高了在复杂规划和推理任务上的鲁棒性和准确性。

Q2:ToT 框架最大的实现“瓶颈”或“成本”在哪里?

A2:计算成本(或 LLM 调用成本)。ToT 的搜索空间是指数级的。如果一个任务需要 L=5L=5L=5 步,每一步都探索 N=3N=3N=3 个分支,理论上最多需要 35≈2433^5 \approx 24335243 次 LLM 调用(生成+评估)。而 ReAct 只需要 5 次。这导致 ToT 的延迟非常高且成本昂贵。

Q3:在 ToT 中,“评估器 (Evaluator)” 是如何实现的?它必须是 LLM 吗?

A3:不必。评估器是 ToT 的灵魂,其实现方式多样:

  1. 启发式 (Heuristic):如我们代码实战中,使用一个基于规则的 Python 函数。它速度快、成本低,但只适用于规则明确的领域(如下棋、24点)。
  2. LLM 评估 (Self-Correction):用 LLM 本身来评估分支。例如,向 LLM 提问:“这三个方案中,哪个最有可能解决问题?请打分。”
  3. 训练的价值模型 (Value Model):(Agentic RL 的做法) 单独训练一个模型(Critic),其唯一工作就是给“部分思考”打分。这个模型可以用 PPO 等 RL 算法来优化,使其能准确预测该分支的未来价值。

Q4:在你的项目中,你会优先使用 ReAct 还是 ToT?

A4:这是一个权衡 (Trade-off) 问题。

  • 我会默认使用 ReAct。对于 90% 的任务(如信息提取、API 调用、简单问答),ReAct 成本低、速度快,已经足够。
  • 只会在那些“高风险、高复杂度”的任务上使用 ToT。例如,需要深度规划的“法律合同分析”、“多步骤的科学实验设计”或“关键的数学推导”。在这些场景下,准确性远比成本和延迟更重要,ToT 的“指数级成本”是值得付出的代价。

六、总结与参考链接

  • 总结:今天我们从 ReAct 的“线性困境”出发,深入学习了 ToT 框架。我们知道了 ToT 是如何通过分解 (Decomposition)生成 (Generation)评估 (Evaluation)搜索 (Search) 四大组件,将思考模式从“链”升级为“树”的。我们还从零手写了一个基于 BFS 搜索的 ToT 执行器,直观地看到了它“剪枝”无效路径的过程。
  • 串联:我们再次打通了专栏的知识。ToT 的“生成器”和“评估器”正是 PPO (专栏一) 可以大显身威的地方——PPO 的 Policy 网络可以优化“生成”,而 PPO 的 Value 网络可以优化“评估”。
  • 展望(下一步):我们已经解决了“如何做”(ReAct) 和“如何深入思考”(ToT)。但目前为止,Agent 的“知识”完全依赖于 LLM 内部的参数。如果任务需要**“此时此地”的外部知识**(例如:“总结一下这篇刚发布的 100 页财报”),Agent 该怎么办?
  • 这就是我们专栏的下一篇要探讨的核心问题:RAG (Retrieval-Augmented Generation),即 Agent 如何拥有“外部记忆”。

参考链接

  1. ToT 原始论文 (必读):Yao, S., et al. (2023). Tree of Thoughts: Deliberate Problem Solving with Large Language Models. arXiv:2305.10601
  2. ToT 的 GitHub 实现 (参考):Original Implementation for Game of 24
http://www.dtcms.com/a/536166.html

相关文章:

  • 宁德城乡建设网站首页无法删除wordpress
  • Spring Boot3零基础教程,天气 API 测试,笔记73
  • 基于 STM32 的智能语音唤醒与关键词识别系统设计 —— 从硬件集成到算法实现
  • Redis数据库基础命令
  • 1.3 StorageTransient的gas计算
  • 物联网技术与基础第六节上课笔记
  • SAP SD系统开票查询报表分享
  • it网站设计培训制作网站的工具
  • 南京网站建设工作室电商卖什么产品有前景
  • iOS 开发推送功能全流程详解 从 APNs 配置到上架发布的完整实践(含跨平台上传方案)
  • 李宏毅机器学习笔记33
  • 深入理解Bitbucket Pipelines:强大的CICD开源解决方案
  • Android 14 系统启动流程深度解析:rc文件的语法、解析及常见语法详解
  • 商城网站的seo优化改怎么做软件公司 网站建设费分录
  • 免费做淘宝客网站有哪些石家庄网站优化推广
  • 【瑆箫】正式入驻爱发电平台
  • 第三十三篇:贪心算法(二):区间调度与跳跃游戏
  • 美颜SDK跨平台适配实战解析:让AI美颜功能在iOS与Android都丝滑运行
  • 条款24:区分通用引用和右值引用
  • zookeeper + kafka
  • 旅游网站建设与规划网站可以做被告嘛
  • 医护上门系统开发的关键技术解析与实践指南
  • 大模型-模型压缩:量化、剪枝、蒸馏、二值化 (3
  • Win10结束支持后,Linux发行版ZorinOS下载量两天破10w?怎么安装?
  • php和mysql做租车网站平台广告投放
  • Spring Boot3零基础教程,KafkaTemplate 发送消息,笔记77
  • 强化学习2.3 MDP价值迭代和策略迭代收敛证明
  • 网站建设公司相关资质重养网站建设
  • Android 中 RecyclerView 控件实现局部刷新而非整行刷新
  • STM32学习路线!软硬件兼修:裸机+RTOS+LVGL+硬件设计+项目实战 (STM32多核心开发板)