LLM 为什么有推理和写代码的能力

先给结论

大语言模型(LLM)的推理能力和写代码能力,不是因为模型内部显式安装了一个“逻辑引擎”或“编译器”,而是来自多种因素叠加:

  • 大规模文本和代码语料中的结构被模型压缩进参数。
  • Transformer 能在上下文中建立 token 之间的依赖关系。
  • “预测下一个 token”的训练目标,迫使模型学习语法、事实、因果、步骤、模式和约束。
  • 代码本身是高度结构化语言,语法严格、模式重复、反馈明确,适合被模型学习。
  • 指令微调、RLHF/RLAIF、测试反馈、工具调用增强了模型按人类需求解决问题的能力。

Summary

LLM 的推理和代码能力,本质上是大规模模式学习、上下文表征、多步生成和反馈对齐共同涌现出的能力。它很有用,但不是完美可靠的形式化推理系统。

1. 从“预测下一个 token”开始

LLM 的基础训练目标通常很简单:给定前面的 token,预测下一个 token。

例如:

如果 A 大于 B,B 大于 C,那么 A ___

模型要预测后面可能是:

大于 C

表面上看,这只是补全文字。但要长期、稳定地补对,模型必须学会很多隐藏结构:

  • 词语之间的搭配。
  • 句法结构。
  • 概念之间的关系。
  • 常见事实。
  • 数学、代码、证明、解释的模板。
  • 人类写作中解决问题的步骤。

(附件 llm-next-token-training.svg 未随站点发布)

Note

“预测下一个 token”不是简单背诵。模型要在海量场景中降低预测误差,就会倾向于学习能泛化的结构。

2. 为什么预测文字会产生推理能力

很多文本本身就包含推理过程。

例如:

  • 数学题解。
  • 代码注释。
  • 论文证明。
  • 法律论证。
  • 调试记录。
  • 教程文章。
  • 问答网站。
  • Pull Request 讨论。
  • 设计文档。

这些文本里经常出现这样的结构:

问题 -> 条件 -> 分析 -> 中间步骤 -> 结论

模型反复学习这种结构后,会掌握大量“问题到解法”的模式。

所以它面对新问题时,不是从零发明逻辑,而是在上下文中匹配和组合曾经学到的推理模板。

(附件 llm-reasoning-layers.svg 未随站点发布)

这种能力可以表现为:

  • 分类:判断问题属于哪一类。
  • 归纳:从例子中总结规律。
  • 演绎:根据条件推出结论。
  • 类比:把一个领域的方法迁移到另一个领域。
  • 分解:把复杂问题拆成多个子问题。
  • 规划:生成一组有顺序的操作步骤。

3. Transformer 为什么适合学习推理模式

Transformer 的关键机制是 self-attention。

它让模型在处理当前 token 时,可以关注上下文中相关的 token。

例如面对:

张三把书给了李四,因为他已经看完了。

模型需要判断“他”更可能指谁。self-attention 可以让模型把“他”和前面的“张三”“李四”“看完了”建立关联。

在更复杂的问题中,attention 可以帮助模型关注:

  • 条件。
  • 变量。
  • 函数名。
  • 类型定义。
  • 约束。
  • 目标。
  • 已经生成的中间步骤。

这使得模型不是只看最后几个词,而是能在较长上下文里组合信息。

不过要注意:attention 不是逻辑证明器。它提供的是强大的上下文关联机制,而不是保证结论一定正确的形式系统。

4. LLM 的推理更像“可泛化的模式组合”

人们说 LLM 有推理能力,容易产生误解。

它不是像传统定理证明器那样,每一步都严格按照形式逻辑规则搜索证明。

更准确地说,LLM 学到了大量可泛化模式:

  • “如果 A>B 且 B>C,则 A>C”。
  • “递归函数需要终止条件”。
  • “数据库索引可以提高查询效率,但会增加写入成本”。
  • “并发代码可能有竞态条件”。
  • “看到错误堆栈时先定位最内层异常”。

这些模式被压缩到模型参数中,并在上下文中被激活、组合、重排。

Warning

LLM 可以表现出推理能力,但也会犯低级逻辑错误。它生成的是最可能的输出,不是天然带证明保证的正确结论。

5. 为什么 LLM 会写代码

代码能力是 LLM 能力中最容易理解的一部分。

代码本质上也是语言,只是比自然语言更严格。

代码具有几个对模型很友好的特点:

  • 语法规则明确。
  • 常见模式重复度高。
  • API 调用有固定写法。
  • 函数名、类型名、注释提供强语义线索。
  • 编译器和测试可以提供明确反馈。
  • 开源仓库中有大量真实项目代码。

(附件 llm-code-ability.svg 未随站点发布)

LLM 在训练中会看到大量这样的材料:

  • 源代码。
  • README。
  • API 文档。
  • 教程。
  • Stack Overflow 问答。
  • GitHub issue。
  • Pull Request 评论。
  • 单元测试。
  • 错误日志。

这些材料把“需求、代码、错误、修复、解释”连接在一起。

所以模型不只是学会代码语法,还学会了:

  • 用户需求通常对应什么代码结构。
  • 某个库的常见使用方式。
  • 某种报错常见原因是什么。
  • 某个函数应该如何命名和组织。
  • 什么样的代码看起来更像可维护代码。

6. 写代码能力来自哪些具体能力

6.1 语法补全

模型能补全括号、缩进、函数结构、类型声明。

例如看到:

def add(a, b):

它很容易预测后面应该是:

    return a + b

这是基础层能力。

6.2 模式迁移

