在这里插入图片描述

一、导读

关联文章:
Claude Code 源码解构 :如何生成深度摘要(一)

Claude Code 的记忆系统并非简单的日志堆砌,而是一个由后台子代理(Forked Agent)自动运转的三层记忆流水线。它巧妙解决了 AI 编程中常见的“聊得越多忘得越快”的问题。核心要点如下:

  • 短期记忆(Session Memory):像一个实时的会议记录员,在后台默默写笔记,让 AI 在清理冗长对话时不会“断片”。
  • 中期记忆(Auto Memory):每轮对话结束后,自动提取你的偏好、踩过的坑等“代码里找不到的经验”,并长期保存。
  • 长期记忆(Auto Dream):类似于人类的睡眠机制,定期在后台清理过期信息、合并重复内容,防止记忆越来越臃肿。
  • 底层魔法:它利用了“提示词缓存(Prompt Cache)”技术,让这些后台动作几乎不花额外的 Token,也不影响你的操作速度。

二、记忆四类型系统

Claude Code 的记忆系统遵循一个核心原则:只保存不可从当前项目状态推导的信息。

Claude Code 的记忆系统服务于 4 种类型:用户偏好(user)、反馈修正(feedback)、项目信息(project)、外部引用(reference)。每种类型有独立的存储文件和触发条件。这种“闭合系统”的设计逻辑是为了防止类型爆炸(Type Explosion)检索歧义

类型 核心功能 解决的问题
user 沟通风格 解决“沟通风格”问题,决定输出是专业还是通俗。
feedback 行为对齐 解决“对齐”问题,防止 Agent 在同一个错误上犯两次。
project 决策上下文 解决“决策上下文”问题,让 Agent 理解代码背后的动机。
reference 工具链打通 解决“工具链打通”问题,连接外部生态。

实战练习:记忆类型分类
以下信息应该保存为哪种记忆类型?

  • “我们团队使用 Linear 项目 ‘BACKEND’ 来追踪后端 Bug” —— reference(外部系统指针)
  • “用户是初级开发者,第一次使用 TypeScript” —— user(用户画像)
  • “集成测试必须使用真实数据库,不要 mock —— 上次 mock 导致生产事故” —— feedback(行为指导,包含 Why: 生产事故)
  • “认证中间件重写是因为法律合规要求,不是技术债” —— project(项目决策,包含 Why: 法律合规)
  • “API 文档的 Swagger UI 在 http://localhost:3000/api-docs” —— reference(外部引用)
  • “每次创建新组件时,先写测试再写实现” —— feedback(行为指导,包含明确的规则和隐含的原因)

有意思地方:Feedback 类型的正负反馈平衡
其中最有意思的设计在 feedback 类型上。大多数 AI 系统只记“负反馈”——你骂它“别 mock 数据库!”,它记住了。但 Claude Code 的 Prompt 里明确要求正面反馈也要记

“Record from failure AND success: if you only save corrections, you will avoid past mistakes but drift away from approaches the user has already validated, and may grow overly cautious.”

“如果只记批评不记表扬,时间长了模型就会趋向保守——不敢做决定、不敢主动行动,事事请示。Prompt 还特别提醒:‘批评很容易注意到;肯定更安静——要主动留意。’”

三、记忆文件格式与底层架构解析

1. 记忆文件的物理形态:纯文本的胜利

每条记忆在底层并不是存在高大上的向量数据库里,而是被保存为一个独立的 Markdown 文件,并使用 YAML Frontmatter 声明元数据。
格式强制要求包含 name(记忆名称)、description(一行描述,用于判断未来对话的相关性)和 type(必须是前文提到的四种类型之一)。

