当 AI 的记忆快要爆炸时它是如何自救的你有没有遇到过这种情况和 Claude Code 聊到一半它突然变健忘了或者你手动输入/compact眼看着整段对话被一段摘要替代这背后藏着一套精密到令人惊叹的上下文压缩系统。今天我们来聊一聊/compact上下文压缩希望对你有所帮助、有所借鉴为什么需要上下文压缩一个你必须理解的残酷现实残酷现实AI 的“记忆”是会爆炸的每个大语言模型都有一个上下文窗口Context Window。Claude 的默认窗口是200K tokens约 15 万字。听起来很大但当你在一个复杂项目里让 Claude Code 帮你读文件、搜代码、执行命令时——每一次工具调用的输入输出都会吃掉 tokens。读文件 ×10执行命令 ×5代码搜索 ×3多轮对话 ×N你让 Claude Code 帮你重构一个认证模块它读了 10 个文件、跑了 5 个命令、搜了 3 次代码……此时 token 用量可能已经飙到了 15 万。再来几轮对话它就炸了——API 直接拒绝请求。不是 AI 不聪明是你把它的“脑子”塞满了。Claude Code 的解决方案在你不知不觉间悄悄把记忆压缩。本质不是压缩而是“分层记忆管理”Claude Code 的上下文压缩并不是一刀切而是一套三层递进架构┌─────────────────────────────────────────────────┐ │ 第一层Microcompact微压缩 │ │ 压缩工具调用结果不动对话内容 │ │ 触发每次查询前自动执行 │ ├─────────────────────────────────────────────────┤ │ 第二层Auto-compact自动压缩 │ │ 用 AI 生成整段对话摘要替换原始消息 │ │ 触发token 用量超过阈值时自动触发 │ ├─────────────────────────────────────────────────┤ │ 第三层Manual /compact手动压缩 │ │ 用户主动执行可附加自定义压缩指令 │ │ 触发用户输入 /compact │ └─────────────────────────────────────────────────┘第一层Microcompact——给工具输出瘦身这是最轻量的一层。它不触碰对话内容只压缩工具调用的返回结果。看看哪些工具的输出会被压缩// src/services/compact/microCompact.tsconstCOMPACTABLE_TOOLSnewSetstring([FILE_READ_TOOL_NAME,// 文件读取...SHELL_TOOL_NAMES,// Shell 命令GREP_TOOL_NAME,// 代码搜索GLOB_TOOL_NAME,// 文件匹配WEB_SEARCH_TOOL_NAME,// 网络搜索WEB_FETCH_TOOL_NAME,// URL 抓取FILE_EDIT_TOOL_NAME,// 文件编辑FILE_WRITE_TOOL_NAME,// 文件写入])比如你让 Claude Code 读了一个 2000 行的文件过了几轮对话之后那次读取的完整内容其实已经不那么重要了——Microcompact 会把它压缩成更短的表示同时保留关键信息。图片和文档也不放过——它们被替换为简单的[image]和[document]占位符constIMAGE_MAX_TOKEN_SIZE2000// 图片按 2000 tokens 估算第二层Auto-compact——AI 的自动体检这是整套系统最精妙的部分。每次 Claude Code 向 API 发送请求前都会执行一次体检——检查当前 token 用量是否已经逼近上限。这个检查的核心是一组精心设计的阈值// src/services/compact/autoCompact.ts// 为压缩输出预留的 tokens压缩摘要本身也需要空间constMAX_OUTPUT_TOKENS_FOR_SUMMARY20_000// 自动压缩的缓冲区——距离上限还剩 13K tokens 时触发exportconstAUTOCOMPACT_BUFFER_TOKENS13_000// 警告阈值——距离自动压缩阈值还剩 20K 时开始警告exportconstWARNING_THRESHOLD_BUFFER_TOKENS20_000// 硬限制缓冲——手动压缩的最后防线仅留 3K tokensexportconstMANUAL_COMPACT_BUFFER_TOKENS3_000用一张图来理解这些阈值在 200K 上下文窗口中的位置0 200K tokens ├────────────────────────────────────────────────────────┤ │ │ │ [可用空间] │ [警告区] │ [自动压缩触发] │ [输出预留20K] │ │ │ 20K │ 13K │ │ │ ↑ ↑ ↑ │ │ ~147K ~167K ~180K │阈值计算的源码// 有效上下文窗口 模型上下文窗口 - 压缩输出预留exportfunctiongetEffectiveContextWindowSize(model:string):number{constreservedTokensForSummaryMath.min(getMaxOutputTokensForModel(model),MAX_OUTPUT_TOKENS_FOR_SUMMARY,// 20,000)letcontextWindowgetContextWindowForModel(model,getSdkBetas())returncontextWindow-reservedTokensForSummary}// 自动压缩阈值 有效窗口 - 13K 缓冲exportfunctiongetAutoCompactThreshold(model:string):number{consteffectiveContextWindowgetEffectiveContextWindowSize(model)returneffectiveContextWindow-AUTOCOMPACT_BUFFER_TOKENS}以 200K 窗口为例有效窗口 200K - 20K 180K自动压缩阈值 180K - 13K 167K。也就是说当你的对话 tokens 超过167K时自动压缩就会悄悄启动。但触发前还有一系列安全检查// src/services/compact/autoCompact.ts - shouldAutoCompact()// 1. 不在压缩Agent中递归压缩防死锁// 2. 不在协调Agent中压缩防状态损坏// 3. 用户没有禁用自动压缩// 4. token 用量确实超过了阈值还有一个熔断机制——如果连续 3 次自动压缩都失败了就停止重试constMAX_CONSECUTIVE_AUTOCOMPACT_FAILURES3// 源码注释揭露了一个惊人的事实// BQ 2026-03-10: 1,279 sessions had 50 consecutive failures// (up to 3,272) in a single session, wasting ~250K API calls/day globally.// 修复前有些会话疯狂重试了 3272 次每天全球浪费 25 万次 API 调用这个注释太真实了——它告诉我们即便是 Anthropic 的工程师也经历了从没有熔断到加上熔断的血泪教训。第三层Manual /compact——用户的紧急手术刀当你手动输入/compact时触发的是第三层。它和 Auto-compact 共用核心压缩引擎compactConversation()但有几个关键区别1. 支持自定义压缩指令你可以告诉 AI “压缩时重点关注什么”/compact 关注 TypeScript 代码变更和测试输出包含完整的代码片段这段文字会作为customInstructions追加到压缩 Prompt 的末尾// src/services/compact/prompt.tsexportfunctiongetCompactPrompt(customInstructions?:string):string{letpromptNO_TOOLS_PREAMBLEBASE_COMPACT_PROMPTif(customInstructionscustomInstructions.trim()!){prompt\n\nAdditional Instructions:\n${customInstructions}}promptNO_TOOLS_TRAILERreturnprompt}2. 三条压缩路径的优先级手动/compact的执行流程并不是直接调用压缩引擎而是有一条三级尝试链// src/commands/compact/compact.tsexportconstcall:LocalCommandCallasync(args,context){constcustomInstructionsargs.trim()// 路径 1Session Memory 压缩最轻量无自定义指令时优先尝试if(!customInstructions){constsessionMemoryResultawaittrySessionMemoryCompaction(messages,context.agentId)if(sessionMemoryResult){getUserContext.cache.clear?.()runPostCompactCleanup()return{type:compact,compactionResult:sessionMemoryResult}}}// 路径 2Reactive Compact如果启用了 reactive-only 模式if(reactiveCompact?.isReactiveOnlyMode()){returnawaitcompactViaReactive(messages,context,customInstructions,reactiveCompact)}// 路径 3传统压缩先 Microcompact 瘦身再 AI 总结constmicrocompactResultawaitmicrocompactMessages(messages,context)constresultawaitcompactConversation(microcompactResult.messages,context,awaitgetCacheSharingParams(context,microcompactResult.messages),false,// suppressFollowUpQuestions falsecustomInstructions,false,// isAutoCompact false)return{type:compact,compactionResult:result}}注意传统路径的第一步——先执行 Microcompact。这意味着手动/compact实际上同时触发了第一层和第三层先用微压缩给工具输出瘦身再用 AI 生成整体摘要双管齐下让压缩效果最大化。3. 不受熔断机制限制自动压缩有连续失败 3 次就停止的熔断保护但手动/compact没有——因为这是用户的主动操作用户有权决定我就要压缩。压缩的核心那个总结 Prompt到底长什么样当自动压缩或手动/compact被触发后Claude Code 需要让 AI阅读整段对话然后生成一份高质量摘要。这个总结 Prompt 的设计极其讲究。首先它会用一段强硬的前置指令防止 AI 手痒去调用工具// src/services/compact/prompt.tsconstNO_TOOLS_PREAMBLECRITICAL: Respond with TEXT ONLY. Do NOT call any tools. - Do NOT use Read, Bash, Grep, Glob, Edit, Write, or ANY other tool. - You already have all the context you need in the conversation above. - Tool calls will be REJECTED and will waste your only turn — you will fail the task. - Your entire response must be plain text: an analysis block followed by a summary block.为什么要这么强硬源码注释透露了原因在 Sonnet 4.6 的自适应思考模型上即便有弱一些的尾部指令模型有时候仍然会试图调用工具。在缓存共享的 fork 路径中maxTurns 设为 1一旦工具调用被拒绝就意味着没有文本输出。在 4.6 上这个概率是 2.79%而 4.5 只有 0.01%。然后是压缩 Prompt 的主体——它要求 AI 输出 9 个严格的章节constBASE_COMPACT_PROMPTYour task is to create a detailed summary of the conversation so far... Your summary should include the following sections: 1. Primary Request and Intent // 用户的核心诉求 2. Key Technical Concepts // 涉及的技术概念 3. Files and Code Sections // 操作过的文件和代码片段 4. Errors and fixes // 遇到的错误和修复方式 5. Problem Solving // 解决问题的过程 6. All user messages // 所有用户消息非工具结果 7. Pending Tasks // 待办任务 8. Current Work // 当前正在做什么 9. Optional Next Step // 下一步建议注意第 6 点——所有用户消息都要保留。这意味着即使对话被压缩了你之前说过的每一句话都会以某种形式被记住。第 9 点更有趣——它要求包含原对话的直接引用“Include direct quotes from the most recent conversation showing exactly what task you were working on and where you left off. This should be verbatim to ensure there’s no drift in task interpretation.”防止任务漂移——这是工程上一个非常精妙的设计。另一个亮点是analysissummary双层结构// AI 先在 analysis 标签中打草稿思考过程// 然后在 summary 标签中输出正式摘要// 最终 formatCompactSummary() 会把 analysis 剥掉——只保留 summaryexportfunctionformatCompactSummary(summary:string):string{letformattedSummarysummary// 剥掉分析部分——它是提升摘要质量的草稿纸// 摘要写完后就没有信息价值了formattedSummaryformattedSummary.replace(/analysis[\s\S]*?\/analysis/,,)// 提取并格式化摘要部分constsummaryMatchformattedSummary.match(/summary([\s\S]*?)\/summary/)if(summaryMatch){constcontentsummaryMatch[1]||formattedSummaryformattedSummary.replace(/summary[\s\S]*?\/summary/,Summary:\n${content.trim()},)}returnformattedSummary.trim()}这种“先让 AI 充分思考再提取精华”的 Prompt 工程技巧值得每一位做 AI 应用开发的同学借鉴。压缩的核心那个总结 Prompt到底长什么样当自动压缩或手动/compact被触发后Claude Code 需要让 AI阅读整段对话然后生成一份高质量摘要。这个总结 Prompt 的设计极其讲究。首先它会用一段强硬的前置指令防止 AI 手痒去调用工具// src/services/compact/prompt.tsconstNO_TOOLS_PREAMBLECRITICAL: Respond with TEXT ONLY. Do NOT call any tools. - Do NOT use Read, Bash, Grep, Glob, Edit, Write, or ANY other tool. - You already have all the context you need in the conversation above. - Tool calls will be REJECTED and will waste your only turn — you will fail the task. - Your entire response must be plain text: an analysis block followed by a summary block.为什么要这么强硬源码注释透露了原因在 Sonnet 4.6 的自适应思考模型上即便有弱一些的尾部指令模型有时候仍然会试图调用工具。在缓存共享的 fork 路径中maxTurns 设为 1一旦工具调用被拒绝就意味着没有文本输出。在 4.6 上这个概率是 2.79%而 4.5 只有 0.01%。然后是压缩 Prompt 的主体——它要求 AI 输出 9 个严格的章节constBASE_COMPACT_PROMPTYour task is to create a detailed summary of the conversation so far... Your summary should include the following sections: 1. Primary Request and Intent // 用户的核心诉求 2. Key Technical Concepts // 涉及的技术概念 3. Files and Code Sections // 操作过的文件和代码片段 4. Errors and fixes // 遇到的错误和修复方式 5. Problem Solving // 解决问题的过程 6. All user messages // 所有用户消息非工具结果 7. Pending Tasks // 待办任务 8. Current Work // 当前正在做什么 9. Optional Next Step // 下一步建议注意第 6 点——所有用户消息都要保留。这意味着即使对话被压缩了你之前说过的每一句话都会以某种形式被记住。第 9 点更有趣——它要求包含原对话的直接引用“Include direct quotes from the most recent conversation showing exactly what task you were working on and where you left off. This should be verbatim to ensure there’s no drift in task interpretation.”防止任务漂移——这是工程上一个非常精妙的设计。另一个亮点是analysissummary双层结构// AI 先在 analysis 标签中打草稿思考过程// 然后在 summary 标签中输出正式摘要// 最终 formatCompactSummary() 会把 analysis 剥掉——只保留 summaryexportfunctionformatCompactSummary(summary:string):string{letformattedSummarysummary// 剥掉分析部分——它是提升摘要质量的草稿纸// 摘要写完后就没有信息价值了formattedSummaryformattedSummary.replace(/analysis[\s\S]*?\/analysis/,,)// 提取并格式化摘要部分constsummaryMatchformattedSummary.match(/summary([\s\S]*?)\/summary/)if(summaryMatch){constcontentsummaryMatch[1]||formattedSummaryformattedSummary.replace(/summary[\s\S]*?\/summary/,Summary:\n${content.trim()},)}returnformattedSummary.trim()}这种“先让 AI 充分思考再提取精华”的 Prompt 工程技巧值得每一位做 AI 应用开发的同学借鉴。压缩完成后发生了什么善后工作比你想的复杂得多压缩生成摘要只是第一步。之后还有一系列精密的善后操作1. 构建压缩边界标记// src/services/compact/compact.tsconstboundaryMarkercreateCompactBoundaryMessage(isAutoCompact?auto:manual,// 标记是自动还是手动触发preCompactTokenCount??0,// 压缩前的 token 数messages.at(-1)?.uuid,// 最后一条消息的 UUID)这个边界标记就像一个分隔线——告诉系统“从这里往前的对话已经被压缩了。”2. 重新注入关键上下文压缩会把所有消息替换成摘要但有些信息不能丢// 并行生成后续需要的附件const[fileAttachments,asyncAgentAttachments]awaitPromise.all([// 重新注入最近读过的文件内容createPostCompactFileAttachments(preCompactReadFileState,context,POST_COMPACT_MAX_FILES_TO_RESTORE),// 重新注入异步 Agent 信息createAsyncAgentAttachmentsIfNeeded(context),])// 如果有计划文件重新注入constplanAttachmentcreatePlanAttachmentIfNeeded(context.agentId)// 如果有技能被调用过重新注入constskillAttachmentcreateSkillAttachmentIfNeeded(context.agentId)3. 压缩后的消息结构最终被压缩的对话变成了这样一个结构exportfunctionbuildPostCompactMessages(result:CompactionResult):Message[]{return[result.boundaryMarker,// 1. 系统边界标记...result.summaryMessages,// 2. AI 生成的摘要...(result.messagesToKeep??[]),// 3. 保留的近期消息如有...result.attachments,// 4. 重新注入的文件/计划/技能...result.hookResults,// 5. 钩子执行结果]}4. 大面积缓存清理// src/services/compact/postCompactCleanup.tsexportfunctionrunPostCompactCleanup(querySource?:QuerySource):void{resetMicrocompactState()// 重置微压缩状态getUserContext.cache.clear?.()// 清除用户上下文缓存让记忆文件重新注入resetGetMemoryFilesCache(compact)// 重置记忆文件缓存clearSystemPromptSections()// 清除动态系统提示clearClassifierApprovals()// 清除权限分类决策clearSpeculativeChecks()// 清除推测性检查clearBetaTracingState()// 清除遥测状态clearSessionMessagesCache()// 清除会话消息缓存}但有两样东西故意不清除——源码注释给出了理由不清除已调用的技能内容重新注入完整技能列表约 4K tokens是纯粹的 cache_creation 开销收益微乎其微。模型的 Schema 中仍然有 SkillToolinvoked_skills 附件会保留已使用的技能内容。这种**“每一个 token 都要精打细算”** 的工程思维才是大规模 AI 应用的真功夫。Prompt Too Long 容错当压缩本身也炸了有一个极端情况对话太长了连压缩请求本身都超出了 prompt 长度限制。Claude Code 为此设计了一套PTLPrompt Too Long重试机制// src/services/compact/compact.tsletptlAttempts0for(;;){summaryResponseawaitstreamCompactSummary({messages:messagesToSummarize,summaryRequest,// ...})summarygetAssistantMessageText(summaryResponse)// 如果没有 PTL 错误正常退出if(!summary?.startsWith(PROMPT_TOO_LONG_ERROR_MESSAGE))break// PTL 了砍掉最老的消息组重试ptlAttemptsconsttruncatedptlAttemptsMAX_PTL_RETRIES// 最多重试 3 次?truncateHeadForPTLRetry(messagesToSummarize,summaryResponse):nullif(!truncated){thrownewError(ERROR_MESSAGE_PROMPT_TOO_LONG)// 实在不行报错}messagesToSummarizetruncated// 用截断后的消息重试}策略是每次砍掉最老的 “API 轮次组”最多重试 3 次。如果 3 次之后还是太长就只能认输了。Prompt Cache 共享省钱的骚操作压缩过程中调用 AI 生成摘要也是要花钱的。Claude Code 想了一个办法——复用主对话的 Prompt Cache// 两条路径// 路径 1Prompt Cache 共享省钱// 用 runForkedAgent() 搭主线程的 cache 便车// 复用系统提示、工具定义等已缓存的 tokens// 路径 2独立流式请求保底// 直接调用模型提供最小工具集// 设置 maxOutputTokens 上限为 20,000源码中的实验数据显示“2026 年 1 月的实验确认不共享缓存的路径有 98% 的缓存未命中率消耗了全舰队约 0.76% 的 cache_creation tokens每天约 380 亿 tokens集中在临时环境CCR/GHA/SDK。”每天 380 亿 tokens 的浪费——这就是为什么缓存共享不是优化而是必须。我们可以用这些知识做什么1. 合理使用/compact指令你可以给/compact传递自定义指令/compact 关注 TypeScript 代码变更和测试输出包含完整的代码片段这段指令会被追加到压缩 Prompt 中让摘要更聚焦于你关心的内容。2. 理解自动压缩的时机当你看到 Claude Code 突然卡了一下或者输出了Compacting conversation…之类的提示——它正在自动压缩。这不是 Bug是 Feature。3. 环境变量调优# 手动设置自动压缩触发百分比比如用 80% 的窗口就开始压缩CLAUDE_AUTOCOMPACT_PCT_OVERRIDE80# 限制上下文窗口大小测试用CLAUDE_CODE_AUTO_COMPACT_WINDOW100000# 禁用自动压缩不推荐除非你知道自己在做什么DISABLE_AUTO_COMPACTtrue4. 借鉴到自己的 AI 应用中如果你正在构建基于 LLM 的应用Claude Code 的这套压缩架构值得深度借鉴多层递进压缩先压缩工具输出再压缩整体对话熔断机制失败 N 次后停止重试结构化摘要 Prompt用严格的章节要求保证摘要质量analysis草稿 summary成稿提升输出质量的 Prompt 技巧缓存共享在压缩这种辅助操作中复用主流程的缓存总结Claude Code 的上下文压缩系统远不是一个简单的总结对话功能。它是一套包含三层压缩架构、动态阈值计算、PTL 容错重试、Prompt Cache 共享、结构化摘要生成、熔断保护的完整工程系统。每一行代码背后都是真实的线上问题和性能数据——从每天浪费 25 万次 API 调用的熔断修复到每天 380 亿 tokens 的缓存优化。这才是工业级 AI 应用该有的样子。本文基于 2026 年 3 月公开暴露的 Claude Code 源码快照进行分析仅用于教育和技术研究目的。原始代码所有权属于 Anthropic。