资讯动态

Claude Code 记忆系统源码分析:extractMemories 与 autoDream 如何协作

发布时间:2026/10/10 0:36:59 来源:尧图企业网站定制
1. 从一次“记忆丢失”说起extractMemories 与 autoDream 到底在做什么如果你用 Claude Code 写过几天代码大概率遇到过这种场景昨天刚跟它强调过“这个仓库的集成测试不要 mock 数据库”今天开新会话它又给你写了一个 mock 版本。你以为是模型变笨了其实是记忆系统没把这条信息接住。Claude Code 的记忆系统是一套文件驱动的外部记忆机制核心不在内存里而在磁盘上。它由两个关键机制协作完成extractMemories负责“每轮对话结束后把值得记的东西写下来”autoDream负责“定期把积累的记忆整理、合并、裁剪”。一个管增量写入一个管存量整理两者靠时间分离和独立锁避免打架。这套机制适合谁适合所有想让 Claude Code 在长期项目里保持上下文一致性的开发者尤其是多 worktree、多会话并行、需要跨天延续上下文的团队。它不适合把记忆当数据库用的场景——因为它的写入没有并发保护索引有 200 行硬上限compact 之后还有游标边界问题。我试过在自己的一个 Go React 混合项目里复现整套读写流程踩了几个坑下面按“配置 → 触发 → 验证 → 排障”的顺序拆开讲。先给结论记忆目录默认在~/.claude/projects/sanitized-git-root/memory/MEMORY.md是纯索引正文在独立的.md文件里每轮对话结束后后台分叉代理负责提取满足“距上次整理 ≥24h 且新增 ≥5 个会话”才触发自动整理。理解这套链路之前先记住一个数字5。相关记忆筛选最多注入 5 条提取代理最多跑 5 轮。这个数字贯穿全文很多边界问题都跟它有关。2. 前置准备TaoToken 接入与记忆目录初始化在复现记忆读写流程之前得先让 Claude Code 能正常跑起来。我用的是 TaoToken 作为模型接入层它的 Base URL 和 Key 配置方式和 Anthropic 官方兼容改一下环境变量就能用。先拿 Key。打开 https://taotoken.net/api-keys 创建一个 API Key复制出来。注意这个 Key 只在创建时完整显示一次丢了就得重建。然后配置 Claude Code 的接入。Claude Code 读取的是环境变量最稳妥的方式是写进 shell 配置文件而不是每次手动 export。以 zsh 为例编辑~/.zshrcexport ANTHROPIC_BASE_URLhttps://taotoken.net/api export ANTHROPIC_AUTH_TOKENsk-你的TaoToken密钥 export ANTHROPIC_MODELclaude-sonnet-4-5-20250929这里有个容易踩的坑ANTHROPIC_BASE_URL不要带末尾斜杠也不要带/v1Claude Code 内部会自己拼路径。我第一次配的时候多写了个/v1结果所有请求 404排查了半小时。如果你用的是 Codex 或 Cline 这类工具配置位置不一样。Codex 读的是~/.codex/auth.json结构长这样{ OPENAI_API_KEY: sk-你的TaoToken密钥, OPENAI_BASE_URL: https://taotoken.net/api }Cline 的 MCP 配置则在 VS Code 的 settings 里走的是cline_mcp_settings.json。不管哪个工具三件套必须齐全Base URL Key Model ID。少一个都会报 401 或 model not found。配置完先验证一下能不能通。用 curl 直接打一次模型对话接口curl https://taotoken.net/api/v1/messages \ -H x-api-key: sk-你的TaoToken密钥 \ -H anthropic-version: 2023-06-01 \ -H content-type: application/json \ -d { model: claude-sonnet-4-5-20250929, max_tokens: 64, messages: [{role: user, content: 回复 OK 两个字母}] }返回里能看到content数组带一段文本就说明接入通了。这一步不通后面记忆系统根本不会触发因为 extractMemories 是挂在对话结束钩子上的。接入通了之后确认记忆目录。默认路径是~/.claude/projects/sanitized-git-root/memory/其中sanitized-git-root是把 git 根路径里所有非字母数字字符替换成-。比如你的项目在/Users/zhang/my-project目录就是-Users-zhang-my-project。想改路径有两个办法。一是环境变量CLAUDE_COWORK_MEMORY_PATH_OVERRIDE优先级最高二是在settings.json里配autoMemoryDirectory支持~/展开。注意autoMemoryDirectory只信任 policy/local/user 来源不信任提交到仓库的projectSettings——这是防止恶意仓库把记忆目录重定向到~/.ssh之类的敏感位置。初始化目录mkdir -p ~/.claude/projects/-Users-zhang-my-project/memory touch ~/.claude/projects/-Users-zhang-my-project/memory/MEMORY.mdMEMORY.md必须是纯索引每行一条指针格式固定- [用户角色](user_role.md) — 数据科学家关注可观测性 - [测试规范](feedback_testing.md) — 集成测试必须用真实数据库上限 200 行 / 25KB超了会被截断并注入警告。这个上限后面会引出“孤儿记忆”问题先记住。3. 可复制配置extractMemories 与 autoDream 的触发参数这一节给可直接复制的配置片段。记忆系统的行为受settings.json和几个特性开关控制我把关键项列出来。先看settings.json里跟记忆相关的部分。路径是~/.claude/settings.json用户级或项目级.claude/settings.json{ autoMemory: true, autoDream: { enabled: true, minHours: 24, minSessions: 5 }, autoMemoryDirectory: ~/.claude/projects/-Users-zhang-my-project/memory }autoMemory控制 extractMemories 的总开关。autoDream.enabled控制自动整理minHours和minSessions是双重门控——必须同时满足“距上次整理 ≥24 小时”和“新增 ≥5 个会话”才会触发。这两个值实际由远程特性tengu_onyx_plover控制本地配置只是默认值。如果你用 KAIROS 助手模式assistant: trueautoDream 会被禁用改由/dream技能按计划任务执行。KAIROS 模式下记忆写入走的是追加日志# 自动记忆 本会话是长期运行的。工作过程中将任何值得记住的内容追加写入今日的日志文件 {memoryDir}/logs/YYYY/MM/YYYY-MM-DD.md日志是纯追加的不重写不整理由夜间进程蒸馏成MEMORY.md和主题文件。日志路径写成YYYY/MM/YYYY-MM-DD.md模式而不是当天实际路径是因为这段提示词会被 prompt cache 缓存跨午夜不会失效——模型从currentDate上下文附件里拿当前日期。再看记忆文件的格式。每个记忆文件都有 YAML frontmatter--- name: 测试规范 description: 集成测试必须使用真实数据库禁止 mock type: feedback --- 规则集成测试必须连接真实数据库。 Why: 上个季度 mock 测试通过但生产迁移失败造成事故。 How to apply: 所有涉及数据库迁移的测试用例禁止使用 mock 框架。type有四种user、feedback、project、reference。其中feedback和project强制要求正文包含Why和How to apply目的是让未来的模型能判断边界情况而不是盲目执行规则。description字段很关键——它是findRelevantMemories筛选时唯一读取的内容只读 frontmatter 前 30 行不加载正文。所以description写得准不准直接决定这条记忆会不会被选中注入。extractMemories 的触发挂在对话结束钩子上代码逻辑大致是// 每轮对话结束后 if (inProgress) return inProgress true try { await executeExtractMemories() } finally { inProgress false }inProgress是进程内互斥锁防止同一会话多轮同时触发。但它对跨进程的 autoDream 无效所以 autoDream 另有一套跨进程文件锁.consolidate-lock。autoDream 的抢锁逻辑用 PID 文件加二次验证await writeFile(lockPath, String(process.pid)) const verify await readFile(lockPath, utf8) if (parseInt(verify.trim(), 10) ! process.pid) return null两个进程同时抢锁时最后写入 PID 的胜出另一个回退。这是乐观锁不是原子操作没有 flock但最终一致性可接受。配置完这些重启 Claude Code 让环境变量生效。接下来验证触发链路。4. 验证请求观察记忆读写与成功结果配置好之后怎么确认 extractMemories 真的在写记忆我用的办法是手动制造一条明确的 feedback然后看目录里有没有新文件。第一步开一个新会话给 Claude Code 一条明确的纠正指令不要在这个项目里用 npm统一用 bun。之前混用导致 lock 文件冲突过。第二步等它回复完去看记忆目录ls -la ~/.claude/projects/-Users-zhang-my-project/memory/正常情况下几秒内会多出一个feedback_*.md文件MEMORY.md里也会多一行指针。如果没出现先检查autoMemory是不是 true再看特性开关EXTRACT_MEMORIES有没有启用。第三步验证记忆文件内容cat ~/.claude/projects/-Users-zhang-my-project/memory/feedback_package_manager.md应该能看到 frontmatter 里type: feedback正文里有Why和How to apply。如果只有规则没有 Why说明提取代理没按提示词要求写这种情况在早期版本里出现过。第四步验证相关记忆注入。开一个新会话问一个跟包管理器相关的问题这个项目装依赖用什么命令Claude Code 在会话开始时会加载MEMORY.md进系统提示会话中通过findRelevantMemories用 Sonnet 从全量记忆文件里筛最多 5 条相关记忆。筛选时只读 frontmatter按修改时间排序最多扫 200 个文件。筛选用的系统提示词里有一句关键约束Return a list of filenames for the memories that will clearly be useful... (up to 5). Only include memories that you are certain will be helpful.也就是说不确定有用的不选宁缺毋滥。如果返回空列表说明 Sonnet 判断没有明确相关的记忆。第五步验证 autoDream。手动触发/dream技能观察四阶段整理/dream整理提示词分四个阶段定向ls 记忆目录、读 MEMORY.md、收集信号看日志、找漂移的记忆、grep transcript、整理合并到现有主题文件、相对日期转绝对日期、删除被推翻的事实、裁剪索引更新 MEMORY.md 保持在 200 行 / 25KB 以内。整理完会返回一份摘要说明合并、更新、裁剪了哪些内容。如果记忆已经很紧凑它会说“无需变更”。自动触发的话得等 24 小时且新增 5 个会话。想快速验证可以把minHours和minSessions临时调小但注意这两个值可能被远程特性覆盖本地改不一定生效。验证过程中有个细节值得注意findRelevantMemories的筛选结果不持久化只是临时拼成清单发给 Sonnet用完即丢。它跟MEMORY.md是两个东西——MEMORY.md是持久化索引加载进系统提示筛选清单是内存里临时拼的不落盘。5. 常见报错排查401、local proxy failed 与游标问题这一节对照真实报错。记忆系统本身不直接报网络错但接入层出问题会导致 extractMemories 根本不触发因为对话都没跑完。报错一401 Unauthorized{type:error,error:{type:authentication_error,message:invalid x-api-key}}原因通常是 Key 没配对或者 Base URL 带了多余路径。检查三件套echo $ANTHROPIC_BASE_URL # 应该是 https://taotoken.net/api echo $ANTHROPIC_AUTH_TOKEN # 应该是 sk- 开头如果 Base URL 末尾有/v1或斜杠去掉。如果 Key 是从别处复制的注意有没有多余空格。报错二local proxy failedError: local proxy failed to connect这个报错通常出现在工具层配置了本地代理但代理没起来。检查你的工具配置里有没有指向localhost的代理设置有的话去掉直接用 TaoToken 的 Base URL。Claude Code 本身不需要本地代理。报错三reading choices of undefinedTypeError: Cannot read properties of undefined (reading choices)这是 OpenAI 格式和 Anthropic 格式混用导致的。Claude Code 走的是 Anthropic Messages API返回结构是content数组不是choices。如果你在 Codex 或 Cline 里配了 Anthropic 的 Base URL 但工具按 OpenAI 格式解析就会报这个。检查工具的 API 格式设置或者换用对应的端点。报错四OAuth token expiredOAuth token has expired, please re-authenticate如果你之前用 OAuth 登录过官方账号环境变量里的ANTHROPIC_AUTH_TOKEN可能被 OAuth token 覆盖。清掉 OAuth 缓存rm -rf ~/.claude/oauth然后重新用 API Key 配置。记忆系统特有的问题记忆没写入对话正常但记忆目录没新文件排查顺序先看autoMemory是不是 true。再看主代理是不是已经直接写过记忆——hasMemoryWritesSince()会检测主代理是否已写过写过就跳过提取避免重复。如果你在对话里明确让 Claude Code “记住这个”它可能直接写了后台提取就不再跑。再看提取代理的 5 轮上限。maxTurns: 5是硬上限策略是第 1 轮并行 Read、第 2 轮并行 Write。如果一次对话触及 10 个主题5 轮内写不完未写完的记忆会静默丢失——runForkedAgent正常返回游标正常推进没有截断检测或补偿机制。游标与 compact 的边界问题lastMemoryMessageUuid是提取代理的游标记录上次处理到哪条消息。正常完成时游标推进异常时游标不推进下轮重新处理。但 compact上下文压缩之后有个未定义行为compact 把旧消息压缩成摘要原始消息从context.messages里删除而游标指向的 UUID 可能已经不在消息列表里。postCompactCleanup清理了 microcompact 状态、context collapse、内存文件缓存但没有重置lastMemoryMessageUuid。compact 后首次提取的行为取决于countModelVisibleMessagesSince找不到游标时的降级逻辑源码没有明确处理这个边界。实际影响compact 前未被提取的消息细节可能永久丢失。规避办法是在长对话里定期手动触发/dream或者主动让 Claude Code 把重要信息写进记忆文件别等后台提取。多 worktree 并发覆盖同一 git 仓库的所有 worktree 共享同一个memory/目录。autoDream 有跨进程文件锁保护但 extractMemories 的记忆文件写入完全没有并发保护。两个 worktree 的提取代理同时写feedback_testing.md后写的覆盖先写的无合并无冲突检测。规避办法多 worktree 并行时尽量错开对话结束时间或者手动指定不同的autoMemoryDirectory。这是设计取舍源码注释里有“same repo shares memory dir”的说明但没有承认覆盖风险。6. 语义一致 CTA把记忆系统接进你的长期项目整套链路跑通之后你会发现 Claude Code 的记忆系统本质上是“文件 提示词 后台代理”的组合没有黑魔法。extractMemories 管增量autoDream 管存量findRelevantMemories 管按需注入三者靠时间分离和独立锁协作。如果你想在自己的项目里稳定用起来几个实操建议把MEMORY.md当索引维护别往里塞正文。每行一条指针控制在 150 字符以内。正文写进独立的.md文件description字段写准因为筛选时只读这个。feedback和project类型的记忆务必写清Why和How to apply。没有 Why 的规则未来的模型没法判断边界情况只能盲目执行反而容易出错。长对话记得手动/dream别完全依赖 24 小时 5 会话的自动触发。compact 之后的游标边界问题目前没有确定性修复主动整理更稳。多 worktree 并行时注意写入覆盖风险必要时给每个 worktree 配独立的记忆目录。接入层用 TaoToken 的话Base URL 固定https://taotoken.net/apiKey 从 https://taotoken.net/api-keys 拿模型 ID 按需选。三件套配齐401 和格式错基本就没了。想验证模型对话是否正常可以直接在 https://taotoken.net/api 的对话入口试一条消息确认返回结构是content数组而不是choices。长期跑编码任务或 Agent 的话Coding Plan 比按量计费更划算适合把记忆系统当基础设施用的场景。接入文档在 https://taotoken.net/doc 里面有各工具的完整配置示例。最后留一个我踩过的坑别把autoMemoryDirectory配到项目仓库里。它只信任 policy/local/user 来源不信任projectSettings配在.claude/settings.json里提交到仓库是不生效的而且有安全风险。配在用户级~/.claude/settings.json里最稳。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