别再让 LLM 像掷骰子一样一次性猜答案了,试着给它加个搜索树
其实人类处理复杂问题时不是这么干的,我们会不停地尝试分支:“如果按这个方向走行不行?”、“不对,得退回去重新想”,这种“分支-评估-回溯”的机制,就是 Tree of Thoughts (ToT) 以及更进阶的蒙特卡洛树搜索 (MCTS) 想要在 LLM 上实现的逻辑。
本质上,这是把推理算法从简单的 生成 → 持续生成 变成了 提议 → 评估 → 分支 → 探索 → 回溯 → 提交。
对比一下 Chain of Thought (CoT) 和 ToT 的区别就很明显。拿那个经典的“用 4, 6, 7, 8 算 24”来说,普通的 CoT 可能会这样跑:8 - 6 = 2 → 7 * 2 = 14 → 14 + 4 = 18 → 失败。
一旦它走上了这条路,就死在了这个路径上。
而 ToT 把中间推理过程看作“搜索状态”。它会同时生成几个候选状态,然后评估哪个更有潜力。如果发现当前分支没戏,它会直接跳回上一个节点,换个方向试。
这里有个非常硬核的对比数据:在 24 点游戏的实验中,GPT-4 跑普通的 CoT 成功率只有 4% 左右,但如果用 ToT 框架,成功率直接飙升到了 74%。这说明搜索机制能极大地弥补单次采样推理的脆弱性。
这种逻辑其实就是 AlphaGo 的简化版。AlphaGo 强在它结合了神经网络和 MCTS,通过两个核心组件来控制搜索:
- Policy(策略网络): 决定哪些走法值得探索(过滤掉垃圾分支)。
- Value(价值网络): 评估当前局面好不好(给状态打分)。
把这套东西搬到 LLM 上,就是让模型不仅担任“生成器”,还要担任“评委”。
如果你想在自己的项目里实现简单的 ToT 逻辑,不能只靠一个 Prompt,得写一个循环控制逻辑。大概的伪代码流程是这样的:
# 这是一个极其简化的 ToT 逻辑示意
def tree_of_thoughts_solve(problem):
# 1. 初始化根节点(问题本身)
current_state = problem
candidates = [current_state]
while not solution_found:
# 2. 提议阶段:让 LLM 为当前状态生成 N 个可能的下一步
potential_next_steps = llm.generate_candidates(current_state, n=3)
# 3. 评估阶段:让 LLM 给这 N 个候选方案打分 (1-10分)
scores = llm.evaluate_steps(potential_next_steps)
# 4. 筛选与分支:选择得分最高的分支继续走
best_step = get_max_score(potential_next_steps, scores)
if best_step.is_solution:
return best_step
elif best_step.score < threshold:
# 5. 回溯:如果得分太低,退回到上一个有潜力的节点
current_state = backtrack_to_previous_promising_node()
else:
current_state = best_step这种架构最关键的改变在于:它把 LLM 从一个“快速反应机器”变成了“慢思考系统”。
当然,这么搞的代价是 Token 消耗量剧增。因为你不再是跑一次推理,而是在跑一个搜索树,每次评估都要消耗 Token。但对于那些容错率极低、逻辑链条极长的复杂规划问题,这种用 Token 换正确率的买卖是非常划算的。
现在很多所谓的“推理模型”(比如 OpenAI 的 o1 系列)底层大概率就在做类似的强化学习和搜索优化,让模型在输出最终答案前,先在内部完成大量的自我博弈和路径筛选。