模型见过大量类似模式,可以把一个场景迁移到另一个场景。

例如它学过:

items = [x for x in items if condition(x)]

就能迁移到过滤日志、过滤用户、过滤订单等不同业务对象。

6.3 API 使用

LLM 训练语料中包含大量库文档和代码示例。

因此它可能知道:

  • Python 的 requests 怎么发 HTTP 请求。
  • React 组件怎么写。
  • SQL 查询如何聚合。
  • Rust 的 Result 和 Option 如何处理。
  • Go 的 context.Context 如何传递。

不过 API 会更新,所以涉及最新库版本时必须查官方文档或本地代码。

6.4 局部逻辑推理

写代码需要持续维护局部约束:

  • 变量从哪里来。
  • 类型是否匹配。
  • 循环边界是否正确。
  • 错误是否被处理。
  • 函数返回值是否符合调用方预期。

LLM 能在上下文中跟踪一部分这样的约束,所以能写出很多可运行代码。

6.5 调试能力

调试语料中常见模式是:

错误信息 -> 可能原因 -> 修改建议

例如:

TypeError: cannot read property 'map' of undefined

模型会联想到:

  • 数据可能还没加载。
  • 变量初始值可能是 undefined。
  • 渲染前需要判空。
  • 可以给默认空数组。

这就是调试能力的来源之一。

7. 工具反馈让代码能力更强

纯语言模型生成代码时,可能写出看起来合理但不能运行的代码。

如果加上工具反馈,能力会明显增强:

  • 编译器能发现语法和类型错误。
  • 单元测试能发现行为错误。
  • Linter 能发现风格和潜在问题。
  • 运行日志能暴露异常。
  • 搜索和文档能补齐最新 API。

(附件 llm-tool-feedback-loop.svg 未随站点发布)

这种过程类似:

生成代码 -> 运行测试 -> 读取错误 -> 修改代码 -> 再测试

这也是为什么 coding agent 往往比单轮聊天模型更可靠:它不只生成,还能观察环境反馈并迭代。

8. 推理能力为什么会随着模型规模增强

经验上,模型规模、数据规模和训练计算量增加后,某些能力会明显增强。

原因包括:

  • 更大的模型能存储和压缩更多模式。
  • 更多层和参数能表示更复杂的函数。
  • 更多训练数据覆盖更多任务类型。
  • 更长上下文能容纳更多条件和中间步骤。
  • 指令微调让模型更会按任务格式输出。

一些能力看起来像“突然出现”,常被称为涌现能力。但从工程视角看,更稳妥的理解是:当模型容量和训练覆盖度达到某个水平后,之前不稳定的模式组合变得足够可靠,于是表现为新能力。

9. 为什么 Chain-of-Thought 有帮助

让模型写出中间步骤,常常能提高复杂任务表现。

原因是:

  • 中间步骤把隐含计算显式化。
  • 后续 token 可以依赖前面已经生成的中间结果。
  • 复杂问题被拆成更短的局部预测。
  • 人类或工具可以检查中间过程。

例如:

先列出条件,再逐步推导,最后给出结论。

比直接要求:

给出答案。

更容易得到稳定结果。

不过 Chain-of-Thought 也不是保证正确。模型可能生成看似合理但实际错误的步骤。

10. LLM 和传统程序的区别

传统程序是人明确写规则:

如果满足条件 A,就执行规则 B。

LLM 是从大量数据中学习规则的统计近似:

在类似上下文中,接下来最可能出现什么输出。

所以二者差异很大:

维度传统程序LLM
规则来源人手写数据中学习
行为确定性强概率性强
优势稳定、可验证泛化、理解自然语言
弱点不灵活可能幻觉或推理错误
适合明确规则模糊任务、语言任务、代码辅助

11. 为什么它会犯错

LLM 的能力很强,但错误也很自然。

常见原因:

  • 训练目标是预测,不是验证。
  • 参数里保存的是压缩后的统计规律,不是数据库。
  • 上下文太长时可能丢失细节。
  • 罕见问题缺少足够训练样本。
  • 问题本身有歧义。
  • 模型可能把相似模式错误迁移。
  • 没有工具反馈时无法真正执行代码。

因此,用 LLM 写代码时,最好遵循:

  • 小步生成。
  • 运行测试。
  • 查官方文档。
  • 做代码审查。
  • 对关键逻辑添加断言和测试。
  • 不直接信任安全、财务、医疗、权限相关代码。

12. 一个更准确的理解

LLM 的推理和代码能力,不应该被理解成“模型像人一样真的懂了所有东西”,也不应该被贬低成“只是背答案”。

更准确的说法是:

Summary

LLM 在大规模训练中学到了语言、知识和代码的高维统计结构。推理和写代码,是这些结构在具体上下文中被激活、组合、生成和反馈修正后的表现。

它既不是神秘魔法,也不是简单查表。

13. 小结

LLM 有推理能力,是因为:

  • 推理文本大量存在于训练数据中。
  • Transformer 能建模上下文依赖。
  • 多步生成可以把复杂任务拆成局部预测。
  • 指令微调和反馈训练让模型更符合人类任务格式。

LLM 有写代码能力,是因为:

  • 代码是语言,而且结构更严格。
  • 开源代码、文档、测试、错误日志提供了大量训练样本。
  • 代码任务有强模式、强约束和明确反馈。
  • 工具调用能把“生成”变成“生成-测试-修正”的闭环。

Summary

LLM 会推理、会写代码,不是因为它内置了人类式理性,而是因为它学会了在上下文中生成符合知识、模式和约束的高概率步骤;再配合工具反馈,就能完成大量真实任务。