项目记忆系统:AI 辅助开发中的上下文持久化实践 ​

7 阅读14分钟

背景

AI 辅助开发正在改变日常编码方式,但它也把一个老问题放大了:上下文很容易丢。

在短会话里,这个问题不明显。一次对话里把背景讲清楚,AI 通常就能继续往下做。但只要把时间拉长,问题就会迅速出现:

  • 隔了一天再继续,很多前提需要重新解释
  • 换了人接手,同样的问题又要从头讲一遍
  • 文档越积越多,却很难判断哪一份才是当前事实
  • 代码能说明“现在是什么”,却很难说明“为什么会这样”

很多团队会在两个极端之间摇摆:

  • 完全依赖对话上下文,把知识留在聊天记录里
  • 试图用大而全的文档系统覆盖一切

前者的问题是上下文不可持续,后者的问题是维护成本极高,最后往往一起失效。

所以我更倾向于把“长期记忆”从普通文档里单独抽出来,设计成一套更轻、更结构化、同时适合人和 AI 阅读的记忆层。本文把这套做法称为“项目记忆系统”。


它解决的不是“文档不足”,而是“上下文失真”

这套系统最核心的目标,不是多写文档,而是降低上下文在流转过程中的失真。

在实际协作里,失真通常来自四个地方:

  1. 对话是短期记忆 AI 助手擅长处理当前窗口里的问题,但不天然擅长跨会话延续历史判断。

  2. 普通文档容易混杂不同层次的信息 有的文件同时写背景、过程、方案、结论、待办,时间一久就没人知道应该先看哪一段。

  3. 同一事实散落在多个地方 README 写了一份,设计文档写了一份,缺陷复盘里又写了一份,最后三份内容彼此不完全一致。

  4. 代码无法完整承载决策背景 代码能表达实现,但无法天然表达边界、权衡、已知坑点和历史替代方案。

所以,“项目记忆系统”不是文档系统的替代品,而是代码与文档之间的一层结构化记忆接口。


设计目标

如果把目标说得足够克制,我认为它应该只追求四件事:

  1. 支持跨会话续作 不管是隔天继续,还是换人接手,都能快速恢复工作上下文。

  2. 沉淀稳定结论 重点记录“为什么这样做”“哪些约束不能破坏”“哪些历史结论已经过时”。

  3. 服务人机共读 人类阅读时要足够清晰,AI 读取时也要足够结构化、可检索、可引用。

  4. 保持低维护成本 如果一套记忆系统自身需要大量维护,它很快也会变成新的负担。

这四个目标里,第四个经常被忽略,但它决定了系统能不能活下来。


一句话定义

项目记忆系统是一组与代码仓并存的结构化文件,用来沉淀长期有效的技术上下文,并为人类协作和 AI 协作提供统一入口。

这里的关键词有三个:

  • 结构化:不是随手堆文档,而是明确分类和职责
  • 长期有效:优先记录可复用的结论,而不是一次性过程
  • 统一入口:让“从哪里开始理解系统”这件事变得确定

与普通文档的区别

普通文档项目记忆
常常按写作主题组织优先按“信息职责”组织
容易混合过程、背景和结论强调分层存放
面向人阅读同时面向人和 AI 读取
可以很长倾向索引化、专题化
常见问题是“写了但找不到”常见问题是“是否放在了唯一正确位置”

普通文档当然仍然需要,只是它更适合承载长分析、方案说明、架构阐述。

项目记忆系统更像一层“知识索引和稳定结论层”,它不追求面面俱到,而追求快速找准事实源。


目录结构:先分层,再落文件

下面给出一个可直接落地的目录示例。目录名本身不是重点,重点是分层思路。比如 .project-memory/ 这个名字完全可以替换成 memory/context/ 或其他更适合你的命名。