为什么使用 Markdown 文件而非数据库?
这是一个极具工程智慧的架构选择,优势显而易见:

  • 可读性:开发者可以直接用 VSCode 等文本编辑器查看和修改记忆。
  • 版本控制:记忆文件天然支持 Git 追踪(如果放在项目目录下)。
  • 可移植性:文件系统是最低公共 denominator(公分母),无需额外依赖。
  • 可调试性:出现问题时,直接 lscat 就能诊断。
  • 成本:无需维护数据库连接、索引、备份。

“对于 Agent 的记忆场景,查询模式是简单的‘加载所有相关记忆’而非复杂的关联查询,文件系统的能力已经足够。”

2. MEMORY.md 索引文件:双重容量保护

MEMORY.md 是整个记忆系统的入口点——它不是记忆本身,而是一个索引文件。每次对话开始时,它会被自动加载到上下文中,让 Agent 快速了解已有的记忆概况。

为了防止索引文件过载,系统执行严格的双重截断逻辑。
源码位置:index.ts

const MAX_ENTRYPOINT_LINES = 200;
const MAX_ENTRYPOINT_BYTES = 25_000; // 约 25KB

export function truncateEntrypointContent(content: string) {
  // 1. 先按行截断(保持自然边界,200行上限)
  let lines = content.split('\n').slice(0, MAX_ENTRYPOINT_LINES);
  // 2. 再按字节截断(硬性底线,25KB上限)
  let result = Buffer.from(lines.join('\n')).slice(0, MAX_ENTRYPOINT_BYTES).toString();
  return result;
}

双重容量保护的设计智慧:
为什么需要两层限制?它们各有侧重:

  • 行数限制(200行):保护的是 Agent 的理解效率。即使每行很短,200 行以上的索引也需要 Agent 花费更多 Token 去筛选。它确保了索引始终是一个“快速浏览”的工具,而非“深度阅读”的文档。
  • 字节限制(25KB):保护的是上下文预算。如果每条索引的描述都很长(接近 150 字符),200 行可能达到 30KB。字节限制提供了一个硬性的成本底线。
  • 顺序的讲究:先按行,再按字节。当记忆少但描述长时,触发字节限制;当记忆多但描述短时,触发行数限制。

四、三层记忆流水线实战解读

第一层:Session Memory —— 会话内的“实时快照”

当对话 Token 增长 ≥5K 且工具调用 ≥3 次时,后台会 Fork 一个子代理,更新 {sessionId}/session-memory/summary.md。需要明确的是,记忆最终落盘就是纯文本的 Markdown 文件,而不是存入向量数据库,这保证了极低的管理成本和极高的可读性。

核心配置(阈值控制)
源码位置:session.ts

export const DEFAULT_SESSION_MEMORY_CONFIG = {
  minimumMessageTokensToInit: 10_000,    // 首次提取阈值
  minimumTokensBetweenUpdate: 5_000,     // 增量更新阈值
  toolCallsBetweenUpdates: 3,            // 动作频率阈值
}

更新方式:就地编辑而非追加
Session Memory 不是追加写入,而是让 forked agent 用 Edit 工具就地编辑笔记文件的每个 section。Prompt 中有严格约束:

“NEVER modify, delete, or add section headers… ONLY update the actual content that appears BELOW the italic section descriptions”

深度下钻:Auto Compact 协同优化
这是 Session Memory 最巧妙的用法,但原理比“直接复用”更细致。
源码里有一条专门的 sessionMemoryCompact 分支:当 Auto Compact(自动压缩)触发后,系统会先等正在进行的 Session Memory 提取结束,再尝试直接用 summary.md 构造压缩摘要。

  • 成功时:就不会再额外发起一次昂贵的 LLM 压缩请求。
  • 回退机制:如果 feature gate 没开、Session Memory 还是空模板、或者边界算不出来,才回退传统 compact。

“所以更准确的说法不是‘永远零调用’,而是‘优先走一条零额外 compact 调用的快路径’。”

第二层:Auto Memory Extraction —— 跨会话的“自动沉淀”

在每轮对话结束(Handle Stop Hooks)时触发。为了节省 Token,它采用 Prompt Cache 共享模式

