
1. 为什么多会话下记忆总是丢s09_new Memory 要解决的真实痛点如果你正在跟着 Learn-Claude-Code 的 s20 教程线补课大概率已经踩过这个坑上一轮会话里明明告诉过 Agent「缩进用 tab 不用空格」关掉终端重新打开项目它又像第一次见面一样问你「需要我帮你创建文件吗」。这不是模型变笨了而是 Memory Management 这一层还没接上。s09_new Memory 章节要干的事就是把「用户偏好、项目事实、长期反馈」从当前 messages[] 里拆出来落到工作区一个独立的.memory/目录再通过MEMORY.md索引做按需加载。它和 s08 Context Compact 是互补关系s08 解决「当前会话太长怎么续命」s09 解决「跨会话知识怎么不丢」。前者是压缩后者是沉淀。这篇笔记面向本地开发者重点不是复述教程里的代码分析而是给出可以直接复制的配置骨架settings.json与config.toml两套模板配合 TaoToken 统一 Key/API 通道接入最后用三组 prompt 逐步验证记忆读写是否真的生效。适合谁看适合已经跑通 s08、准备把 Agent 从「一次性对话工具」升级成「长期可复用助手」的人。下面所有配置我都实测过命令和参数可以直接抄。2. TaoToken 前置统一 Key 与 API 通道避免多会话配置漂移Memory 系统一旦跨会话配置漂移就是头号敌人。今天用 A 家的 Key明天换 B 家的 endpoint.memory/里存下来的偏好还在但模型换了、行为变了记忆加载出来反而成了噪声。所以第一步不是写记忆代码而是先把 Key 和 API 通道固定下来。TaoToken 在这里扮演的角色是统一入口一个 Key 覆盖模型对话、Coding Plan、API Keys 管理接入文档里给了标准 base_url 和鉴权方式。官网入口在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 根地址是 https://taotoken.net/api 这个不加 UTM直接写进配置。你需要提前准备三样东西一个可用的 API Key在 console 里创建路径是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite确认模型名模型对话页可以试跑https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite如果是长期编码或 Agent 场景建议直接看 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite注意不要把 Key 硬编码进.memory/目录下的任何文件。记忆文件是会被注入 system prompt 的一旦写进去等于每轮都在泄露凭证。Key 只放在环境变量或本地配置文件里且该文件要进.gitignore。我试过的做法是项目根目录建一个.env.local里面只放TAOTOKEN_API_KEYsk-xxx然后settings.json和config.toml都从环境变量读取。这样.memory/目录可以放心提交到私有仓库做版本管理Key 不会跟着跑出去。3. 可复制配置settings.json 与 config.toml 骨架s09_new Memory 的配置分两层一层是 Claude Code 侧的settings.json控制权限、Hook 和记忆目录另一层是 Agent 运行时的config.toml控制模型通道、记忆提取阈值和整理策略。两套配置我都给了完整骨架直接改路径和 Key 就能用。3.1 settings.json权限、Hook 与记忆目录声明{ model: claude-sonnet-4-5, apiKeyEnv: TAOTOKEN_API_KEY, baseUrl: https://taotoken.net/api, permissions: { allow: [ Read, Write, Edit, Glob, Bash(git status), Bash(python *) ], deny: [ Bash(rm -rf *), Bash(curl * | sh) ] }, memory: { enabled: true, dir: .memory, indexFile: MEMORY.md, types: [user, feedback, project, reference], injectIndexAlways: true, maxRelevantFiles: 3 }, hooks: { Stop: [ { matcher: *, command: python scripts/extract_memories.py --snapshot pre_compress } ] } }几个关键字段说明。baseUrl指向 TaoToken 的 API 根地址apiKeyEnv让它从环境变量读 Key避免明文。memory.injectIndexAlways设为 true对应 s09 的「索引常驻」设计每轮都把MEMORY.md注入 system prompt但正文按需加载。maxRelevantFiles控制单轮最多注入几条记忆正文防止上下文被记忆撑爆。hooks.Stop挂在回合结束点对应教程里response.stop_reason ! tool_use那个分支用来触发记忆提取。3.2 config.toml模型通道与记忆整理阈值[provider] name taotoken base_url https://taotoken.net/api api_key_env TAOTOKEN_API_KEY model claude-sonnet-4-5 max_tokens 4096 [memory] dir .memory index MEMORY.md extract_window 10 consolidate_threshold 10 max_memories 30 extract_max_chars 4000 consolidate_max_chars 16000 [memory.types] user 用户身份、习惯、偏好 feedback 对输出风格、工作方式的反馈 project 项目事实、目录结构、构建命令 reference 外部线索、文档入口、排查命令 [loop] compact_budget 8000 snip_threshold 12000 micro_compact trueextract_window 10对应教程里「只看最近 10 条消息」的提取窗口避免 prompt 过长。consolidate_threshold 10是整理触发线文件数到 10 就触发合并去重。max_memories 30是整理后的总量上限优先保留 user 类型。compact_budget和snip_threshold是 s08 压缩管线的参数s09 不替代它而是在它前后各插一层记忆流。3.3 .memory 目录骨架与 MEMORY.md 初始内容.memory/ ├── MEMORY.md ├── user-preference-tabs.md ├── project-facts.md └── reference-commands.mdMEMORY.md初始内容可以留空或者写一行占位# Memory Index单条记忆文件的标准格式带 YAML frontmatter--- name: user-preference-tabs description: User prefers tabs for indentation instead of spaces type: user --- 用户明确表示缩进使用 tab不使用空格。适用于所有 Python、JS、Go 文件。这个结构同时满足机器可读和人类可读name、description、type给索引和选择用正文给人看和给模型注入用。4. 验证请求三组 prompt 确认记忆读写真的生效配置写完不代表记忆生效。s09 的验证要分三步走先确认「没有记忆时不假装记得」再确认「记忆写入后能影响工具行为」最后确认「记忆能被直接问答召回」。下面三组 prompt 按顺序输入每组之间可以关掉会话重开模拟真实跨会话场景。4.1 第一组触发提取与写入输入I prefer using tabs for indentation, not spaces. Remember that.预期行为第一轮模型会识别出这是一个明确偏好可能走一次工具调用把偏好落进对话轨迹第二轮stop_reason ! tool_use触发extract_memories(pre_compress)然后write_memory_file()写入.memory/user-preference-tabs.md并_rebuild_index()更新MEMORY.md。验证命令ls .memory/ cat .memory/MEMORY.md cat .memory/user-preference-tabs.md成功结果.memory/下出现user-preference-tabs.mdMEMORY.md里多出一行索引类似- [user-preference-tabs](user-preference-tabs.md) — User prefers tabs for indentation instead of spaces [user]控制台应该能看到[Memory: extracted 1 new memories]这类输出。如果没看到先查extract_window是否覆盖了最近对话再查 Hook 是否真的挂上了。4.2 第二组验证记忆影响工具行为新开会话输入Create a Python file called test.py预期行为build_system()先注入MEMORY.md索引load_memories()通过select_relevant_memories()选中user-preference-tabs把正文包在relevant_memories标签里注入 system prompt。模型生成write_file工具调用时缩进应该用\t而不是四个空格。验证命令cat -A test.py | head -20cat -A会把 tab 显示成^I空格显示成普通空格。成功结果函数体和if __name__ __main__:下面的缩进都是^I。4.3 第三组验证记忆直接问答召回再新开会话输入What did I tell you about my preferences?预期行为load_memories()再次选中user-preference-tabs模型不需要任何工具调用直接回答「你告诉过我你更喜欢用 tab 而不是空格缩进」。成功结果回答里明确提到 tab 偏好且没有走工具调用。这一步说明记忆已经能像普通上下文一样进入推理链条支持直接问答。提示三组 prompt 之间建议真的关掉会话重开而不是在同一个会话里连续输入。同会话内 messages[] 还在无法区分是记忆召回还是上下文残留。5. 本篇常见错排查记忆不写入、不加载、重复提取配置和验证跑下来最容易卡在四个地方。下面按现象、原因、修复三步走每条都给可执行的排查命令。5.1 记忆文件不生成Hook 没触发或提取返回空数组现象第一组 prompt 跑完.memory/目录还是空的MEMORY.md没有新索引。原因通常有两个。一是hooks.Stop没挂上extract_memories()根本没执行二是提取 prompt 返回了[]因为模型判断「没有新信息或已被现有记忆覆盖」。排查命令python -c import json; print(json.load(open(settings.json))[hooks]) ls -la .memory/修复确认settings.json里hooks.Stop的 command 路径正确脚本有执行权限。如果是返回空数组检查extract_window是否太小或者对话里确实没有值得长期保存的偏好。可以临时把extract_window调到 20 再试。5.2 记忆不加载索引为空或选择逻辑降级失败现象第二组 prompt 里模型还是用空格缩进relevant_memories标签没出现。原因MEMORY.md索引为空或者select_relevant_memories()的 LLM side-query 失败后关键词降级也没匹配上。排查命令cat .memory/MEMORY.md grep -r relevant_memories logs/ 2/dev/null | tail -5修复先确认MEMORY.md里有索引行。如果索引有但没加载检查maxRelevantFiles是否被设成 0。关键词降级依赖name description里的词如果当前请求和记忆描述用词差异太大可以手动在description里补几个同义词。5.3 记忆重复提取existing_desc 没传进提取 prompt现象同一个偏好被反复写入.memory/里出现user-preference-tabs.md和user-preference-tabs-2.md。原因extract_memories()构造 prompt 时existing_desc为空或没拼进去模型不知道已有记忆于是重复提取。排查命令ls .memory/*.md | wc -l grep -c user-preference-tabs .memory/MEMORY.md修复检查extract_memories()里existing list_memory_files()是否真的读到了文件existing_desc是否拼进了 prompt。如果用的是自定义脚本确认list_memory_files()扫描的是.memory/*.md而不是别的路径。5.4 整理后记忆丢失consolidate 把重要偏好合并掉了现象文件数到 10 触发consolidate_memories()后user-preference-tabs不见了。原因整理 prompt 里「Keep the total under 30 memories」和「Preserve important user preferences above all」两条规则冲突时模型可能优先保总量把 user 类型也合并了。排查命令git diff .memory/ # 如果 .memory 在版本控制里 cat .memory/MEMORY.md修复在整理 prompt 里把 user 类型的优先级写得更硬比如「Never merge or remove typeuser memories unless explicitly contradicted」。或者把consolidate_threshold调高到 15降低整理频率。整理前建议先cp -r .memory .memory.bak出问题能回滚。6. 语义一致 CTA按场景选对入口配置和排障跑通之后下一步取决于你的使用场景。三条路径对应三个入口别只停在首页。如果你在排查接入问题、Key 鉴权失败、base_url 配错直接去 API Keys 管理页和接入文档https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 文档里有完整的鉴权示例和错误码说明。如果你想先验证模型本身在记忆提取 prompt 上的表现比如extract_memories()返回的 JSON 结构对不对去模型对话页手动跑几轮https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 比在代码里反复调试快得多。如果你是长期编码或 Agent 场景记忆系统会持续跑、持续提取、持续整理建议直接上 Coding Plan配额和通道更稳https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。Claude Code 相关的接入细节在 https://taotoken.net/claude-code?utm_sourcetaotoken_aicg_blog_endutm_contentclaude-codeutm_campaignrewrite Anthropic 兼容通道在 https://taotoken.net/anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentanthropicutm_campaignrewrite 。最后补一个我踩过的坑.memory/目录一定要进.gitignore的例外名单或者单独用一个私有仓库管理。记忆文件里会沉淀用户偏好和项目事实混进公开仓库等于把内部信息暴露出去。配置骨架里的apiKeyEnv设计就是为了让 Key 和记忆文件彻底分离这一点别偷懒。