Claude Code Memory 系统深度解析
本文基于 Claude Code 源代码(
src/目录)深入分析其 Memory 机制的设计与实现。
一、Memory 系统总览
Claude Code 的 Memory 系统是一个多层次、多类型的持久化知识管理体系。它解决的核心问题是:如何让 AI 助手在不同会话之间保留有价值的上下文信息,同时避免信息污染。
系统分为两大层:
- 指令型记忆(Instruction Memory):CLAUDE.md 文件体系,存储用户/团队对 AI 行为的持久指令
- 事实型记忆(Fact Memory):AutoMem/MEMORY.md 体系,AI 自动从对话中提炼并持久化的知识点
二、记忆类型体系
2.1 文件层面的 Memory 类型(MemoryType)
源码:src/utils/memory/types.ts,src/utils/claudemd.ts
| 类型 | 文件路径 | 说明 |
|---|---|---|
Managed |
/etc/claude-code/CLAUDE.md |
机构级全局策略,对所有用户生效 |
User |
~/.claude/CLAUDE.md + ~/.claude/rules/*.md |
用户私有全局指令,适用于所有项目 |
Project |
CLAUDE.md、.claude/CLAUDE.md、.claude/rules/*.md |
项目级指令,提交到代码仓库 |
Local |
CLAUDE.local.md |
用户私有的项目级指令,不提交仓库 |
AutoMem |
~/.claude/projects/<repo-hash>/memory/MEMORY.md |
AI 自动提炼的跨会话记忆索引 |
TeamMem |
团队共享目录(feature-gated) | 团队同步记忆(实验特性) |
加载优先级(越后加载优先级越高,模型更关注):
Managed → User → Project(根目录→CWD) → Local → AutoMem → TeamMem
2.2 知识点层面的 Memory 类型
源码:src/memdir/memoryTypes.ts
AI 在 AutoMem 目录中写入的每个记忆文件,通过 frontmatter 的 type 字段声明知识类型:
| 类型 | 作用域 | 描述 |
|---|---|---|
user |
私有 | 用户画像:角色、目标、经验背景、偏好 |
feedback |
私有/团队 | 行为反馈:用户的纠正指令或确认的做法 |
project |
团队优先 | 项目上下文:进行中的工作、目标、决策、截止日期 |
reference |
团队优先 | 外部系统指针:Linear、Grafana、Slack 等外部资源的位置 |
不应写入记忆的内容:
- 可从代码推导出的信息(架构、代码模式、文件路径)
- Git 历史、近期变更
- 调试方案和修复记录(代码本身就是记录)
- 临时状态、当前会话上下文
- 已在 CLAUDE.md 中记录的内容
三、核心数据结构
MemoryFileInfo(内存中的记忆文件表示)
typescript
// src/utils/claudemd.ts
type MemoryFileInfo = {
path: string // 文件绝对路径
type: MemoryType // 记忆类型(见2.1)
content: string // 处理后的内容(去除HTML注释、frontmatter、截断)
parent?: string // 通过 @include 引入时,父文件路径
globs?: string[] // 条件规则:适用的文件 glob 模式
contentDiffersFromDisk?: boolean // 内容是否与磁盘不同(被截断或处理过)
rawContent?: string // 磁盘原始内容(当 contentDiffersFromDisk=true 时)
}
MemoryHeader(目录扫描时的记忆文件元数据)
typescript
// src/memdir/memoryScan.ts
type MemoryHeader = {
filename: string // 相对路径
filePath: string // 绝对路径
mtimeMs: number // 修改时间(用于排序)
description: string | null // frontmatter 中的 description 字段
type: MemoryType | undefined // frontmatter 中的 type 字段
}
四、文件发现与加载机制
4.1 CLAUDE.md 文件发现流程
源码:src/utils/claudemd.ts → getMemoryFiles()
getMemoryFiles()
│
├─ 1. 加载 Managed:/etc/claude-code/CLAUDE.md + /etc/claude-code/.claude/rules/
│
├─ 2. 加载 User(若启用):~/.claude/CLAUDE.md + ~/.claude/rules/
│
├─ 3. 目录向上遍历(CWD → 根目录),每层加载:
│ ├─ Project: ./CLAUDE.md
│ ├─ Project: ./.claude/CLAUDE.md
│ ├─ Project: ./.claude/rules/*.md(无条件规则)
│ └─ Local: ./CLAUDE.local.md
│
├─ 4. 附加目录(--add-dir):同 Project 逻辑
│
├─ 5. AutoMem 入口:~/.claude/projects/<repo>/memory/MEMORY.md
│
└─ 6. TeamMem 入口(若启用)
关键设计:
- 越靠近 CWD 的文件越后加载,模型更关注(优先级越高)
- 使用
memoize缓存结果,会话内只读一次磁盘 @include指令:支持在 CLAUDE.md 中通过@path引入其他文件(最深5层)- 支持 frontmatter
paths:字段做条件规则(仅对特定文件路径生效)
4.2 @include 指令解析
markdown
# 在 CLAUDE.md 中可以这样引用其他文件:
@./common-rules.md
@~/shared-config/coding-standards.md
@/absolute/path/to/rules.md
通过 marked 库的词法分析器提取文本节点中的 @path 模式,排除代码块内容,递归处理最多 5 层。
4.3 内容处理流程
每个 CLAUDE.md 文件加载后经过:
- Frontmatter 解析 :提取
paths:等控制字段,保留正文 - HTML 注释剥离 :移除
<!-- -->注释(仅块级,不影响代码块内的注释) - 截断处理:AutoMem/TeamMem 的 MEMORY.md 限制 200 行 / 25KB
五、AutoMem 自动记忆系统
这是最核心的"跨会话记忆"机制。
5.1 存储路径
源码:src/memdir/paths.ts
~/.claude/projects/<sanitized-git-root>/memory/
├── MEMORY.md # 索引文件(最多200行,每行一个条目)
├── user_role.md # 用户画像记忆
├── feedback_testing.md # 测试相关反馈记忆
├── project_deadline.md # 项目截止日期上下文
├── reference_linear.md # 外部系统指针
└── logs/ # 日志模式(KAIROS 特性)
└── 2026/04/2026-04-08.md
路径解析优先级:
CLAUDE_COWORK_MEMORY_PATH_OVERRIDE环境变量(CoWork 场景)- settings.json 的
autoMemoryDirectory(trusted sources only) ~/.claude/projects/<git-root-hash>/memory/
关键:使用 git 仓库的 canonical root(处理 worktree 场景),确保同一仓库的所有 worktree 共享同一个 memory 目录。
5.2 记忆文件格式
markdown
---
name: 用户喜欢简洁回复
description: 用户偏好风格 - 直接给出结果,不做总结
type: feedback
---
不在回复末尾总结刚做的操作,用户可以看 diff 了解变化。
**Why:** 用户明确反馈不需要结尾总结。
**How to apply:** 每次回复结束后不要加"我刚才做了..."的段落。
5.3 MEMORY.md 索引格式
markdown
- [用户偏好简洁回复](feedback_concise.md) --- 不做结尾总结,直接给结果
- [用户背景:数据科学家](user_role.md) --- 关注可观测性/日志分析
- [项目:移动端发版冻结](project_release.md) --- 2026-04-10 之前不合并非关键PR
5.4 自动记忆提取流程
源码:src/services/extractMemories/extractMemories.ts
每次对话轮次结束(模型返回最终响应,无工具调用)
│
└─ executeExtractMemories() [fire-and-forget]
│
├─ 检查:模型是否已在本轮直接写入记忆文件?
│ └─ 是:跳过(避免重复),更新游标
│
├─ 扫描现有记忆文件(scanMemoryFiles)→ 生成 manifest
│
├─ 构造提取 prompt(含对话历史 + 现有记忆清单)
│
└─ runForkedAgent():启动独立子 Agent
├─ 工具权限:Read/Grep/Glob 全开,Write/Edit 仅限 memory 目录
├─ 最多 5 轮(防止无限循环)
└─ 写入记忆文件 + 更新 MEMORY.md 索引
关键设计:
- 使用 forked agent(共享父对话的 prompt cache,节省 token)
- 有游标机制(lastMemoryMessageUuid),每次只处理新增消息
- 互斥保护:同时只有一个提取 agent 运行,后续请求被 stash 等待
- 频率控制 :通过 feature flag
tengu_bramble_lintel控制每 N 轮提取一次
六、记忆注入到对话上下文
6.1 CLAUDE.md 注入(User Context)
源码:src/context.ts → getUserContext()
typescript
const claudeMd = getClaudeMds(filterInjectedMemoryFiles(await getMemoryFiles()))
生成的格式:
Codebase and user instructions are shown below. Be sure to adhere to these instructions...
Contents of /project/CLAUDE.md (project instructions, checked into the codebase):
<文件内容>
Contents of ~/.claude/CLAUDE.md (user's private global instructions for all projects):
<文件内容>
6.2 AutoMem 提示注入(System Prompt)
源码:src/constants/prompts.ts → getSystemPrompt(),src/memdir/memdir.ts → loadMemoryPrompt()
在系统提示中注入记忆行为指导:
# auto memory
You have a persistent, file-based memory system at `~/.claude/projects/.../memory/`.
This directory already exists --- write to it directly...
## Types of memory
[user / feedback / project / reference 四种类型说明]
## How to save memories
Step 1: 写入独立 .md 文件(带 frontmatter)
Step 2: 在 MEMORY.md 中添加一行索引指针
## MEMORY.md
[MEMORY.md 当前内容]
七、SessionMemory(会话级记忆)
源码:src/services/SessionMemory/sessionMemory.ts
这是为**上下文压缩(auto-compact)**服务的短期记忆,与 AutoMem 不同:
- 生命周期:单次会话内有效,用于辅助上下文压缩
- 触发条件:context 超过阈值(token 数 + 工具调用次数)时自动提取
- 存储位置:会话目录下的临时文件
- 用途:在压缩时保留重要的会话状态,避免信息丢失
八、条件规则(Conditional Rules)
源码:src/utils/claudemd.ts → processConditionedMdRules()
.claude/rules/ 目录下的 .md 文件可以通过 frontmatter 指定仅对特定文件路径生效:
markdown
---
paths:
- "**/*.py"
- "src/tests/**"
---
Python 文件专属规则:不要使用 unittest,始终使用 pytest...
这实现了上下文感知的记忆注入------只有在操作匹配文件时,相关规则才会被加载。
九、完整数据流图
╔══════════════════════════════════════════════════════════════╗
║ 会话启动时 ║
╚══════════════════════════════════════════════════════════════╝
getMemoryFiles()
↓
[Managed] → [User] → [Project/Local(root→CWD)] → [AutoMem]
↓
getClaudeMds() → userContext.claudeMd
↓
loadMemoryPrompt() → systemPrompt[memory section]
↓
API Call: system=[...memory prompt...], user=[claudeMd + 用户输入]
╔══════════════════════════════════════════════════════════════╗
║ 每轮对话后 ║
╚══════════════════════════════════════════════════════════════╝
用户输入 → Claude 响应(无工具调用时触发)
↓
executeExtractMemories() [后台 fire-and-forget]
↓
scanMemoryFiles() → 已有记忆清单
↓
runForkedAgent(提取prompt + 对话历史)
↓
子Agent分析对话 → 写入 memory/*.md + 更新 MEMORY.md
↓
下次会话时 MEMORY.md 被加载进 system prompt
十、关键设计原则
- 优先级即顺序:后加载的文件优先级更高(模型注意力机制的特性)
- 只保存不可推导的信息:代码可读到的内容不进记忆
- 索引+详情双层结构:MEMORY.md 作为目录(200行限制),详情存独立文件
- 安全隔离:提取 agent 只能写 memory 目录,防止误操作
- 缓存共享:forked agent 共享父对话 prompt cache,降低成本
- 幂等设计:memoize 缓存 + cursor 机制,避免重复处理