Claude Code 背后藏了一位“档案管理员”:不删档案,只建索引

127 阅读13分钟

Claude Code 的上下文窗口到底是怎么管理的?

这几乎是 Agent 项目面试里的必答题。只要简历上做过 Agent 项目,面试官都会追问一句:你这个项目的上下文是怎么管的?

坦白说,这问题问的是技术,考的却是设计思想。 Claude Code 在这个问题上走得远比“快满了做一次摘要”要深——它采用的是一套分层递进的上下文管理机制,先前置压缩,再兜底恢复。

更有趣的是,如果把上下文管理比作一个档案室,Claude Code 的做法不是“满了就扔档案”,而是建了一套由浅入深的档案管理流程,层层分工,把新旧档案安排得明明白白。

下面我就用这套 “档案管理员”模型,把 Claude Code 的设计一层层拆给你看。

一、档案室为什么总会满?

要理解 Claude Code 怎么管,得先理解上下文窗口的本质。

大模型没有真正的“记忆”。每次你问它一个问题,它都得把 system prompt、所有历史对话、当前问题,一股脑塞进去,然后生成回复。这堆东西的总长度有上限,叫 “上下文窗口”

这就像档案室的 工作台面。模型能看见多少东西,全看台面上能摆下多少。台面大小固定,超出去的东西要么放不下,要么得挤掉已有的。

当前主流模型的窗口大约这么大:

模型窗口大小约合字数
Claude 3.5 Sonnet200k token30 万字
Claude Opus 4.7 (1M 版)1M token200 万字

200 万字听起来很大,但你真去做 Agent,会发现这张台面根本不够用。

为什么? 因为:

  • system prompt + 工具定义就已经占了约 20,000 token —— 在你说第一句话之前,档案管理员已经把台面的一角占满了。
  • MCP 工具生成的 schema,可能再吞掉 900 到 51,000 token
  • 每轮对话还会堆积大量“旧工具结果”——读过的文件、grep 输出、shell 日志。

以跨 20 个文件的重构为例,对话到第 7 步时,台面上的大部分东西是第 2-3 步的旧档案,已经被修改过或早已过时,却依然占着位置。

更让人头疼的是:性能实际上早在 147K token 时就开始下降了——这叫“lost in the middle”问题,模型对中间部分的信息变得越来越不敏感。

档案室就这样一点一点被占满了,越往后工作台面上的“噪声档案”越多,模型反而越难集中注意力。

那么 Claude Code 是怎么解决这个问题的?源码里定义了 5 层机制

📍 读源码时你会发现,Claude Code 不是在 query() 的入口处做一次检查,而是在主循环中放了 5 个固定的 context‑management 调用点。这几个点顺序固定,但每一层是否真正执行取决于当前 token 状态和配置。越靠前的层越便宜,越靠后的层成本越高。

主循环的调用链主要在 src/services/compact/index.ts 里实现。

二、5 层档案管理流程,从最便宜的开始

Claude Code 在调用 API 之前,会在主循环中依次经过 5 个管理关卡。这 5 层从便宜到昂贵、从轻量到深度,层层递进。

层级名称成本是否调用 LLM
1Tool Result Budget极低(磁盘 I/O)
2Snip Compact极低(字符串删除)
3Microcompact低(占位符替换)
4Context Collapse中(结构重组)❌(调用前最后一道)
5Auto‑Compact高(LLM 摘要)

下面逐一拆解。

🔹 第一层 – 临时档案归档(Tool Result Budget)

这是最便宜的关卡。Claude Code 会把大尺寸的工具输出结果直接存到磁盘上,不放在对话中来回传递。

相当于把那些偶尔查阅、但没必要一直摆在台面上的临时档案,直接搬进了档案库房。

源码中的关键阈值:50,000 字符

// 单个工具结果超过 50,000 字符的,不会完整塞进模型输入,
// 而是写入磁盘文件,再给模型发送一段替代消息。
// 替代消息里包含完整结果保存路径,以及开头约 2KB 的预览。

用完了怎么办?需要的时候再从磁盘读回来。这层操作几乎零成本,但省下的台面空间非常可观。

🔹 第二层 – 信息精简(Snip Compact)

有些档案不能搬走,但可以精简。比如一个执行过的 shell 命令,你不在乎它输出了多少行,只在乎它成功还是失败。

snipCompactIfNeeded 的实现逻辑是:在 snip_boundary 消息中标记哪些消息的 UUID 需要被移除,然后从消息列表中删掉它们。这类消息的典型特征就是纯工具输出且没有后续依赖

🔹 第三层 – 轻量清理(Microcompact)

到这一层,手段就稍微重一些了。Microcompact 会清空某些类型的内容,或使用 cache_edits 做缓存层面的更新。

