Memory 系统
Claude Code 的 memory 系统为 agent 提供了跨 session 持久保存的文件型知识库。与短暂的对话 context(在 compaction 或 session 结束后会丢失)不同,memory 文件会随时间积累学习内容、偏好和项目知识。
graph TD
subgraph "Memory Directory (~/.claude/projects/<project>/memory/)"
E["MEMORY.md<br/>(index file, max 200 lines)"]
T1["topic-1.md"]
T2["topic-2.md"]
L["logs/YYYY/MM/YYYY-MM-DD.md<br/>(daily logs)"]
end
subgraph "System Prompt"
SP[Memory Prompt Section]
end
subgraph "Team Memory"
TM["Team member shared memory"]
end
E --> SP
T1 --> SP
TM --> SP
memory 目录遵循标准结构:
- MEMORY.md — 索引/入口文件,加载到每个 system prompt 中
- 主题文件 — 特定主题的详细 memory,从 MEMORY.md 引用
- 日志文件 — 按时间顺序排列的活动日志(用于 KAIROS/助手模式)
Memory 路径由 src/memdir/paths.ts 中的 getAutoMemPath() 按优先级链解析:
export const getAutoMemPath = memoize((): string => { // 1. Env var override (Cowork/SDK) const override = getAutoMemPathOverride() ?? getAutoMemPathSetting() if (override) return override
// 2. Default: ~/.claude/projects/<sanitized-git-root>/memory/ const projectsDir = join(getMemoryBaseDir(), 'projects') return join(projectsDir, sanitizePath(getAutoMemBase()), AUTO_MEM_DIRNAME) + sep})解析优先级:
| 优先级 | 来源 | 使用场景 |
|---|---|---|
| 1 | CLAUDE_COWORK_MEMORY_PATH_OVERRIDE 环境变量 | Cowork 空间级挂载 |
| 2 | settings.json 中的 autoMemoryDirectory | 用户自定义目录 |
| 3 | ~/.claude/projects/<sanitized-path>/memory/ | 默认 |
Git Worktree 感知
Section titled “Git Worktree 感知”基础路径使用 findCanonicalGitRoot(),因此同一仓库的所有 worktree 共享同一个 memory 目录:
function getAutoMemBase(): string { return findCanonicalGitRoot(getProjectRoot()) ?? getProjectRoot()}启用/禁用逻辑
Section titled “启用/禁用逻辑”自动 memory 可通过多种机制禁用:
export function isAutoMemoryEnabled(): boolean { // 1. Env var override (explicit off) if (isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_AUTO_MEMORY)) return false
// 2. Bare mode (--bare / SIMPLE) if (isEnvTruthy(process.env.CLAUDE_CODE_SIMPLE)) return false
// 3. CCR without persistent storage if (isEnvTruthy(process.env.CLAUDE_CODE_REMOTE) && !process.env.CLAUDE_CODE_REMOTE_MEMORY_DIR) return false
// 4. Settings.json opt-out const settings = getInitialSettings() if (settings.autoMemoryEnabled !== undefined) return settings.autoMemoryEnabled
// 5. Default: enabled return true}MEMORY.md:索引文件
Section titled “MEMORY.md:索引文件”MEMORY.md 是加载到 system prompt 中的入口文件,具有严格的大小限制:
export const ENTRYPOINT_NAME = 'MEMORY.md'export const MAX_ENTRYPOINT_LINES = 200export const MAX_ENTRYPOINT_BYTES = 25_000 // ~125 chars/line at 200 lines当 MEMORY.md 超出这些限制时,会被截断并附上警告:
export function truncateEntrypointContent(raw: string): EntrypointTruncation { const contentLines = raw.trim().split('\n') const wasLineTruncated = contentLines.length > MAX_ENTRYPOINT_LINES const wasByteTruncated = raw.trim().length > MAX_ENTRYPOINT_BYTES
if (!wasLineTruncated && !wasByteTruncated) { return { content: raw.trim(), /* ... */ } }
// Line-truncate first (natural boundary), then byte-truncate let truncated = wasLineTruncated ? contentLines.slice(0, MAX_ENTRYPOINT_LINES).join('\n') : raw.trim()
if (truncated.length > MAX_ENTRYPOINT_BYTES) { const cutAt = truncated.lastIndexOf('\n', MAX_ENTRYPOINT_BYTES) truncated = truncated.slice(0, cutAt > 0 ? cutAt : MAX_ENTRYPOINT_BYTES) }
return { content: truncated + `\n\n> WARNING: ${ENTRYPOINT_NAME} is ${reason}.Only part of it was loaded. Keep index entries to one line under ~200 chars;move detail into topic files.`, // ... }}这一设计强制 MEMORY.md 充当索引而非信息堆砌处。每条条目应为约 200 字符以内的单行,将详细内容指向主题文件。
Memory 类型
Section titled “Memory 类型”Memory 内容按类型分类,定义于 src/memdir/memoryTypes.ts:
// Memory taxonomy (from memoryTypes.ts)// - User preferences and style// - Project architecture and conventions// - Feedback and corrections// - Reference documentationmemory prompt 包含关于应保存什么、不应保存什么的具体指导:
import { MEMORY_FRONTMATTER_EXAMPLE, TRUSTING_RECALL_SECTION, TYPES_SECTION_INDIVIDUAL, WHAT_NOT_TO_SAVE_SECTION, WHEN_TO_ACCESS_SECTION,} from './memoryTypes.js'Agent 专属 Memory
Section titled “Agent 专属 Memory”自定义 agent 可以声明自己的 memory 作用域:
// In agent frontmatter or JSONmemory: 'user' | 'project' | 'local'| 作用域 | 位置 | 共享范围 |
|---|---|---|
user | 用户级目录 | 跨所有项目 |
project | 项目级目录 | 项目内 |
local | 仅本地 | 不共享 |
当 agent 启用 memory 时,其 system prompt 会被动态追加:
getSystemPrompt: () => { if (isAutoMemoryEnabled() && memory) { return systemPrompt + '\n\n' + loadAgentMemoryPrompt(agentType, memory) } return systemPrompt}此外,当 memory 启用时,文件 tool(Read、Edit、Write)会自动注入到 agent 的 tool 集合中:
if (isAutoMemoryEnabled() && parsed.memory && tools !== undefined) { const toolSet = new Set(tools) for (const tool of [FILE_WRITE_TOOL_NAME, FILE_EDIT_TOOL_NAME, FILE_READ_TOOL_NAME]) { if (!toolSet.has(tool)) { tools = [...tools, tool] } }}Memory 快照
Section titled “Memory 快照”AGENT_MEMORY_SNAPSHOT feature 支持项目级 memory 快照——一种向项目所有用户分发预构建 memory 的方式:
async function initializeAgentMemorySnapshots(agents: CustomAgentDefinition[]): Promise<void> { await Promise.all(agents.map(async agent => { if (agent.memory !== 'user') return const result = await checkAgentMemorySnapshot(agent.agentType, agent.memory) switch (result.action) { case 'initialize': // Copy snapshot to local if no local memory exists await initializeFromSnapshot(agent.agentType, agent.memory, result.snapshotTimestamp!) break case 'prompt-update': // Newer snapshot available — flag for user prompt agent.pendingSnapshotUpdate = { snapshotTimestamp: result.snapshotTimestamp! } break } }))}Team Memory
Section titled “Team Memory”TEAMMEM feature 支持跨团队成员的共享 memory:
// src/memdir/memdir.ts — conditional importconst teamMemPaths = feature('TEAMMEM') ? require('./teamMemPaths.js') : nullTeam memory 路径(src/memdir/teamMemPaths.ts)和 team memory prompt(src/memdir/teamMemPrompts.ts)允许团队成员共享同一个 memory 目录,实现同一 team 中 agent 之间的知识传递。
Extract 模式
Section titled “Extract 模式”EXTRACT_MEMORIES feature 运行一个后台 agent 从对话中提取 memory:
export function isExtractModeActive(): boolean { if (!getFeatureValue_CACHED_MAY_BE_STALE('tengu_passport_quail', false)) return false return !getIsNonInteractiveSession() || getFeatureValue_CACHED_MAY_BE_STALE('tengu_slate_thimble', false)}后台提取 agent 扫描对话轮次中的可记忆信息并写入 memory 目录。当主 agent 自行写入 memory 时,后台 agent 会跳过相应范围(通过 hasMemoryWritesSince 去重)。
在 KAIROS/助手 session 中,memory 采用不同形式——日志文件:
export function getAutoMemDailyLogPath(date: Date = new Date()): string { const yyyy = date.getFullYear().toString() const mm = (date.getMonth() + 1).toString().padStart(2, '0') const dd = date.getDate().toString().padStart(2, '0') return join(getAutoMemPath(), 'logs', yyyy, mm, `${yyyy}-${mm}-${dd}.md`)}agent 不再维护 MEMORY.md 作为实时索引,而是在工作过程中向以日期命名的日志文件追加内容。一个单独的每夜 /dream skill 会将这些日志提炼为主题文件和 MEMORY.md。
Memory 路径经过严格的安全验证:
// src/memdir/paths.ts — validateMemoryPathfunction validateMemoryPath(raw: string | undefined, expandTilde: boolean): string | undefined { // Reject: relative paths, root/near-root, Windows drive-root, // UNC paths, null bytes if ( !isAbsolute(normalized) || normalized.length < 3 || /^[A-Za-z]:$/.test(normalized) || normalized.startsWith('\\\\') || normalized.startsWith('//') || normalized.includes('\0') ) { return undefined } return (normalized + sep).normalize('NFC')}isAutoMemPath() 函数被文件系统 permission 系统用于在无需明确用户授权的情况下授予对 memory 文件的写权限——这一豁免使 memory 写入无缝流畅,但需要严格的路径验证以防止滥用。