Memory System - 跨会话认知积累
L09 要回答一个问题:如何在 L08 的基础上实现跨会话认知积累,而不让 Memory 变成垃圾堆?
L08 给了我们 Hook 管道:SessionStart + PreToolUse + PostToolUse + 统一返回协议。但每次会话结束,所有认知丢失——用户的偏好、团队的设计约定、重要的正负反馈。下次会话又从零开始,重复学习。怎么做?
本章讲三个机制:
| 机制 | 解决什么问题 | 核心设计 |
|---|---|---|
| 四种类型边界 | 存什么不存什么 | user/feedback/project/reference + 不存清单 |
| 同步更新机制 | Memory 立刻生效 | save_memory → 写文件 → 更新字典 → 重建索引 |
| 漂移防护 | Memory 过时处理 | 方向提示非绝对答案 + 验证当前状态 |
这三个机制叠加在 L08 的 Hook System 上——SessionStart Hook 加载记忆,PostToolUse Hook 记录关键信息。循环本身不变。理解 L09,就理解了 Agent 如何”跨会话积累认知不丢失”。
一、Memory 本质:存不可推导的信息
“Memory 是跨会话有价值且不可重新推导的信息持久化。”
这句话概括了 Memory System 的本质。不是所有信息都存——只存”以后还可能有价值、但当前代码里不容易直接重新看出来”的信息。
Memory vs Context Compact
很多人混淆 Memory 和 Context Compact:
| 机制 | 解决什么问题 | 生命周期 | 位置 |
|---|---|---|---|
| Memory | 跨会话认知积累 | 持久化(写文件) | System prompt(每轮新建) |
| Context Compact | 会话内上下文预算 | 临时(不写文件) | Messages[](可被压缩) |
核心区别:
- Memory:解决持久化问题——会话结束后仍保留。
- Context Compact:解决临时容量问题——会话内上下文爆炸。
位置差异:
- Memory 在 System prompt 中,每轮新建,不被 Compact 影响。
- Context Compact 在 Messages[] 中,压缩 tool_result。
设计张力:持久化问题用 Memory,临时容量问题用 Compact。两套机制各有边界,互不干扰。
验证你的理解
Q1:Context Compact 会压缩 Memory 吗?为什么?
:::details 答案 不会。Memory 在 System prompt 中,不在 Messages[]。
Context Compact 只压缩 Messages[] 中的 tool_result——替换为占位符或生成摘要。
System prompt 每轮新建——build_system_prompt() 重新生成 memory_section,注入 System prompt。System prompt 不累积追加,不会被 Compact。
设计理由:Memory 是持久化认知积累,应该每轮可见。如果放在 Messages[],会被 Compact 压缩,导致模型看不到历史记忆。 :::
二、四种类型边界:存什么不存什么
Memory 本质解决了”为什么需要 Memory”,四种类型解决”存什么”。
四种类型定义
| 类型 | 定义 | 典型内容 | 判断法 |
|---|---|---|---|
| user | 个人偏好,无法从代码推导 | ”我喜欢简洁回答” | 问:这是个人偏好吗? |
| feedback | 用户明确纠正或认可 | ”不要这样改” | 问:用户明确表达了吗? |
| project | 团队约定或设计原因 | ”commit 必须带 ticket” | 问:是团队约定吗? |
| reference | 外部资源指针 | ”bug 在 Linear 看板” | 问:信息在项目外吗? |
关键点:user 是个人偏好,project 是团队约定。不要混淆。
案例对比:
| 信息 | 类型 | 理由 |
|---|---|---|
| ”我喜欢简洁回答” | user | 个人偏好,代码看不出用户喜欢什么 |
| ”团队约定 commit 必须带 ticket ID” | project | 团队规则,代码看不出约定原因 |
| ”不要用 rm -rf,上次差点删了项目” | feedback | 用户明确纠正,防止再犯 |
| ”bug 列表在 Linear 看板” | reference | 信息在项目外,代码看不出看板地址 |
不存清单:能从代码看出来就不存
不是所有信息都该存 Memory。有明确边界:
| 类别 | 不存内容 | 原因 |
|---|---|---|
| 代码结构 | 文件路径、模块关系、import 链 | 能从代码直接看出来 |
| 当前任务 | todo、计划、临时上下文 | 会过期,下次任务不同 |
| 密钥凭证 | API key、密码、token | 安全风险,不该持久化 |
核心原则:能从代码直接看出来就不存。
反例分析:
| 信息 | 是否存 Memory? | 理由 |
|---|---|---|
| ”src/auth.py 是认证模块入口” | ❌ 不存 | import 关系能从代码看出,存了会有漂移风险(文件改名后 Memory 过时) |
| “认证模块设计原因是支持多租户” | ✅ 存 project | 代码看不出设计原因,存决策追溯有价值 |
| ”用户说记住密码是 abc123” | ❌ 不存 | 密钥不该持久化,安全风险 |
四类型判断流程
验证你的理解
Q2:用户说”记住 src/auth.py 是认证模块入口”,应该存吗?
:::details 答案 不应该存。
理由:
- 文件路径可从代码直接看出(看 import 关系)——能从代码看出来就不存。
- 如果文件改名,Memory 会过时——造成漂移风险。
- Reference 类型是外部资源指针(看板 URL),不是代码内部路径。
正确处理:不存 Memory。模型每次从代码 import 关系推断认证模块位置。
如果存的是”认证模块设计原因是支持多租户”,应该存——代码看不出设计原因,存决策追溯有价值。 :::
三、同步更新机制:save_memory 立刻生效
Memory 不是异步加载——save_memory 调用后立刻生效。
save_memory 流程
def save_memory(name: str, content: str, type_: str) -> str: # 1. 写文件 path = MEMORY_DIR / f"{name}.md" frontmatter = f"---\nname: {name}\ntype: {type_}\n---\n" path.write_text(frontmatter + content)
# 2. 更新字典 memories[name] = {"type": type_, "content": content}
# 3. 重建索引 rebuild_index()
return f"Memory '{name}' saved."三步同步:
| 步骤 | 操作 | 结果 |
|---|---|---|
| 写文件 | 写入 .memory/{name}.md | 持久化保存 |
| 更新字典 | memories[name] = ... | 缓存更新 |
| 重建索引 | 更新 MEMORY.md | 索引刷新 |
关键点:save_memory 返回前已完成三件事——下一轮 build_system_prompt 直接从字典读取。
Memory 注入流程
流程说明:
- 会话初始化:load_all 扫描
.memory目录,填充 memories 字典。 - Agent Loop:build_system_prompt 从字典读取,生成 memory_section,注入 System prompt。
- 保存 Memory:save_memory 写文件 + 更新字典 + 重建索引——下一轮 Loop 可见。
验证你的理解
Q3:save_memory 调用后,Memory 什么时候被模型看到?
:::details 答案 下一轮 build_system_prompt 时看到。
同步更新机制:save_memory 返回前已完成三件事:
- 写文件(持久化)
- 更新 memories 字典(缓存)
- 重建索引(刷新 MEMORY.md)
下一轮 agent_loop 入口,build_system_prompt 从 memories 字典读取——直接看到新 Memory,不需要重新加载文件。
不是异步加载——save_memory 立刻更新缓存,下一轮可见。 :::
四、漂移防护:Memory 是方向提示
Memory 是过去快照,可能过时——漂移风险。
漂移本质
| 类比 | 含义 |
|---|---|
| Memory | 地图——可能过时,给方向提示 |
| 当前代码 | 实际地形——真实状态,优先相信 |
漂移风险:Memory 记录的文件路径、模块结构可能过时——文件改名、重构后 Memory 失效。
案例:
Memory: "认证模块在 src/auth.py"实际状态: 文件已迁移到 src/core/auth.py结果: Memory 过时,模型按旧路径查找会失败漂移处理流程
处理原则:
- 方向提示:Memory 给方向,不当作绝对答案。
- 验证当前状态:使用 Memory 前读当前文件/配置。
- 冲突优先相信代码:Memory vs 当前状态不一致,优先相信代码。
为什么优先相信代码?
代码是真实状态——文件改名后 Memory 过时,但代码路径是当前正确的。Memory 是历史快照,代码是实时状态。
验证你的理解
Q4:Memory 说”认证模块在 src/auth.py”,但实际文件已迁移到 src/core/auth.py,怎么处理?
:::details 答案 优先相信当前代码状态。
处理步骤:
- Memory 给方向提示——“认证模块存在”
- 读当前代码结构——发现文件在 src/core/auth.py
- Memory vs 当前状态冲突——路径不一致
- 优先相信当前状态——使用 src/core/auth.py
- 给用户结论时再验证一次——确认认证模块确实在新位置
不建议更新 Memory——文件路径可从代码看出,不该存 Memory。正确的 Memory 应存”认证模块设计原因”,不是路径。 :::
五、Dream Consolidator:防止垃圾堆化
Memory 如果不清理,会堆积过期内容——垃圾堆化。Dream Consolidator 是后台清理机制。
7 Gates 检查
Dream Consolidator 触发前,先过 7 Gates:
| Gate | 检查内容 | 阻止触发的原因 |
|---|---|---|
| G1 | enabled 开关 | 功能未启用 |
| G2 | 目录存在 | Memory 目录不存在 |
| G3 | 非 plan 模式 | plan 模式只读,不执行后台任务 |
| G4 | 24h 冷却 | 频率过高,等待冷却 |
| G5 | 10min 间隔 | 上次运行不到 10 分钟 |
| G6 | ≥5 sessions | 数据积累不够,等待更多会话 |
| G7 | 锁释放 | 并发安全,避免同时运行 |
Gate 分类:
| 类别 | Gates | 作用 |
|---|---|---|
| 功能开关 | G1-3 | 基础条件检查 |
| 频率控制 | G4-6 | 防止过度清理 |
| 并发安全 | G7 | 防止重复运行 |
4 Phases 执行
通过 7 Gates 后,执行 4 Phases:
| Phase | 操作 | 说明 |
|---|---|---|
| Phase 1: Orient | 扫描索引 | 识别候选 Memory |
| Phase 2: Gather | 读取文件 | 加载候选 Memory 内容 |
| Phase 3: Consolidate | 合并删除 | 合并相似 Memory,删除过期内容 |
| Phase 4: Prune | 限制 200 行 | 索引截断,防止过长 |
Phase 4 200 行限制:
MEMORY.md 索引限制 200 行——给人阅读定位,不限制注入量。
load_memory_prompt() 加载所有 Memory 文件,不受索引截断限制。真正限制注入量的是 Dream Consolidator 整合机制。
Dream Consolidator 流程
设计理由:
- Gate 6 为什么需要 ≥5 sessions?数据积累不够——清理太早会丢失有价值 Memory。等待更多会话,更准确判断哪些 Memory 真正过期。
- Gate 模式迁移:后台任务防护——前置条件 + 频率限制 + 并发保护。可用于其他后台清理机制(如数据库日志清理)。
验证你的理解
Q5:Gate 6 为什么需要 ≥5 sessions 才触发 Dream Consolidator?
:::details 答案 数据积累不够——清理太早会误删有价值 Memory。
理由:
- 少于 5 次会话——Memory 数量少,判断”过期”的依据不足
- 等待更多会话——更准确判断哪些 Memory 真正被频繁使用,哪些确实过期
- 防止过度清理——Dream Consolidator 是删除机制,误删代价高
Gate 模式迁移:后台任务防护——前置条件(G1-3)+ 频率控制(G4-6)+ 并发安全(G7)。可用于其他后台清理机制(如数据库日志清理、缓存过期检查)。 :::
六、回顾:L09 在循环上叠加了什么
回到开头的问题:如何在 L08 的基础上实现跨会话认知积累,而不让 Memory 变成垃圾堆?
答案:
| 叠加机制 | 代码位置 | 循环的变化 |
|---|---|---|
| Memory 加载 | SessionStart Hook | 不变——只是 Hook 触发加载 |
| Memory 注入 | build_system_prompt | 不变——只是 System prompt 内容扩展 |
| Memory 保存 | save_memory 工具 | 不变——只是 dispatch map 加一项 |
| Dream Consolidator | 后台异步 | 不变——主循环不参与清理 |
循环本身仍是 L01 的 while True + stop_reason + messages[]。L09 只是在”持久化层”叠加了 Memory System。
与后续课程的关系
理解 L09 后,你会发现后续课程都在 Memory System 上叠加——循环本身始终不变:
| 课程 | 关系 |
|---|---|
| L08 Hook System | SessionStart Hook 加载记忆,PostToolUse Hook 记录关键信息 |
| L10 System Prompt | Memory 在 System prompt 中注入,不累积追加 |
| L11 Background Tasks | Dream Consolidator 是后台任务,Gate 模式迁移 |
核心洞察:Memory System 是”跨会话认知积累”的机制。理解 L09,就理解了 Agent 如何”持久化有价值信息不丢失”。后续课程都在扩展 Memory——System Prompt 设计、后台任务清理。
七、心智模型升级:从 L0 到 L2
读完这篇文章,你的理解经历了怎样的升级?
L09 核心概念的理解升级路径
| 概念 | L0(表面) | L1(关联) | L2(深层) |
|---|---|---|---|
| Memory 本质 | ”跨会话保存信息" | "持久化有价值信息" | "存不可推导——能从代码直接看出来就不存。vs Context Compact:持久化问题用 Memory,临时容量问题用 Compact。Memory 在 System prompt 不被压缩,Compact 在 Messages[] 压缩 tool_result” |
| 四种类型 | ”user/feedback/project/reference" | "个人偏好/纠正/约定/外部" | "边界清晰——user 是个人偏好(代码看不出用户喜欢什么),project 是团队约定(代码看不出约定原因)。判断法:问’能从代码看出来吗?’ + 问’是个人/团队/外部?‘“ |
| 同步更新 | ”保存后生效" | "写文件 + 更新字典" | "save_memory 返回前已完成三件事——写文件(持久化)+ 更新字典(缓存)+ 重建索引(刷新)。下一轮 build_system_prompt 从字典读取,不需要重新加载文件。同步更新机制——立刻生效” |
| 漂移防护 | ”Memory 可能过时" | "优先相信当前状态" | "地图 vs 地形类比——Memory 是地图(方向提示),当前代码是地形(真实状态)。冲突时优先相信代码——代码是实时状态,Memory 是历史快照。使用前验证,给用户结论时再验证一次” |
| Dream Consolidator | ”后台清理机制" | "7 Gates + 4 Phases" | "Gate 模式——后台任务防护:前置条件(G1-3)+ 频率控制(G4-6)+ 并发安全(G7)。Gate 6 需要 ≥5 sessions:数据积累不够,清理太早会误删有价值 Memory。Phase 4 200 行限制:给人阅读定位,不限制注入量” |
如何检验你达到了 L2?
用这个问题自测:
用户说”团队约定所有 commit 必须带 ticket ID,否则 CI 会失败”,应该存 Memory 吗?存什么类型?
:::details L2 层级回答 应该存 Memory,类型是 project。
分析:
- 判断是否存 Memory:团队约定不能从代码直接看出——代码看不出”为什么必须带 ticket ID”,存决策追溯有价值。
- 判断类型:不是个人偏好(user),不是用户纠正(feedback),不是外部资源(reference)——是团队约定,属于 project 类型。
- 存储内容:“commit 必须带 ticket ID,否则 CI 会失败”——约定 + 原因。不存”commit 规范”,那是 Task/Plan,会过期。
注意边界:
- 如果存”commit 规范模板”,不该存 Memory——那是临时任务规则,会过期。
- 如果存”CI 配置文件路径”,不该存 Memory——文件路径可从代码看出,存了会有漂移风险。 :::
本章目标:让每个核心概念都从 L0 升级到 L2。理解 L09,不只是记住四种类型,而是掌握”如何判断什么该存什么不该存”的设计原则。
八、复习可视化
Memory 生命周期
四类型判断流程
漂移处理流程
Dream Consolidator 流程
Memory vs Context Compact
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!