Skip to content

Claude Code 与 Codex 的 /compact:上下文压缩到底怎么运作

更新: 7/5/2026 字数: 0 字 时长: 0 分钟

做深度编程时你大概遇到过这样的怪现象:一开始 AI 又快又准,聊到几十轮之后它却越来越"迟钝"、开始丢三落四。根本原因是上下文窗口被塞满了——大模型的窗口是有限的(比如 200k token),一场长会话里的系统提示、你的每条指令、AI 的每次回复、读过的每个文件、跑过的每条命令,全都在持续吃掉这个额度。窗口一旦逼近上限,模型不仅可能直接报错,还会因为"注意力被稀释"而出现所谓的 context rot(上下文腐化),把埋在中间的关键信息忽略掉[1]

/compact 就是用来对抗这个问题的:把长会话历史压缩成一段精炼摘要,让工作能在窗口内继续,而不是撞墙重来。它和直接删除文件、和 /clear 清空会话都不是一回事。

上下文压缩 /compact 概念示意图

先给一个贯穿全文的直观场景:假设你在修一个登录接口返回 401 的 bug,整个调试会话有 26 条消息、约 15,400 token。其中光是 5 次工具调用返回的大块结果(搜索命中、文件全文、测试堆栈)就占了约 12,500 token,占比 81%[2]。这些数据在调试当下是关键,bug 修完后却成了纯粹的"上下文死重"。两款工具压缩的,主要就是这类东西。

说明:Codex CLI 是开源项目,以下流程基于其公开源码(codex-rs/core/src/compact.rs 等);Claude Code 未开源,其内部机制来自社区逆向工程与公开资料,凡属推断结论我都会明确标注为「基于公开实测的推断」,实际实现可能随版本变化。

一、Codex 的 /compact:写一封干脆的"交接备忘录"

Codex 的思路非常符合人的直觉,一句话概括就是:把整段对话交给一个 LLM 写成"交接摘要",然后用这份摘要替换掉原始历史[3]

处理流程

  1. 先抽取最近的用户消息(硬上限约 20,000 token 的量)单独保留;
  2. 把整段历史加上一段简短的摘要提示词发给模型。这段提示词的核心只有四点:当前进度与关键决策、重要约束与用户偏好、剩余待办、继续工作所需的关键数据;
  3. 收集模型返回的摘要;
  4. 重建历史 = 初始上下文(系统提示等)+ 最近用户消息 + 摘要,用这个压缩版整体替换掉旧会话[4]

它的官方提示词开宗明义:"You are performing a CONTEXT CHECKPOINT COMPACTION. Create a handoff summary for another LLM that will resume the task."(你正在做一次上下文检查点压缩,为另一个将接手任务的 LLM 写一份交接摘要)[4]。关键词是 handoff(交接)——它写的不是会议纪要,而是一份能让"下一个人"立刻上手的交接单。

信息保留 / 丢弃规则

  • 用户消息:逐字完整保留。Codex 深度尊重"用户意图",会物理删除所有 assistant 回复和工具相关消息,但把你说过的每一句原话都留下[2]
  • AI 回复与工具结果:物理删除,由一条"伪造的"assistant 摘要消息替代。放到登录 bug 场景里,26 条消息会直接坍缩成约 4 条——新模型不需要看那些文件全文和测试堆栈,只要知道"测试已修复"就够了。

触发时机与兜底

  • 自动触发:当 token 用量逼近模型上限时自动压缩,无需手动 /compact。它用的是基于 token 数的阈值model_auto_compact_token_limit),而非百分比;不同模型默认阈值不同(如某些模型 180k、某些 244k),并保留约 95% 的安全余量[4]
  • 兜底:如果压缩后空间仍不够,会退化为更粗暴的"头部截断"——从最早的消息开始砍,保证对话能继续。
  • 本地 / 远程双路径:本地路径(compact.rs)由客户端调用任意模型生成摘要;远程路径(compact_remote.rs)直接调 OpenAI 内部的 responses/compact 端点让服务端压缩,仅限 OpenAI 模型,但预处理和后处理仍在客户端完成[2]
  • 还带有指数退避的重试逻辑,并会警告用户"多次压缩可能让模型准确度下降"[4]

Codex 单层交接摘要流程示意图

