Claude Code 的记忆系统(memdir)是一个基于文件的持久化记忆架构,支持跨会话的上下文保持与团队协作。该系统采用封闭四类型分类法(user/feedback/project/reference),通过文件系统存储和 API 同步机制,实现了私人和团队两个作用域的记忆管理。核心设计原则是只存储不可从当前项目状态推导的信息,代码模式、架构和 Git 历史等可推导内容被明确排除在外。
系统架构概览
记忆系统采用分层架构,从底层的文件存储到顶层的智能检索,每一层都有明确的职责边界。私有记忆存储在用户级目录,团队记忆通过 OAuth 认证的 API 进行同步,两者通过统一的 prompt 构建层暴露给 AI 模型。
graph TB
subgraph "存储层 Storage Layer"
A[私有记忆目录<br/>~/.claude/projects/<slug>/memory/]
B[团队记忆目录<br/>~/.claude/projects/<slug>/memory/team/]
C[MEMORY.md<br/>索引入口点]
D[Topic Files<br/>主题文件 .md]
end
subgraph "核心层 Core Layer"
E[memdir.ts<br/>Prompt 构建]
F[memoryTypes.ts<br/>类型定义]
G[paths.ts<br/>路径解析]
H[memoryScan.ts<br/>文件扫描]
end
subgraph "服务层 Service Layer"
I[teamMemorySync<br/>团队同步]
J[findRelevantMemories<br/>智能检索]
K[extractMemories<br/>记忆提取]
end
subgraph "集成层 Integration Layer"
L[System Prompt<br/>系统提示]
M[Tool Operations<br/>工具操作]
N[Memory Command<br/>记忆命令]
end
A --> E
B --> I
C --> E
D --> H
F --> E
G --> A
G --> B
H --> J
I --> B
J --> L
E --> L
M --> A
M --> B
N --> A
style A fill:#e1f5ff
style B fill:#fff4e1
style I fill:#f3e5f5
style J fill:#e8f5e9
Sources: memdir.ts | memoryTypes.ts | paths.ts | teamMemorySync
记忆类型系统
记忆系统采用封闭类型分类法,定义了四种互斥的记忆类型,每种类型都有明确的作用域、保存时机和使用场景。这种设计确保了记忆内容的非冗余性和可验证性——所有可从代码或 Git 历史推导的信息都不应存储为记忆。
四种核心类型
| 类型 | 作用域 | 描述 | 保存时机 | 示例 |
|---|---|---|---|---|
| user | 始终私有 | 用户角色、目标、偏好、知识背景 | 学习到用户的任何细节 | ”用户是数据科学家,专注可观察性” |
| feedback | 默认私有,项目级约定可为团队 | 工作方式指导(避免/保持) | 用户纠正或确认非显而易见方法 | ”集成测试必须用真实数据库,不模拟” |
| project | 强烈偏向团队 | 项目上下文(目标、决策、时间线) | 了解谁在做什么、为什么、何时 | ”合并冻结从 2026-03-05 开始” |
| reference | 通常为团队 | 外部系统指针 | 学习外部资源及其用途 | ”管道 Bug 追踪在 Linear INGEST 项目” |
类型结构规范要求每个记忆文件包含三要素:规则本身、Why 行(用户给出的原因,通常是过去事件或强烈偏好)、How to apply 行(该指导何时/何地生效)。这种结构让未来实例能够判断边缘情况,而非盲目遵循规则。
Sources: memoryTypes.ts | TYPES_SECTION_INDIVIDUAL | TYPES_SECTION_COMBINED
持久化机制
记忆系统的持久化基于文件系统 + frontmatter 格式,通过多层路径解析策略支持环境变量、用户设置和默认路径的优先级覆盖。系统对记忆文件施加了严格的容量限制,防止索引膨胀影响加载性能。
路径解析优先级
记忆目录的路径解析遵循严格的优先级链,确保了灵活性同时防止了安全风险:
graph TD
A[路径解析请求] --> B{环境变量<br/>CLAUDE_COWORK_MEMORY_PATH_OVERRIDE?}
B -->|是| C[使用环境变量路径<br/>不展开 ~]
B -->|否| D{settings.json<br/>autoMemoryDirectory?}
D -->|是| E[使用设置路径<br/>支持 ~/ 展开]
D -->|否| F[默认路径<br/>~/.claude/projects/<slug>/memory/]
C --> G[安全验证]
E --> G
F --> G
G --> H{检查路径安全性}
H -->|相对路径| I[❌ 拒绝]
H -->|根路径/过短| I
H -->|Windows 驱动器根| I
H -->|UNC 路径| I
H -->|空字节| I
H -->|安全| J[✅ 返回规范化路径]
style I fill:#ffebee
style J fill:#e8f5e9
安全验证机制拒绝以下危险路径模式:相对路径(../foo)、根路径或过短路径(长度 < 3)、Windows 驱动器根(C:)、UNC 路径(\\server\share)、包含空字节的路径。所有路径在返回前都经过 normalize() 处理和 NFC Unicode 规范化。
Sources: paths.ts | validateMemoryPath | getAutoMemPath
文件格式与容量限制
记忆文件使用 YAML frontmatter + Markdown 正文格式,每个文件对应一个主题记忆。系统通过 frontmatter 的 type 字段进行类型分类,缺失或无效的类型值会优雅降级而非报错。
容量限制策略对 MEMORY.md 索引文件施加双重约束:最大 200 行和最大 25,000 字节(约 125 字符/行)。超过任一限制时,系统会在截断内容后追加警告信息,指明触发的是哪个限制。单行索引条目建议保持在 150 字符以内,过长的索引条目会被字节限制捕获(实测案例:197KB 的索引文件在 200 行内)。
---
name: User Role
description: Data scientist focused on observability
type: user
---
# User Role
The user is a data scientist currently investigating logging and observability
infrastructure. They have deep expertise in backend systems and statistical
analysis, but are new to this project's React frontend.
**Why:** Understanding their background helps frame explanations in terms of
data pipelines and statistical concepts they're familiar with.
**How to apply:** When explaining frontend code, draw analogies to backend
data processing patterns. Avoid assuming frontend expertise.
记忆文件扫描通过 scanMemoryFiles 函数实现单次遍历优化:读取前 30 行(frontmatter 最大行数)并获取 mtime,然后按修改时间降序排序,最多返回 200 个文件。这种设计避免了 stat-then-read 的双倍系统调用开销。
Sources: memdir.ts | truncateEntrypointContent | memoryScan.ts
团队同步机制
团队记忆同步基于 repo-scoped API,通过 OAuth 认证实现跨用户的记忆共享。同步语义采用 server-wins pull + delta upload push 模型,使用 SHA-256 内容哈希进行增量检测,并通过 ETag 实现乐观并发控制。
同步流程架构
sequenceDiagram
participant U as 用户会话
participant L as 本地文件系统
participant S as SyncState
participant A as Team Memory API
Note over U,A: 会话启动 - Pull 阶段
U->>A: GET /team_memory?repo=owner/repo<br/>If-None-Match: "{etag}"
alt 304 Not Modified
A-->>U: 未修改,跳过写入
else 200 OK
A-->>U: {entries, entryChecksums, checksum}
U->>S: 更新 serverChecksums
U->>L: 写入本地文件<br/>(跳过相同内容)
else 404 Not Found
A-->>U: 无远程数据
U->>S: 清空 serverChecksums
end
Note over U,A: 会话运行 - 本地编辑
U->>L: 写入/编辑团队记忆文件
Note over U,A: 会话结束/周期性 - Push 阶段
U->>L: 读取所有团队记忆文件
U->>U: 扫描敏感信息
U->>U: 计算 delta<br/>(local hash ≠ server hash)
alt delta 为空
U-->>U: 无需上传
else delta 非空
loop 每个 batch(≤200KB)
U->>A: PUT /team_memory<br/>If-Match: "{etag}"<br/>{entries: delta}
alt 200 OK
A-->>U: {checksum, lastModified}
U->>S: 更新 lastKnownChecksum
else 412 Precondition Failed
A-->>U: ETag 冲突
U->>A: GET ?view=hashes<br/>刷新 serverChecksums
U->>U: 重新计算 delta
end
end
end
增量上传算法通过比对本地文件哈希与 serverChecksums 映射表,只上传内容发生变化的条目。这种设计在首次 push 后的典型场景(1-3 个文件编辑)中实现了接近 100% 的带宽节省。当发生 412 冲突时,系统通过 GET ?view=hashes 轻量级探针刷新服务器状态,然后重新计算 delta——队友推送的内容因哈希匹配而被自然排除,避免了重新上传。
Sources: teamMemorySync | pullTeamMemory | pushTeamMemory
容量管理与错误处理
团队记忆同步实施了多层容量限制:单文件 250KB(服务器强制限制)、PUT 请求体 200KB(避免网关 413 错误)、条目数量由服务器动态返回(通过结构化 413 响应的 extra_details.max_entries 字段)。客户端不硬编码条目数上限,而是从服务器学习组织级配额。
错误分类与重试策略将错误分为五类:auth(OAuth 不可用)、timeout(30 秒超时)、network(网络故障)、parse(响应格式无效)、unknown(其他)。只有非 auth 和非 parse 错误才会触发最多 3 次重试,使用指数退避算法计算延迟。412 冲突有独立的 2 次重试限制,通过 ETag 链确保乐观锁的正确性。
敏感信息扫描在 push 前使用 gitleaks 规则集检测团队记忆中的秘密(API 密钥、访问令牌等)。检测到秘密的文件会被排除在上传集合之外,但不会阻塞整个 push 操作——系统会记录跳过的文件路径和规则 ID(如 github-pat、aws-access-token),但不记录秘密值本身。
Sources: types.ts | batchDeltaByBytes | secretScanner
智能检索机制
记忆系统通过两阶段检索流程实现上下文相关的记忆召回:第一阶段扫描记忆目录并解析 frontmatter,第二阶段使用轻量级 Sonnet 模型从候选集合中选择最相关的记忆。
检索流程
graph LR
A[用户查询] --> B[扫描记忆目录]
B --> C[解析 Frontmatter<br/>最多 200 文件]
C --> D[格式化记忆清单<br/>type filename timestamp: description]
D --> E{记忆数量 > 0?}
E -->|否| F[返回空集合]
E -->|是| G[构建选择 Prompt]
G --> H[调用 Sonnet<br/>sideQuery]
H --> I[解析 JSON 响应<br/>最多 5 个文件名]
I --> J[过滤无效文件名]
J --> K[返回记忆路径 + mtime]
style H fill:#fff4e1
style K fill:#e8f5e9
记忆清单格式将扫描结果转换为单行摘要格式:- [type] filename (timestamp): description。例如:- [feedback] testing_policy.md (2026-03-05T10:30:00Z): Integration tests must use real database。这种紧凑格式让选择模型能够在单个上下文窗口中评估大量候选记忆。
选择模型 Prompt 明确指示模型只选择确定有用的记忆,不确定的则排除。当用户正在积极使用某个工具时,该工具的参考文档记忆会被过滤(避免关键词重叠导致的假阳性),但包含警告或已知问题的记忆仍会被选中——这正是需要提醒的时候。
记忆新鲜度追踪通过 memoryAge 函数将 mtime 转换为人类可读的年龄字符串(“today”、“yesterday”、“47 days ago”)。超过 1 天的记忆会附加过期警告:This memory is 47 days old. Memories are point-in-time observations, not live state — claims about code behavior or file:line citations may be outdated. 这种设计解决了用户报告的过期代码状态记忆被当作事实断言的问题。
Sources: findRelevantMemories.ts | memoryScan.ts | memoryAge.ts
配置与功能开关
记忆系统的行为通过环境变量、settings.json 和 GrowthBook 特性标志三层配置控制,支持从完全禁用到细粒度功能调整的多种部署场景。
配置优先级矩阵
| 配置项 | 优先级 1 | 优先级 2 | 优先级 3 | 默认值 |
|---|---|---|---|---|
| 启用/禁用 | CLAUDE_CODE_DISABLE_AUTO_MEMORY=1 | CLAUDE_CODE_SIMPLE=1 | settings.autoMemoryEnabled=false | 启用 |
| 存储路径 | CLAUDE_COWORK_MEMORY_PATH_OVERRIDE | settings.autoMemoryDirectory | ~/.claude/projects/<slug>/memory/ | - |
| 团队同步 | GrowthBook tengu_herring_clock | OAuth 认证可用性 | - | 禁用 |
| 记忆提取 | GrowthBook tengu_passport_quail | 非交互式会话 | - | 禁用 |
| 索引跳过 | GrowthBook tengu_moth_copse | - | - | 使用索引 |
环境变量覆盖提供了最高优先级的控制:CLAUDE_CODE_DISABLE_AUTO_MEMORY=1 完全禁用记忆系统(包括 memdir、代理记忆、过去会话搜索),CLAUDE_CODE_SIMPLE=1(--bare 模式)同样禁用以保持最小化 footprint。在远程执行环境(CCR)中,CLAUDE_CODE_REMOTE_MEMORY_DIR 指定持久化存储位置。
安全性约束在路径配置中尤为关键:autoMemoryDirectory 设置只从受信任源读取(policySettings、flagSettings、localSettings、userSettings),故意排除 projectSettings(.claude/settings.json 提交到仓库)——恶意仓库可能设置 autoMemoryDirectory: "~/.ssh" 来获得对敏感目录的静默写访问权限。
Sources: paths.ts | isAutoMemoryEnabled | getAutoMemPathSetting
功能开关详解
团队记忆(tengu_herring_clock) 是私有记忆的子目录特性,要求自动记忆必须启用。团队记忆目录位于 <autoMemPath>/team/,有独立的 MEMORY.md 索引和同步机制。当团队记忆启用时,系统提示会切换到 buildCombinedMemoryPrompt,在类型定义中嵌入 <scope> 标签指导模型选择私有或团队作用域。
记忆提取(tengu_passport_quail + tengu_slate_thimble) 控制后台代理是否在会话结束时提取记忆。主代理的提示始终包含完整的保存指令——当主代理写入记忆时,后台代理会跳过该范围(通过 hasMemoryWritesSince 检测);当主代理未写入时,后台代理捕获遗漏的内容。tengu_slate_thimble 扩展支持非交互式会话的记忆提取。
索引跳过(tengu_moth_copse) 切换记忆保存模式:默认使用两步流程(写入主题文件 → 更新 MEMORY.md 索引),启用后跳过索引维护,模型直接写入主题文件。这适用于高频写入场景(如 KAIROS 助手模式的每日日志),由独立的夜间进程将日志蒸馏为 MEMORY.md。
Sources: teamMemPaths.ts | isExtractModeActive | loadMemoryPrompt
安全与权限模型
记忆系统的安全设计围绕路径遍历防护、敏感数据检测和权限隔离三个核心原则,确保记忆文件不会成为攻击载体或数据泄露源头。
路径安全验证
团队记忆路径验证实施了最严格的防护措施,通过 validateTeamMemKey 函数拒绝所有可能的路径遍历向量:
| 攻击向量 | 检测方法 | 示例 |
|---|---|---|
| 空字节注入 | key.includes('\0') | file\0.md → 截断后续路径 |
| URL 编码遍历 | decodeURIComponent + .. 检测 | %2e%2e%2f → ../ |
| Unicode 规范化 | NFKC 规范化 + 遍历字符检测 | 全宽 ../ → ../ |
| 反斜杠 | key.includes('\\') | Windows 路径分隔符滥用 |
| 绝对路径 | key.startsWith('/') | /etc/passwd 直接访问 |
路径解析后的额外防护通过 resolveSymlinksForAncestor 函数处理符号链接:对于不存在的目标文件,从最深存在的祖先目录开始解析符号链接,然后将不存在的尾部路径重新连接到解析后的祖先。这防止了通过符号链接跳出预期目录的攻击。
Sources: teamMemPaths.ts | sanitizePathKey | validateTeamMemKey
文件操作权限
记忆文件检测通过 isAutoMemFile 和 isTeamMemFile 函数识别文件是否属于记忆系统,这些检测被用于:
-
文件系统权限绕过:记忆目录获得写入权限豁免(绕过
DANGEROUS_DIRECTORIES检查),但当CLAUDE_COWORK_MEMORY_PATH_OVERRIDE设置时,该豁免被禁用——SDK 调用者显式选择自动记忆机制时才授予权限。 -
工具操作摘要:
appendTeamMemorySummaryParts函数为工具使用摘要生成记忆操作的动词形式(“Recalling 3 team memories”、“Writing 1 team memory”),区分活动状态(“Recalling”)和完成状态(“Recalled”)。 -
搜索目标检测:
isTeamMemorySearch和isTeamMemoryWriteOrEdit函数检查 Grep/Glob/Write/Edit 工具的目标路径是否为团队记忆文件,用于权限验证和审计日志。
Sources: memoryFileDetection.ts | teamMemoryOps.ts | isAutoMemPath
与其他持久化机制的关系
记忆系统是 Claude Code 多种持久化机制之一,每种机制都有明确的适用场景和生命周期。系统提示中明确区分了记忆与其他机制的使用时机:
| 机制 | 生命周期 | 适用场景 | 不适用场景 |
|---|---|---|---|
| Memory | 跨会话 | 用户偏好、项目上下文、工作指导 | 当前会话临时状态、可推导信息 |
| Plan | 当前会话 | 非平凡实现任务的对齐 | 已有计划时的更新、跨会话持久化 |
| Tasks | 当前会话 | 工作分解、进度追踪 | 未来会话引用、团队共享状态 |
| CLAUDE.md | 跨会话 | 项目级约定、架构文档 | 用户特定偏好、临时决策 |
| Git History | 永久 | 代码变更上下文、责任追踪 | 未提交的决策、非代码信息 |
明确排除的记忆内容包括:代码模式和约定(可从当前代码推导)、Git 历史和最近变更(git log/git blame 是权威来源)、调试解决方案(修复在代码中,上下文在提交消息中)、CLAUDE.md 已文档化的内容、临时任务细节。即使用户显式要求保存这些内容,系统也会询问什么令人惊讶或不显而易见——那才是值得保留的部分。
会话记忆(Session Memory) 是独立的机制,存储在 ~/.claude/session-memory/ 目录,用于单个长生命周期会话的上下文保持(如 KAIROS 助手模式)。会话记忆通过 detectSessionFileType 函数识别,与项目记忆系统并行运作但不共享存储。
Sources: memoryTypes.ts | buildMemoryLines | memoryFileDetection.ts
命令与用户交互
用户通过 /memory 命令与记忆系统交互,该命令提供了记忆文件的可视化选择器和编辑器集成。命令实现遵循惰性加载模式,在渲染前清除并预填充缓存,避免 Suspense fallback 的闪烁。
/memory 命令流程
graph TD
A[用户执行 /memory] --> B[清除并预填充缓存]
B --> C[渲染 MemoryFileSelector]
C --> D{用户选择文件}
D -->|选择| E[创建 .claude 目录<br/>recursive mkdir]
E --> F[创建文件<br/>wx 标志避免覆盖]
F --> G[在编辑器中打开]
G --> H[显示编辑器信息<br/>$EDITOR 或 $VISUAL]
D -->|取消| I[返回取消消息]
style C fill:#e3f2fd
style G fill:#e8f5e9
文件创建策略使用 wx 标志(排空写入)尝试创建文件,如果文件已存在(EEXIST 错误)则保留现有内容。这确保了 /memory 命令的幂等性——重复执行不会丢失已有记忆。
编辑器集成优先使用 $VISUAL 环境变量,回退到 $EDITOR,两者都未设置时使用系统默认编辑器。命令完成后显示实际使用的编辑器和设置方法,引导用户自定义编辑环境。
Sources: memory.tsx | index.ts
总结与最佳实践
Claude Code 的记忆系统通过文件系统持久化 + API 同步 + 智能检索的三层架构,实现了跨会话的上下文保持和团队协作。系统的核心优势在于:
- 非冗余性:封闭类型分类法确保只存储不可推导的信息
- 渐进式同步:基于内容哈希的增量上传最小化带宽消耗
- 安全优先:多层路径验证和敏感信息扫描防止数据泄露
- 性能优化:单次扫描、容量限制、智能选择保证系统响应性
最佳实践建议:
- 保持索引简洁:MEMORY.md 条目控制在 150 字符内,避免触发截断
- 定期清理:删除过时的 project 记忆,避免过期信息污染上下文
- 作用域意识:团队约定用 team 作用域,个人偏好用 private 作用域
- 结构化记忆:始终包含 Why 和 How to apply 字段,提高记忆可操作性
- 避免敏感数据:团队记忆中不要存储 API 密钥或凭证,同步前会被自动拦截
通过理解这些架构原理和设计决策,开发者可以更有效地利用记忆系统提升 Claude Code 的协作效率和上下文连续性。