从零学习 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)](#从零学习 Agentic RL(四)—— 超越 ReAct 的线性束缚:深入解析 Tree-of-Thoughts (ToT))
    • [🦈 一、前言:ReAct 的"线性"困境](#🦈 一、前言:ReAct 的“线性”困境)
    • [二、[原理] 从"链"到"树":ToT 的核心思想](#二、[原理] 从“链”到“树”:ToT 的核心思想)
      • [2.1 ToT 的四大核心组件](#2.1 ToT 的四大核心组件)
    • [三、[实战] 从零实现一个简化的 ToT 框架](#三、[实战] 从零实现一个简化的 ToT 框架)
      • [3.1 步骤 1:定义"评估器" (Evaluator)](#3.1 步骤 1:定义“评估器” (Evaluator))
      • [3.2 步骤 2:定义"生成器" (Generator)](#3.2 步骤 2:定义“生成器” (Generator))
      • [3.3 步骤 3:定义"搜索算法" (BFS Executor)](#3.3 步骤 3:定义“搜索算法” (BFS Executor))
      • [3.4 运行与分析](#3.4 运行与分析)
    • [四、[进阶] ToT, PPO 与 ReAct 的"大一统"](#四、[进阶] ToT, PPO 与 ReAct 的“大一统”)
      • [4.1 ToT vs ReAct:成本与收益的权衡](#4.1 ToT vs ReAct:成本与收益的权衡)
      • [4.2 PPO 如何优化 ToT?](#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) : S t a t e 0 → S t a t e 1 → S t a t e 2 → . . . State_0 \rightarrow State_1 \rightarrow State_2 \rightarrow ... State0→State1→State2→...
  • ToT 是树 (Tree) :在任何一个 S t a t e State State 节点,都可以分岔出 N N N 个可能的下一步 S t a t e State State。

``

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

2.1 ToT 的四大核心组件

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

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

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

python 复制代码
# 代码块 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' (完整且有效)
    """
    
    # 检查线索 1
    if solution.get('A') == '钥匙':
        return 'invalid'
    
    # 检查线索 2
    if 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,它在当前状态下,生成所有可能的下一步"思考"。

python 复制代码
# 代码块 2: 定义"思考"生成器 (Thought Generator)

ALL_ITEMS = ['钥匙', '硬币', '钻石']

def generate_thoughts(current_solution: dict, all_items: list) -> list[dict]:
    """
    在当前解的基础上,生成所有可能的下一步"思考" (新解)
    """
    thoughts = []
    
    # 找到第一个未分配的盒子
    box_to_fill = None
    for box in ['A', 'B', 'C']:
        if current_solution[box] == '?':
            box_to_fill = box
            break
            
    if box_to_fill is None: # 已经填满了
        return []

    # 尝试所有可能的物品
    for item in all_items:
        new_solution = current_solution.copy()
        new_solution[box_to_fill] = item
        thoughts.append(new_solution)
        
    return thoughts

print("[System] 生成器 (Generator) 已定义。")

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

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

python 复制代码
# 代码块 3: ToT 执行器,使用广度优先搜索 (BFS)

from collections import deque

def 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 = 0
    while queue and step < max_steps:
        step += 1
        
        current_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_solution
        
        if status == 'invalid':
            print("  [Pruning]   此分支无效,剪枝。")
            continue # 剪枝,不再探索此路径

        # -----------------------------------
        # 3. 生成下一步"思考"
        # -----------------------------------
        # 将 solution 转换为不可变类型 (tuple) 以便存入 set
        solution_tuple = tuple(sorted(current_solution.items()))
        if solution_tuple in visited:
            print("  [Pruning]   已访问,跳过。")
            continue
        visited.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 时,你会在控制台看到一个清晰的"搜索树":

python 复制代码
[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 3 和 4 的分支) ...
... (例如,探索 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 调用成本 (任务 L L L 步 ≈ \approx ≈ L L L 次 LLM 调用) 极高 ( L L L 步, N N N 分支 ≈ \approx ≈ O ( N L ) 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) 预测的是游戏状态 s s s 的未来总回报。
    • 在这里,Critic V ( thought ) V(\text{thought}) V(thought) 预测的就是这个"思考" t h o u g h t thought thought 未来的成功概率。
    • 有了一个 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 = 5 L=5 L=5 步,每一步都探索 N = 3 N=3 N=3 个分支,理论上最多需要 3 5 ≈ 243 3^5 \approx 243 35≈243 次 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
相关推荐
恋猫de小郭2 小时前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅9 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606110 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了10 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅10 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
执笔论英雄10 小时前
【大模型学习cuda】入们第一个例子-向量和
学习
wdfk_prog10 小时前
[Linux]学习笔记系列 -- [drivers][input]input
linux·笔记·学习
崔庆才丨静觅10 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅11 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment11 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端