Claude Code 如何管理上下文的记忆与压缩
本文是“Claude Code”系列的第五篇。前四篇分别介绍了任务/团队/时间管理、文件操作、子代理机制以及 Skill 渐进式披露。本篇聚焦于 Claude Code 的上下文管理核心——记忆(Memory)与压缩(Compaction),带你深入理解它如何在有限的上下文窗口中,既保留关键信息,又避免“爆窗”。核心死循环,查询问不停,结果需分类,各自做回调。工具归一化,渐进式加载,业务特别大,交给自代理。本地用工具,封装上下文,沙箱保安全,Hook发通知。外部信息也封装,需要小心GEO。蜂群团队齐上阵,搞定任务最重要。笔记随手做,查询要精确。压缩先工具,用完就扔掉。执行流程——总览记忆和压缩的点位记忆(Memory)相关节点启动记忆预取时机:在 queryloop 循环外执行一次。作用:提前加载相关记忆文件,避免每次工具调用都重新扫描磁盘。预取任务以Promise形式存在,并绑定AbortController,用户按下Escape时可立即取消。消费记忆预取时机:每轮工具执行后。作用:将预取的记忆内容作为附件注入对话,同时过滤重复项——防止同一个文件路径的记忆在上下文中被重复提供。预取与消费是“记忆注入”的前后两步。预取负责异步加载,消费负责去重并拼接到模型上下文。写过滤:按需去重(管理层视角)业务目的:防止同一个记忆文件在一次对话中多次出现,浪费 token 且干扰模型。工作流程:遍历输入的附件列表。仅处理类型为'relevant_memories'的附件,其他类型原样保留。筛选出readFileState中尚未记录的路径(即当前轮次模型尚未通过工具调用读取过的文件)。将筛选出的每个记忆写入readFileState(标记为“已提供”)。若某个附件过滤后仍有记忆,则返回更新后的附件;若无记忆,则删除该附件(返回null)。去除null项,返回有效附件列表。关键设计:先过滤,再标记(mark-after-filter)。如果预取阶段就写入状态,会导致所有预取记忆都被视为“已在上下文中”而过滤掉。只有真正被消费的附件才标记,打破了自引用循环。案例:假设用户问“上次那个配置文件怎么改的?”,系统预取到 3 个相关记忆文件。第一轮注入后,readFileState记录了它们的路径。第二轮如果又触发相同的预取,这 3 个文件会被自动过滤,不会重复发送给模型。读召回:基于 LLM 的选择(而非 BM25)Claude Code 没有使用传统的 BM25 或向量相似度,而是让模型自己判断哪些记忆文件对当前查询有帮助。系统提示核心要求从给定的记忆文件列表(文件名 + 描述)中选出最有可能对处理查询有帮助的记忆,最多 5 个。只选择确定有帮助的,不确定则排除。如果提供了最近使用过的工具列表,则不要选择这些工具的使用文档或 API 参考(因为对话中已有示例),但仍需选择包含警告、注意事项或已知问题的记忆。工作流程(上层)检查自动记忆功能和特性开关,未启用则返回undefined。从消息列表中查找最后一条真实用户消息(type='user'且isMeta !== true)。提取用户消息文本,若为空或单字,放弃预取。统计当前会话已浮现的记忆总字节数,若超过MAX_SESSION_BYTES,放弃预取。创建一个继承自toolUseContext.abortController的子AbortController。调用getRelevantMemoryAttachments执行实际搜索。错误处理:中止错误忽略,其他错误记录并返回空数组。构造MemoryPrefetch对象(实现Disposable),包含promise