思维树提示-教-LLMs-慢慢思考

思维树提示:教 LLMs 慢慢思考

原文:towardsdatascience.com/tree-of-thought-prompting-teaching-llm-to-think-slowly/

“慢思考(系统 2)是费力的、不频繁的、逻辑的、计算的、有意识的。”

— 丹尼尔·卡尼曼,《思考,快与慢》

简介

大型语言模型(LLMs)如 ChatGPT 通常表现为 2002 年诺贝尔经济学奖获得者丹尼尔·卡尼曼(Daniel Kahneman)所定义的系统 1:它快速、自信且几乎不费力。你是正确的:还有一个系统 2,这是一种更慢、更费力的思考模式。

这些年,研究人员一直在开发方法,通过更好的提示策略将系统 2 风格的思考引入 LLMs。思维树(ToT)提示是其中最突出的技术之一,它允许模型遵循多个推理路径,可能导致更好的决策。

在这篇博客文章中,我将进行一个案例研究,其中 ToT 驱动的 LLM 代理玩经典的扫雷游戏——不是通过猜测,而是通过推理。就像你一样。

CoT 和 ToT

我们可能从思维链(CoT)提示开始我们的故事——这是一种引导 LLMs 逐步推理的技术。看看以下例子:

Q: 我在超市买了 10 个苹果。然后我给了邻居 2 个苹果,又给了我的猪 2 个。现在对我来说苹果不够了!!然后我又从杂货店买了 5 个苹果,但有一个是烂的。现在有多少个苹果?

A: 你原本有 10 个苹果。你给了邻居 2 个苹果,又给了猪 2 个。现在你有 6 个苹果。然后你买了 5 个更多的苹果,所以你有 11 个苹果。最后,你吃了一个苹果,所以最后你仍然有 10 个苹果。

如你所见,通过逐步引导思考,LLM 模拟了更好的推理。

思维树(ToT)提示扩展了 CoT。正如其名所示,它像树一样组织推理,其中每个节点都是一个潜在的“思想”,分支是可能的路径。它不遵循 CoT 的线性过程。

在实践中,ToT 创建了一个带有分支的树结构,每个分支都有子步骤,最终导致一个解决方案。然后模型评估每个步骤,给每个想法分配一个分类,如“确定”、“可能”或“不可能”。接下来,ToT 通过应用搜索算法,如广度优先搜索(BFS)深度优先搜索(DFS),遍历整个问题空间,以选择最佳路径。

这里有一个在办公楼中的例子:我有一个最多容纳 20 人的办公室,但这个星期有 28 人来。我有以下三个可能的解决方案:

  • 分支 1:移动 8 个人

    • 附近有其他房间吗?→ 是的

    • 它有空间再容纳 8 个人吗?→ 是的

    • 我们能否在不进行任何行政程序的情况下移动人员?→ 可能

    • 评估:有希望!

  • 分支 2:扩大房间

    • 我们能否使房间更大?→ 可能

    • 这在安全方面允许吗?→ 不允许

    • 我们能否在建筑中请求例外?→ 不行

    • 评估:这行不通!

  • 分支 3:将小组分成两组

    • 我们能否将这些人分成两组?→ 可以

    • 我们能否让他们在不同的日子来?→ 可能

    • 评估:有很好的潜力!

正如你所见,这个过程模仿了我们解决难题的方式:我们不是直线思考。相反,我们探索、评估和选择。

案例研究

矿雷扫雷

图片

矿雷扫雷:维基百科

几乎不可能,但如果你不知道的话,扫雷是一款简单的视频游戏。

棋盘被分成单元格,其中地雷随机分布。单元格上的数字显示与它相邻的地雷数量。当你打开所有单元格时,你就赢了。然而,如果你在打开所有单元格之前触碰到地雷,游戏就结束了,你输了。

我们在扫雷中应用 ToT,这需要一些逻辑规则和约束下的推理。

我们用以下代码来模拟游戏:

# --- Game Setup ---
def generate_board():
   board = np.zeros((BOARD_SIZE, BOARD_SIZE), dtype=int)
   mines = set()
   while len(mines) < NUM_MINES:
       r, c = random.randint(0, BOARD_SIZE-1), random.randint(0, BOARD_SIZE-1)
       if (r, c) not in mines:
           mines.add((r, c))
           board[r][c] = -1  # -1 represents a mine

   # Fill in adjacent mine counts
   for r in range(BOARD_SIZE):
       for c in range(BOARD_SIZE):
           if board[r][c] == -1:
               continue
           count = 0
           for dr in [-1, 0, 1]:
               for dc in [-1, 0, 1]:
                   if 0 <= r+dr < BOARD_SIZE and 0 <= c+dc < BOARD_SIZE:
                       if board[r+dr][c+dc] == -1:
                           count += 1
           board[r][c] = count
   return board, mines

你可以看到我们生成了一个 BOARD_SIZE*BOARD_SIZE 大小的棋盘,上面有 NUM_MINES 个地雷。