.project-memory/
├── MEMORY_RULES.md      # 更新规则:写入前先看
├── MEMORY.md            # 长期记忆索引:只放链接和一句话摘要
├── TOOLS.md             # 环境、构建、调试入口
├── architecture/        # 架构结论与主链路
│   ├── overview.md
│   └── core-flows.md
├── runtime-rules/       # 运行时约束与稳定规则
│   └── <topic>-runtime-rules.md
├── bug-fix/             # 缺陷复盘
│   └── YYYYMMDD_<desc>.md
├── plans/               # 实施计划与技术评估
│   └── YYYY-MM-DD-<desc>.md
├── decisions/           # 决策记录(ADR)
│   └── NNNN-<desc>.md
├── operations/          # 当前状态、交接、脆弱区域、变更摘要
│   ├── current-status.md
│   ├── handoff.md
│   ├── fragile-areas.md
│   └── changelog.md
└── templates/           # 模板文件

这套结构里最关键的不是目录数量,而是下面四种分层:

  • 索引层:告诉你先看哪里
  • 结论层:沉淀稳定规则和架构事实
  • 过程层:记录缺陷、计划和决策过程
  • 操作层:承载当前活跃事项和交接信息

一旦这四层混在一起,记忆系统很快就会腐烂。


核心原则一:索引优先,不把入口写成正文

MEMORY.md 的职责应该非常单纯:它是入口,不是正文。

错误的做法是把它越写越长,最后变成“总述 + 历史 + 结论 + 待办”的混合文件。这样做短期看起来方便,长期一定会失控。

更好的做法是让索引文件只承担两件事:

  • 指向事实源
  • 给出一句话摘要,帮助判断是否值得点进去

例如:

# MEMORY.md

- [渲染运行规则](runtime-rules/render-runtime-rules.md) — 线程模型、缓存约束、生命周期边界
- [核心链路](architecture/core-flows.md) — 主数据流、状态同步和关键回写顺序
- [脆弱区域](operations/fragile-areas.md) — 高频踩坑点与历史认知偏差

索引一旦保持精简,入口就不会失效。


核心原则二:同一种信息只有一个归属位置

很多文档失效,不是因为没人写,而是因为同一种内容被写到了多个地方。

例如:

  • 架构结论既出现在架构文档里,也出现在缺陷复盘里
  • 待办事项既出现在 handoff,也出现在 README
  • 运行规则既出现在实现说明里,也出现在 issue 记录里

这时真正的问题不是“内容够不够”,而是“谁才是事实源”。

所以项目记忆系统要强制回答一个问题:

每一种信息,唯一应该放在哪里?

一个可执行的划分方式如下:

内容类型推荐归属位置
架构总览architecture/overview.md
关键链路architecture/core-flows.md
运行时约束runtime-rules/<topic>-runtime-rules.md
缺陷复盘bug-fix/YYYYMMDD_<desc>.md
计划与评估plans/YYYY-MM-DD-<desc>.md
决策记录decisions/NNNN-<desc>.md
活跃事项operations/current-status.md
交接待办operations/handoff.md
脆弱点与历史偏差operations/fragile-areas.md

只要这个映射足够清楚,后续维护成本会明显下降。


核心原则三:同一事实只维护一份

这条原则和上一条相关,但更严格。

不是“同类信息集中”,而是“同一事实只能有一份正文”。其他地方如果需要提到它,只能:

  • 链接过去
  • 做一句话引用
  • 标明“以 X 文件为准”

这会强迫我们停止写“看起来方便、实际上危险”的重复内容。

例如,“某个模块的状态同步顺序”一旦被确认,就应该只在对应的运行时规则文件里维护。缺陷复盘里如果需要引用,只写一句:

最终行为以 runtime-rules/... 中的顺序约束为准。

这样做的直接收益是:

  • 减少口径分裂
  • 降低更新漏改概率
  • 让 AI 在检索时更容易命中真实事实源

核心原则四:把“稳定结论”和“当前事项”拆开