你可以理解为档案管理员开始整理台面了——把那些完全不值钱的内容直接清理掉,腾出空间。

白名单机制(源码 microCompact.ts):

// 只有这些工具的输出结果才会被标记为“可压缩”:
// Read, Bash, Grep, Glob, WebSearch, WebFetch, Edit, Write

具体怎么压缩?

  • 查找消息列表中所有的 tool_result
  • 根据时间衰减(time‑decay)判断哪些结果已经够“老了”
  • 把这些不再需要的工具结果替换为一个占位符字符串:
    [Old tool result content cleared]
  • 原始数据依然保留在本地 JSONL 日志中,只是不会在调用 API 时发送出去。

⚠️ 这一层不调用大模型,纯粹是本地字符串替换,所以成本极低,每轮都跑。

🔹 第四层 – 档案重组(Context Collapse)

当前面三层都还不够用时,第四层开始动真格的。Claude Code 会对当前上下文做一次结构性重组,把松散的信息整合压缩。这是大模型介入前的最后一道防线。

📌 不过要注意,当前公开源码里这一部分的实现文件缺失,部分逻辑是基于调用方和源码注释来还原的。从调用链来看,Context Collapse 的作用主要是把 LLM 响应生成之前的 token 压力降到最低。

🔹 第五层 – 全馆整理(Auto‑Compact + Session Memory Compact)

这是最重的一层,也是 Claude Code 的“兜底”方案。当上下文窗口使用率达到约 80% 时,Auto‑Compact 自动触发,发起一次彻底的档案整理。

关键区别在于:

  • 前四层是“前置压缩”,试图在不调用大模型的情况下解决问题
  • 第五层才是真正动用大模型做全面摘要

越往前的关卡越廉价、越适合高频执行,越往后的关卡越昂贵、只在必要时才启用。

三、Auto‑Compact – 那位不声不响的“档案管理员”

如果说前四层是档案室里的日常整理,那 Auto‑Compact 就是那位真正掌握档案管理核心能力的专业人士——它的职责不是“删档案”,而是 “建索引、做摘要、重分类”

触发阈值:比你以为的更早

看过源码会发现,Auto‑Compact 并不是在窗口“刚刚满”时触发,而是在 token 预算跨过一个动态阈值时触发:

effectiveWindow = contextWindow - max(maxOutputTokens, 20_000)
autoCompactThreshold = effectiveWindow - 13_000

对于 200K 窗口的模型,这大概是 167K token 左右触发。

  • 前 13K token → 检查间隔的缓冲
  • 后 20K token → 预留给模型生成响应的空间(模型必须留够余地来写出压缩请求的回复)

三个阶段,环环相扣

📁 第一阶段:清理旧档案(PreCompact hooks & 工具结果清理)

  1. PreCompact hooks
    用户配置的钩子脚本可以判断是否应该继续压缩。如果某个脚本返回 exit code 2,压缩会被阻止——这给了用户一个拦截点。

  2. 工具结果清理
    处理最占地方的旧档案:FileRead 结果、grep 输出、bash 执行日志。清理之后台面立刻空出一大片。

🧹 注意:这一步不是删除,而是“移出主台面”。档案本身还在磁盘上,只是不再占用宝贵的上下文空间。

📝 第二阶段:做全馆摘要(Conversation Summarized)

这是最关键的一步。Claude Code 把整个对话和一个专门设计的摘要 prompt 一起发送给 LLM,同时设置:

thinkingConfig: disabled,
querySource: "compact"

这是为了避免 LLM 在摘要时进行深层推理,从而消耗更多 token。

摘要 prompt(源码里叫 AGB)会明确要求模型产出特定的结构化摘要

  • 完成了什么
  • 正在做什么
  • 修改了哪些文件
  • 测试结果是什么

关键信息(如代码变更、测试输出、文件路径)要尽可能保留原始文本,而不是抽象概括。

♻️ 第三阶段:续接对话(Session Continues)

压缩完成后,Claude Code 会把旧消息全部丢弃,从一个新的状态开始。

源码里的做法是:

// 创建一个 compact_boundary 系统消息作为新旧分界的标记。
// 标记前的所有消息都丢弃。
// 标记后的状态包括:
//   - 压缩后生成的摘要
//   - 重新注入的 CLAUDE.md
//   - 待办事项、plan 状态、Agent 输出

文件恢复策略(很重要!)

Claude Code 会恢复最多 5 个最近读取的文件,总 token 预算不超过 50K token,每个文件的上限是 5K token

为什么要恢复文件?因为模型的摘要可能只有“修改了 auth.tsx”这么一句话,但继续工作时它需要重新知道 auth.tsx 里到底有什么代码。
所以档案管理员会把这些文件从磁盘提出来,重新摆在台面上。