解释一下 Prompt Cache
把它理解为餐厅里的“常客菜单”。当主 Agent 已经和模型交流了半天(产生了大量上下文),这些对话>内容已经在服务器端被“缓存”了。此时后台子代理(Forked Agent)接手去提取记忆时,它不需要把前面的聊天记录重新发送一遍让模型重读,而是直接说“照着刚才那份菜单给我提取一下重点”,这不仅极大地省了 Token 钱,还让提取速度快如闪电。

权限沙箱(最小权限原则)
后台子代理的权限被严格限制,确保安全。
源码位置:sandbox.ts

// 权限过滤逻辑
if (tool === Read || tool === Grep || tool === Glob) allow(); 
if (tool === Bash && isReadOnly(command)) allow();           
if ((tool === Edit || tool === Write) && isAutoMemPath(file_path)) allow(); 
deny(); // 禁止修改源码、调用 MCP 或网络工具

Prompt 引用:提取指令(完整中文版)
为了确保提取质量,系统为后台 Agent 提供了核心提取指令。这里分为两种情况:单用户模式团队模式(包含 Team Memory)

源码位置:prompts.ts

基础行为准则(所有模式通用):

“你现在作为记忆提取子代理(subagent)运行。分析上方最近的约 {newMessageCount} 条消息,并使用它们来更新你的持久化记忆系统。
你的 turn(轮次)预算有限。FileEdit 需要先对同一个文件执行 FileRead,因此高效的策略是:第 1 轮 —— 并行发出所有你可能更新的文件的 FileRead 调用;
第 2 轮 —— 并行发出所有 FileWrite/FileEdit 调用。不要在多个轮次中交替进行读写操作。
如果用户明确要求你记住某事,请立即将其保存为最适合的类型。如果他们要求你忘记某事,请找到并删除相关条目。”

单用户模式(无 Team Memory)保存策略:
“保存记忆是一个两步过程:
第 1 步 —— 将记忆写入其独立的文件中(例如:user_role.mdfeedback_testing.md),并使用以下 frontmatter 格式:
[示例格式]
第 2 步 —— 在 MEMORY.md 中添加指向该文件的指针。MEMORY.md 是一个索引,而不是记忆本身 —— 每个条目应该是一行,长度在 150 个字符以内:- [标题](file.md) — 单行吸引人的描述。它没有 frontmatter。绝不要将记忆内容直接写入 MEMORY.md 中。

  • MEMORY.md 始终会被加载到你的 system prompt 中 —— 超过 200 行的内容将被截断,所以请保持索引简洁
  • 按主题而非时间顺序对记忆进行语义组织
  • 不要写入重复的记忆。在写入新记忆之前,先检查是否存在可以更新的现有记忆。”

实战案例解读:索引与内容的物理分离
在上面的 Prompt 中明确规定了“两步走”。这到底长什么样?

  • 记忆内容(Content):它保存在如 ~/.claude/projects/my-app/memory/feedback_ci.md 这样的独立文件中,里面写着“CI 经常因为缓存挂掉,记得跑 rm -rf .cache”。
  • 记忆索引(Index):它保存在 ~/.claude/projects/my-app/memory/MEMORY.md 中,内容可能只有一行:- [CI 缓存问题解决](feedback_ci.md) - 遇到构建报错时需要清理缓存目录
  • 为什么要这样设计? 因为如果把所有长篇大论的记忆都塞进 Prompt 里,不但贵,还会让模型分心。通过只加载轻量级的 MEMORY.md(类似于一本书的目录),Agent 在需要时再通过 FileRead 去读取具体的 .md 内容,这是一种极其优雅的按需加载(Lazy Loading)策略。

