思维链的演进脉络
大语言模型的推理能力不完全是"涌现"出来的,很多时候取决于我们如何引导它思考。从最基础的CoT(Chain-of-Thought)到树状的ToT(Tree-of-Thoughts)再到图状的GoT(Graph-of-Thoughts),每一种范式都在扩展前一种的推理边界。
CoT:线性推理的基石
核心思想
CoT的本质是让模型"展示推理过程"。通过在输出最终答案前生成中间推理步骤,降低模型跳过关键逻辑的风险。
# Zero-shot CoT
提示: 一个教室有32个学生,其中3/8是女生。又来了4个女生。现在女生占多少?
附加: 让我们一步一步思考。
# 模型输出
原女生数: 32 × 3/8 = 12
加入后女生数: 12 + 4 = 16
总人数: 32 + 4 = 36
比例: 16/36 = 4/9 ≈ 44.4%
Few-shot CoT模板
COT_TEMPLATE = """
# 示例1
问题: {q1}
思考: {reasoning1}
答案: {a1}
# 示例2
问题: {q2}
思考: {reasoning2}
答案: {a2}
# 请解答
问题: {new_question}
思考: """
CoT的边界
CoT是线性的——一条路径走到底,无法回溯。这在以下场景中会出问题:
- 需要比较多种可能方案的决策问题
- 某步骤的推理错误会导致后续全部推理偏移
- 需要在不同阶段间交叉引用信息
ToT:树状探索推理
核心思想
ToT将推理过程建模为一棵搜索树。在每个决策节点生成多个候选思路,通过评估函数筛选有前途的分支,再继续展开。
[初始状态]
/ | \
[思路A] [思路B] [思路C]
评估:0.9 评估:0.3 评估:0.7
/ \ |
[A1] [A2] [C1]
...
工程实现
from dataclasses import dataclass
from typing import Callable
@dataclass
class ToTNode:
state: str # 当前推理状态描述
thought: str # 本步思考
score: float = 0.0 # 评估分数
parent: str | None = None
children: list[str] = None
class TreeOfThoughts:
def __init__(self, llm_generate, llm_evaluate,
n_branches: int = 3, max_depth: int = 5, beam_width: int = 2):
self.generate = llm_generate # 生成候选思路
self.evaluate = llm_evaluate # 评估思路质量
self.n_branches = n_branches
self.max_depth = max_depth
self.beam_width = beam_width
async def search(self, problem: str) -> str:
root = ToTNode(state=problem, thought="初始状态")
frontier = [root]
for depth in range(self.max_depth):
# 扩展:为每个前沿节点生成候选思路
candidates = []
for node in frontier:
thoughts = await self.generate(
problem, node.state, n=self.n_branches
)
for t in thoughts:
score = await self.evaluate(problem, t)
candidates.append(ToTNode(
state=t, thought=t, score=score,
parent=node.state
))
# 剪枝:保留beam_width个最优
candidates.sort(key=lambda x: x.score, reverse=True)
frontier = candidates[:self.beam_width]
# 检查是否找到解
if frontier[0].score > 0.95:
return self._trace_path(frontier[0])
return self._trace_path(frontier[0])
def _trace_path(self, node: ToTNode) -> str:
path = []
while node:
path.append(node.thought)
node = node.parent
return " → ".join(reversed(path))
ToT适用场景
- 数学证明:需要尝试不同证明路径
- 策略游戏:需要评估多个走法的前景
- 创意写作:需要比较不同情节发展方向
GoT:图状关联推理
核心思想
GoT(Graph of Thoughts)将推理结构从树推广到有向图。节点之间可以有交叉引用、合并和循环,更接近人类解决复杂问题时的真实思维模式。
[信息收集A] → [分析模块1] ↘
[综合判断] → [决策]
[信息收集B] → [分析模块2] ↗ ↑
↓ |
[信息收集B] → [交叉验证模块] ──┘
关键操作
GoT定义了三种核心图操作:
| 操作 | 说明 | 类比 |
|---|---|---|
| Aggregation | 合并多个节点的推理 | 多角度汇总 |
| Refinement | 对某节点迭代优化 | 自我修正 |
| Backtracking | 从某节点回退重试 | 反思纠错 |
class GraphOfThoughts:
def __init__(self, llm, max_nodes: int = 20):
self.llm = llm
self.graph: dict[str, dict] = {} # 邻接表
self.max_nodes = max_nodes
async def aggregate(self, node_ids: list[str], question: str) -> str:
"""合并多个推理路径的结论"""
thoughts = [self.graph[nid]["thought"] for nid in node_ids]
prompt = f"综合以下多个分析视角,给出统一结论:\n"
prompt += "\n".join(f"视角{i+1}: {t}" for i, t in enumerate(thoughts))
prompt += f"\n问题: {question}"
merged = await self.llm(prompt)
new_id = f"agg_{len(self.graph)}"
self.graph[new_id] = {"thought": merged, "parents": node_ids}
return new_id
async def refine(self, node_id: str, feedback: str) -> str:
"""对特定节点迭代优化"""
original = self.graph[node_id]["thought"]
refined = await self.llm(
f"原始推理: {original}\n改进建议: {feedback}\n请优化上述推理。"
)
new_id = f"ref_{node_id}_{len(self.graph)}"
self.graph[new_id] = {"thought": refined, "parents": [node_id]}
return new_id
三种范式对比
| 维度 | CoT | ToT | GoT |
|---|---|---|---|
| 结构 | 线性链 | 搜索树 | 有向图 |
| 复杂度 | O(n) | O(b^d) | 变参,最坏O(n^2) |
| 回溯能力 | 无 | 有(剪枝+回溯) | 有(显式backtracking) |
| 合并能力 | 无 | 无 | 有(aggregation) |
| LLM调用次数 | 1次 | 多次(扩展+评估) | 多次+合并 |
| 适用问题 | 简单推理 | 搜索空间明确 | 需多视角综合 |
| 工程复杂度 | 低 | 中 | 高 |
选择指南
根据问题特征选择
问题是否需要多步推理?
├─ 否 → 直接提问,不需要思维链
└─ 是 → 推理路径是否唯一?
├─ 是 → 用CoT,线性推理足够
└─ 否 → 是否需要合并不同视角?
├─ 否 → 用ToT,树搜索最优路径
└─ 是 → 用GoT,图结构综合推理
成本考量
CoT几乎无额外开销,ToT的LLM调用次数是O(n_branches × max_depth),GoT更难预估。在生产中,建议:
- 默认用CoT,覆盖80%的推理任务
- 需要高质量决策的场景用ToT+beam search
- 离线复杂分析可用GoT,在线服务慎用
总结
思维链优化的核心不是"哪种范式最好",而是匹配问题复杂度与推理结构。CoT是基石,ToT扩展了搜索空间,GoT补全了多视角综合能力。从工程角度看,CoT→ToT→GoT的演进路线也对应着从低延迟到高延迟、从低成本到高成本的梯度。在实际系统中,合理搭配才能在质量和成本间取得最优。