如今的模型已经足够聪明了, 但为什么 AI 写出来的东西, 总是差点意思?

我使用 standalone 的方式打包并部署在 fly.io 上的 Next.js 应用, 每次遇到问题让 AI 排查时, 它都没有从一开始就排除 Vercel 平台的可能性, 甚至会沿着 Vercel 的方向去排查. 第一次我纠正了, 第二次我又纠正了, 第三次我开始烦了. 到第四次的时候, 它已经基于 "Vercel 部署" 这个错误前提, 跑偏了整个排查方向, 我才意识到它一开始的方向就错了.

这不是模型不够聪明的问题. Sonnet, Opus, 随便哪个模型, 都会犯同样的错. next.config.js 里写了 output: 'standalone', 但代码库里没有任何地方明确写着 "这个项目部署在 fly.io, 不是 Vercel". 对 AI 来说, 看不到的东西就不存在.

这也是为什么如今关于 AI 的讨论, 焦点已经从 prompt engineering 转向了 context engineering. 问题的关键不在于如何跟 AI 说话, 而在于如何构建 AI 能看到的上下文. 用更好的 prompt 解决不了这个问题, 换更强的模型也解决不了. 编程也不例外.

代码说不了的事

AI 在一个代码库里能看到什么? 它能看到代码本身, 知道项目当前是怎么做的. 它能看 git log, 知道最近改了什么. 如果你给它权限, 它还能跑测试, 看 CI 结果.

但有大量关于这个项目的知识, 不存在于代码中:

  • 项目的部署方式, 运行环境, 已知的坑, 或许可以从一些脚本中窥见, 但藏得很深
  • 架构的分层规则, 模块之间的依赖方向, 哪些惯例必须遵守
  • 为什么选了方案 A 而不是方案 B, 当时的权衡是什么
  • 领域里的核心概念和术语定义
  • 产品接下来要做什么, 优先级是什么
  • 跨多个 PR 的长线规划和进度

这些东西对于一个真正理解项目的开发者来说, 全都装在脑子里. 但 AI 没有脑子, 或者说, 它的 "脑子" 在每次对话开始时都是空白的. 你不把这些知识显式地放在它能看到的地方, 它就只能猜. 猜对了是运气好, 猜错了你还得花时间纠正.

所以本质上, AI 编程要解决的核心问题, 不是怎么写更好的 prompt, 而是 Context Engineering: 怎么让 AI 拥有足够的上下文来做出正确的判断.

文档是共享的大脑

一个自然的想法是: 把这些上下文写成文档, 放在代码库里, AI 就能看到了.

但这里有一个容易被忽略的点: 文档不是 "写给 AI 看的说明书". 文档是开发者和 AI 共享的 "大脑记忆".

人和人协作的时候, 如果两个人对项目的理解不一致, 就需要不断交流来同步. 人脑天生有记忆, 聊过一次, 下次就记住了. 但 LLM 没有持久记忆, 每次对话都是从零开始. 所以我们需要显式地构造和维护它的 "记忆", 而这个记忆, 就是文档.

很多开发者抱怨 "AI 不听话", "AI 做出来的东西不是我想要的". 这种体验的根因, 往往不是 AI 能力不够, 而是双方的 "共脑" 不够. 你脑子里有一套对项目的完整理解, 但 AI 脑子里只有它能看到的那些代码. 两边的信息不对称, 产出自然不对.

反过来, 写文档的过程, 也在迫使开发者自己把隐性的想法显式化. 很多时候你以为自己想清楚了, 一写下来才发现还有模糊的地方. 文档是给 AI 看的, 也是给自己看的, 更是给未来的自己看的.

需要记录什么

哪些上下文需要被记录? 一个简单的判断标准: 如果这个信息只存在于你的脑子里, 而 AI 在开发过程中可能需要它, 那就应该记录下来.

基于实际的开发经验, 我把需要记录的上下文分成了几类:

项目状态 (STATE.md): 项目的部署方式, 运行环境, 基础设施, 已知限制. 比如 "部署在 fly.io, standalone 模式", "数据库用的 PlanetScale", "目前已知的性能瓶颈在 xxx". 这是 AI 每次开始工作前都应该读的东西, 防止它基于错误的假设行动.

架构规则 (ARCHITECTURE.md): 系统的分层规则, 模块边界, 依赖方向, 以及关键的编码惯例. 比如 "用 Server Action, 不用 API Route", "UI 层不能直接访问数据库", "日期数据怎么处理". 不写下来, AI 就只能从代码中猜模式, 猜错的概率很高.

决策记录 (DECISIONS.md): 每个重要的技术决策, 记录选了什么, 为什么选, 考虑过哪些备选方案, 为什么放弃. 这是最容易被忽视, 但可能是最有价值的一类文档. AI 如果不知道你为什么做了某个决策, 它就可能在某次改动中悄悄推翻它.