🔐 一个容易被忽略的设计:CLAUDE.md 是“永久馆藏”

CLAUDE.md 是 Claude Code 在每次会话开始时自动加载到 system prompt 中的配置文件。它的加载规则有三层:

路径用途
项目根目录 CLAUDE.md团队共享,纳入版本控制
~/.claude/CLAUDE.md个人偏好,不纳入版本
~/.claude/projects/.../memory/Agent 自己学习积累(Auto Memory)

在压缩过程中,CLAUDE.md 会被保留并重新从磁盘读取

📌 你在 CLAUDE.md 里写的任何内容,无论是项目规范还是个人偏好,都不会因为压缩而被丢失。
相比之下,如果只在对话中口头说了一句“记住这个路径”,压缩后就可能被摘要掉。

设计哲学: 永久性的信息必须放进永久性的档案柜,不能放在临时台面上。

⚡ 微紧凑 vs 全紧凑:一个容易被忽略的层级分化

除了这 5 层调用点,Claude Code 还按照激进程度把压缩分成了三个层级:

层级是否调用 LLM执行频率
Microcompact每轮执行
Full compact触发时执行
Session memory compact❌(用预提取笔记)特殊场景

Session Memory Compact 的特殊之处:

不向 LLM 发送完整对话来生成摘要,而是从预提取的结构化笔记中直接读取状态,从而完全避免一次 LLM 调用。对成本敏感的场景,这个设计非常关键。

四、“不删档案”的真正含义

你可能注意到了:在整个 5 层流程和源码实现里,Claude Code 几乎没有“删除”任何东西。

  • 旧的工具输出 → 存到了磁盘,不是销毁
  • 旧的对话 → 压缩成了摘要,不是丢弃
  • 关键的 CLAUDE.md → 压缩后从磁盘重新读取注入

什么都不删,只是换了一种方式存放。

这才是这套档案管理模型最有意思的地方。

传统做法有什么问题?

方案缺点
简单截断丢掉用户最初的需求描述,甚至丢掉任务关键背景
一次性摘要摘要调用本身就要消耗一大笔 token,且无法保证不丢关键细节

Claude Code 的第三条路:分层 + 索引 + 持久化

信息类型处理方式
不重要但体积大的档案存磁盘(随时可读回,单结果 >50K 字符就落盘)
重要但可以精简的档案做摘要索引(保留核心信息,通过专用摘要 prompt 控制质量)
永久重要的档案放 CLAUDE.md(永不丢失,每次压缩后重新读入)

这套方案的本质,不是“治标”地清理台面,而是建立了一套档案管理的标准流程:知道什么该存、什么该压缩、什么该永久保留。

这与人类档案员的工作逻辑完全一致:当你把一份文档交给他,他不会问“要不要删掉旧的那份”,而是问 “这份归档到哪里、索引关键词是什么、要不要跟旧文档合并”

五、如果面试官再问,你怎么答?

说实话,把 Claude Code 的上下文管理机制讲清楚并不难。难的是让人记住它的精髓。

读源码的时候,我的第一反应不是“哦,它会在阈值处做压缩”,而是:

这套设计从头到尾都在执行一个极其朴素的档案管理原则——从最便宜的手段开始,不行再升级手段,同时保证关键信息永不丢失。

面试时如果被问到这道题,你的回答可以分三步走:

第一步:架构认知

Claude Code 不是简单截断或单次摘要,而是用了一套 5 层压缩流水线

  1. Tool Result Budget(单结果超 50K 字符落盘)
  2. Snip Compact(通过 snip_boundary 移除特定消息)
  3. Microcompact(白名单 + 时间衰减 + 占位符替换)
  4. Context Collapse(重组)
  5. Auto‑Compact(全面压缩)

五层从便宜到昂贵,从浅到深,层层递进。

第二步:Auto‑Compact 的机制

当上下文估算达到约 167K token 时自动触发,执行流程:

PreCompact hooks 检查 → 工具结果清理 → 结构化摘要(专用 prompt,禁用 thinking) → 恢复最多 5 个文件(共 50K token 预算) → 创建 compact_boundary 继续对话

其中 CLAUDE.md 是永久保留的特殊档案,压缩后会从磁盘重新读取。

第三步:一句话总结

Claude Code 的上下文管理,本质上是一个 “从磁盘到摘要、从存放到索引”的分层档案管理体系,既不删旧内容,也不让台面过载,把有限的工作台面留给最值得放的信息。

这道题的考点,从来不是背得出几个技术名词,而是 能不能理解这套设计背后的思想

而在我看来,这套思想用一个类比就能讲清楚:

Claude Code 背后站着的,不是什么炫技的黑科技,而是一位训练有素的档案管理员——他不删档案,只建索引。