很多团队会把长期知识和短期状态写在一个文件里,这几乎一定会导致入口失效。

例如一个文件既有:

  • 当前在做什么
  • 哪些问题待验证
  • 系统为什么这么设计
  • 历史上踩过哪些坑

那它最终一定会越来越长,也越来越不可信。

比较稳妥的做法是明确区分:

  • 长期记忆:稳定结论、架构规则、决策、可复用经验
  • 短期操作信息:当前阻塞、待办优先级、这轮变更的验证状态

前者应该追求稳定,后者应该允许高频更新。

这也是为什么 operations/ 这一层很重要。它让“今天该做什么”不必污染“系统长期应该怎么理解”。


为什么还需要 fragile-areas.md

如果只保留“正确结论”,很多系统仍然不够好用,因为真实协作里还有一种高价值信息:

哪些地方最容易被误判,哪些历史说法已经不再成立。

这类信息不完全属于架构,也不完全属于缺陷复盘,但它对接手者和 AI 都非常重要。

例如下面这些内容就很适合放进脆弱区域文件:

  • 哪些旧文档已经部分过时
  • 哪些现象表面看像 UI 问题,实际根因常在底层
  • 哪些改动表面上是局部修改,实际会影响整条链路
  • 哪些区域过去反复出问题,回归时必须优先关注

这份文件本质上是一张“认知偏差清单”。它能显著降低重复踩坑的概率。


命名规范不是形式主义,而是检索能力的一部分

我很建议所有专题文件都使用语义化命名,而不是随手起名。

推荐格式示例:

目录命名格式示例
bug-fix/YYYYMMDD_<brief-desc>.md20260328_cache-race.md
plans/YYYY-MM-DD-<brief-desc>.md2026-03-28-render-refactor.md
decisions/NNNN-<brief-desc>.md0001-state-sync-strategy.md
runtime-rules/<topic>-runtime-rules.mdimage-cache-runtime-rules.md

这样做有三个好处:

  • 文件名本身携带主题信息
  • 人类能快速扫读
  • AI 在检索时更容易建立弱语义匹配

文件命名其实也是记忆系统的一部分。


Frontmatter:给机器看的元信息层

对于 bug-fix/plans/decisions/ 这类专题文件,建议统一使用 YAML frontmatter。

---
date: 2026-03-28
status: fixed
type: bug-fix
modules: [render, cache]
related:
  - ../runtime-rules/image-cache-runtime-rules.md
---

这样做并不是为了“文档更正式”,而是为了让这些文件具备可解析性。

它至少能支持三类能力:

  • 自动筛选某个阶段仍未完成的计划
  • 关联某次缺陷修复涉及的模块和规则
  • 为 AI 或脚本提供更稳定的结构化输入

如果没有这层元信息,后续很多自动化能力都很难做稳。


预警机制:防止记忆系统重新长成一堆杂物

一个容易被忽略的经验是:记忆系统也会腐烂。

所以除了定义目录和职责,还需要定义“什么时候该警觉”。

例如可以设定一些预警阈值:

文件预警信号
MEMORY.md 超过 80 行很可能开始混入正文
current-status.md 超过 40 行很可能混入历史日志
handoff.md 待办超过 10 条很可能没有及时清理完成项
单个复盘文件超过 150 行很可能混入方案设计或背景长文

这些阈值不是为了机械限制篇幅,而是为了触发一次检查:

这个文件是否开始承载不属于它的内容?

这一步很关键。因为大多数系统不是在设计阶段失败的,而是在维护阶段慢慢变形的。


什么内容不应该进入长期记忆

这条规则比“应该写什么”更重要。

以下内容我通常不会放进长期记忆:

  • 一次性试错命令
  • 大段原始日志
  • 没有复用价值的讨论记录
  • 很快会失效的临时判断
  • 敏感配置、账号、密钥、私有地址

长期记忆最怕的不是内容少,而是噪音太多。

