Claude Code 源码被公开后,有人对它做了完整的反向工程。它的记忆和上下文管理系统不是一个模块,是七层,每一层都比上一层更贵但更强,系统设计的基本逻辑是:让便宜的层拦截贵的层

基础:Token 计数与上下文窗口

Claude Code 标准上下文窗口是 200K token(加 [1m] 后缀可扩展到 1M)。一次编码会话轻松超出这个限制——几次文件读取、grep 结果、几个编辑周期,就没了。

Token 计数的标准函数是 tokenCountWithEstimation():以上一次 API 响应的 input_tokens 为基数,加上后续消息的粗估。粗估规则:普通文本每 token 4 字节,JSON 每 token 2 字节(JSON tokenize 密度更高),图片和文档一律固定 2000 token。

有效上下文窗口 = 标称窗口 - 20K(预留 compaction 输出的空间)——你不能用满整个窗口,因为需要留空间生成把你救回来的摘要。

七层记忆架构

第一层:工具结果存储(Tool Result Storage)

文件:src/utils/toolResultStorage.ts|成本:仅磁盘 I/O,无 API 调用|触发:每个工具结果实时

问题是:一次 grep 可能返回 100KB+ 文本,一次 cat 大文件可能 50KB。这些结果占据大量上下文,且在几分钟后就会过时。

解法:超过阈值的工具结果,完整内容写入磁盘(tool-results/<sessionId>/<toolUseId>.txt),上下文里只保留约 2KB 预览,用 <persisted-output> 标签包裹。模型需要时可用 Read 工具访问完整结果。

关键细节:ContentReplacementState 会冻结每次替换决策——同一结果在后续所有 API 调用中都获得相同预览,保证 prompt cache 命中。状态甚至通过 transcript 持久化,支持 session resume。

第二层:Microcompaction(微压缩)

文件:src/services/compact/microCompact.ts|成本:零到极低 API 成本|触发:每轮对话,API 调用前

Microcompaction 不做摘要,只是清除不太可能再被用到的旧工具结果。三种机制:

时间触发的微压缩:空闲超过 60 分钟后(Anthropic 服务端 prompt cache 约 1 小时 TTL 过期),清除旧工具结果,保留最近 N 条。

缓存微压缩(最有技术含量的机制):不修改本地消息(会破坏 prompt cache),而是用 API 的 cache_edits 机制从服务端 cache 删除指定工具结果,本地消息不变。API 层操作,不影响 cache 前缀。

API 层上下文管理apiMicrocompact.ts):用 context_management API 参数直接告诉服务端处理,客户端不需要跟踪。

可被清除的工具:FileRead、Bash/Shell、Grep、Glob、WebSearch、WebFetch、FileEdit、FileWrite。不包括:thinking blocks、assistant 文本、用户消息、MCP 工具结果。

第三层:Session Memory(会话记忆)

文件:src/services/SessionMemory/|成本:每次提取一次 fork agent API 调用|触发:对话中周期性(采样后钩子)

核心思想:不要等到上下文满了才手忙脚乱地总结一切,而是持续维护会话笔记。等到 compaction 真正需要时,摘要已经在那儿了,不需要昂贵的 summarizer API 调用。

Session memory 文件在 ~/.claude/projects/<slug>/.claude/session-memory/<sessionId>.md,有结构化模板:

# Session Title # Current State # Task specification # Files and Functions # Workflow # Errors & Corrections # Codebase and System Documentation # Learnings # Key results # Worklog

触发条件:自上次提取以来 token 增量 ≥ 最小阈值 (工具调用数 ≥ 阈值 或 上一轮没有工具调用)。Token 阈值是必选项,"上一轮无工具调用"捕捉自然对话中断点。

提取作为 fork subagent 运行,只允许对记忆文件使用 FileEdit,其他工具全部拒绝,共享父 prompt cache 以节省成本。

第四层:Session Memory Compaction(会话记忆压缩)

成本:无需 API 调用(使用已有的 session memory 文件)|触发:autocompact 时,session memory 有实际内容

这是整个系统的关键回报:当 autocompact 触发时,系统先尝试 trySessionMemoryCompaction()——用已有的 session memory markdown 作为压缩摘要,不需要 API 调用,直接注入上下文。系统只需要决定保留哪些最近的对话消息。

相比完整压缩,这不需要 summarizer API 调用,不需要构建摘要 prompt,不消耗输出 token。Session memory 文件本身就是摘要。

第五层:Full Autocompact(完整自动压缩)