团队模式(Team Memory 开启)保存策略:
“保存记忆是一个两步过程:
第 1 步 —— 将记忆写入所选目录(私人或团队,根据该类型的作用域指导)中独立的文件,并使用以下 frontmatter 格式:
[示例格式]
第 2 步 —— 在同一目录的 MEMORY.md 中添加指向该文件的指针。每个目录(私人和团队)都有其自己的 MEMORY.md 索引。

  • 两个 MEMORY.md 索引都会被加载到你的 system prompt 中 —— 超过 200 行的内容将被截断,所以请保持它们简洁。
  • 你绝对不能在共享的团队记忆中保存敏感数据。例如,绝不要保存 API 密钥或用户凭证。”

深度解读

  • 两步走策略:强制要求先写内容文件,再更新索引文件。这种“指针机制”避免了主 MEMORY.md 膨胀。
  • 团队模式的隔离:在团队模式下,Prompt 明确区分了私人目录和团队目录,并加入了强硬的安全底线(禁止保存敏感数据)。

互斥机制:如果主 Agent 在本轮已手动写了记忆(如用户说“记住这个”),后台提取会检测到并自动跳过,避免冲突。

第三层:Auto Dream —— 记忆的“熵减与整合”

当满足特定阈值时(通常是累积 ≥5 个新会话且距上次整合 ≥24 小时),系统会在后台自动触发“梦境”(Auto Dream)任务。这不是用户主动敲命令,而是系统自我维护的“垃圾回收”机制。

“梦境”四个阶段

  1. Orient(定位):扫描 memory/ 目录构建地图。
  2. Gather(采集信号):通过 Grep 搜索 JSONL 转录文件,寻找用户纠错、显式保存等信号。
  3. Consolidate(整合)关键动作——将相对日期(“下周二”)转为绝对日期(“2026-04-07”),合并重叠条目。
  4. Prune(修剪):删除过时事实,更新 MEMORY.md 索引(保持在 200 行/25KB 以内)。

核心算法:MEMORY.md 索引保护与修剪
在定位和整合后,为了防止索引文件过载,系统会执行严格的截断逻辑(上文提到的 200行/25KB)。
“Auto Dream 通过将相对日期转为绝对日期,并利用双重截断保护索引,解决了记忆随时间失效与上下文爆炸的问题。”

四、实战场景:三层记忆如何协同工作?

为了直观理解这三层管线,我们模拟一个真实开发场景:

1. 新项目接入(Onboarding)

应用:开发者首次进入项目。
价值project 记忆会自动解释代码库中那些“看起来不合理但有特殊原因”的设计(如:为了兼容旧版浏览器而保留的 Polyfill),reference 则直接拉齐工具链地址,极大缩短了开发者的摸索周期。

2. 复杂重构(Refactoring)

应用:对核心模块进行大规模调整(Session Memory 发力)。
操作:你正在让 Agent 重构 50 个文件。
表现:对话进行到第 20 轮时,上下文已满。此时 Session Memory 已经在后台更新了 3 次笔记。触发 Auto Compact 时,Agent 直接从 summary.md 读取“当前重构进度”和“待办列表”,而不需要重新分析前 20 轮对话。
结论Session Memory 保证了长任务中途“不断片”。

3. 跨天处理环境 Bug(Auto Memory 表现)

应用:在多个微服务或仓库间频繁切换,并解决偶发问题。
操作:周五下午,你和 Agent 发现由于 node_modules 缓存导致了一个诡异的 CI 报错,并找到了 rm -rf .cache 的方案。
表现:对话结束时,Auto Memory 提取了这条反馈。周一你开启新会话问“CI 又报错了”,Agent 会立即从 memory/feedback_ci.md 提示你尝试清理缓存。
结论Auto Memory 实现了跨会话的“经验传承”。

4. 技术栈迁移半年后(Auto Dream 价值)

应用:系统后台运行 Auto Dream 进行知识沉淀与清理。
操作:项目从 Webpack 迁移到 Vite 已半年。
表现:半年前的记忆里有大量“Webpack 调试技巧”。Auto Dream 在某次运行中发现这些文件很久没被引用,且最近 50 次会话都在用 Vite,它会自动合并“构建工具”相关笔记,删除陈旧的 Webpack 记录。
结论Auto Dream 负责“知识保鲜”,防止认知污染。