一旦噪音比例过高,AI 和人类都会更难从中提取真正有价值的上下文。


这套系统与 README、ADR、Wiki 的关系

项目记忆系统不是要取代已有文档,而是给它们补一层结构。

可以简单理解为:

  • README:给第一次进入仓库的人看,回答“这是什么、怎么跑起来”
  • Wiki / docs:承载长分析、方案说明、背景材料
  • ADR:记录关键技术决策及其权衡
  • 项目记忆系统:作为统一入口,把长期有效的上下文组织起来

其中 decisions/ 目录本身可以直接采用 ADR 形式,但它不应孤立存在,而应被索引系统引用,并与计划、复盘、运行规则建立关联。

也就是说,这套设计不是“另起一套文档体系”,而是在现有文档之上建立“更适合协作和检索的骨架”。


为什么它特别适合 AI 辅助开发

这套方法对 AI 友好,主要不是因为“文件多”,而是因为它把上下文变成了可预测的结构。

对于 AI 来说,最重要的不是信息总量,而是:

  • 入口是否明确
  • 文件职责是否清楚
  • 事实源是否唯一
  • 哪些内容稳定、哪些内容临时,能否区分

只要这四件事成立,AI 在会话启动时就可以按固定顺序读取上下文,例如:

  1. 先读更新规则,避免写错位置
  2. 再读总索引,建立整体地图
  3. 再读工具入口,确认构建和验证方式
  4. 按任务需要进入架构、运行规则、复盘或计划
  5. 最后补充当前状态和交接信息

这个顺序本质上就是在告诉 AI:

  • 先理解地图
  • 再理解约束
  • 最后进入具体问题

这比“把整个仓库全扫一遍”更高效,也更可靠。


如果今天开始落地,我会先做哪三件事

如果你不想一次性搭完整套体系,我建议先做最小可用版本:

1. 建一个真正精简的 MEMORY.md

它只做入口,不做正文。

2. 建一个 operations/handoff.md

把“下一次继续时最需要知道什么”固定下来。

3. 建一个 runtime-rules/

把那些“代码里不容易直接看出来,但改坏了后果很明显”的规则单独沉淀出来。

只做这三件事,很多协作效率问题就已经会明显改善。

后续再逐步补上:

  • bug-fix/ 用于缺陷复盘
  • decisions/ 用于 ADR
  • fragile-areas.md 用于认知偏差和高频坑点

这比一开始就试图设计完整知识库更容易成功。


维护成本从哪里来

任何记忆系统都有成本,关键是成本要可控。

这套设计的主要成本不在“写文档”,而在三件事:

  • 判断一条信息该写到哪里
  • 发现重复内容后主动合并
  • 在系统演进后及时标记过时结论

换句话说,它真正要求的不是写作能力,而是信息治理能力。

如果一个团队已经习惯把所有内容都塞进一个 README,或者把大量判断留在聊天记录里,那么这套方法一开始会有一点阻力。但一旦入口和职责稳定下来,后续成本通常会越来越低,而不是越来越高。


总结

项目记忆系统想解决的,本质上不是“缺少文档”,而是“上下文无法长期稳定流转”。

它的核心思想可以压缩成四句话:

  1. 入口要轻:索引优先,不让入口承担正文职责
  2. 事实要唯一:同一事实只维护一份
  3. 信息要分层:稳定结论、过程记录、当前状态分开存放
  4. 结构要可持续:通过命名、模板和预警机制控制维护成本

如果把它用一句更直白的话总结,那就是:

不要试图让 AI 记住一切,而要为它准备一套可持续读取的长期记忆。

这套方法并不依赖某个特定工具,也不依赖某个具体平台。目录名、文件名和细节规则都可以调整,但有两个原则我认为最好不要放弃:

  • 单一事实源
  • 索引优先

只要这两个原则成立,整套系统就会有很强的可迁移性。