ToT LLM 代理

我们现在准备好构建我们的 ToT LLM 代理来解决扫雷难题。首先,我们需要定义一个函数,该函数使用 LLM(如 GPT-4o)返回当前棋盘上的想法。

def llm_generate_thoughts(board, revealed, flagged_mines, known_safe, k=3):

   board_text = board_to_text(board, revealed)

   valid_moves = [[r, c] for r in range(BOARD_SIZE) for c in range(BOARD_SIZE) if not revealed[r][c] and [r, c] not in flagged_mines]

   prompt = f"""

You are playing a 8x8 Minesweeper game.

- A number (0–10) shows how many adjacent mines a revealed cell has.

- A '?' means the cell is hidden.

- You have flagged these mines: {flagged_mines}

- You know these cells are safe: {known_safe}

- Your job is to choose ONE hidden cell that is least likely to contain a mine.

- Use the following logic:

 - If a cell shows '1' and touches exactly one '?', that cell must be a mine.

 - If a cell shows '1' and touches one already flagged mine, other neighbors are safe.

 - Cells next to '0's are generally safe.

You have the following board:

{board_text}

Here are all valid hidden cells you can choose from:

{valid_moves}

Step-by-step:

1\. List {k} possible cells to click next.

2\. For each, explain why it might be safe (based on adjacent numbers and known info).

3\. Rate each move from 0.0 to 1.0 as a safety score (1 = definitely safe).

Return your answer in this exact JSON format:

[

 {{ "cell": [row, col], "reason": "...", "score": 0.95 }},

 ...

]

"""

   try:

       response = client.chat.completions.create(

           model="gpt-4o",

           messages=[{"role": "user", "content": prompt}],

           temperature=0.3,

       )

       content = response.choices[0].message.content.strip()

       print("\n[THOUGHTS GENERATED]\n", content)

       return json.loads(content)

   except Exception as e:

       print("[Error in LLM Generation]", e)

       return []

这可能看起来有点长,但函数的核心部分是提示部分,它不仅向 LLM 解释游戏的规则(如何理解棋盘、哪些移动是有效的等),还解释了每个有效移动背后的推理。此外,它还说明了如何为每个可能的移动分配分数。这些构成了我们的思维分支,最终形成了我们的树形 ToT。例如,我们有一个逐步指南:

1\. List {k} possible cells to click next.

2\. For each, explain why it might be safe (based on adjacent numbers and known info).

3\. Rate each move from 0.0 to 1.0 as a safety score (1 = definitely safe).

这些行指导 LLM 提出多个移动,并基于当前状态为每个移动进行辩护;然后它必须通过从 0 到 1 的分数来评估每个可能的移动。代理将使用这些分数来找到最佳选项。

我们现在使用这些生成的想法构建一个 LLM 代理来执行“真实”的移动。考虑以下代码:

def tot_llm_agent(board, revealed, flagged_mines, known_safe):

   thoughts = llm_generate_thoughts(board, revealed, flagged_mines, known_safe, k=5)

   if not thoughts:

       print("[ToT] Falling back to baseline agent due to no thoughts.")

       return baseline_agent(board, revealed)

   thoughts = [t for t in thoughts if 0 <= t["cell"][0] < BOARD_SIZE and 0 <= t["cell"][1] < BOARD_SIZE]

   thoughts.sort(key=lambda x: x["score"], reverse=True)

   for t in thoughts:

       if t["score"] >= 0.9:

           move = tuple(t["cell"])

           print(f"[ToT] Confidently choosing {move} with score {t['score']}")

           return move

   print("[ToT] No high-confidence move found, using baseline.")

   return baseline_agent(board, revealed)

代理首先调用 LLM 建议几个可能的下一步移动,并带有置信度分数。如果 LLM 无法返回任何想法,代理将回退到之前定义的基线代理,并且它只能做出随机移动。如果我们足够幸运,得到了 LLM 提出的几个移动,代理将进行第一轮过滤,排除无效的移动,例如那些超出棋盘范围的移动。然后,它将根据置信度分数按降序排列有效的想法,如果分数高于 0.9,则返回最佳移动。如果没有建议的分数高于这个阈值,它将回退到基线代理。

开始游戏

现在我们将尝试玩一个标准的 8×8 扫雷棋盘游戏,其中有 10 个隐藏的地雷。我们玩了 10 场游戏,达到了 100% 的准确率!请查看笔记本以获取完整的代码。

结论

ToT 提示词赋予 GPT-4o 等大型语言模型更强的推理能力,超越了快速直观思考。我们已经将 ToT 应用于扫雷游戏,并取得了良好的效果。这个例子表明,ToT 可以将 LLM 从聊天助手转变为具有真实逻辑和推理能力的复杂问题解决者。

posted @ 2026-03-28 10:01  布客飞龙III  阅读(42)  评论(0)    收藏  举报