Appearance
Claude Code 与 Codex 的 /compact:上下文压缩到底怎么运作
更新: 7/5/2026 字数: 0 字 时长: 0 分钟
做深度编程时你大概遇到过这样的怪现象:一开始 AI 又快又准,聊到几十轮之后它却越来越"迟钝"、开始丢三落四。根本原因是上下文窗口被塞满了——大模型的窗口是有限的(比如 200k token),一场长会话里的系统提示、你的每条指令、AI 的每次回复、读过的每个文件、跑过的每条命令,全都在持续吃掉这个额度。窗口一旦逼近上限,模型不仅可能直接报错,还会因为"注意力被稀释"而出现所谓的 context rot(上下文腐化),把埋在中间的关键信息忽略掉[1]。
/compact 就是用来对抗这个问题的:把长会话历史压缩成一段精炼摘要,让工作能在窗口内继续,而不是撞墙重来。它和直接删除文件、和 /clear 清空会话都不是一回事。

先给一个贯穿全文的直观场景:假设你在修一个登录接口返回 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]。
处理流程
- 先抽取最近的用户消息(硬上限约 20,000 token 的量)单独保留;
- 把整段历史加上一段简短的摘要提示词发给模型。这段提示词的核心只有四点:当前进度与关键决策、重要约束与用户偏好、剩余待办、继续工作所需的关键数据;
- 收集模型返回的摘要;
- 重建历史 = 初始上下文(系统提示等)+ 最近用户消息 + 摘要,用这个压缩版整体替换掉旧会话[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]。

效果与局限:优点是直观——"交接备忘录"人人都懂。缺点是近乎"全有或全无":所有 AI 回复和工具结果被一份摘要顶替,一旦摘要漏掉某个关键细节,那就是真的永久丢失了[2]。
二、Claude Code 的 /compact:三层递进的"精准遗忘"
Claude Code 走的是更精细的路线:不追求一次性物理删除,而是设计了三层由轻到重的清理机制,能不调用 LLM 就尽量不调[2]。有源码级逆向也印证了这一点——它不是单一机制,而是三层依次施加、一层比一层重[5]。
下述三层机制及阈值来自社区逆向与公开材料,属基于公开实测的推断结论,非官方文档。

第一层:工具结果裁剪(零 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 段结构的摘要:
- 用户原始意图
- 核心技术概念
- 相关文件与代码
- 遇到的错误及修复方式
- 问题解决的逻辑链
- 所有用户消息的汇总
- 待办事项(TODO)
- 当前正在做的工作
- 建议的下一步
这份摘要要求极严——提示词要求模型直接引用原文关键短语而非全部转述,以防"上下文漂移"(模型在复述中悄悄偏离原意)[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 的"头部截断"其实就是它在压缩失败后的兜底截断动作——两种手段在同一个工具里都存在,只是优先级不同。

四、两者横向对比
| 对比维度 | Codex CLI | Claude 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.md、AGENTS.md或项目文档等持久文件——这些不受压缩影响,才是真正安全的[8]。 - 分清
/compact与/clear:前者压缩保留摘要、能续接工作;后者彻底清空历史、从零开始。
一句话总结:Codex 的 /compact 是"单层交接备忘录"——逐字留用户话、物理删其余、一份摘要顶上,直观但一刀切;Claude Code 的 /compact 是"三层精准遗忘"——先零成本裁剪工具结果、再缓存友好地只删尾部、实在不行才做 9 段式结构化摘要,复杂但省钱且更抗信息丢失。二者与普通截断的根本区别都在于:截断按位置丢,压缩按理解留。
需要的话,我可以把这份内容整理成一篇排版好的飞书文档,方便你归档或分享给团队。
References
- Managing Your Context Window: Clear vs. Compact in Claude Code
- Context Compaction in Codex, Claude Code, and OpenCode
- Context Compaction Research: Claude Code, Codex CLI, OpenCode, Amp
- Context Compaction Research
- Claude Code's Compaction Engine: What the Source Code Actually Reveals
- Claude Code's Compaction Engine
- How to Use the /compact Command in Claude Code
- Claude Code /compact: What It Does, What Survives