文件:src/services/compact/compact.ts|成本:一次全量 API 调用(输入 = 整段对话,输出 = 摘要)|触发:上下文超过 autocompact 阈值且 session memory compaction 不可用

触发条件:tokenCountWithEstimation(messages) > effective window - 13K

熔断机制:连续失败 3 次后,autocompact 在本次会话剩余时间停止。加入这个机制是因为发现 1279 个会话各有 50+ 次连续失败(个别高达 3272 次),每天全球浪费约 25 万次 API 调用。

压缩算法四步:

  1. 预处理:执行用户配置的 PreCompact 钩子,剥离图片和技能发现附件
  2. 生成摘要:fork summarizer agent,请求 9 段结构化摘要(分析块 + 摘要块,分析块最终被剥离,不消耗压缩后的 token)
  3. 压缩后恢复:重新注入最近读取的 5 个文件(每个 5K token)、调用的技能内容(每个 5K)、Plan 附件、延迟工具 schema 等
  4. 写入边界消息 SystemCompactBoundaryMessage 标记压缩点

第六层:Auto Memory Extraction(自动记忆提取)

文件:src/services/extractMemories/extractMemories.ts|成本:一次 fork agent API 调用|触发:每个完整查询循环结束时(模型产出最终响应且无工具调用)

功能是将对话中有价值的知识提炼为持久化记忆,存入 ~/.claude/projects/<path>/memory/

四种记忆类型及保存标准:User(你是谁)、Feedback(你给过的指导)、Project(代码库里发生的事)、Reference(外部系统指针)。提取 prompt 明确排除了:代码模式/规范/架构(可从代码推导)、Git 历史、调试解决方案(修复已在代码里)、CLAUDE.md 内容、临时任务细节。

如果主 agent 本轮已经写了记忆文件,后台提取 agent 跳过——避免重复劳动。

第七层:Dreaming(梦境整合)

文件:src/services/autoDream/autoDream.ts|成本:一次 fork agent API 调用(可能多轮)|触发:后台运行,积累足够时间和会话后

概念是跨会话记忆整合——后台进程回顾过去的会话记录,将记忆组织进长期存储。类似于生物记忆在睡眠期间的整合过程:回顾一天的经历,组织并整合进长期记忆。

四级递进门控(每级比下一级便宜,大多数检查提前退出):

  • Phase 1 Orient:读取记忆目录,理解当前索引
  • Phase 2 Gather Recent Signal:回顾每日日志,检查是否有与代码库矛盾的 drift 记忆,窄范围 grep session transcripts("不要穷尽式读 transcript,只看你已怀疑重要的事物")
  • Phase 3 Consolidate:写或更新记忆文件,合并新信号到已有主题而非创建近似重复,将相对日期转换为绝对日期
  • Phase 4 Prune and Index:更新 MEMORY.md 保持在 200 行 / 25KB 以内,删除过时/错误/被取代的记忆指针

梦境 agent 工具有严格限制:Bash 仅读命令,Edit/Write 仅限记忆目录,无 MCP 工具,无 Agent 工具,无破坏性操作。

Forked Agent 模式

Claude Code 几乎所有后台操作(session memory、自动记忆、梦境整合、压缩、agent summaries)都使用 fork agent 模式。核心设计:

缓存安全参数(CacheSafeParams):fork 的系统 prompt、工具、消息前缀必须与父 agent 字节完全一致,所以 API 能 cache 命中。

Fork 创建隔离上下文(克隆的 LRU cache、独立的 abortController、独立的 denialTracking),但共享 prompt cache(保持相同的 cache 关键参数)。

所有 fork child 产生字节级相同的 API 请求前缀,只有最后一个指令文本块因 child 而异——实现跨并发 fork 的最大 prompt cache 共享。

核心工程洞察

几乎每个设计决策都考虑 prompt cache 影响。 Anthropic 的 API 服务端 prompt cache 约 1 小时 TTL。Cache 命中意味着你只需为新 token 付费;Cache 未命中意味着要从头 tokenize 整个 prompt。在 200K token 规模上,这是每次请求 ~$0.003 和 ~$0.60 的差别。

七层系统的设计原则是让更便宜的层拦截更贵的层

  • 工具结果存储 → 防止 microcompact 需要清除太多
  • Microcompaction → 防止 session memory compaction
  • Session memory compaction → 防止完整压缩
  • 完整压缩 → 防止 context overflow 错误

每个系统都设有 GrowthBook feature flag 支持快速回滚。梦境扫描有 10 分钟节流阀。梦境锁是 PID-based mutex 加 60 分钟过期检测。完整压缩有 3 次熔断机制。每个系统静默失败,交给下一层接住。