效果与局限:优点是直观——"交接备忘录"人人都懂。缺点是近乎"全有或全无":所有 AI 回复和工具结果被一份摘要顶替,一旦摘要漏掉某个关键细节,那就是真的永久丢失了[2]

二、Claude Code 的 /compact:三层递进的"精准遗忘"

Claude Code 走的是更精细的路线:不追求一次性物理删除,而是设计了三层由轻到重的清理机制,能不调用 LLM 就尽量不调[2]。有源码级逆向也印证了这一点——它不是单一机制,而是三层依次施加、一层比一层重[5]

下述三层机制及阈值来自社区逆向与公开材料,属基于公开实测的推断结论,非官方文档。

Claude Code 三层精准遗忘示意图

第一层:工具结果裁剪(零 LLM 成本)

这是最频繁、最轻量的一层,不需要任何 LLM 调用,纯粹是每次请求前自动运行的本地规则引擎[2]

  • 始终保护最近几次工具调用的结果(正在用的活数据不能删);
  • 超出保护窗口的旧工具结果 → 替换成 [Old tool result content cleared] 占位符

它的巧妙之处在于维持了 AI 的"心流":AI 仍记得自己"搜过代码""读过文件"(tool_call 还在),只是不记得搜索返回了什么、文件内容是什么。真需要再看,它会重新发一次 read_file。这是"选择性失忆"而非"彻底遗忘"。

第二层:缓存友好策略(Prompt Cache)

这是 Claude Code 相较另外两家最独特的差异化优势。Anthropic 的 API 支持 Prompt Cache——如果你这次请求的前缀和上次匹配,服务端就能复用之前的计算结果,大幅降低成本和延迟[2]

因此清理消息时,Claude Code 刻意只从尾部裁剪、绝不改动消息序列的前半段。"天真"的做法是从最早的消息删起,但那样整个前缀就变了、缓存全部失效。Claude Code 宁可少删一点,也要保证前缀与上次请求完全一致,让缓存命中。对长任务(如重构整个模块)来说,这意味着可观的成本节约[2]

有意思的是,源码级分析发现:即便触发第三层全量摘要,那次摘要调用也复用了主对话完全相同的系统提示、工具、模型和消息前缀,正是为了维持缓存命中率、避免昂贵的缓存失效[6]

第三层:9 段式结构化 LLM 摘要(最后手段)

当前两层都拦不住上下文继续膨胀,才触发全量摘要。据逆向,自动压缩阈值 = 有效上下文窗口 − 13,000 token(有效窗口 = 模型窗口 − min(最大输出 token, 20,000))[2]

但即便到阈值,也不会直接跳到 LLM 摘要——系统会先尝试 Session Memory Compact,用会话记忆里已有的结构化信息替代一次完整 LLM 调用;只有这条路不可用或不够,才回退到传统 LLM 摘要,生成一份固定 9 段结构的摘要:

  1. 用户原始意图
  2. 核心技术概念
  3. 相关文件与代码
  4. 遇到的错误及修复方式
  5. 问题解决的逻辑链
  6. 所有用户消息的汇总
  7. 待办事项(TODO)
  8. 当前正在做的工作
  9. 建议的下一步

这份摘要要求极严——提示词要求模型直接引用原文关键短语而非全部转述,以防"上下文漂移"(模型在复述中悄悄偏离原意)[2]

压缩后的"状态重建"

压缩完 Claude Code 会做一串后处理:在新对话开头注入引子("本会话续自之前的对话……");自动重新读取最近编辑过的文件(最多 5 个、总预算 50,000 token、每个 5,000 token),保证 AI 拿到最新代码;重新声明工具与技能定义。而 CLAUDE.md 里的项目规范作为系统提示的一部分常驻、不受压缩影响[2]

此外还有被动兜底:当 API 返回 prompt_too_long 错误时,系统自动发起一次反应式压缩再重试;为防死循环,连续 3 次压缩失败后自动暂停[2]。你也可以在手动压缩时附加指令,如 /compact 重点关注 API 变更,把压缩导向特定焦点。

效果与局限:三者中最复杂,却也最"划算"——大多数时候只跑第一层规则引擎、或走 Session Memory 路径,根本不额外调用 LLM。局限在于它未开源、行为随版本变动,且第三层摘要一旦发生仍是不可逆的。

