思维图(Graph of Thoughts,简称GoT)并不是突然冒出来的概念,它建立在两种已有推理框架之上:思维链(Chain of Thoughts,CoT)和思维树(Tree of Thoughts,ToT)。CoT把推理过程拉成一条直线,每个中间结果只与前后相邻的节点相关;ToT则引入分支,但整体结构仍然是一棵树,子节点只能有一个父节点。而图结构去掉了这个限制,任意两个节点之间都可以存在有向边,这意味着多个上游推理结果可以同时指向同一个下游节点,从而进行聚合。这也是“图”相比“树”最本质的差异:从一对多变成多对一甚至多对多。接下来我们围绕GoT的图结构表示、聚合机制以及实践中的设计策略展开讨论。

GoT的图结构表示与节点类型
在GoT中,每个节点代表一个独立的“想法”或中间推理步骤。想法可以是一个完整的句子、一段Python代码、一个数学表达式,甚至是模型生成的一段结构化JSON。节点由大模型根据上下文和已存在的图状态生成。与CoT不同,GoT不要求节点必须按顺序依次生成;相反,模型可以在一次推理循环中同时生成多个候选节点,并将它们作为新节点挂载到现有图的适当位置。
边表示节点之间的依赖关系:从节点A到节点B的有向边意味着B的内容基于A的输出。这种依赖关系可以是“展开”关系,比如从问题节点派生出多个子问题;也可以是“细化”关系,比如将一个粗略的想法改写成更精确的版本;或者更常见的“聚合”关系,即多个节点合并成一个更全面的结果。GoT的灵活性在于,边和节点的语义完全由开发者定义,不需要预先规定固定的图拓扑。
从实现角度看,一个简单的GoT系统可以用邻接表存储图,节点对象携带文本内容、分值以及生成该节点时使用的提示词。例如:
class ThoughtNode:
def __init__(self, content, score=0.0):
self.content = content
self.score = score
self.parents = [] # 上游节点列表
self.children = [] # 下游节点列表
class GraphOfThoughts:
def __init__(self):
self.nodes = []
self.edges = [] # 元素为 (src_idx, dst_idx)
def add_node(self, node):
self.nodes.append(node)
return len(self.nodes) - 1
def add_edge(self, src, dst):
self.edges.append((src, dst))
self.nodes[src].children.append(dst)
self.nodes[dst].parents.append(src)
上面的代码只是最基础的骨架,真实系统中还需要管理生成节点时的模型调用、评分函数以及停止条件。但核心思想就是:节点就是文本,边就是依赖,图的结构完全由推理过程动态构建。
聚合操作:让多条路径汇成一条结论
聚合是GoT区别于ToT的关键机制。在树结构中,如果一个任务需要综合多个分支的信息,通常只能先选择一个最优分支继续往下走,或者简单地把所有叶子节点的输出拼接后交给模型再做一次总结。这种做法本质上是在树之外额外加了一层线性处理,树结构本身并没有提供多对一的表达。而在GoT中,聚合直接表现为图中的一个特殊节点,它接收多个上游节点的输入,输出一个综合后的新节点。
最简单的聚合方式是拼接:将多个上游想法的文本用分隔符连接起来,再让模型基于拼接结果生成一个总结。这种方式实现门槛低,但对输入顺序敏感,且当上游节点很多时容易超过上下文长度。更有效的方式是让模型自己判断哪些上游节点值得采纳,并生成一个融合版本。比如在代码调试场景中,多个上游节点可能分别指出了不同的错误假设,聚合节点可以输出一段修正后的代码,同时标注吸收了哪些修正意见。
聚合节点的生成提示词通常需要明确说明输入来源和期望输出格式。下面是一个使用OpenAI API进行聚合的代码片段:
import openai
def aggregate_thoughts(upstream_contents, task_description):
prompt = f"""你正在解决一个复杂任务:{task_description}
下面是来自不同推理路径的想法,请综合它们的关键信息,生成一个更完善的结论。
想法列表:
"""
for i, content in enumerate(upstream_contents):
prompt += f"{i+1}. {content}\n"
prompt += "\n请输出综合后的结论:"
response = openai.ChatCompletion.create(
model="gpt-4",
messages=[{"role": "user", "content": prompt}],
temperature=0.7,
)
return response.choices[0].message.content
除了模型驱动的聚合,也可以使用非学习的聚合策略,例如对上游节点的评分进行加权平均后选择最高分的节点作为聚合结果。这种策略更适合对延迟敏感的场景。实际项目中往往混合使用多种聚合方式,在不同阶段选择不同的聚合粒度。
GoT的实际应用与设计考量
GoT最适合的任务类型是那些需要多角度分析、相互印证或迭代修正的问题。例如数学证明:可以从不同引理出发推导同一个结论,GoT允许这些推导路径并行存在,并在某个节点处交汇,从而发现不同证明之间的关系。另一个典型场景是开放域问答中的多跳推理,需要同时从多个文档片段收集证据,GoT的聚合节点天然支持这种证据融合。
但在设计GoT系统时,有几个现实问题需要处理。首先是图规模的失控。如果每轮迭代都生成大量新节点,图的节点数和边数会迅速膨胀,导致模型调用成本激增。常见的控制手段包括:限制每轮生成的新节点数量、使用评分函数剪枝低分节点、以及设置最大迭代深度。其次是如何评估节点的质量。可以训练一个专门的奖励模型,也可以利用大模型自身进行打分,甚至可以结合任务特定的验证器,比如在代码生成任务中直接用单元测试结果作为评分依据。
另一个值得注意的点是,GoT的推理过程本质上是异步和并行的。同一轮中生成的多个候选节点可以并发调用大模型,这能显著提升整体吞吐量。但在聚合时必须等待所有上游节点完成,因此需要设计合理的任务调度。对于生产环境,可以把每个节点生成看作一个独立任务,使用消息队列或异步框架进行管理,聚合节点只在依赖全部就绪后才触发。
最后,GoT并不意味着一定要抛弃CoT或ToT。很多实用系统会在早期使用线性CoT快速得到初始想法,再用GoT进行扩展和修正;或者在树的某些节点上局部引入图聚合来提升信息利用率。这种混合范式往往比纯GoT更容易落地,也更容易调试。

