让大模型做一道复杂的逻辑题,它往往自信地给出一个错误答案。问题的根源在于,模型默认只走一条推理路径,一旦中途某一步走错,后面就会越错越远。思维算法(Algorithm of Thoughts,简称AoT)正是针对这个痛点提出的解决方案:把深度优先搜索、广度优先搜索等经典算法的探索过程显式地写进提示词,让模型像执行搜索算法一样,系统性地探索多条推理分支,在必要时回溯,最终收敛到正确答案。这种方式不需要微调模型,只靠精心设计的提示词就能显著提升复杂推理任务的准确率。

一、思维算法的核心原理:让模型模拟搜索树
要理解思维算法,首先要理解大模型推理的固有局限。传统的直接提问方式,相当于让模型从问题直接跳到答案,中间的推理完全靠模型内部隐式完成。思维链(Chain of Thought)方法虽然让模型展示中间步骤,但本质仍然是单路径推理——模型沿着一条自己认为对的路走到底,没有探索其他分支,也没有任何纠错机制。一旦某一步推理出错,错误会被放大到最终答案。
思维算法的关键洞察在于:大模型在预训练阶段已经“见过”海量的算法代码和搜索过程,因此模型对“展开分支、评估、回溯”这套流程有先验知识。如果我们把一个完整的搜索树探索过程写进提示词作为示例,模型就能模仿这个过程,对新问题执行类似的系统性搜索。这就是AoT与思维链最大的区别:CoT是一条链,AoT是一棵树。
具体来说,思维算法在提示词中构造三个层次的思维状态:
- 中间思维(Intermediate Thoughts):搜索过程中的中间节点,代表问题求解到某一步的状态;
- 分支评估(Branch Evaluation):对当前节点的各个可能扩展方向进行判断,判断其是否值得继续深入;
- 回溯操作(Backtracking):当某个分支被证明走不通时,显式地返回上一个可行节点,尝试其他分支。
通过这三要素的组合,模型不再是“猜”答案,而是“算”答案。实践中,AoT在24点游戏、数学推理等任务上相比标准CoT有明显的准确率提升,而且由于提示词一次到位,不需要像自一致性(Self-Consistency)那样多次采样,推理成本反而可能更低。
二、如何把DFS和BFS结构写进提示词
思维算法的落地关键是提示词模板的设计。以深度优先搜索为例,我们要在少样本示例(few-shot examples)中展示一棵完整的搜索树,让模型看到节点如何展开、如何评估、如何回溯。下面用一个经典的数字拆分问题演示模板的写法。
prompt = """
问题:用 7, 3, 4, 5 这四个数字各一次,通过加减乘除得到 24。
搜索树探索过程(深度优先):
节点1: 7 + 3 = 10
剩余数字: [10, 4, 5]
节点1.1: 10 * 4 = 40
剩余数字: [40, 5]
40 与 5 无法通过一次运算得到 24,回溯到节点1
节点1.2: 10 - 4 = 6
剩余数字: [6, 5]
节点1.2.1: 6 * 5 = 30,不等于 24,回溯
节点1.2.2: 6 + 5 = 11,不等于 24,回溯到节点1
节点1.3: 10 - 5 = 5
剩余数字: [5, 4]
5 与 4 无法得到 24,回溯到根节点
节点2: 7 - 3 = 4
剩余数字: [4, 4, 5]
节点2.1: 4 * 5 = 20
剩余数字: [20, 4]
节点2.1.1: 20 + 4 = 24,等于 24!
搜索成功,记录解: (7 - 3) * 5 + 4 = 24
最终答案:(7 - 3) * 5 + 4 = 24
现在请用同样的搜索树方式解决:
问题:用 8, 3, 8, 3 这四个数字各一次,通过加减乘除得到 24。
"""这个模板有几个设计要点。第一,缩进和“节点X.Y”编号构成了树的层级结构,模型能清晰地感知父子关系;第二,每个节点失败后显式写“回溯到节点X”,这是教会模型纠错的关键,缺少回溯语句,模型就会退化成普通思维链;第三,找到解之后要“记录解”并停止搜索,避免模型无限探索。
如果问题更适合广度优先搜索,比如需要先穷举所有一层的可能性再深入,可以把模板改成按层展开的形式:
prompt = """ 问题:将字符串 "abc" 的字符重新排列,按字典序找出第 2 个排列。 按层展开搜索(广度优先): 第 0 层(初始状态): abc 第 1 层(确定第一位): 候选1: a 开头,剩余 "bc" 候选2: b 开头,剩余 "ac" 候选3: c 开头,剩余 "ab" 评估:按字典序优先扩展候选1 第 2 层(在候选1下确定第二位): 候选1.1: ab 开头,剩余 "c" 候选1.2: ac 开头,剩余 "b" 第 3 层(确定第三位): 候选1.1.1: abc —— 完整排列,序号 1 候选1.2.1: acb —— 完整排列,序号 2 找到目标:第 2 个排列是 acb """
选择DFS还是BFS取决于问题性质。解空间深、且一旦深入就有明确成败信号的问题(如数学运算、约束求解)适合DFS配合回溯;解空间浅但每层分支多、需要全局比较的问题(如排序枚举、路径规划中的多起点比较)适合BFS。也可以混合使用:外层BFS收集候选,内层DFS深入验证。
三、控制搜索规模的实用技巧与常见陷阱
思维算法最容易出现的问题是搜索爆炸:模型为了模仿搜索过程,把所有分支都展开,导致输出超长、上下文溢出,或者模型在多层回溯后迷失位置。解决这个问题需要在提示词中引入剪枝和预算控制。
第一个技巧是启发式剪枝。在每个节点评估时,要求模型先用一句话判断该分支的前景,明确写出“剪掉”或“保留”的结论,被剪掉的分支不再展开。比如在上面24点的例子中,可以加一条规则:“如果当前剩余数字经过任意运算都明显偏离24超过一倍,直接剪枝”。这与算法课里的分支限界法思想一致,能大幅压缩树的规模。
第二个技巧是深度与分支数预算。在提示词开头声明约束,例如“搜索树最多展开到第 4 层,每个节点最多评估 3 个子分支,超过预算即停止并给出当前最优解”。这种写法相当于给模型一个止损线,即使找不到完美解也能输出次优答案,避免任务失败。以下是一个带预算控制的完整框架代码:
AOT_PROMPT_TEMPLATE = """
你是一个使用思维算法解决问题的助手。请严格按照以下规则探索:
【搜索规则】
1. 使用深度优先搜索,从最有可能的分支开始探索
2. 每个节点展开前先评估:写一句评估理由,标记 [保留] 或 [剪枝]
3. 分支失败时必须显式写 "回溯到节点 X.Y"
4. 搜索深度不超过 {max_depth} 层,每层最多评估 {max_branches} 个分支
5. 找到满足条件的解后立即停止,总结最终答案
【输出格式】
节点编号: 操作
评估: 保留/剪枝 + 理由
...
最终答案: ...
置信度: 高/中/低
【示例】
(此处粘贴一个完整的搜索树少样本示例)
【问题】
{question}
"""
def build_aot_prompt(question, max_depth=4, max_branches=3):
return AOT_PROMPT_TEMPLATE.format(
question=question,
max_depth=max_depth,
max_branches=max_branches
)
# 调用示例
prompt = build_aot_prompt("一个笼子里有鸡和兔共 35 个头,94 只脚,问鸡兔各几只?")
print(prompt)第三个技巧是让模型维护状态摘要。多层回溯之后,模型容易忘记当前剩余的资源、数字或约束。可以在提示词中要求每个节点旁标注当前状态,比如“剩余数字: [10, 4, 5],已用运算: +, *”。这种状态标注相当于给搜索树节点附加了哈希标签,即使回溯多次,模型也能准确恢复上下文。
最后要提醒几个常见陷阱。其一,示例中的搜索树不要太庞大,两到三个成功回溯的节点就足够教会模型范式,过长的示例反而挤占上下文空间;其二,回溯语句的措辞要与示例保持完全一致,混用“退回”“返回”“撤销”等多种说法会让模型困惑;其三,对于答案空间很小的问题(比如简单的事实问答),思维算法是杀鸡用牛刀,普通CoT更快更省token,AoT的价值体现在分支多、单路径错误率高的复杂推理任务上。
四、思维算法与其他提示技术的对比
把AoT放到整个提示工程的版图中看,能更清楚它的定位。与思维链相比,AoT多了分支探索和回溯纠错;与自一致性相比,AoT在一次生成内完成多次探索,无需多次采样投票;与思维树(Tree of Thoughts,ToT)相比,AoT不依赖外部代码循环调用模型,整个搜索过程由模型在单次生成中完成,部署成本更低。
| 方法 | 推理结构 | 是否需要多次调用 | 纠错能力 | 适用场景 |
|---|---|---|---|---|
| 直接提问 | 无 | 否 | 无 | 简单事实类问题 |
| 思维链 CoT | 单链 | 否 | 无 | 步骤明确的数学应用题 |
| 自一致性 SC | 多条独立链 | 是,多次采样 | 靠投票间接纠错 | 答案离散且可比较 |
| 思维树 ToT | 树,外部控制 | 是,代码循环调用 | 强 | 极复杂规划任务 |
| 思维算法 AoT | 树,单次生成内 | 否 | 强,显式回溯 | 中等复杂度的搜索推理 |
实践中的选择建议是:先用普通CoT跑一遍,如果准确率明显不足且错误集中在“中途某步走岔”,再升级到AoT;如果问题规模大到单次生成根本装不下搜索树,那就应该转向ToT这类由外部代码驱动搜索的方案。提示词工程没有银弹,理解每种方法背后的算法思想,根据问题特征灵活组合,才是提升大模型推理能力的正道。