三、/compact 与"普通上下文截断"的本质差异

这是本文最该讲透的一点。截断(truncation)是"按位置无脑丢弃",压缩(compaction)是"理解后重写"

对比维度普通截断(Truncation)/compact 压缩(Compaction)
处理方式按位置直接删(通常删最旧的)先理解语义,再重写成摘要
是否理解内容否,纯机械操作是,靠 LLM 或规则提炼要点
信息取舍依据位置(旧的先丢)重要性(关键决策/约束/待办优先保留)
用户意图可能连同旧消息一起丢失显式保留(Codex 逐字留用户消息;CC 单列"用户意图"段)
典型代价前缀改变→缓存失效、逻辑断裂有 LLM 成本;多次压缩会累积信息损耗
可逆性不可逆Codex/CC 不可逆(部分工具如 OpenCode 用时间戳隐藏保留原数据)

一句话:截断是"把书架最左边的书直接扔掉",压缩是"把整本书浓缩成一页要点再上架"。Codex 的"头部截断"其实就是它在压缩失败后的兜底截断动作——两种手段在同一个工具里都存在,只是优先级不同。

上下文压缩 vs 普通截断 对比示意图

四、两者横向对比

对比维度Codex CLIClaude Code
压缩层数单层(摘要)三层(裁剪 / 缓存 / 摘要)
是否必调 LLM是(每次压缩都要)否(多数只跑规则引擎或走会话记忆,仅第三层调 LLM)
用户消息处理逐字永久保留摘要化(保留在第 6 段"用户消息汇总")
工具结果处理物理删除占位符替换
触发阈值token 数阈值(如 180k/244k),留 ~95% 余量有效窗口 − 13,000 token(推断)
缓存优化无专门设计深度 Prompt Cache 集成,坚持前缀稳定
压缩后行为被动等待主动重读相关文件、重声明工具、注入续接引子
失败保护指数退避重试 + 头部截断兜底3 次失败自动暂停 + prompt_too_long 反应式重试
可配置性手动 /compact + 自动阈值手动 /compact [自定义指令] + 三层自动

五、已知共性局限与实用建议

  • 多次压缩会累积信息损耗。两家都会警告:长对话叠加多次压缩,会让模型准确度下降、逐渐"跑偏"[4]。摘要是"有损压缩",压一次掉一点细节。
  • 自动压缩在任务中途触发最危险。用户普遍反馈:auto-compact 若卡在关键操作中间发生,模型容易"go off the rails"(脱轨)[4]。因此社区建议在里程碑节点(一个功能做完、bug 修复后)主动手动 /compact,别等自动触发。
  • 别等到 95% 才压。等窗口快满才压缩往往太晚,在约 60% 容量、任务自然告一段落时主动压缩,效果更好[7]
  • 重要信息落盘,别只靠对话记忆。凡是不该被摘要掉的(架构决策、路径规则、长期偏好),写进 CLAUDE.mdAGENTS.md 或项目文档等持久文件——这些不受压缩影响,才是真正安全的[8]
  • 分清 /compact/clear:前者压缩保留摘要、能续接工作;后者彻底清空历史、从零开始。

一句话总结:Codex 的 /compact 是"单层交接备忘录"——逐字留用户话、物理删其余、一份摘要顶上,直观但一刀切;Claude Code 的 /compact 是"三层精准遗忘"——先零成本裁剪工具结果、再缓存友好地只删尾部、实在不行才做 9 段式结构化摘要,复杂但省钱且更抗信息丢失。二者与普通截断的根本区别都在于:截断按位置丢,压缩按理解留

需要的话,我可以把这份内容整理成一篇排版好的飞书文档,方便你归档或分享给团队。

References

  1. Managing Your Context Window: Clear vs. Compact in Claude Code
  2. Context Compaction in Codex, Claude Code, and OpenCode
  3. Context Compaction Research: Claude Code, Codex CLI, OpenCode, Amp
  4. Context Compaction Research
  5. Claude Code's Compaction Engine: What the Source Code Actually Reveals
  6. Claude Code's Compaction Engine
  7. How to Use the /compact Command in Claude Code
  8. Claude Code /compact: What It Does, What Survives