产品知识 (knowledge-base.md, glossary.md): 核心功能的描述, 关键文件路径, 数据模型, 以及领域术语的定义. 确保 AI 和你说的是同一种语言. 这其实就是 DDD 中 Ubiquitous Language 的思想, 只不过现在需要对齐的不只是团队成员, 还有 AI.

设计文档 (design-docs/): 复杂变更的技术方案, 包括问题描述, 备选方案对比, 最终选择和理由. 不是所有任务都需要, 但当涉及新模块, 跨模块重构, 或者有多种可行方案时, 先写设计文档再写代码, 能大幅降低返工的概率.

执行计划 (exec-plans/): 跨多个 PR 的长线任务, 比如 SEO 优化, 大规模重构, 数据迁移. 记录目标, 步骤, 当前进度. 这样每次开新 PR, AI 都能从文档中接上之前的上下文, 而不是每次都要从头解释.

光有文档还不够

文档放在代码库里, AI 不一定会主动去读. 或者读了一部分, 漏了关键的一部分. 又或者它改完代码, 不会去更新受影响的文档, 下次再读就是过时的信息.

所以除了文档本身, 还需要一套规则来约束 AI 的行为. 我在实践中总结了五条硬规则, 写在 AGENTS.md 里 (大部分 AI 编程工具都支持读取这个文件作为初始指令):

1. 先读文档. 收到任何开发任务后, 第一步是读相关文档, 理解上下文, 不要直接动手写代码.

2. 文档先于代码. 如果一个任务需要新建模块, 跨模块重构, 或者有多种可行方案, 必须先写设计文档, 经确认后再写代码. 如果任务涉及多个模块, 有数据库迁移, 或者步骤之间有依赖关系, 必须先写执行计划.

3. 计划先于执行. 在动手之前, 先说明打算改哪些文件, 为什么改, 怎么改, 等用户确认后再执行. 这不是走流程, 是防止 AI 在错误的方向上跑太远.

4. 自审 + 同步文档. 代码改完后, 跑自检清单, 然后更新所有受影响的文档. 如果改了部署方式, 更新 STATE.md. 如果做了架构决策, 记录到 DECISIONS.md. 如果完成了执行计划的某个步骤, 更新进度. 这一条是整个体系的命脉. 文档不更新, 很快就会比没有文档更糟糕, 过时的文档会主动误导 AI.

5. 测试先行. 核心业务逻辑用 TDD, 先写测试确认失败, 再写实现. 测试定义了 "程序应该怎么工作", 从这个意义上说, 测试也是一种文档, 而且是可执行的, 能自动验证的文档. 当 AI 改动代码后, 测试能立刻告诉你它有没有破坏原有的行为, 不需要你逐行去审查.

关于第二条, 还有一个实践值得一提. Agent 创建完设计文档后, 可以新建一个 Session 让 Agent 来审查, 避免在同一个上下文里既当作者又当审查者. 甚至可以用不同的模型, 比如用 Claude 写设计, 用 GPT 或 Gemini 来审查. 这一点在 OpenCode, pi 等开源的 Coding Agent 中很方便做到, 因为可以自由切换模型. 如果用 Claude Code, 也可以在 Opus 和 Sonnet 之间切换.

这五条规则的目的不是让开发流程变重, 而是确保文档这个 "共享大脑" 始终被读取, 被遵守, 被更新. 但说实话, 即便把这些约束写进了 AGENTS.md, AI 也不是每次都能严格遵守. 有时候它还是会跳过读文档的步骤直接动手, 或者改完代码忘了更新相关文档. 规则能覆盖大部分情况, 但不是万能的, 开发者仍然需要留意和提醒. 否则文档还是会逐渐腐烂, 而腐烂的文档比没有文档更危险.

这套体系从零搭建比较麻烦, 尤其是对于已有的项目, 怎么把这套文档范式融入进去, 是一个很常见的痛点. 所以我把这些实践整合成了一个开源模板: agentic-docs-templates. 对于已有的项目, 一行命令就能完成初始化:

claude "Read https://raw.githubusercontent.com/Sukitly/agentic-docs-templates/main/bootstrap.md and follow the instructions to set up agentic docs for this project."

AI 会先分析你的项目, 包括技术栈, 架构, 现有文档和编码惯例, 然后跟你确认分析结果, 最后基于真实的项目内容生成整套文档, 不是空模板. 生成完之后还会跑一个完整性检查脚本, 确保所有文档链接有效, 索引完整.

写在最后

代码告诉 AI "现在是什么样", 文档告诉 AI "为什么是这样, 不能是那样, 以及接下来要怎样". 缺了后者, AI 就是一个每次都失忆的协作者, 能力再强, 也只能反复犯同样的错.

AI 编程发展到今天, 开发者的核心工作正在从 "写代码" 变成 "维护一个知识系统". 代码是 AI 写的, 但知识系统是你建的. 你定义架构规则, 记录决策和权衡, 维护产品知识, 追踪长线计划. 这些东西加在一起, 构成了 AI 能正确工作的前提.