为什么用 Python 思考能让 LLM 更聪明: 代码提示的力量

如果你曾尝试解读复杂的法律文件或确定签证资格,你就会知道其中的逻辑很少是直截了当的。它就像是条件语句组成的迷宫: “如果你年满 18 岁,并且在此居住满 5 年,或者与公民结婚,那么……”

这被称为条件推理 (conditional reasoning) , 是人类智能的基本组成部分。然而,对于大型语言模型 (LLM) 来说,这可能是一个巨大的绊脚石。虽然 GPT-4 这样的模型令人印象深刻,但当面对隐藏在自然语言文本中的长串条件时,它们经常会产生幻觉或迷失逻辑。

但是,如果我们改变提问时使用的语言呢?

一篇引人入胜的研究论文 “Code Prompting Elicits Conditional Reasoning Abilities in Text+Code LLMs” 提出了一种反直觉的解决方案。研究人员发现,如果你将自然语言问题转化为代码 (特别是类似 Python 的伪代码) 并反馈给 LLM,模型的推理性能会突飞猛进——即使这些代码实际上从未被计算机执行过。

在本文中,我们将拆解“代码提示”是如何工作的,为什么它能触发 LLM 中特定的推理回路,以及那些证明“一段代码有时胜过千言万语”的实验。


问题所在: 文本的歧义性

自然语言推理是混乱的。词汇可能有歧义,句子可能很晦涩,要在几个段落中跟踪多个实体 (如“申请人”、“配偶”、“文件”) 的状态需要巨大的认知负荷。

Text+Code LLM (在自然语言和编程语言上都训练过的模型,如 GPT-3.5、Mistral 和 Mixtral) 的最新进展表明,接触代码可以提高通用推理能力。然而,大多数利用代码的技术,如“思维程序” (Program of Thoughts) ,都依赖外部 Python 解释器来运行代码并获得答案。这把 LLM 当作解析器,将实际的思考过程外包给了计算机。

本文作者提出了一个不同的问题: 代码表示形式本身能否在没有外部执行的情况下,触发 LLM 内部更好的推理能力?

解决方案: 代码提示 (Code Prompting)

研究人员介绍了一种称为代码提示的方法。核心思想是将自然语言任务转换为基于代码的表示形式,促使模型以结构化逻辑进行“思考”。

如下图 1 所示,该过程涉及两步流水线:

  1. 转换 (Transformation) : 模型将自然语言 (NL) 问题转换为“代码提示”。
  2. 推理 (Inference) : 将此代码反馈给 LLM,由其处理代码并生成最终的自然语言答案。

Figure 1: Code prompting converts a natural language problem into a code prompt and prompts a large language model with such code to generate an answer.

“代码”长什么样?

生成的代码并不一定是为了在编译器上运行。它是逻辑的结构化表示。它通常包括:

  • 变量赋值: 将实体转换为变量 (例如 husband_pass_away = True) 。
  • 逻辑结构: 使用 ifelseandor 语句来映射文本中发现的规则。
  • 注释: 关键的是,代码保留了原始的自然语言文本作为注释,确保没有语义信息的丢失。

图 2 展示了在一个关于丧葬费用的问题上的转换。请注意那些混乱的文本规则 (“如果你符合……即有资格”) 是如何被转换为干净、合乎逻辑的 if 代码块的。

Figure 2: Code prompting converts natural language descriptions into code to be solved with a large language model. The figure shows a transformed instance from the ConditionalQA dataset.

通过将问题强制转换为这种格式,LLM 被明确要求在尝试回答问题之前,先识别变量及其之间的逻辑关系。


有效吗?实验结果

为了验证这一点,研究人员在三个具有挑战性的数据集上评估了“文本提示”与“代码提示”的对比:

  1. ConditionalQA: 一个需要基于复杂场景回答问题的阅读理解数据集。
  2. BoardgameQA: 一个涉及相互冲突的规则和偏好的数据集,高度依赖逻辑。
  3. ShARC: 一个处理自然语言规则的对话数据集。

他们测试了三个著名的模型: GPT-3.5Mixtral 8x7BMistral 7B

显著的性能提升

结果令人震惊。如表 1 所示,代码提示始终优于标准的文本提示。

Table 1: Comparison (F1 score)of text prompt and code prompts. All results use one demonstration per class Delta CP = Code Prompt - Text Prompt, i.e., the average performance gain from code prompts across all datasets.

结果的关键要点:

  • 巨大提升: GPT-3.5 在重逻辑的 BoardgameQA (BGQA) 数据集上看到了巨大的提升 (高达 22.5 分 )。
  • 一致性: 这种提升在不同模型规模和架构中都成立 (Mistral 和 Mixtral 也看到了提升) 。
  • 复杂性很关键: 代码提示和文本提示之间的差距在最困难的数据集 (BGQA-3) 上最大,这表明推理任务越难,代码结构的益处就越大。

减少不确定性

为什么文本提示会失败?查看混淆矩阵 (图 4) 揭示了一个有趣的模式。

