Memory System - 跨会话认知积累

4775 字
24 分钟
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”❌ 不存密钥不该持久化,安全风险

四类型判断流程#

flowchart TD A[信息出现] --> B{只对这次任务有用?} B -->|是| C[Task/Plan<br/>不存Memory] B -->|否| D{能从代码直接看出来?} D -->|是| E[❌ 不存] D -->|否| F{是项目级固定规则?} F -->|是| G[CLAUDE.md<br/>不是Memory] F -->|否| H[Memory] H --> I{判断类型} I --> J{个人偏好?} J -->|是| K[user类型] J -->|否| L{用户明确纠正/认可?} L -->|是| M[feedback类型] L -->|否| N{团队约定/设计原因?} N -->|是| O[project类型] N -->|否| P{外部资源指针?} P -->|是| Q[reference类型] style E fill:#ff6b6b,color:#fff style K fill:#45b7d1,color:#fff style M fill:#45b7d1,color:#fff style O fill:#45b7d1,color:#fff style Q fill:#45b7d1,color:#fff

验证你的理解#

Q2:用户说”记住 src/auth.py 是认证模块入口”,应该存吗?

:::details 答案 不应该存。

理由:

  1. 文件路径可从代码直接看出(看 import 关系)——能从代码看出来就不存。
  2. 如果文件改名,Memory 会过时——造成漂移风险。
  3. 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 注入流程#

flowchart TD subgraph Init["会话初始化"] A1[会话开始] --> A2[load_all] A2 --> A3[扫描 .memory 目录] A3 --> A4[解析 .md 文件] A4 --> A5[填充 memories 字典] end subgraph Loop["Agent Loop"] B1[agent_loop 入口] --> B2[build_system_prompt] B2 --> B3[生成 memory_section] B3 --> B4[注入 System prompt] B4 --> B5[LLM 调用] end subgraph Save["保存 Memory"] C1[用户请求记住] --> C2[调用 save_memory] C2 --> C3[写入 .md 文件] C3 --> C4[更新 memories 字典] C4 --> C5[rebuild_index] C5 --> C6[返回 tool_result] end A5 --> B1 B5 --> C1 C6 --> B1 style A5 fill:#e8f5e9,color:#000 style B4 fill:#fff3e0,color:#000 style C4 fill:#e8f5e9,color:#000

