在使用 AI 编程工具进行长期开发、问题排查、功能调试和代码重构时,很多开发者都会遇到一个非常典型的问题:对话越长,模型越容易变得不稳定

一开始,AI 可以准确理解你的需求,也能记住项目结构、错误日志和已经尝试过的修复方案。但随着对话轮次不断增加,聊天记录、代码片段、运行日志、报错堆栈、工具调用结果都会持续堆积,最终不断挤占模型的上下文窗口。

当 token 用量接近上限时,问题就会集中爆发:

  • 模型开始遗忘前面已经确认过的要求;
  • 已经修过的问题被重新讨论;
  • 最新任务和历史任务混在一起;
  • 报错日志被遗漏;
  • 代码修改方向开始跑偏;
  • AI 看似还在回答,但上下文理解已经明显衰减。

这类问题可以概括为 Context Overflow(上下文溢出),更深一层则是 Context Degradation(上下文退化)

Claude Code 为了解决这个问题,设计了 /compact 命令。它并不是简单清空历史,也不是让模型随便总结一下聊天记录,而是一套完整的上下文压缩与任务状态重建机制。它的目标不是“少占一点 token”这么简单,而是在有限上下文窗口中,尽量保留后续工作真正需要的信息。

从工程视角看,/compact 的价值不只属于 Claude Code。它背后的分层压缩、自动触发、熔断保护、结构化摘要、关键文件恢复等设计,同样可以为对话类 AI 产品、AI Agent 系统以及多模型 API 服务的长会话优化提供参考,例如在类似TreeRouter这类多模型统一接入与调度的系统中,也同样需要解决长上下文管理与状态一致性问题。

一、/compact 的核心定位:不是提示词,而是本地上下文重构命令

很多人第一次看到 /compact,会以为它只是一个普通的提示词命令,例如让模型执行“请总结前面的对话”。但实际上,Claude Code 中的 /compact 和普通 prompt 有本质区别。

普通 prompt 的流程是:

用户输入指令 -> 模型理解指令 -> 模型生成回答 -> 追加到对话历史

/compact 的流程更接近:

本地命令触发 -> 程序判断上下文状态 -> 调用摘要逻辑 -> 生成结构化摘要 -> 替换原始对话历史

也就是说,/compact 的控制权不完全交给模型,而是由本地程序负责执行压缩流程。模型只承担其中的摘要生成任务,真正的触发、替换、恢复和失败处理,都由系统逻辑控制。

这点非常关键。

如果把压缩完全交给模型,可能会出现 prompt injection、格式不稳定、摘要不完整、关键信息丢失等问题。而本地命令模式可以让压缩行为更加确定,也方便加入自动触发、失败重试、熔断保护和 token 预算控制。

从命令注册代码可以看到这一点:

const compact = {
  type: 'local',
  name: 'compact',
  description: 'Clear conversation history but keep a summary in context.',
  isEnabled: () => !isEnvTruthy(process.env.DISABLE_COMPACT),
  argumentHint: '<optional custom summarization instructions>',
  load: () => import('./compact.js'),
} satisfies Command

这段代码里有几个非常重要的设计点。

第一,type: 'local' 明确说明它是本地执行命令,而不是直接发送给大模型的普通对话内容。

第二,description 里的描述是:清除对话历史,但保留摘要上下文。这说明它不是单纯删除历史,而是用摘要替代历史。

第三,isEnabled 支持通过 DISABLE_COMPACT 环境变量关闭该功能。这对企业级部署很重要,因为不同团队可能对自动压缩有不同策略。

第四,argumentHint 支持用户提供自定义摘要指令,例如要求保留错误日志、重点记录代码变更、强调测试输出等。

所以,/compact 更准确的定位是:一个本地上下文重构引擎,而不是一个普通总结命令。

二、为什么长会话不能只靠“更大的上下文窗口”解决?

在讨论 /compact 之前,必须先理解一个误区:很多人以为只要模型上下文窗口足够大,长会话问题就自然消失。

但真实情况并不是这样。

即使模型支持 200K、500K 甚至 1M token,上下文仍然需要管理。原因有三个。

第一,长上下文会增加成本。 每次请求携带大量历史内容,都会消耗输入 token。对于 AI 编程工具来说,一次任务往往不是一轮对话,而是几十轮交互。如果每一轮都携带全部历史,成本会持续累积。

第二,长上下文会增加噪音。 上下文越长,不代表模型理解越好。很多历史内容可能已经过期,例如失败的方案、旧日志、已经废弃的代码片段。如果这些内容一直存在,模型反而容易被干扰。

第三,长上下文不等于结构化记忆。 原始聊天记录只是时间顺序的堆叠,并不天然等于“当前任务状态”。模型真正需要的不是每一句历史原文,而是:

用户最终目标是什么?
当前任务进展到哪一步?
哪些文件被修改过?
哪些方案已经失败?
当前还剩哪些待办?
下一步应该做什么?

这就是 /compact 的核心价值:它不是单纯减少 token,而是把不断增长的对话历史转换为更紧凑、更结构化、更适合继续执行任务的状态摘要。

三、三层递进式压缩策略:优先低成本,失败再兜底

Claude Code 的 /compact 并不是只有一种压缩方式,而是设计了三层递进式压缩策略。它不会一上来就对完整历史做全量总结,而是优先尝试更轻量的方案,只有在前一层不可用或失败时,才进入下一层。