Figure 4: Confusion matrices of text and code prompts for each model on al datasets.

在像 BoardgameQA 这样的困难数据集中,文本提示经常默认预测“信息不足” (文本列中较浅/白色的方块) 。模型表现得犹豫不决。然而,代码提示减少了这种不确定性,使模型能够更频繁地正确识别“是”或“否”的答案。


为什么代码提示有效?

这是论文中最具科学趣味的部分。仅仅是因为代码更短吗?是因为语法吗?还是因为 LLM 的“思考”方式有一些更深层的原因?研究人员在 GPT-3.5 上进行了广泛的消融研究来找出答案。

1. 这不仅仅是文本简化

你可能会争辩说,将文本转换为代码只是去除了废话,使问题更容易阅读。为了验证这一点,作者将代码提示与以下方法进行了比较:

  • 原子陈述 (Atomic Statements) : 将文本分解为简单的陈述性要点。
  • 回译代码 (Back-Translated Code) : 将生成的代码转换清晰、结构化的自然语言 (例如,“如果变量 X 为真……”) 。

Table 2: Performance gap of atomic statements and back-translated code when compared to code prompts using GPT 3.5.Results from the dev set of each dataset.

表 2 显示了结果。这两种替代文本方法都比代码提示表现更差 (由负数表示) 。这表明代码本身的语法——括号、缩进、== 运算符——触发了自然语言结构无法触发的特定推理能力。

2. 语义很重要 (不能只是伪造)

代码真的必须有意义吗?研究人员尝试通过以下方式混淆模型:

  • 匿名化代码: 将变量重命名为 var_1var_2 等。
  • 随机代码: 在保留自然语言注释的同时插入随机、无关的代码逻辑。

Table 3: Performance gap to code prompts for each code perturbation. cQA stands for CondQA, CQA-YN for the partition of CondQA with yes-no answers,BG for BGQA. Results reported on the dev set of each dataset.

如表 3 所示,当代码被随机化或匿名化时,性能显著下降。这证明自然语言概念与代码变量之间的语义联系至关重要。模型依赖有意义的变量名 (如 husband_passed_away) 来跟踪逻辑。

3. 样本效率

代码提示最实用的好处之一是它能帮助模型学得更快。在 LLM 的世界里,“学习”通常意味着上下文学习 (In-Context Learning,即在提示中提供示例) 。

图 3 表明,仅使用一个演示 (1 Dem./Class) 的代码提示通常优于使用三个演示的文本提示。这使得代码提示在上下文窗口有限或提供的示例很少的情况下非常高效。

Figure 3: Performance comparison of GPT 3.5 between text (green) and code prompts (blue) using 1, 2,and 3 demonstrations per class. Results reported on dev sets.

4. “状态跟踪”假设

也许最深刻的见解是状态跟踪 (State Tracking)

在编程中,你经常在第 1 行定义一个变量 (x = 5) ,写一百行其他代码,然后再次引用 x。代码 LLM 经过大量训练,可以在长距离内跟踪这些变量的“状态”。然而,自然语言模型倾向于关注局部上下文 (当前句子周围的词) 。

研究人员假设代码提示激活了这个“变量跟踪”回路。为了证明这一点,他们在生成过程中打断模型并进行探查: “变量 X 的当前值是多少?”

Table 4: Comparison of the percentage of memory errors made by GPT 3.5. For each dataset, we separately compute memory errors for the instances where the model gives the correct and incorrect answers.Lower is better. Results from the dev set of each dataset.

表 4 的结果令人咋舌。

  • Correct Ans / Text 列: 文本提示在 ConditionalQA 上的记忆错误率为 71.08% 。 这意味着即使文本模型答对了,它也往往无法正确回忆起导致答案的具体事实 (暗示它可能是猜对的) 。
  • 相比之下, Correct Ans / Code 的错误率仅为 4.39%

这证实了代码提示允许 LLM 在整个推理过程中准确跟踪实体和条件的状态 , 本质上给了模型更好的“工作记忆”。


结论与启示

论文 “Code Prompting Elicits Conditional Reasoning Abilities in Text+Code LLMs” 为我们观察大型语言模型的黑盒内部提供了一个引人注目的视角。事实证明,对于在代码上训练过的模型来说,编程语言不仅是构建软件的工具,也是思考的工具。

通过将自然语言问题转化为代码这种严格的、基于状态的结构,我们可以解锁 LLM 中卓越的推理能力。这种方法:

  1. 在复杂的逻辑任务上优于标准文本提示。
  2. 提高模型跟踪变量状态和事实的能力。
  3. 需要有效的语法和语义——你不能只是把文本伪装成代码;逻辑必须是合理的。

对于 AI 领域的学生和从业者来说,这表明提示工程 (prompt engineering) 的未来可能不在于写出更好的英语句子,而在于写出更好的伪代码。如果你需要 LLM 解决逻辑谜题,不要只是让它“一步一步地思考 (think step by step) ”。让它写一个程序——即使你从未打算运行它。