1. 从一次「对话突然变傻」说起Compact 到底在压什么如果你用 Claude Code 写过稍大一点的项目大概率遇到过这种场景前面聊得好好的让它改一个文件、跑一次测试、再解释一段报错来回几十轮之后它突然开始「失忆」——不记得你十分钟前定的命名规范也忘了刚才读过的那个配置文件长什么样。这不是模型变笨了而是上下文窗口快满了Claude Code 在背后悄悄做了一次上下文压缩也就是 Compact。Compact 是 Claude Code 里负责「对话历史太长、Token 快溢出」时把历史消息重新整理成摘要的机制。它要解决的核心矛盾很朴素AI 需要历史才能连贯工作但模型的 Context Window 有硬上限不可能无限堆消息而每次压缩都要额外调用一次模型写摘要既花时间又花钱。所以 Claude Code 没有一上来就全量压缩而是设计了一套从轻到重的分层策略能本地清空就不调模型能读缓存就不重新总结。这篇面向想读源码、也想在本地复现压缩行为的开发者。我会沿着src/services/compact/这条路径把触发条件、压缩边界、配置项讲清楚最后给出一份可复制的settings.json骨架和验证动作让你能亲眼看到压缩日志。需要说明的是下面涉及的阈值和字段名以 Claude Code 2.1.88 附近的实现为参考不同版本可能有出入读的时候以你本地源码为准。在动手之前先把「记忆」这件事的边界划清楚否则很容易把 Compact 当成全部。Claude Code 的记忆其实分几块多轮对话由QueryEngine.ts维护 messages 数组Token 超限时交给services/compact/跨会话的结构化记忆在services/SessionMemory/项目级静态说明则是CLAUDE.md这类文件每次会话注入。本篇只聚焦 compact 包也就是「对话历史太长」这一个问题。2. 前置准备用 TaoToken 拿到可调用的 API 入口要复现压缩行为你得先有一个能稳定调用 Claude 系列模型的入口。我这边习惯用 TaoToken 做统一接入它的 API 地址是https://taotoken.net/api兼容 Anthropic 风格的调用方式Claude Code 这类工具配置起来比较顺。官网在https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end注册后在控制台生成 Key 即可。第一步登录后进入控制台找到 API Keys 页面创建一个新 Key。建议按用途分开建比如一个专门给 Claude Code 用方便后面排查额度消耗。创建后立刻复制保存页面刷新后就看不到完整 Key 了。第二步确认你要用的模型名。Claude Code 默认走 Anthropic 的模型标识你在 TaoToken 的模型列表里核对一下当前可用的名称避免配置里写了一个不存在的模型导致请求直接 400。第三步把 Key 和 Base URL 写进环境变量或 Claude Code 的配置。Claude Code 读取的是ANTHROPIC_BASE_URL和ANTHROPIC_API_KEY这类变量指向 TaoToken 的 API 地址即可。这里不要写官网首页地址接口调用必须用/api这个路径。注意Key 属于敏感凭证不要提交到 Git 仓库也不要在截图里露出完整字符串。建议放在本地 shell 的 profile 文件或系统的密钥管理里。如果你还想先验证模型本身通不通可以到模型对话页面手动发一条消息试试如果打算长期跑编码和 Agent 任务Coding Plan 会更划算额度模型和按量调用不一样适合高频使用。3. 可复制配置settings.json 骨架与压缩相关项Claude Code 的行为大量通过settings.json控制压缩相关的开关和阈值也在这里。下面这份骨架你可以直接抄重点看autoCompact和compact两段。字段名以你本地版本为准如果某个键不生效先去源码里搜一下对应的读取位置。{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: sk-你的Key }, autoCompact: { enabled: true, bufferTokens: 13000, maxConsecutiveFailures: 3 }, compact: { keepRecentMinTokens: 10000, keepRecentMaxTokens: 40000, minTextBlockMessages: 5, recentFilesToReinject: 5 }, hooks: { PreCompact: [], PostCompact: [] } }逐项解释一下。autoCompact.enabled控制是否开启自动压缩关掉之后只有你手动敲/compact才会压。bufferTokens对应源码里的AUTOCOMPACT_BUFFER_TOKENS它的作用是给输出预留空间阈值大致等于「上下文窗口减去最大输出 Token再减去这个 buffer」。以 200K 窗口、20K 输出为例有效上下文约 180K再减 13K触发线落在 167K 附近。maxConsecutiveFailures是熔断阈值。压缩本身也可能失败比如要总结的历史太长连压缩请求都超限了。连续失败达到这个次数就停止自动压缩避免陷入「失败—重试—再失败」的死循环。这个设计很像服务治理里的熔断器防止级联故障。compact段控制压缩后保留多少近期消息。keepRecentMinTokens和keepRecentMaxTokens划定保留区间minTextBlockMessages保证至少留下几条有实际文本的消息recentFilesToReinject决定压缩后重新注入几个最近访问的文件内容——这一步很关键否则 AI 压缩完就「忘了」文件长什么样没法继续编辑。hooks段是扩展点。PreCompact在压缩开始前执行可以往摘要里注入项目规范PostCompact在压缩完成后执行适合做通知或清理。它们不修改核心代码就能介入流程类似 Spring 里的 BeanPostProcessor。4. 验证请求观察压缩触发与日志输出配置好之后怎么确认压缩真的发生了最直接的办法是制造一个长对话把 Token 用量顶到阈值附近然后看日志。下面给一个可操作的验证流程。先开一个 Claude Code 会话让它连续读取几个较大的文件比如把一个几千行的日志文件、一个配置文件、一个源码文件依次读进来。每读一个观察 Token 计数往上走。当用量接近触发线时你会看到类似「正在压缩对话历史」的提示压缩完成后通常会显示压缩前后的 Token 对比比如从 160K 降到 25K。如果你想更精确地观察可以在压缩前后各发一条消息让模型复述「刚才读过哪些文件」。压缩前它能列出全部压缩后如果只重新注入了最近 5 个文件它可能只记得其中一部分这正好验证了recentFilesToReinject的效果。日志层面Claude Code 会在压缩时输出边界标记形如一条 System 消息标注压缩发生的时间点。你可以在会话历史里搜这个标记快速定位每次压缩的位置。如果开了调试日志还能看到calculateTokenWarningState()的状态流转normal、warning、error、autoCompact、blocking 五个级别压缩通常发生在 autoCompact 这一级。手动触发也值得试一次。在会话里输入/compact它会走全量压缩流程部分版本支持指定范围对应源码里的from和up_to两种方向——前者压缩从某条消息到最新后者压缩从最旧到某条消息。手动压缩的好处是你能控制时机比如在切换任务前先压一次把无关历史清掉。验证时建议配合一个简单的脚本记录 Token 变化。你可以让 Claude Code 在每次压缩后把preCompactTokenCount和postCompactTokenCount打印出来这两个字段就在CompactionResult里。多跑几次你就能摸清自己项目下压缩的频率和收益。5. 本篇常见错排查压缩不触发、报错与信息丢失压缩一直不触发。先确认autoCompact.enabled是 true再检查 Token 用量是否真的到了阈值。很多人以为聊了很久就该压缩但实际上如果消息里工具结果不多Token 增长很慢可能离触发线还远。另外如果之前连续失败触发了熔断自动压缩会被停掉需要重启会话或手动/compact恢复。压缩请求本身报 Prompt Too Long。这是压缩的经典坑要总结的历史太长加上摘要提示词后总长度反而超限。源码里的处理是truncateHeadForPTLRetry()每次重试丢弃最旧的一组 user-assistant 轮次最多重试 3 次。如果你看到这个报错反复出现说明单次对话积累得太多建议更早手动压缩或者调低bufferTokens让触发更早。压缩后 AI 不记得文件内容。检查recentFilesToReinject是不是设得太小。压缩后上下文重建时会重新注入最近访问的文件数量不够就会导致 AI 编辑文件时「凭印象」改容易出错。把它调到 5 到 8 之间通常够用但注意注入本身也占 Token别设太大。压缩后权限或行为异常。压缩改变了消息结构很多基于消息位置的缓存会失效。源码里有runPostCompactCleanup()专门清理这些缓存包括系统提示词分区、权限检查结果、Token 计数等。如果你用的是较旧版本可能清理不完整表现为压缩后系统提示词用错版本。升级到较新版本一般能解决。Hook 没执行。检查settings.json里PreCompact和PostCompact的路径是否正确以及脚本是否有可执行权限。Hook 执行失败通常不会中断压缩但会在日志里留下错误容易被忽略。6. 继续深入从源码到长期编码实践读 compact 源码最大的收获不是记住那几个阈值而是理解它的分层思路时间触发的微压缩先清掉过期的工具结果缓存感知的微压缩再挑结果型工具下手SessionMemory 压缩尝试用持久化记忆替代实时总结最后才轮到全量压缩调模型写摘要。这套「能不调模型就不调」的哲学直接决定了你的使用成本。如果你想在本地把这条链路跑通建议按这个顺序来先用 TaoToken 的 API Keys 配好入口把settings.json骨架落地再到模型对话页面确认模型可用然后跑一次长对话观察压缩日志。等你对压缩时机有感觉了再考虑用 Coding Plan 支撑长期的编码和 Agent 任务额度模型更适合高频场景。真正上手之后你会发现压缩不是「出问题了才处理」的兜底而是可以主动管理的。在切换任务、结束一个功能模块、准备开新分支之前手动压一次往往比等它自动触发更划算。至于那些阈值别死记去源码里搜常量名结合你实际的上下文窗口算一遍比抄任何配置都靠谱。