整体流程可以理解为:

增量式压缩
   ↓ 失败或不适用
响应式压缩
   ↓ 失败或不适用
全量传统压缩

这种设计非常像后端系统里的缓存降级、服务降级或多级兜底策略。它的核心目标是:在压缩效果、执行成本和系统稳定性之间取得平衡

3.1 第一层:增量式会话内存压缩

第一层是优先级最高的增量式压缩。它不需要重新处理完整对话,而是尽量只处理新增内容,并将其合并到已有摘要中。

这种方式适合频繁交互场景。例如用户刚刚连续问了几个小问题,或者模型刚完成一小段代码修改,系统没有必要重新总结全部历史,只需要把最新变化合并进上下文状态即可。

它的优势是:

  • 调用成本更低;
  • 响应速度更快;
  • 对已有摘要扰动较小;
  • 更适合高频长会话。

从工程角度看,增量式压缩类似“增量缓存更新”。它不是每次都重建全量数据,而是在已有状态基础上追加变化。

3.2 第二层:响应式压缩

当增量压缩无法使用,或者当前对话状态已经比较复杂时,系统会进入响应式压缩。

响应式压缩介于增量压缩和全量压缩之间。它通常会对最近一部分对话进行重组,结合当前任务状态重新生成更稳定的摘要。

这种方式适合以下情况:

  • 最近几轮对话出现较大任务变化;
  • 用户修改了原始需求;
  • 模型尝试了多个修复方案;
  • 当前摘要和最新对话可能存在冲突;
  • 需要重新整理短期上下文。

它比增量压缩更重,但比全量压缩更轻。这样可以避免每次复杂一点的变化都触发全量总结。

3.3 第三层:全量传统压缩

如果前两层都无法完成,系统会启动最终兜底方案:全量传统压缩。

全量压缩会对完整对话历史进行处理,重新生成一份结构化摘要。这一层成本最高,但覆盖最完整,适合会话状态已经严重膨胀、前两层无法保证质量的情况。

不过,全量压缩前通常还会进行一个非常重要的预处理步骤:MicroCompact 微压缩

四、MicroCompact:在正式摘要前先做 token 清洗

在 AI 编程对话里,历史内容并不都是同等价值的。很多内容对后续任务帮助有限,却会大量占用 token。

例如:

  • 图片内容;
  • 内嵌文档;
  • 重复日志;
  • 大段无关输出;
  • 已经不再需要的中间结果。

如果直接把这些内容全部交给模型总结,不仅成本高,而且容易让摘要质量下降。于是 Claude Code 在全量压缩前引入 MicroCompact 微压缩。

微压缩会先将一些高 token、低语义价值的内容替换成占位符。例如:

图片 -> [image]
文档 -> [document]

这样做有两个好处。

第一,减少无效 token 消耗。 模型不需要在摘要时反复处理图片或文档的完整内容,只需要知道曾经存在过这类材料即可。

第二,降低摘要失败概率。 如果原始上下文过长,直接进入摘要可能导致调用失败或超出限制。微压缩相当于先做一次预清洗,为后续正式压缩留出空间。

五、自动触发机制:在上下文爆掉之前提前压缩

/compact 不只是一个手动命令,它还可以自动触发。

这点非常关键。因为在真实开发中,用户往往不会时刻关注 token 使用量。等模型开始遗忘上下文、输出质量下降时,通常已经接近上下文上限了。

Claude Code 通过阈值机制提前判断何时需要压缩。

以 200K 上下文窗口的 Claude Sonnet 模型为例,假设系统预留摘要空间为 20000 token,自动压缩缓冲区为 13000 token,那么计算方式如下:

总上下文窗口 = 200000 tokens
摘要预留空间 = 20000 tokens
有效上下文窗口 = 200000 - 20000 = 180000 tokens
压缩缓冲区 = 13000 tokens
自动触发阈值 = 180000 - 13000 = 167000 tokens

也就是说,当上下文使用量达到大约 167000 token 时,系统就会自动触发压缩。

换算一下:

167000 / 200000 ≈ 83.5%

也就是上下文使用率约 83% 时开始压缩。

六、熔断器机制:防止自动压缩变成无限重试

任何自动化系统都必须考虑失败情况。

如果压缩失败怎么办?如果模型调用异常怎么办?如果摘要结果不符合格式怎么办?如果系统一直尝试压缩却一直失败,会不会导致 API 调用失控?

Claude Code 引入了经典的熔断器机制:

连续自动压缩失败 3 次后,系统停止继续自动重试。

七、高质量摘要系统:压缩不是缩短,而是重建任务状态

/compact 最核心的部分,不是触发机制,也不是 token 阈值,而是摘要质量。

其中一个重要设计是两段式输出:

<analysis>
<summary>

八、九维结构化摘要:把聊天记录转成工程状态机

这九个维度包括:

  1. 用户意图
  2. 技术概念
  3. 代码片段
  4. 错误日志
  5. 修复方案
  6. 对话历史状态
  7. TODO任务
  8. 当前工作内容
  9. 下一步计划

九、关键文件恢复机制

最多恢复 5 个文件
单文件上限 5000 tokens
总恢复上限 50000 tokens

十、总结

Claude Code /compact 的本质不是压缩 token,而是:

在有限上下文窗口中重建“可持续执行的任务状态”