五分钟版:一条线、十种做法、三句开场
所有做法都在回答两个问题:剪掉哪一段,剪下来的还能不能拿回来。按「能不能拿回来」从上往下排,就是这张图。每一格都能点,跳到对应小节;小节里有动画,可以逐步播放。
- 03截断 / 滑动窗口:按位置删最旧的。零成本,删掉就没了;工具调用和结果必须成对删;窗口每轮都动,缓存就不命中。
- 04
- 05LLM 摘要:一次性(Claude Code
/compact,9 节模板)、滚动(会漂移)、分层(每层由下一层生成,可追溯)。 - 06结构化状态:目标、计划、决定、待办写成固定字段,放在固定位置或文件里,压完从磁盘装回。
- 07卸载 + 召回:原文写盘只留路径,需要时 grep 或检索取回逐字原文。失败多在模型不知道有东西可取。
- 08子 Agent 隔离:脏活在别的窗口里干,只交回结论。主窗口干净了,多 Agent 系统的总 token 约为聊天的 15 倍。
- 09提示词压缩:小模型按信息量删 token,LLMLingua 报告最高约 20 倍。会删掉否定词和数字,代码和 JSON 别用。
- 10KV 压缩与 prompt cache:KV 压缩在推理服务内部(StreamingLLM、H2O、KIVI、MLA);prompt cache 只认逐字节相同的前缀,Anthropic 读价 0.1 倍、写价 1.25 倍。
- 11厂商原生:Anthropic 有上下文编辑和服务端压缩,摘要可读;OpenAI 的压缩项是加密的,人读不了。
- 12Sage:历史只追加,每轮编译视图:按块定档、留桩可
expand、阶段内冻结、按成本不等式开新阶段。缺口是不压对话文字。 - 14会议场景:原文仓 + 结构化状态 + 分层摘要;实时路径不等摘要;逐字问题走检索。
面试开场三句
- 压缩按可逆性排序:先清能重新取回的,再卸到外部留指针,再抽结构化状态,最后才让模型摘要。
- 每一刀都会改动前缀、打碎 prompt cache,所以要一次压够、在自然断点压,最好先算账再压。
- 会议场景不能只靠摘要:原文仓兜底,状态表对账,分层摘要只负责「聊了什么」。
要压,不只是因为装不下
先说两个词。token 是模型读写文字的最小单位,中文大致一两个字一个 token,英文大致四个字母一个。上下文窗口(context window)是模型一次能看到的 token 总量:系统提示、工具定义、历史对话、工具返回的结果,全挤在这一个窗口里。Agent 每调一次工具就往里追加一段,窗口只会越来越满。
压缩的理由有四个,后三个往往比第一个更早咬人:
- 窗口上限。超过就报错,对话没法继续。窗口做到 100 万 token 的模型也一样,只是撞墙晚一点。
- 成本。API 每一轮都按发出去的全部输入计费。第 50 轮要为前 49 轮的历史再付一次钱,单轮越来越贵,总花费近似按轮数的平方涨。
- 延迟。模型要先把输入全部读一遍才能吐第一个字,这一步叫 prefill(预填充)。输入越长,首字等待越久。戴在脸上的眼镜,这个等待用户直接感觉得到。
- 注意力被稀释。窗口里东西越多,模型越难用上其中某一条。Liu 等人的 Lost in the Middle 把关键信息放在长输入的不同位置,发现放在开头或结尾时模型最容易用上,放在中间时最差,曲线呈 U 形。Chroma 2025 年的 Context Rot 报告测了 18 个模型,结论同向:任务不变,输入变长,表现就掉。Anthropic 把这叫「注意力预算」:上下文是边际收益递减的有限资源。
所以压缩的目标不是「塞得下」,而是每一轮窗口里只留这一步真正要用的东西。塞得下但塞满了,照样又慢又贵又笨。
一句话压缩不是为了塞得下,而是让每一轮只留这一步要用的东西:省钱、省首字延迟、少被「中间」稀释。
一条纸带看全部做法:区别在剪哪一段
把上下文想成一条按 token 计长的纸带,从左到右依次是系统提示和工具定义、记忆文件、早期对话、工具输出、最近几轮。所有压缩做法都是在这条纸带上剪、换、贴、挪,区别在于剪哪一段、剪下来的还能不能拿回来。
选法的顺序:可逆的先做,有损的后做;规则能决定的,不交给模型决定。
这条顺序和 Anthropic、Manus 公开的经验一致:Anthropic 的上下文工程文章把清掉旧工具结果称为最安全、最轻的一种压缩;Manus 主张压缩必须「可恢复」,网页正文可以丢,URL 必须留。
一句话区别只有两点:剪哪一段,剪下来的还能不能拿回来。可逆的先做,规则能定的不交给模型。
截断与滑动窗口:最便宜,也最粗暴
做法就是按长度砍。截断(truncation)是超了就删最早的消息;滑动窗口(sliding window)是永远只留最近 N 轮或最近 N 个 token,像一扇只往前挪的窗。系统提示通常钉住不删。工具层面也有一种截断:单次工具输出超过若干行或字节就只留头(或头加尾),几乎每个 harness 都做。
- 窗口装满:系统提示钉住不动,后面只留最近 5 轮。T1 是用户最早给的约束。
- 第 6 轮进来,窗口整体右移一格,T1 被丢掉。最先被删的,恰恰是最早、往往也最重要的要求。
- 第 7 轮进来,T2 被丢掉。下一格是一次工具调用和它的返回结果,两条消息是一对。
- 第 8 轮:如果只按条数切,调用被删、结果还留着,Anthropic 和 OpenAI 的接口都会拒收(400)。
- 正确做法是成对删:调用和结果一起出去,窗口宁可少放一轮。
- 缓存代价:窗口每轮都动,前缀每轮都变,prompt cache 只能命中系统提示那一小段。
- 机制
- 按位置删:留系统提示和最近 N 轮,其余丢弃。删要成对删:模型发起的工具调用和它的返回结果必须同进同出,否则 Anthropic、OpenAI 的接口都会拒收。
- 好处
- 不调模型,零延迟,完全可预测,好测试。
- 代价
- 删掉的就没了。最早的用户要求和约束恰恰在最前面,最先被删。
- 用在
- 闲聊机器人、只关心最近状态的任务,以及作为其他做法兜底的最后一道硬上限。
- 会怎么坏
- 目标丢失:开头说的「别动 public API」滑出窗口,模型就去动了。悬空调用:只删了工具调用对的一半,请求返回 400。每轮都滑:窗口每轮都动,前缀每轮都变,prompt cache 一次都命中不了(见第 10 节)。
一句话滑窗零成本但不可逆,只配当最后一道硬上限,而且必须成对删。
先删旧的工具输出:最安全的一刀
Agent 上下文里最占地方的不是对话,是工具结果:读进来的整份文件、搜索结果、命令输出。模型读过、用过之后,这些东西大多就没用了,而且多数可以重新取。工具结果清除(tool result clearing)就是把较早的工具结果换成一行占位文字,比如「[此结果已清除]」,工具调用本身(调了什么、参数是什么)保留。
- Agent 干活时,工具结果(读文件、搜索、跑测试)是上下文里最占地方的东西。
- 输入 token 越过
trigger(默认 100,000),服务端在模型看到请求之前执行清除。 - 从最旧的开始,把工具结果换成一行占位,调用本身(工具名、参数)保留;
keep默认保留最近 3 次,exclude_tools里的工具永不清除。 - 清除改动了历史中段,第一个占位之后的缓存全部作废。
clear_at_least规定一次至少清掉多少,清不够就不清,免得为一点点空间付一次缓存重写。 - 代价:模型之后若还要那份内容,只能再调一次工具;文件可能已经改过,命令输出也不一定能重现。
- 机制
- 触发条件满足时(比如输入超过某个 token 数),从最旧的开始把工具结果换成占位符,保留最近 K 次调用的完整结果;某些工具可以列为永不清除,比如记忆工具。Anthropic 的
clear_tool_uses_20250919就是这个做法,参数见第 11 节。 - 好处
- 不调模型,不改写模型说过的话;模型知道自己看过什么,需要时可以再调一次工具。社区把 Claude Code 里的这类机制叫 microcompact,Sage 的前身 harness-pi 也有同名插件。
- 代价
- 「可以重新调」不等于「能取回原文」:文件可能已经改过,命令输出、网页内容不一定能重现。每次清除都改动了历史中段,清除点之后的前缀缓存作废。
- 用在
- 编码 Agent、搜索 Agent 这类工具调用密集、单次结果很大的任务。长任务几乎都该先开这一项。
- 会怎么坏
- 重复劳动:模型忘了结论,把同一个文件又读一遍。结论跟着证据一起没了:要紧的数字只在工具结果里,模型没在对话里复述过,清掉就丢。频繁小清:每次只清一点,却每次都打碎缓存,所以要规定每次至少清掉多少。
同类规则还有两种更细的:覆盖淘汰,同一个文件后来又读了一遍,前一次的结果作废;重试淘汰,一次调用出错,紧接着用同一工具、同样参数又调了一次,出错那次的输出作废。两条都完全确定、可复现,Sage 就是这么做的(第 12 节)。
一句话清旧工具输出是最安全的第一刀,但要一次清够,并在清之前让模型把结论写下来。
LLM 摘要:省得最多,也最容易悄悄走样
做法是让一个模型(同一个,或更便宜的)读一段历史,写一份短总结,再用总结替换原文。按「多久总结一次、总结谁」分三种。
A · 一次性摘要
- 机制
- 上下文快满时,把系统提示以外的全部历史交给模型,按模板写一份「交接单」,用交接单替换整段历史,从这里接着干。
- 好处
- 一次能把十几万 token 压到几千;实现简单;模板可以按任务定制。
- 代价
- 多一次模型调用,要读全部历史(缓存还热时便宜得多,见第 10 节);这一轮用户要等;压完前缀整段换掉,系统提示之后的缓存全部作废。
- 用在
- 长编码任务、长对话,最好在自然断点(一个子任务刚做完)时做。
- 会怎么坏
- 丢关键细节:文件行号、报错原文、用户随口提的一条约束最容易被概括掉。状态写反:把「已决定」写成「在讨论」,或者反过来。被阈值逼着压:压在子任务中间,正在用的细节被摘掉。
B · 滚动 / 增量摘要
- 机制
- 维护一份持续更新的摘要。每过一段(N 轮或 N 分钟),把「上一份摘要 + 这一段新内容」交给模型,产出新摘要。窗口里永远是「当前摘要 + 最近原文」。对话记忆领域的递归摘要(Wang 等 2023)就是这个结构。
- 好处
- 每次只处理一小段,耗时和成本稳定;可以放后台异步跑,不阻塞用户;适合没有终点的流式输入。
- 代价
- 调用次数多;每次都在改写上一份摘要。
- 用在
- 长时间陪伴、客服、会议这类持续输入的场景。
- 会怎么坏
- 摘要漂移(summary drift):每次重写都可能把某个事实改动一点,十几次之后,「周三交付」变成「本周交付」,「张三负责」变成「团队负责」。原文早已不在输入里,误差只累积、不回滚。
- 第 1 段进来,摘要器写出 S1:「张三周三前交付 v2」,与原文一致。
- 第 2 段进来,把「S1 + 第 2 段」交给摘要器,得到 S2。第 1 段原文离开窗口。
- S3 是改写 S2 得来的:「张三」被概括成「团队」,「周三」成了「本周」。这就是摘要漂移。
- S4 再改写一次,预算变成「待定」。窗口里只剩 S4 和最新原文,误差只累积、不回滚。
- 对照分层摘要:每份段摘要都从原文生成,全会摘要从段摘要重算,原文留在仓里,细节可追溯。
C · 分层摘要
- 机制
- 原文按段切块,每块写一份段摘要;若干段摘要再合成更高一层;最顶层是全局摘要。窗口里放顶层、最近几段的摘要和最新原文。原文和每层摘要都存下来,需要细节时按树往下取。MemGPT 把这推广成操作系统式的分级内存:窗口是内存,外部存储是磁盘,模型自己调函数换入换出。
- 好处
- 误差不累积(每层由下一层生成,不由上一份摘要生成);粒度可选;可追溯到原文。
- 代价
- 工程量最大:要存储、要索引、要决定在哪切块、何时合并。
- 用在
- 超长文档、长会议、跨多天的项目历史。
- 会怎么坏
- 切在话题中间:一个决定的前因后果被分到两块,两份段摘要各写一半。上层过时:下层更新了,上层没重算。
Claude Code 的 /compact 和自动 compact
Claude Code 是一次性摘要的标准样本,几个具体事实值得记住(官方文档核对于 2026-10-12):
- 两种触发。手动
/compact可以带重点说明,例如/compact focus on the auth bug fix,摘要就按你指定的重点保留;自动 compact 在上下文接近上限时触发,做的事和/compact相同。另有/rewind:选一条消息,「从这里往后总结」或「总结到这里为止」,只压一段。 - 阈值。官方「Default auto-compact thresholds」现在的写法是:没设压缩窗口时,在模型的上下文上限处压缩;原生 1M 窗口的模型(Fable 系列、Sonnet 5 及以后、Haiku 5.5、Opus 4.7 及以后)默认在约 967K token 时压;Sonnet 4.6 / Opus 4.6 不开扩展上下文时在 200K 边界压。可用
/autocompact 500k、启动参数--autocompact、设置项autoCompactWindow或环境变量CLAUDE_CODE_AUTO_COMPACT_WINDOW修改,范围 100K 到 1M,环境变量优先级最高。 - 早年的阈值。200K 窗口时代,社区从打包代码里读出的公式是「有效窗口 − 13,000 token 缓冲」,约合 83.5%;
CLAUDE_AUTOCOMPACT_PCT_OVERRIDE只能把阈值调低、不能调高(GitHub issue #31806)。「大约 80% 多触发」是旧版本的事实,现在已改为按窗口大小配置。 - 摘要模板。官方没有公开全文。社区从发布包中提取的
BASE_COMPACT_PROMPT要求 9 节:主要请求与意图、关键技术概念、文件与代码片段、错误与修复、问题解决过程、所有用户消息、待办任务、当前工作、可选的下一步。模型先在<analysis>里打草稿,再写<summary>,只保留后者;压缩时明确禁止调工具;「下一步」要求逐字引用最近的对话,原文说明这是为了避免任务理解漂移。「所有用户消息」这一节最值得借鉴:用户的纠正和反馈是最不能丢的信息。 - 压完之后补回什么。官方「What survives compaction」表:系统提示照旧生效;项目根目录的 CLAUDE.md 和自动记忆从磁盘重新注入;重新读入最近改过的最多 5 个文件,超过 5,000 token 的只给路径;已调用的 skill 正文重新注入,每个最多 5,000、合计最多 25,000 token;带
paths:的规则和子目录里的 CLAUDE.md 会被一起摘要掉,要常驻就挪到根目录的 CLAUDE.md。
要点:Claude Code 的摘要不是唯一的记忆来源。真正靠得住的东西(规则、记忆、计划、最近文件)都在磁盘上,压完再从磁盘装回来;摘要只负责「我们聊到哪了」。这就是第 6 节的做法。
一句话摘要省得最多也最会走样:用分层不用滚动,模板字段固定,原文要留着。
结构化状态:把要紧的事从叙述里抽出来
摘要是一段散文,结构化状态是一张表。做法是让 Agent 在干活过程中把关键状态按固定格式写下来:目标、计划、已做的决定、待办清单、踩过的坑。这张表放在窗口里的固定位置,或者存成文件、每轮读回。历史怎么压,表都还在。
- 机制
- 三种常见形态。todo 列表:Agent 用工具维护任务清单,做完一项勾一项,Claude Code 的 TodoWrite 就是这个。笔记文件:Agent 在工作目录写 NOTES.md 或 progress.md 记进度和发现;Anthropic 的文章举过 Claude 玩宝可梦时自己维护笔记、跨越多次上下文重置继续玩的例子。记忆文件:CLAUDE.md / AGENTS.md 这类项目级说明,启动时自动加载,压缩后从磁盘重新注入;Claude Code 还有 auto memory,让模型自己往里积累经验。API 层面,Anthropic 提供 memory 工具(
memory_20250818),让 Claude 在客户端托管的目录里增删查改文件。 - 好处
- 人能读、能改、能 diff;字段固定,漏了什么一眼可见;与摘要互补,摘要丢了也不怕;放在窗口固定位置(开头或结尾),正好躲开「中间被忽略」。
- 代价
- 依赖模型自觉更新,它不写就没有;格式要事先设计;状态块每更新一次,它之后的缓存就作废,所以状态块应放在稳定前缀之后、易变内容之前。
- 用在
- 多步任务、跨会话项目、需要对账的场景(待办、决议、数字)。
- 会怎么坏
- 状态过期:做完了没勾掉,计划改了表没改。记忆污染:一次错误结论写进记忆文件,以后每次会话都被当成事实加载。文件膨胀:CLAUDE.md 越写越长,自己又成了需要压缩的东西。
一句话要紧的事别只放在散文里:抽成固定字段的表,存在磁盘上,压完再装回来。
卸到外部,事后再召回
卸载(offloading)是把大块原文写到窗口外面(文件、数据库、对象存储),窗口里只留一个指针:路径、编号或一句描述。需要时,模型用工具按指针取回,或者系统按当前问题检索相关段落再放回来。后一种就是 RAG(检索增强生成:先检索,再把检索到的内容放进提示让模型回答)。
- 工具一次返回约 5 万 token 的测试日志。大多数时候用不到,偶尔要逐字查。
- 超过阈值就整份写到磁盘,窗口里只留头几行、尾几行和文件路径。Sage 的 bash 工具就是这样:超过 30,000 字节整份写盘,留头 20,000、尾 5,000 字节和路径。
- Agent 继续干活,窗口保持很空;原文一个字没丢。
- 后来用户问「第 3 个错误是什么」:模型按路径 grep / read,或者系统按问题做检索(关键词 BM25 或向量相似度)。
- 命中的几行原文放回窗口尾部。召回的是逐字原文,不会有摘要漂移。
- 最常见的失败:桩只写「已省略」,没有路径、没有线索,模型不知道有东西可取,就开始猜。
- 机制
- 工具输出超过阈值时整份写盘,只返回「头若干行 + 尾若干行 + 文件路径」;历史中段的大块换成带编号的桩;回答问题时按关键词(BM25)或向量相似度从外部存储召回相关片段。Manus 把沙箱文件系统当作「容量不限、可持久、模型能自己操作的上下文」:网页正文丢掉但留 URL,文档内容丢掉但留路径。
- 好处
- 原文一个字不丢,压缩可逆;窗口里只有指针,极省;召回内容是逐字原文,不会有摘要漂移。
- 代价
- 召回可能漏,检索质量决定上限;模型得知道「有东西可取」并主动去取;多一次工具往返的延迟。
- 用在
- 大文件、长日志、网页、长会议原文,以及任何「大多数时候用不到、偶尔要逐字查」的内容。
- 会怎么坏
- 不知道自己不知道:桩写得太简略,模型看不出里面有它要的东西,就不去取,转而瞎猜。召回错段:语义相近,说的却是另一件事。指针失效:文件后来被改或被删,取回的已不是当时那份。
一句话卸载让压缩可逆,成败在桩:写清路径和线索,模型才知道有东西可取。
用子 Agent 隔离上下文:脏活在别的窗口里干
主 Agent 把一个需要大量阅读的子任务(「查清楚这个仓库里谁调用了 X」)派给子 Agent。子 Agent 有自己全新的窗口,读几十个文件,最后只交回一两千 token 的结论。主窗口从头到尾没见过那几十个文件。
- 主 Agent 的窗口里只有系统提示、对话和计划。下一步要查「谁调用了 X」,需要大量阅读。
- 主 Agent 写一段任务说明(目标、范围、返回格式)派给子 Agent。说明写得太短,子 Agent 就会做错事。
- 子 Agent 在自己全新的窗口里读几十个文件、跑十几次搜索,窗口越来越满。
- 子 Agent 只交回一两千 token 的结论,最好附上文件路径,主 Agent 可以抽查。
- 子窗口整个丢弃。主窗口只多了一小块结论,从没见过那几十个文件。
- 账:主窗口省了,总 token 却大涨。Anthropic 公开的数字是单 Agent 约为聊天的 4 倍,多 Agent 系统约 15 倍。
- 机制
- 派任务时给子 Agent 一段说明和一组工具;子 Agent 独立跑循环;结束时返回压缩后的结论,有时附上文件路径作为证据。Claude Code 的 subagent 和 Anthropic 的多 Agent 研究系统都是这个结构。
- 好处
- 主窗口干净;子任务可以并行;每个子窗口都短,各自不受「中间被忽略」影响。
- 代价
- 总 token 大涨。Anthropic 公开的数字:单 Agent 大约用掉聊天 4 倍的 token,多 Agent 系统大约 15 倍。子 Agent 从零开始,不知道主 Agent 之前讨论过的约束。
- 用在
- 能清楚切开、读多写少的调研类子任务:代码搜索、资料调研、并行检查。
- 会怎么坏
- 上下文割裂:Cognition 的《Don't Build Multi-Agents》举过例子,两个子 Agent 各自做了相互冲突的隐含决定,拼起来就不对。结论失真:子 Agent 交回的总结本身就是一次有损摘要,主 Agent 无从核对。说明写得太短:子 Agent 做的不是你要的事。
一句话子 Agent 用更多总 token 换一个干净的主窗口,适合能切开的阅读型任务。
提示词压缩:按 token 删掉「不重要的字」
前面的做法都以「消息」或「块」为单位。提示词压缩深入到字词:用一个小模型判断每个 token 的信息量,删掉信息量低的,留下一段人读起来像电报、大模型仍然看得懂的文字。
- 机制
- LLMLingua(微软,EMNLP 2023)用一个小语言模型算每个 token 的困惑度(perplexity,越难预测信息量越大),删掉容易预测的 token,再由预算分配器决定每段压多少,论文报告最高约 20 倍压缩、性能损失很小。LongLLMLingua 加入「与问题是否相关」的判断,并把相关段落重排到前面,专门对付长上下文和中间丢失。LLMLingua-2(2024)改用 GPT-4 蒸馏出的数据,训练一个 BERT 规模的编码器逐 token 做「留 / 删」分类,比前代快 3 到 6 倍。
- 好处
- 与任务无关,任何文本都能压;压缩器小、快;对检索回来的长文档、示例很多的提示效果最明显。
- 代价
- 多部署一个模型;压完的文字人基本没法读,调试困难;对本来就紧凑的内容(代码、JSON、表格)收益小、风险大。
- 用在
- RAG 召回的长文档、大量 few-shot 示例。ASR 转写里的语气词、重复和口头禅也是好目标,但先用规则删更稳。
- 会怎么坏
- 删掉否定词和数字:「不」「没」、金额、日期被判为低信息 token,意思就反了。弄坏结构:代码和 JSON 少一个括号就用不了。和缓存冲突:按问题动态压缩,每次输出都不同,前缀缓存失效。
一句话按 token 删字适合检索回来的长文和 ASR 噪声,不适合代码、JSON 和带否定词的约束。
KV cache 压缩与 prompt cache:名字很像,是两件事
先把两个词分开。KV cache 是模型推理时为每个读过的 token 存下的中间结果(注意力里的 Key 和 Value 向量);有了它,生成下一个字时不必把前文重算一遍。它的大小和上下文长度成正比,长上下文的显存主要被它吃掉。prompt cache(提示缓存)是服务商把某段前缀的 KV cache 存起来,下次请求只要开头那段逐字节相同就直接复用,并按便宜得多的价格计费。
KV cache 压缩:推理服务内部的事
- 机制
- 三条路。淘汰:只留重要 token 的 KV。StreamingLLM 发现开头几个 token 会吸走大量注意力(attention sink,注意力汇),保留它们再加最近一段窗口,就能稳定生成很长的流;H2O 保留累计注意力最高的「重量级」token;SnapKV 用提示末尾一小段观察窗口,挑出每个注意力头关心的位置。量化:把 KV 从 16 位压到 2 到 4 位,例如 KIVI。改架构:GQA 让多个注意力头共享一份 KV;DeepSeek-V2 的 MLA 把 KV 压成低维潜变量,论文报告 KV cache 减少 93.3%。
- 好处
- 对上层透明,对话内容一个字没改;省显存、提吞吐,能跑更长的上下文。
- 代价
- 只有自己部署模型(vLLM、SGLang 等推理框架)才能做;调闭源 API 时这一层不归你管。
- 用在
- 端侧或私有部署的长上下文推理,比如眼镜或边缘盒子上跑的小模型。
- 会怎么坏
- 被淘汰的 token 后来又重要了:模型对它「失明」,上层却完全不知道,最难调试。量化太狠会让长距离检索类任务掉点。
prompt cache:所有压缩做法都要算的那笔账
Anthropic 的缓存读取价是基础输入价的 0.1 倍,5 分钟档的缓存写入是 1.25 倍。Manus 公开说他们把 KV-cache 命中率当作生产环境最重要的单一指标:Agent 的输入输出比大约 100:1,缓存与不缓存的价差是 10 倍。缓存只认逐字节相同的前缀,中间改一个字,后面全部作废。
由此得出三条硬规则:
- 稳定的放前面,易变的放后面。系统提示、工具定义、术语表在最前;状态块和摘要其次;最新原文和当前问题最后。别在系统提示里放时间戳。
- 一次压够,别频繁小压。Anthropic 的
clear_at_least参数就是为此设的:清不到这么多就干脆不清。 - 压缩请求本身也能吃缓存。Claude Code 文档说明:压缩请求带着同样的系统提示、工具和历史,只在末尾追加一条总结指令,缓存还热时按缓存价读前缀,所以会话中途
/compact远比上下文大小看上去便宜;隔很久再恢复旧会话时缓存已过期,那一次压缩最贵。
一句话KV 压缩是推理服务内部的事;prompt cache 是每一种压缩都要算的账。
厂商原生方案:Anthropic 给了三把刀,OpenAI 给了一个黑盒
两家都已把「清理」和「摘要」做进服务端。差别在于:Anthropic 的产物你能读、能改;OpenAI 的压缩产物是加密的,人读不了。以下参数全部来自官方文档,beta 功能照抄 beta 名。
compact_threshold 由你设,文档示例为 200,000)。绿框是压缩发生的那一步。Anthropic · 上下文编辑(context editing)
- beta 头
context-management-2025-06-27,在请求里加context_management.edits。编辑在服务端、模型看到之前生效,客户端保存的完整历史不用改。 - 工具结果清除
clear_tool_uses_20250919:trigger默认 100,000 输入 token(也可按工具调用次数);keep默认保留最近 3 次工具调用;clear_at_least规定每次至少清多少,清不到就不清;exclude_tools列出永不清除的工具;clear_tool_inputs默认 false,即只清结果、保留调用参数。被清的结果换成占位文字。 - 思考块清除
clear_thinking_20251015:控制保留最近几轮的 thinking 块,keep设为"all"可保住缓存。两种策略同时用时,它必须排在前面。 - 缓存影响:文档明说工具结果清除会让被清位置之后的缓存前缀失效,所以要用
clear_at_least让每次失效都划算。 - 配合 memory 工具:接近清除阈值时 Claude 会收到自动提醒,先把要紧内容写进记忆文件,再被清除。
- 响应里的
context_management.applied_edits报告清了几次、清掉多少 token;token 计数接口可以预览清除后的大小。
Anthropic · 服务端压缩(compaction)
- 阈值压缩:beta 头
compact-2026-01-12,把策略compact_20260112加进context_management.edits。trigger只支持input_tokens,默认 150,000,最低 50,000。达到阈值时服务端自动写摘要并返回一个compaction块;下一次请求把响应原样追加,API 会自动丢弃这个块之前的全部内容。instructions可以自定义摘要提示,但会完全替换默认提示;pause_after_compaction让 API 写完摘要先停下,你可以把最近几轮原文插回去再继续。 - 按需压缩:beta 头
compact-2026-09-04,顶层参数compaction(如{"type":"summarize"}),由应用自己决定何时压。官方概览页现在的建议是:能用按需压缩就用按需压缩。它支持保留最近几轮逐字不动,也支持后台压缩:摘要请求在后台跑,会话照常在完整历史上继续,摘要回来后只替换当初发出去的那部分历史,期间新增的轮次留在后面。它不能和context_management放进同一个请求。 - SDK 里的客户端压缩
compaction_control已标记为废弃(Python SDK 1.0 起移除)。它的默认摘要提示分 5 节:任务概览、当前状态、重要发现、下一步、需保留的上下文,包在<summary>标签里,可作为自写摘要器的模板。文档也列了不适合压缩的情况:需要精确回忆早期细节、需要在很多变量之间维持精确状态的任务。
OpenAI · Responses API 的 compaction
- 服务端自动压缩:在
POST /responses里设context_management: [{"type": "compaction", "compact_threshold": 200000}]。渲染后的 token 数越过阈值时,服务端在同一次流式响应里跑一次压缩,输出一个压缩项,裁掉旧上下文后继续推理。 - 独立端点:
POST /responses/compact,完全无状态。把整个窗口发过去(它本身必须还塞得进模型窗口),返回一个压缩后的新窗口,里面有一个压缩项,也可能保留部分原有项。文档要求原样作为下一次/responses的输入,不要再裁。 - 产物不可读:压缩项类型为
compaction,内容在encrypted_content里,文档原话是「不透明,不打算让人读懂」,携带此前的关键状态和推理。设store=false时对零数据保留(ZDR)友好。 - 串联方式:无状态拼接输入数组时,可以丢掉最新压缩项之前的所有项以降低延迟;用
previous_response_id串联时不要手动裁剪。
怎么选:要审计、要自定义保留规则、要把摘要给人看(会议纪要),选 Anthropic 或者自己写;想省事、希望模型的推理状态也一并带过去,选 OpenAI,但要接受看不见它留下了什么。两家的服务端压缩都不替你保存原文,原文仓得自己建。
一句话Anthropic 给你能读能改的摘要和清理规则,OpenAI 给你看不见的加密压缩;两家都不替你存原文。
Sage:不改写历史,每轮重新编译视图
Sage 和上面所有方案不在同一条轴上。它从不改写历史:历史是一份只追加的 Log;每一轮调模型之前,由「上下文编译器」从 Log 算出这一轮要发的内容(视图),并留一份清单,写明每一块为什么在、为什么不在。于是压缩变成了「这一轮给每块选什么档位」,而不是「把过去改写成什么样」。以下引用基于仓库 main 分支提交 ac6d7f5(2026-10-12 15:27 北京时间)的源码和文档。
ViewProvider 这一个接缝接进来。内核默认的视图是 passthroughView(原样发送),CLI 默认换成编译器,加 passthrough 选项才退回原样。indexChunks(chunks.ts)把只追加的 Log 切成块:一次 tool_use 加它的 tool_result 是一块,纯文字消息也是一块。块只能整进整出。eliminate(eliminate.ts)按顺序取第一条命中的规则:最近 6 个工具块(DEFAULT_KEEP_RECENT)保持全文;#1 的读被后来对同一文件的读覆盖(superseded_by);#2 出错后紧接着用同一工具、同一参数重调(retry_intermediate)。- 仍超预算(
DEFAULT_COMPILER_BUDGET = 40_000)时,从最旧的、非最近的工具块开始降为 none(budget:oldest-first)。用户和助手的文字一律kept。 - 被省掉的块由
renderStub(stub.ts)留一行桩:编号、工具名、参数摘要、token 数和expand("id")的用法。 - 下一轮 #11 只追加在尾部。#5 此时已被 #7 的写覆盖,但
frozenDecisions(freeze.ts)让阶段内档位不变,前缀逐字节相同、缓存命中;#5 记为staleOmittable。 chooseEpoch/classifyEpoch(decide.ts)算账:省下的读成本超过重建多付的写成本,或预计输入超过窗口 90%,才开新阶段(写view_epoch),过时块这时一起清;路由策略也只在这一刻换模型。- 模型想看 #1 原文:调
expand(expand.ts),从 Log 读回当时那份放进尾部,不重跑工具。 - 每轮写
manifest:每块{id, tier, reason, tokens},外加预算、阶段决定、缓存预测与实测。
seq:12#toolu_…。数据结构
- Log:只追加的树,条目带 id / parentId / seq,先写 JSONL 再进内存,读入时丢掉写了一半的尾行。与压缩相关的条目类型登记在
packages/core/src/log/kinds.ts:view_epoch(开了新阶段,旧前缀原样保留)、model_change(换模型也只是一条记录,不改历史)、manifest(每轮的编译清单)、request_header(system 和工具定义只在哈希变化时存全文)。 - 块(Chunk):
packages/plugins/src/compiler/chunks.ts的indexChunks。一次 tool_use 加它的 tool_result 是一块,编号seq:<助手消息 seq>#<toolUseId>;一条纯文字消息是一块,编号seq:<seq>。每块带tokens和accesses(这次调用读写了哪些资源)。块只能整进整出,工具调用对不会被拆散。 - 档位(Tier):
full/summary/line/none。历史块用 none / summary / full,插件的申领单四档都能用。 - 申领单(Claim):
claim.ts。插件不直接往上下文里塞文字,而是交一张单子{id, priority, pinned, placement, tiers}:tiers是每个档位对应的渲染文字;placement是epoch-head(随阶段冻结在开头)、tail(每轮重算放末尾)或tail-if-changed。现有四个:agents-md、progress-ledger、system-reminder、tool-catalog。 - 清单(manifest):每块一行
{id, tier, reason, tokens},另有预算、阶段决定、缓存预测值与实测值、打分器报告。reason必填,例如recent、superseded_by:<id>、budget:oldest-first、scorer:entry:full;summary-rejected。设计文档的原则是「不允许有不知道为什么在那里的 token」。
什么时候触发、保留什么、丢掉什么
- 每轮
compiler.ts的contextCompiler().view调compileView,顺序是:组装冻结视图和从头重编的候选视图 →chooseEpoch→ 重建时打分 →openEpoch(可路由换模型)→ 放缓存断点 → 入口打分 → 影子打分 → 写清单。编译每轮都跑,阶段内前缀不变。- 定档规则
eliminate.ts的eliminate,按顺序取第一条命中的规则:最近DEFAULT_KEEP_RECENT = 6个工具块保持全文(recent);一块的每次访问都被后来的成功调用覆盖时作废(superseded_by,读可被后来对同一资源的读、写、搜覆盖,写只能被后来的写覆盖);出错的块,如果下一个工具块是同一工具、同样参数(即重试),出错那次作废(retry_intermediate);仍超预算就从最旧的非最近工具块开始丢(budget:oldest-first)。用户和助手的文字一律保留全文(kept)。- 预算
- 默认
DEFAULT_COMPILER_BUDGET = 40_000,CLI 用--budget修改。申领单由budget.ts的allocate分配:钉住的取最高档、永不降;其余超预算时,挑「当前档位最高、优先级最低」的一张降一档,循环到放得下为止。token 估算defaultTokenCount是「字符数 ÷ 4」,再由calibrate.ts用实测 usage 校准(固定开销 + 估算 × 比例)。 - 开新阶段
decide.ts的classifyEpoch:预计输入超过窗口 90%(CONTEXT_WINDOW_GUARD_RATIO = 0.9)时强制重建;能省下的不足冻结视图的 20%(DEFAULT_MIN_SAVINGS_RATIO)时不重建;重建当轮就更便宜时立即重建;否则回本轮数不超过预计剩余轮数才重建。剩余轮数remainingTurnsFor= max(1, 20 − 已过轮数, ⌈0.5 × 已过轮数⌉)。读价、写价取自模型价目表,取不到时回落到 0.1 / 1.25。- 阶段内冻结
freeze.ts的frozenDecisions:一个块在本阶段第一次被定的档位,阶段结束前不变,以保证前缀逐字节相同。代价是前缀里可能留着已经过时的块,清单记为staleOmittable,等下一次重建再清。- 被省掉的块
stub.ts的renderStub留一行:[#id · 工具名 · 参数(≤80 字符) · 4.1k tokens · omitted · expand("id") to retrieve]。expand.ts的expand工具按编号从 Log 读回原文,不重跑工具,取回的就是当时那份。- 摘要档
- 默认打分器
scorer/rule.ts的ruleScorer什么都不改(全部 full)。改用--scorer model|jev才会出现 summary:摘要最长 200 字符(SUMMARY_LIMIT);入口模式只给 ≥ 300 token 的块打分;摘要不足 20 字,或不含路径、符号、数字中的任何一种,就被拒收、退回全文,原因记summary-rejected。这条规则的起因写在docs/notes/implemented/2026-09-23-summary-rules.md:一次 1363 token 的 read 被压成「已看过,不再需要」。 - 结构化状态
claims/progress-ledger.ts:模型调用update_ledger写入{goal, plan[], decisions[]},编译器把 Log 里所有这类调用折叠成一份账本,作为钉住、优先级 100、只有全文档的申领单放在尾部,内容变了才更新。- 工具层
tools/src/spill.ts:bash 输出超过 30,000 字节时整份写盘,视图里只留头 20,000 字节、尾 5,000 字节和文件路径;tools/src/truncate.ts按整行截断 read / find / ls 的输出(列表最多 500 项)。- 子任务
subtask-view.ts的compileSubtask:子会话的消息从父会话最近一次请求逐字节重建开始,所以直接命中父会话的前缀缓存;子会话自己从不开新阶段。子 Agent 不复制历史,块编号在父子之间指向同一份原文。
逐项对照
| 前文做法 | Sage 里的对应物 | 差别 |
|---|---|---|
| 截断 / 滑窗 | 工具层 truncate / spill;超预算时 budget:oldest-first | 按块丢,不拆工具调用对;丢掉的块留桩可取回,不是真删 |
| 清工具输出 | 块定档为 none + 桩 | 桩写明编号和大小,expand 取回当时原文,而不是「再调一次工具」;另有覆盖淘汰、重试淘汰两条确定规则 |
| LLM 摘要 | 块级 summary 档(≤200 字符,打分器可选) | 没有整段历史摘要;摘要逐块生成、有格式校验,原文仍在 Log |
| 结构化状态 | progress-ledger、agents-md 申领单 | 账本是钉住的申领单,不会被预算挤掉;但只在模型主动调用时存在 |
| 卸载 + 召回 | Log 本身就是原文仓;expand;bash spill 写盘 | 按编号精确取回,没有按语义检索 |
| 子 Agent | compileSubtask | 子会话共享父会话的前缀缓存,不复制历史 |
| 提示词压缩 | 无 | — |
| KV / prompt cache | 阶段冻结 + 成本不等式 + 缓存预测与实测对账 | 把「何时打碎缓存」从阈值触发改为算账触发,这是它最独特的地方 |
| 厂商服务端压缩 | 不用 | 自己掌握视图,在 Anthropic 和 OpenAI 兼容端点(DeepSeek / Qwen / Kimi)上行为一致 |
独特之处
- 记录和视图分开。别人压缩是改写历史,Sage 只改「这一轮发什么」。任何一次省略都能撤销,任何一轮的视图都能从 Log 逐字节重建、离线回放。
- 缓存失效是算出来的,不是阈值逼出来的。每次「要不要放弃缓存」都有一行可复查的账:
EpochDecision里记着旧 / 新 token、读写价和回本轮数。 - 每个 token 都有来由。清单里每块都有
reason;expand的调用率直接就是打分器的漏判率,不用另外埋点。 - 在阶段边界换模型,不多付缓存钱,因为那一刻缓存本来就要重建。
缺口
- 对话文字从不压缩。
eliminate把用户和助手的文字一律定为kept,打分器也只看工具块。编码任务问题不大(大头在工具输出),但对话本身很长时,唯一的兜底是 90% 窗口护栏,而护栏重建用的还是同一套规则,文字照样全留。直接搬到会议场景,转写稿全是文字,Sage 现在一个字也不会省。 - bash 永远不会被覆盖淘汰。
bash.ts的accesses()固定返回空数组,所以 bash 块只能被「最旧优先」丢掉;跑了十次的pnpm test输出会一直占着位置,直到预算逼迫。 - 中文 token 估算偏低。「字符数 ÷ 4」对英文代码大致成立,对中文会低估好几倍。
calibrate.ts用实测 usage 校准比例能纠正大部分,但每个阶段的第一轮没有校准数据。 - 超预算时按新旧丢,不按相关性丢。默认打分器是空操作;接上模型打分器又要为打分付钱。
- 账本靠模型自觉。
update_ledger不校验输入,模型不调用就没有账本,也没有自动抽取兜底。 - 收益还没被实测证明。
docs/status/2026-09-24-sage-status.md写得清楚:在 DeepSeek flash 上缓存读只要全价的 2%,阶段重建要 37 到 106 轮才回本;默认 40,000 的预算在短任务里从没被触到(omitted=0、epochs=1);同模型入口打分稳定解出 7/10,比不打分的 9/10 少 2 条,费用高 75%。结论是「编译器在 flash 上不出手是对的」,价值要到缓存读价贵的服务商或超长会话上去验证。另外,docs/design/context-compiler.md头部仍写着「设计提案 v2,未实现」,与 README 和状态页的「M1–M4 全部合入」不一致,属于文档过期。
可以怎么改
- 给文字块加分层摘要档,只在阶段边界生成。新增一张
history-digest申领单,placement: "epoch-head":开新阶段时(缓存本来就要重建),把上一阶段的文字块压成段摘要,原文仍在 Log、仍可expand。摘要的成本和缓存重建落在同一轮,不额外打碎缓存,正好补上缺口 1。 - bash 的覆盖规则按「命令 + 工作区状态」判断。同一条命令、工作区没变,后一次覆盖前一次;或者把输出内容的哈希写进
accesses。 - token 计数按文字类型分档。CJK 字符按约 1 token 计,其余按 4 字符计;或者在每个阶段开头调一次服务商的计数接口当锚点。
- 超预算淘汰换成便宜的相关性信号。用最近一条用户消息给各块做 BM25 打分,排在「最旧优先」之前,零模型调用。
- 账本加兜底抽取。阶段边界时如果账本为空或过期,用便宜模型从最近的文字块抽一次 goal / decisions,写成一张带
reason: auto-extract的申领单。 - 在缓存读价为 0.1 倍的服务商和超长会话上补一组 A/B。不等式的结论最依赖读写价比,这在 flash 上测不出来。
面试时怎么讲
按「问题 → 设计 → 结果 → 迁移」四句讲,控制在两分钟:
- 问题:我之前的 harness 是阈值触发压缩,三个插件排队改同一个消息数组,先后顺序只写在注释里;每压一次缓存全丢,而且摘要有损,占位符也取不回原文。
- 设计:我把记录和视图分开。历史只追加;每轮由编译器按块定档、留桩,可以
expand取回;前缀按阶段冻结,开新阶段要过一道成本不等式;每轮写清单,每个 token 都有来由。 - 结果:在 DeepSeek flash 上它没有净收益,因为缓存读只要 2% 的价格,回本要几十轮。这是实测出来的,也说明不等式在起作用:它判断不该出手,就没出手。真正的价值场景是缓存贵、会话超长。
- 迁移到会议眼镜:转写稿就是 Log;每次用户提问编译一次视图;话题切换处是天然的阶段边界,在那里生成段摘要;决议和待办是钉住的账本申领单;「刚才谁说了什么」用
expand按时间戳取原话。我也清楚它现在的缺口是不压文字,会议场景必须先补上第一条改进。
最可能的追问:「为什么不直接用厂商的服务端压缩?」答:要跨服务商、要审计每一轮模型看到了什么、要能离线回放比较策略,这三条服务端黑盒都做不到;代价是自己维护一个编译器。
一句话Sage 把压缩改成「每轮给每块选档位」:历史不改写,省略可取回,打碎缓存前先算账;缺口是不压对话文字。
对比表:按可逆性从上往下排
| 做法 | 可逆 | 额外模型调用 | 缓存 | 主要失败 | 会议场景 |
|---|---|---|---|---|---|
| 卸载 + 召回 | 是 | 检索时可能需要 | 召回内容放尾部则无影响 | 不知道去取、召回错段 | 必用:原文仓 + 检索 |
| 清旧工具输出 | 部分(可重调) | 无 | 清除点之后失效 | 结论随证据丢失、重复劳动 | 用于检索结果、日历等工具输出 |
| 子 Agent | 主窗口不受影响 | 多,约 4–15 倍 token | 子窗口另起 | 上下文割裂、结论失真 | 会后整理、深度问答 |
| 结构化状态 | 状态本身可审 | 抽取时需要 | 状态块之后失效 | 过期、污染、漏写 | 必用:决议、待办、术语 |
| 分层摘要 | 保留底层原文则可追溯 | 每段一次,外加合并 | 追加新段不碰旧段 | 切在话题中间、上层过时 | 首选的摘要形态 |
| 滚动摘要 | 否 | 每段一次 | 每次更新摘要块即失效 | 摘要漂移 | 只作快速概览,须配原文仓 |
| 一次性摘要 | 否 | 一次,读全量 | 系统提示之后全部失效 | 丢细节、压在任务中间 | 不适合实时,适合会后 |
| 截断 / 滑窗 | 否 | 无 | 每轮都滑则从不命中 | 目标丢失、悬空调用 | 只作最后的硬上限 |
| 提示词压缩 | 否 | 小模型一次 | 动态压缩则失效 | 删掉否定词和数字 | 先用规则删语气词 |
| KV 压缩 | 否(上层不可见) | 无 | 推理框架内部的事 | 被淘汰的 token 后来又重要 | 端侧自部署模型时 |
| Anthropic 服务端压缩 | 否,但摘要可读 | 服务端一次 | 压缩块之前全部丢弃 | 默认提示不懂你的领域 | 可用,需自定义 instructions |
| OpenAI 服务端压缩 | 否,产物加密不可读 | 服务端一次 | 压缩项之前可丢 | 看不见保留了什么 | 不宜作为纪要来源 |
| Sage 编译器 | 是(Log 不改写) | 默认无,打分器可选 | 阶段内冻结,按账重建 | 文字不压,漏判靠 expand 兜底 | 框架可用,需补文字摘要档 |
一句话按可逆性看表:越往下越省,也越难追回。
会议场景:原文仓兜底,三层摘要供读,状态表对账
眼镜端会议助手的上下文有三点和编码 Agent 不同。输入是没有终点的文字流:一小时中文会议大约一两万字,token 数相近。延迟敏感:用户戴着眼镜问「刚才他说的截止日期是哪天」,等三秒就嫌慢。精确性要求极高:人名、数字、日期、谁负责什么,错一个就是事故。此外,ASR(自动语音识别)的转写自带噪声:同音错字、说话人标错、大量「嗯」「那个」。
结论:不要只靠摘要。原文仓保证一字不丢,结构化状态保证要紧事项可对账,分层摘要只负责让模型知道整体聊了什么。
八条具体建议
- 原文仓是唯一的真相。转写只追加、不改写,按分钟或按发言切片,带说话人和时间戳;建 BM25 加向量双索引(人名、数字、专有名词靠关键词才找得准)。任何摘要和状态都要能回指到原文时间戳。
- 实时路径不等摘要。段摘要和状态抽取放后台异步跑(Anthropic 的后台压缩是同一思路)。用户提问时只拼现成的内容,首字延迟只取决于输入长度和模型速度。
- 摘要用分层,不用滚动。L1 段摘要从原文生成,L2 从 L1 重算,任何一层都不改写上一份摘要,避免「周三」漂成「本周」。尽量切在话题切换或长停顿处,不按固定 token 数硬切。
- 结构化状态单独抽、单独校验。决议、待办(谁 / 做什么 / 何时)、未决问题、数字、术语表用固定 JSON 结构,每条带出处时间戳。新抽取与旧状态冲突时(比如期限改了),保留两条和时间顺序,不静默覆盖。状态块放在提示的固定位置(靠前),避开「中间被忽略」。
- 先用规则清洗,再考虑 LLMLingua。语气词、重复、口头禅用规则删;数字、否定词、人名一律不许删。ASR 同音错字用术语表纠正(术语表放在稳定前缀里,同时作为 ASR 热词)。
- 按缓存来排布。系统提示、工具、术语表、会议元信息放最前,整场不动;状态块其次;L1 只追加;L0 和召回内容放最后。前缀里别放当前时间。
- 逐字问题走检索,概括问题走摘要。「他原话怎么说」「刚才那个数字」先检索原文仓,把命中片段连同时间戳放进尾部;「这场会主要定了什么」读状态和 L2。回答附上时间戳,用户可以回放核对。
- 端云分工。端侧(眼镜或手机)做 VAD(语音活动检测)、ASR、规则清洗和最近几分钟的缓冲;云端做段摘要、抽取和问答。网络断开时端侧仍保存原文,恢复后补传,后台补跑。端侧若跑小模型,KV 压缩(滑窗加 attention sink)才是你要管的那一层。
怎么评估
- 事实保真:在测试会议里预埋 30 到 50 个「针」(人名、数字、日期、谁负责什么),会后用问答测召回率,并按经历的压缩次数分组,看是否随时间下降。这就是漂移的量化。
- 待办的召回与精度:人工标注的待办清单对照状态表,人、事、期限分别计分。
- 延迟与成本:首字延迟 P50 / P95;每小时会议的 token 数;缓存命中率(缓存读取 token 占总输入的比例)。
- 可追溯:随机抽查摘要里的条目,看能否在 10 秒内回放到原文时间戳。
一句话会议助手 = 原文仓兜底 + 状态表对账 + 分层摘要供读,所有摘要都在后台跑。
面试可能问到的问题
Q1Agent 的上下文压缩有哪些做法?你怎么选?
- 先给框架:按可逆性排序。卸载和清工具输出可逆或部分可逆,结构化状态可审,摘要有损,token 删减和 KV 压缩在推理层。
- 选法:规则能决定的不交给模型;可逆的先做;有损的放在自然断点做、一次压够。
- 每说一种都带上它会怎么坏:截断丢目标、清除丢结论、摘要漂移、子 Agent 割裂、LLMLingua 删掉否定词。
- 收尾讲缓存:每种做法都会改动前缀,压缩决策必须算缓存账。
Q2两小时会议实时转写、眼镜上随时提问,上下文怎么设计?
- 三件套:只追加的原文仓加检索;分层摘要(L0 原文 / L1 段 / L2 全会);结构化状态(决议、待办、问题、数字、术语)。
- 实时路径只拼现成内容,摘要和抽取在后台异步跑;逐字问题走检索,概括问题走摘要和状态。
- 提示按稳定程度排序以吃缓存;术语表放进前缀,同时用于 ASR 纠错。
- 端云分工、断网续传;评估看预埋事实召回、待办召回、首字延迟和缓存命中率。
Q3摘要会漂移、会丢细节,怎么发现、怎么防?
- 防:用分层而不是滚动,每层从原文或下一层生成;摘要模板固定字段(参考 Claude Code 的 9 节,尤其「所有用户消息」和逐字引用的「下一步」);数字、人名、期限单独进结构化状态;摘要条目带原文出处。
- 测:预埋事实问答,按压缩次数分组看准确率是否下降;压缩前后用同一组问题作答对比;抽样回放原文核对。
- 兜底:原文仓永远在,答不准就检索原话。Sage 的规则也可以借:摘要不含路径、符号或数字就拒收,退回全文。
Q4压缩和 prompt cache 有什么冲突?怎么平衡?
- 缓存只认逐字节相同的前缀;清除、摘要、动态压缩都会让改动点之后的缓存失效,下一轮按全价加写入溢价重读。
- 平衡:稳定内容放前、易变内容放后;一次压够(
clear_at_least);在自然断点压;趁缓存还热时发压缩请求。 - 能算就算:用 Sage 的不等式「(旧−新)×读价×剩余轮数 > 新×写价 − 旧×读价」,省下的读成本超过重建多花的写成本才压。
- 举反例:在缓存读只要 2% 价格的服务商上,算出来几乎从不值得压,这是实测结论。
Q5什么时候该用子 Agent,什么时候不该?
- 该用:读多写少、能清楚切开、结果能压成短结论的调研类任务,可以并行。
- 不该用:子任务之间有隐含决定需要协调(比如分头写同一段代码)、对延迟敏感、预算紧(多 Agent 约 15 倍 token)。
- 要点:派任务的说明要写足;子 Agent 返回结论时附证据路径,主 Agent 可以抽查;能共享前缀缓存最好(Sage 的
compileSubtask从父请求逐字节重建)。
Q6讲一个你自己做过的上下文管理设计。
- 用第 12 节的四句:问题(阈值压缩、缓存全丢、占位符取不回原文)→ 设计(记录与视图分开、按块定档留桩、阶段冻结加成本不等式、清单)→ 结果(flash 上不出手是对的,如实给数字)→ 迁移(转写稿是 Log,话题切换是阶段边界,决议和待办是钉住的账本)。
- 主动说缺口:对话文字不压,会议场景要补分层摘要档,并且已经想好放在阶段边界生成,不额外打碎缓存。
一句话每道题都按「框架 → 失败方式 → 缓存账 → 自己的例子」来答。
出处
- Anthropic · Context editing(工具结果清除、思考块清除、配合 memory 工具、SDK 压缩及其默认摘要提示)· platform.claude.com/docs/en/build-with-claude/context-editing
- Anthropic · Compaction 概览 · platform.claude.com/…/compaction;按需压缩 · …/compaction-on-demand;后台压缩 · …/compaction-background;阈值压缩参数 · docs.anthropic.com/en/build-with-claude/compaction
- Anthropic · Prompt caching(缓存读 0.1 倍、5 分钟写入 1.25 倍)· platform.claude.com/…/prompt-caching
- OpenAI · Compaction 指南 · developers.openai.com/api/docs/guides/compaction;
/responses/compact参考 · developers.openai.com/…/responses/methods/compact - Claude Code · Explore the context window(What survives compaction)· code.claude.com/docs/en/context-window;Model configuration(Default auto-compact thresholds)· code.claude.com/docs/en/model-config;Prompt caching(压缩的缓存成本)· code.claude.com/docs/en/prompt-caching
- Claude Code 旧阈值与覆盖变量 · anthropics/claude-code#31806;changelog 摘录见 #64520(社区分析,非官方文档)
- Claude Code 摘要提示全文(社区从发布包提取,非官方)· codeaashu/claude-code · prompt.ts;ccprompts.info · service-compact-base
- Anthropic · Effective context engineering for AI agents · anthropic.com/engineering/effective-context-engineering-for-ai-agents
- Anthropic · How we built our multi-agent research system(约 4 倍 / 15 倍 token)· anthropic.com/engineering/built-multi-agent-research-system
- Manus · Context Engineering for AI Agents: Lessons from Building Manus(KV-cache 命中率、100:1、10 倍价差、文件系统即上下文)· manus.im/en/blog/…
- Cognition · Don't Build Multi-Agents · cognition.ai/blog/dont-build-multi-agents
- Liu et al. · Lost in the Middle · arXiv:2307.03172;Chroma · Context Rot · research.trychroma.com/context-rot
- Wang et al. · Recursively Summarizing Enables Long-Term Dialogue Memory · arXiv:2308.15022;Packer et al. · MemGPT · arXiv:2310.08560
- LLMLingua · arXiv:2310.05736;LongLLMLingua · arXiv:2310.06839;LLMLingua-2 · arXiv:2403.12968
- StreamingLLM · arXiv:2309.17453;H2O · arXiv:2306.14048;SnapKV · arXiv:2404.14469;KIVI · arXiv:2402.02750;DeepSeek-V2(MLA)· arXiv:2405.04434
- Sage · github.com/chasey-myagi/sage(提交 ac6d7f5):
packages/plugins/src/compiler/(compiler、chunks、eliminate、budget、decide、epoch、freeze、stub、expand、calibrate、subtask-view)、packages/plugins/src/claims/progress-ledger.ts、packages/plugins/src/scorer/rule.ts、packages/tools/src/spill.ts、packages/tools/src/bash.ts、packages/core/src/log/kinds.ts、docs/design/context-compiler.md、docs/status/2026-09-24-sage-status.md、docs/notes/implemented/2026-09-23-summary-rules.md
局限:Claude Code 的摘要模板与旧阈值来自社区对发布包的提取,官方未公开全文;Anthropic 两种服务端压缩都还是 beta,参数可能再变;论文数字取自各论文摘要,未逐一复现;图 1、图 2 中的比例是示意,不是实测数据。