五、记忆系统评估与工业级避坑指南

1. 记忆系统的能力边界与隐性成本

在享受自动化记忆带来的便利时,必须清晰认知其架构层面的代价:

  • 提取质量高度依赖模型能力
    Forked agent 本质上是让 LLM 做“阅读理解 + 笔记整理”。为了控制成本,提取 Prompt 明确要求它只依据最近的对话内容写 memory,绝对不要再去 grep 代码或读源码验证
    “这让流程变得便宜,但也意味着它写入的是‘对话里被说过且模型认为值得记住的事实’,而不是‘再次核实后的客观事实’。一旦写错,后续会话中这个错误记忆很容易被召回,产生极强的放大效应。”

  • 截断保护可能导致信息丢失
    MEMORY.md 强制设定的“200 行 + 25KB”硬限意味着记忆总量存在绝对物理上限。对于积累了大量记忆的长期项目,索引截断是不可避免的。虽然 Auto Dream 的修剪机制能缓解这一问题,但无法从根本上解决超大规模项目记忆溢出的痛点。

  • 后台 Fork 的隐性 Token 成本
    尽管 Forked agent 巧妙地利用了 Prompt Cache 共享前文,但每次触发提取时,它仍需要生成全新的输出(Output Tokens)。在每分钟多轮工具调用的“高频对话场景”下,Session Memory 的频繁更新会导致不容忽视的 Token 消耗积少成多。

2. 避坑指南:正确使用记忆系统的姿势

误区一:盲信记忆(Stale Data)

  • 错误做法:记忆说“入口在 main.ts”,Agent 直接去改。
  • 正确做法:记忆只是时间点的快照。Agent 在根据记忆行动前,必须先通过 FileReadls 验证目标文件或状态是否依然存在。

误区二:把记忆系统当作项目文档

  • 错误做法:试图用记忆系统替代项目文档,让 Agent 记住长篇大论的技术规范和接口设计。
  • 正确做法:这违背了“只保存不可推导信息”的原则。技术规范应该放在代码仓库的 docs/ 目录中,而架构决策的“为什么(Why)”才应该放在记忆系统中。

误区三:忽略相对日期问题

  • 错误做法:用户说“这个功能下周二上线”,Agent 原封不动保存了“下周二上线”。但两天后,“下周二”变成了“这周二”;再过一周,变成了“上周二”。这种记忆不仅无用,还会严重误导后续的逻辑推理。
  • 正确做法:所有涉及时间的记忆必须在提取或 Auto Dream 阶段转化为绝对日期。Agent 应该将“下周二”转换为具体日期(如 2026-04-07)后再行保存。

误区四:缓存击穿(Cache Miss)

  • 错误做法:为了安全,给后台 Agent 阉割掉大量主 Agent 的工具。
  • 正确做法:后台 Agent 的工具列表必须与主 Agent 保持字节级一致(哪怕部分工具在逻辑上被禁用),否则会导致 Prompt Cache 校验失败,致使每次提取都变成昂贵的全量请求。

3. 五层加载体系(优先级从低到高)

Claude Code 将记忆与指令分离,加载顺序如下:

  1. 全局级 (/etc/claude-code/CLAUDE.md)
  2. 用户级 (~/.claude/CLAUDE.md)
  3. 项目级 (.claude/rules/*.md)
  4. 本地级 (CLAUDE.local.md)
  5. 自动记忆 (MEMORY.md) —— 注意:MEMORY.md 作为第一条 User Message 注入。

4. 验证优先原则与避坑总结

  • 认知冗余:手动维护的 CLAUDE.md 与自动记忆是互斥的。如果你写了明确规则,系统会优先跳过自动提取。

参考文献

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