3. 记忆系统 — 动态上下文在哪里、如何积累
不是 RAG,不是向量数据库——用几个 Markdown 文件构建的记忆。四种记忆类型、索引策略、自动加载与按需加载的分离,以及让记忆不只增不减的修剪纪律。
这是 Alice Way 系列的第 3 篇。第 2 篇 人格设计 把人格(「这个协作者是谁」)与记忆(「他们当前认为是真实的内容」)区分开来。本篇是我如何构建那个记忆的记录。
我一开始想到了复杂的东西:向量数据库、嵌入、RAG 流水线。几天内就全部放弃了。我一天实际积累的内容并没有那么多。我需要的是一个非易失、人类可读、且工具可自动加载的地方。答案是文件系统。
我认为记忆系统的关键不在存储容量,而在检索纪律。以前我们用复杂基础设施换取覆盖率;如今用一个索引文件和按需 Read,因为纪律比基础设施更重要。
0. 记忆是心智的延伸
如果人格是「这个协作者是谁」,记忆就是「这个协作者当前心里有什么」。
每次蒸发的上下文迫使操作者每天付出重建同一套心智的代价。昨天决定了什么、为何决定、什么没起效——如果这一切在下一次会话开始时都消失了,操作者每天早上都要重建昨天那部分心智。
记忆不是 RAG,不是向量数据库。它是防止操作者的决策、反馈和上下文在下一次会话中丢失的最简设备。
从第 1 节起,本篇涵盖那个设备是如何构建的——用几个 Markdown 文件和一个单一索引。
1. 简单的决定 — Markdown 文件
我收敛出来的结论:
memory/
├── MEMORY.md # 索引(自动加载)
├── user_role.md # 用户类型
├── feedback_testing.md # 反馈类型
├── project_q2_release.md # 项目类型
├── reference_grafana.md # 引用类型
└── ...- 一条记忆 = 一个文件
- 一个索引(
MEMORY.md),每个文件一行指针 - 每个文件都是 Markdown,人类可直接阅读
- 工具在会话开始时自动加载索引的前 N 行
就这些。没有花哨的基础设施。用 git 管理,用文本编辑器编辑,用 grep 搜索。
你每天实际积累的上下文出人意料地少。纪律比基础设施更重要。
2. 四种记忆类型
并非所有信息都被当作同一类型处理。我拆分为四种。
2.1 user — 关于操作者的事实
操作者是谁、扮演什么角色、了解哪些领域。
---
name: user-role-and-expertise
description: Operator's role · expertise · platforms
metadata:
type: user
---
Operator is a senior engineer, primary languages X·Y·Z, mostly works on A·B.
Frontend is recent — explain frontend concepts via backend analogies.这种类型最大的收益是回答会根据操作者的水平和领域进行校准。学生语气消失了,操作者已经知道的内容不再被重复解释。
2.2 feedback — 明确指导
来自操作者的明确指导。包括"不要做这个"和"继续做这个"。
---
name: integration-test-policy
description: Integration tests must run against a real DB — no mocks
metadata:
type: feedback
---
Run integration tests against the real DB, not a mocked one.
**Why:** Last quarter the mocked test passed but the prod migration broke.
**How to apply:** Every test under integration/. unit/ may still mock.对于反馈类型,始终在规则旁边写上 Why 和 How to apply。没有理由的规则在边缘情况下会失效。有了理由,同样的意图才能被带入新情况。
反馈既记录失败后的纠正,也记录成功后的验证。第二种很容易被跳过,但跳过意味着在下一次会话中要重新协商同一个好决策。
2.3 project — 进行中工作的上下文
关于当前工作的事实。最易变的类型。
---
name: q2-merge-freeze
description: Merge freeze schedule for Q2 mobile release cut
metadata:
type: project
---
Merge freeze begins 2026-03-05 (mobile release branch cut).
**Why:** Mobile team is cutting their release branch.
**How to apply:** Flag non-critical PR work scheduled after that date.关键是把相对时间转换为绝对时间。「下周」或「周四」这样的表达在进入记忆几天后就失去意义。转换并记录为「2026-03-05」这样的绝对日期。
project 类型过时最快,所以也是修剪最多的(见第 5 节)。
2.4 reference — 指向外部系统的指针
指向信息所在位置的指针。不是信息本身。
---
name: oncall-latency-dashboard
description: Grafana board grafana.internal/d/api-latency — the oncall latency dashboard
metadata:
type: reference
---
Dashboard to check when touching request-handling code.
Oncall watches this; it's what pages someone, so compare before/after on changes.reference 记录某样东西在外部系统(Linear、Grafana、Notion、Slack 等)中某个位置是什么。信息本身会变;位置是稳定的。记住位置可以免去每次重新搜寻之苦。
3. 索引 — 单一的 MEMORY.md
每个记忆文件都通过一个索引被发现。
# Memory
> First 200 lines auto-load. Core rules live in the persona; this is pointers only.
## Memory file index
### Rules / procedures
- [work-process.md](rules/work-process.md) — work intake procedure
- [multi-agent.md](rules/multi-agent.md) — subagent delegation, Quality Gate
- ...
### Feedback / references
- [feedback/](feedback/) — testing / model_policy / env_minimalism / ...
- [reference/dashboards.md](reference/dashboards.md) — oncall dashboard index索引不是记忆。它是记忆的一行挂钩集合。索引本身从不携带内容——只有指针。
这个拆分让索引保持简短。即使工具在会话开始时只自动加载前 N 行,每条记忆的挂钩都能装进去。当特定记忆变得相关时,只有那一个文件在那一刻被 Read。
索引加上触发式 Read,比 RAG 更简单地覆盖了大多数情况。
4. 自动加载 vs 按需加载 — 令牌经济
这个系统结合了两种加载模式。
| 模式 | 进入什么 | 何时 |
|---|---|---|
| 自动加载 | MEMORY.md 的前 N 行 | 每次会话开始 |
| 按需 Read | 索引指向的特定文件 | 进入与该文件相关的工作时 |
这个拆分是令牌经济的核心。每次会话读取所有记忆文件会让令牌爆炸。只自动加载索引,只在需要时 Read 正文,几乎每次会话都以极轻量的状态开始。
两个触发器决定何时 Read 正文。
- 操作者明确引用它 — 「看一下集成测试策略」
- 工作本身进入相关领域 — 开始 DB 相关的 PR 会拉入 DB 相关的记忆
第二种更重要。操作者不必每次都说「看这个策略」——协作者会自行拉入相关记忆。索引中的一行 description 是做出那个判断的依据。
5. 修剪 — 只增不减的记忆会崩溃
记忆系统最大的陷阱是只积累不修剪。纯粹的积累会产生两个问题。
- 过时信息继续存活 — 一个月前结束的项目的排期仍在记忆中,将判断拉向错误方向
- 索引膨胀 — 条目增长超过 200 行的自动加载范围,自动加载就失去了意义
我运行以下修剪模式。
| 类型 | 修剪频次 | 标准 |
|---|---|---|
user | 几乎从不 | 身份变化缓慢 |
feedback | 每季度一次 | 删除不再适用的指导 |
project | 每月一次 | 将已关闭的项目移至归档目录 |
reference | 每半年一次 | 删除失效链接和已消失的系统 |
归档不是删除。移到 memory/archived/ 这样的独立目录下。它们从索引中消失,但留在 git 历史和磁盘上,需要时可以拉回来。
还有一个模式——自动审计工具。它定期扫描记忆目录,生成「该条目 60 天以上未被引用」这样的报告。操作者阅读那份报告,快速做出修剪决策。
6. 失败模式 — 我尝试过的每一种
走到这个系统之前,我尝试过并放弃了的模式。
6.1 单一巨型文件
最初我把所有记忆放进一个文件。一个 notes.md。几天内就超过了 1000 行,工具几乎把所有会话开始令牌都花在读它上面。
→ 教训:记忆必须拆分。索引 + 正文文件分离。
6.2 拆分过细
相反的情况——拆分过细。每个决策一个文件。出现了 50 个文件,索引膨胀,耗尽了整个自动加载窗口。
→ 教训:一个文件 = 一个「主题」,而不是一个「决策」。同一主题的多个决策放进同一个文件。
6.3 无差别积累
我尝试了一个自动化:「在会话结束时总结今天的工作并追加到记忆。」记忆每天都在增长,重要决策淹没在噪声里。
→ 教训:进入记忆的是在下一次会话中仍有用的决策、事实或指针。仅限今天的进度放到任务板或历史日志里,而不是记忆里。
6.4 没有元数据的纯文本
我一开始用纯 Markdown,没有 frontmatter。随着数量增长,「这是哪种类型」和「为何写这个」变得无法追踪。
→ 教训:强制至少三个 frontmatter 字段——name、description、type。正文里再加两行——Why 和 How to apply。
7. 实际效果
采用这个系统后发生了什么变化。
| 条目 | 之前 | 之后 |
|---|---|---|
| 会话开始时的上下文恢复 | 5—10 分钟 | 即时(索引自动加载) |
| 重新谈判同一决策 | 经常 | 几乎不 |
| 重新解释昨天的工作 | 每次 | 从不(在记忆里) |
| 来自过时信息的错误判断 | 有时 | 罕见(修剪) |
| 令牌消耗模式 | 每次会话平均偏高 | 与工作成比例(浅层保持轻量) |
最大的变化是最后一行。令牌消耗跟踪工作的深度。浅层工作几乎免费完成;资源流向深层工作。
8. 压缩成一条原则
记忆系统设计压缩成一条原则:
「索引总是在场。正文只在需要时才进入。不再有效的内容从索引中退出。」
三条都成立时,记忆就不再是日益增长的负担,而是加速工作的资产。
下一篇涵盖人格和记忆所指向的下一层——Skills 与 slash commands,即哪些重复值得提升为一行调用。