流程说明

  • 会话初始化: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 返回前已完成三件事:

  1. 写文件(持久化)
  2. 更新 memories 字典(缓存)
  3. 重建索引(刷新 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 过时,模型按旧路径查找会失败

漂移处理流程#

flowchart TD A[使用 Memory] --> B[当作方向提示] B --> C[读当前文件/配置] C --> D{Memory vs 当前状态冲突?} D -->|是| E[优先相信当前状态] D -->|否| F[正常使用 Memory] E --> G[给用户结论时再验证一次] style E fill:#e8f5e9,color:#000 style G fill:#e8f5e9,color:#000

处理原则

  • 方向提示:Memory 给方向,不当作绝对答案。
  • 验证当前状态:使用 Memory 前读当前文件/配置。
  • 冲突优先相信代码:Memory vs 当前状态不一致,优先相信代码。

为什么优先相信代码?

代码是真实状态——文件改名后 Memory 过时,但代码路径是当前正确的。Memory 是历史快照,代码是实时状态。

验证你的理解#

Q4:Memory 说”认证模块在 src/auth.py”,但实际文件已迁移到 src/core/auth.py,怎么处理?

:::details 答案 优先相信当前代码状态。

处理步骤:

  1. Memory 给方向提示——“认证模块存在”
  2. 读当前代码结构——发现文件在 src/core/auth.py
  3. Memory vs 当前状态冲突——路径不一致
  4. 优先相信当前状态——使用 src/core/auth.py
  5. 给用户结论时再验证一次——确认认证模块确实在新位置

不建议更新 Memory——文件路径可从代码看出,不该存 Memory。正确的 Memory 应存”认证模块设计原因”,不是路径。 :::

五、Dream Consolidator:防止垃圾堆化#

Memory 如果不清理,会堆积过期内容——垃圾堆化。Dream Consolidator 是后台清理机制。

7 Gates 检查#

Dream Consolidator 触发前,先过 7 Gates:

Gate检查内容阻止触发的原因
G1enabled 开关功能未启用
G2目录存在Memory 目录不存在
G3非 plan 模式plan 模式只读,不执行后台任务
G424h 冷却频率过高,等待冷却
G510min 间隔上次运行不到 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 流程#

flowchart TD subgraph Gates["7 Gates 检查"] G1[Gate 1: enabled开关] --> G2[Gate 2: 目录存在] G2 --> G3[Gate 3: 非plan模式] G3 --> G4{Gate 4: 24h冷却} G4 -->|通过| G5{Gate 5: 10min间隔} G4 -->|未通过| E1[等待冷却] G5 -->|通过| G6{Gate 6: ≥5 sessions} G5 -->|未通过| E2[等待间隔] G6 -->|通过| G7{Gate 7: 锁释放} G6 -->|未通过| E3[等待数据积累] G7 -->|通过| P1 G7 -->|未通过| E4[等待锁释放] end subgraph Phases["4 Phases 执行"] P1[Phase 1: Orient扫描索引] --> P2[Phase 2: Gather读取文件] P2 --> P3[Phase 3: Consolidate合并删除] P3 --> P4[Phase 4: Prune限制200行] end style G1 fill:#e8f5e9,color:#000 style G2 fill:#e8f5e9,color:#000 style G3 fill:#e8f5e9,color:#000 style G4 fill:#fff3e0,color:#000 style G5 fill:#fff3e0,color:#000 style G6 fill:#fff3e0,color:#000 style G7 fill:#fff3e0,color:#000 style P1 fill:#e8f5e9,color:#000 style P2 fill:#e8f5e9,color:#000 style P3 fill:#e8f5e9,color:#000 style P4 fill:#e8f5e9,color:#000

设计理由

  • Gate 6 为什么需要 ≥5 sessions?数据积累不够——清理太早会丢失有价值 Memory。等待更多会话,更准确判断哪些 Memory 真正过期。
  • Gate 模式迁移:后台任务防护——前置条件 + 频率限制 + 并发保护。可用于其他后台清理机制(如数据库日志清理)。

验证你的理解#

Q5:Gate 6 为什么需要 ≥5 sessions 才触发 Dream Consolidator?

:::details 答案 数据积累不够——清理太早会误删有价值 Memory。

理由:

  1. 少于 5 次会话——Memory 数量少,判断”过期”的依据不足
  2. 等待更多会话——更准确判断哪些 Memory 真正被频繁使用,哪些确实过期
  3. 防止过度清理——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 SystemSessionStart Hook 加载记忆,PostToolUse Hook 记录关键信息
L10 System PromptMemory 在 System prompt 中注入,不累积追加
L11 Background TasksDream 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。

分析:

  1. 判断是否存 Memory:团队约定不能从代码直接看出——代码看不出”为什么必须带 ticket ID”,存决策追溯有价值。
  2. 判断类型:不是个人偏好(user),不是用户纠正(feedback),不是外部资源(reference)——是团队约定,属于 project 类型。
  3. 存储内容:“commit 必须带 ticket ID,否则 CI 会失败”——约定 + 原因。不存”commit 规范”,那是 Task/Plan,会过期。

注意边界:

  • 如果存”commit 规范模板”,不该存 Memory——那是临时任务规则,会过期。
  • 如果存”CI 配置文件路径”,不该存 Memory——文件路径可从代码看出,存了会有漂移风险。 :::

本章目标:让每个核心概念都从 L0 升级到 L2。理解 L09,不只是记住四种类型,而是掌握”如何判断什么该存什么不该存”的设计原则。

八、复习可视化#

Memory 生命周期#

flowchart TD subgraph Init["会话初始化"] A1[会话开始] --> A2[load_all] A2 --> A3[扫描 .memory 目录] A3 --> A4[解析 .md 文件] A4 --> A5[填充 memories 字典] end subgraph Loop["Agent Loop"] B1[agent_loop 入口] --> B2[build_system_prompt] B2 --> B3[生成 memory_section] B3 --> B4[注入 System prompt] B4 --> B5[LLM 调用] end subgraph Save["保存 Memory"] C1[用户请求记住] --> C2[调用 save_memory] C2 --> C3[写入 .md 文件] C3 --> C4[更新 memories 字典] C4 --> C5[rebuild_index] C5 --> C6[返回 tool_result] end A5 --> B1 B5 --> C1 C6 --> B1 style A5 fill:#e8f5e9,stroke:#333 style B4 fill:#fff3e0,stroke:#333 style C4 fill:#e8f5e9,stroke:#333

四类型判断流程#

flowchart TD A[信息出现] --> B{只对这次任务有用?} B -->|是| C[Task/Plan<br/>不存Memory] B -->|否| D{能从代码直接看出来?} D -->|是| E[❌ 不存] D -->|否| F{是项目级固定规则?} F -->|是| G[CLAUDE.md<br/>不是Memory] F -->|否| H[Memory] H --> I{判断类型} I --> J{个人偏好?} J -->|是| K[user类型] J -->|否| L{用户明确纠正/认可?} L -->|是| M[feedback类型] L -->|否| N{团队约定/设计原因?} N -->|是| O[project类型] N -->|否| P{外部资源指针?} P -->|是| Q[reference类型] style E fill:#ff6b6b,color:#fff style K fill:#45b7d1,color:#fff style M fill:#45b7d1,color:#fff style O fill:#45b7d1,color:#fff style Q fill:#45b7d1,color:#fff

漂移处理流程#

flowchart TD A[使用 Memory] --> B[当作方向提示] B --> C[读当前文件/配置] C --> D{Memory vs 当前状态冲突?} D -->|是| E[优先相信当前状态] D -->|否| F[正常使用 Memory] E --> G[给用户结论时再验证一次] style A fill:#fff3e0,stroke:#333 style E fill:#e8f5e9,stroke:#333 style G fill:#e8f5e9,stroke:#333

Dream Consolidator 流程#

flowchart TD subgraph Gates["7 Gates 检查"] G1[Gate 1: enabled开关] --> G2[Gate 2: 目录存在] G2 --> G3[Gate 3: 非plan模式] G3 --> G4{Gate 4: 24h冷却} G4 -->|通过| G5{Gate 5: 10min间隔} G4 -->|未通过| E1[等待冷却] G5 -->|通过| G6{Gate 6: ≥5 sessions} G5 -->|未通过| E2[等待间隔] G6 -->|通过| G7{Gate 7: 锁释放} G6 -->|未通过| E3[等待数据积累] G7 -->|通过| P1 G7 -->|未通过| E4[等待锁释放] end subgraph Phases["4 Phases 执行"] P1[Phase 1: Orient扫描索引] --> P2[Phase 2: Gather读取文件] P2 --> P3[Phase 3: Consolidate合并删除] P3 --> P4[Phase 4: Prune限制200行] end style G1 fill:#e8f5e9,stroke:#333 style G2 fill:#e8f5e9,stroke:#333 style G3 fill:#e8f5e9,stroke:#333 style G4 fill:#fff3e0,stroke:#333 style G5 fill:#fff3e0,stroke:#333 style G6 fill:#fff3e0,stroke:#333 style G7 fill:#fff3e0,stroke:#333 style P1 fill:#e8f5e9,stroke:#333 style P2 fill:#e8f5e9,stroke:#333 style P3 fill:#e8f5e9,stroke:#333 style P4 fill:#e8f5e9,stroke:#333

Memory vs Context Compact#

flowchart LR subgraph Memory["Memory System"] M1[跨会话持久化] --> M2[写文件] M2 --> M3[System prompt] M3 --> M4[每轮新建] M4 --> M5[不被Compact] end subgraph Compact["Context Compact"] C1[会话内临时] --> C2[不写文件] C2 --> C3[Messages] C3 --> C4[累积追加] C4 --> C5[可被压缩] end style M5 fill:#e8f5e9,stroke:#333 style C5 fill:#fff3e0,stroke:#333

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!

赞助
Memory System - 跨会话认知积累
https://firefly.cuteleaf.cn/posts/learn-claude-code/09-memory-system/
作者
AltumSisy
发布于
2026-04-18
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
AltumSisy
Hello, I'm AltumSisy.
公告
欢迎来到我的博客!这是一则示例公告。
音乐
封面

音乐

暂未播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章
32
分类
3
标签
25
总字数
68,347
运行时长
0
最后活动
0 天前

文章目录