关于上下文压缩和magic context

heylong 2026-08-09 12:01 1

书接上文 Pi个人扩展设置与讨论,发表了部分关于上下文管理的拙见,评论区也有佬友吐槽pi的compact逻辑,这篇帖子就来详细讨论一下上下文压缩和magic context的一些细节实现。


先整理一些主流的,我自己用过的一些harness的压缩策略。这些网上也有很多分析,我简单汇总一下。如果有哪里说的不对或者遗漏也欢迎佬友指出!


Claude Code


Claude Code算是我接触的第二个coding agent框架,第一个是谷歌的反重力。主要是因为那时候antigravity刚发布,可以免费使用各种模型,额度也还不错(但由于是刚接触这类应用,并没有特别深入去了解,很多概念也不是很清晰,所以我们就从Claude Code开始讲起)。


Claude Code的上下文压缩策略还是挺清晰的,并不是一股脑地交给模型自己总结。从之前泄露的源码来看,Claude code的压缩是逐层递进的,大致可以分为4层。


Snip


第一层是snip,一个轻量的剪裁。每次发送请求(不只是user message,可以理解为每次api调用)系统自动调用,模型参与决策,不参与执行,在下一次请求前处理,主要清理一些不需要的旧工具输出,比如bash,read等(magic context的ctx_reduce和这个有点类似),生成一份Snip boundary记录,并修复父节点连接,下次上下文会过滤类似的boundary信息。


MicroCompact


第二层是microcompacct,这一层主要考虑的是kv cache。两种触发方式,模型同样不参与执行:一种是上轮对话ttl已过(比如60分钟),在重新计算prefix时主动节省token,模型会保留最近N个工具结果(并不是N轮),将更早的替换成占位符[Old tool result content cleared];第二种是工具输出达到阈值,本地消息不变,服务端发送cache_edits请求,按已注册的可压缩工具选择删除对象,用局部的cache miss换取长期处理成本下降。


Collapse


第三层是collapse,在上下文窗口达到90%时触发(这里特地设置在AutoCompact触发阈值之前,collapse作为更精细的操作有更高的优先级,只是在实践中一般先执行手动/compact,不会达到90%的阈值),开始commit压缩操作,将被动压缩转换成主动重构,在95%左右进入blocking,阻止创建新的子任务。Collapse被启用时,会有stage和commit两个阶段。在上下文达到阈值前,每隔一段token interval会调用ctx-agent子代理生成一段包括起止uuid,risk评分的摘要进入staged queue;达到90%阈值才进入commit,替换原文本。Collapse的过程是可逆的,依然可以通过transcript和commit log重构原文。


Autocompact


最后的压缩兜底,触发阈值一般是上下文上限-13k tokens。分为session memory compact和full compact:session memory由后台子代理周期性维护与更新,默认上下文达到10k初始化,从此每增长5k并满足至少3次工具调用或自然对话断点(最后一轮assistant没有工具调用)启动一次,进行格式化更新;达到阈值之后,先尝试执行一次session memory compact,保留会话摘要和最近的10k-40k原始对话,这次执行不调用llm;若压缩后仍超阈值,则进行full compact:由模型按规则进行总结,保留最近最多5个文件的读取记录,预算每个文件小于等于5k,总预算小于50k(恢复操作排除CLAUDE.md这类记忆文件,这些由hooks或其他单独处理)。


Claude这套设计机制挺完善的,在实际使用过程中还是最好在达到上下文阈值之前手动执行/compact,并说明要保留的重点。我感觉偶尔还是会丢失一些细节吧。


Codex


之后A/逐渐不当人,并且gpt的模型和claude的差距越来越小,我也转到了codex。这里主要参考codex cli的源码,没有分级策略,默认触发阈值90%。


Local compact


Codex的压缩分两种:一种是local compact,当remote compact不可用时启用。让模型根据prompt生成一份handoff summary,不走专门的compact endpoint,保留最多20k token的user message,最后追加一条新的“user” message,也就是模型总结的summary,相当于做一次handoff交接。我没怎么体验过local compact,但单纯看原理和站里其他佬的反馈,效果很难评。


Remote compact


当provider时openai时,则启用remote compact,分v1和v2,这里主要对v2进行介绍。服务端收到压缩请求后,会返回给客户端一个opaque compaction item(不是明文,相当于一个黑盒);客户端重建transcript中保留真实的user message,一些hook prompt(比如stop hook的“retry with tests”),保留不超过10k的非final answer的agentmessage(父、子agent之间的通信内容),丢弃assistant message,tool output,reasoning等,这些在64k预算内,保留最近消息,最后加入compaction item。


我个人体验下来感觉codex的压缩感受是好于Claude Code的(而且在使用过程中,由于他那个上下文用量显示不是很显眼,我经常忘了手动compact),但核心科技还是被CloseAI藏起来了。


Magic Context


用了codex之后我也尝试过opencode和pi,这里就不对他们原生的compact进行展开了,本质就是用prompt让模型对历史进行摘要总结,再加上最近的消息,效果不说特别差,也是 ^-^;好在是他们的compact机制可以通过插件来修改。这里我们来具体介绍一个我在前文推荐过的插件magic context,链接: https://github.com/cortexkit/magic-context


在开始之前,先简单说一下,magic context在每轮模型调用前,会对上下文先进行一次transform,大部分行动都是在这一阶段完成的,包括tag,drop,expand等等,完成transform后的内容才会输出给模型。


ctx-reduce


首先来看tag分配。tag是后续drop操作的主要参考,分配对象包括message,tool和file,每轮调用消息会被打上一个带序号的tag,模型会依据prompt的指导(除了静态的system prompt,magic context还会动态在符合条件的tool output后附加reminder提醒可drop,触发条件是:tokens大于10k;上下文达到阈值(默认65%)的80%;未drop的tool output占上下文的20%以上),在会话中主动分析调用ctx_reduce,此时不会立刻删除,而是会创建一个pending queue。在cache bust的时机或达到阈值才会实际执行drop操作,默认最新20个tag为protected tags,会被deferred drop。除了模型主动调用外,pending操作还会由historian(后文会讲到),规则判断(较旧的 reasoning,已被新调用取代的tool等)这些执行。处于pending queue中的tool如果是最近调用的(最近20个),会保留骨架,tool result被drop,更早的则全量舍弃;message和file则替换成占位符。

对于之前有佬提出:



在一次对话中大量的工具调用,但pi原生压缩是在agent end执行的,导致上下文被撑爆



针对这个问题,首先我们前文提到过,transform阶段是在模型调用前发生的,一轮对话中可能会有多次模型调用,也会有多轮transform,所以drop的时机不一定是在agent end,mid turn也可以执行。但这个策略也有一个小问题,如果两次调用间有大量输出怎么办?Magic context设置了一个emergency drop的阈值85%:达到这个阈值后,可以在模型调用前drop以及强制hard fold(后文会提到)等一系列压缩的操作。


注:tag元数据会保存在context.db中,字段包括tag id,session_id,message_id,type,status,byte size等,还有source_contents表保存原文(不包含详细的tool output,主要用于transform恢复原文,防止tag前缀影响)


ctx_expand


如果重要的细节被不小心删除或折叠了怎么办?Magic context的压缩操作均是可逆的(除非原始transcript被破坏)。若遇到信息需要被展开的情况(比如historian的compartment不够详细,或者被显示为占位符[drop N]的信息),模型会调用ctx_expand(根据historian元数据中的range,或drop的tag id)准确读取transcript进行上下文恢复。ctx_expand返回结果仅针对单次调用,并不会撤销dropped状态


ctx_search


Magic context默认在每次transform的阶段会对最新user message进行一次auto search,触发条件是消息至少20个字符,若最高分数达到0.6,则给予一个低成本hint(提示最多3个片段,总长度小于200tokens,防止占用过多上下文),提示模型可以调用ctx-search搜索相关片段。这类似于rag,包括关键词倒排索引以及向量检索(memory以及compartment等向量在生成时已经存在sqlite数据库中,不用重复计算,所需下载的向量模型就是被用在这;message只进行关键词索引),若不进行向量计算,依旧可以使用关键词检索。搜索到相关内容由ctx_expand进行扩展。


Historian


触发阈值80%,或普通drop无法降到目标压力,Historian会根据:


上一个compartment结束位置
当前context压力
token budget
tool调用和用户对话边界

指定不同于主session的特定模型执行,每次处理特定的一部分生成结构化摘要,并根据压力、重要性等由p1-p4逐步降级精简(包含start,end等,便于ctx_expand恢复,内容类似原生compact的模型总结,也有protect tail保护最新消息),生成的结构化compartment会进入m[1](首次生成会进行一次hard fold进入m[0],后面生成均会进入m[1])。/ctx-warpup提供手动触发historian,类似/compact命令吧。


注:m[0]是长期稳定的基线,m[1]是基线后的新变化,目的是为了保持kv cache稳定;hard fold会将m[1]合并进m[0],主要是cache bust的场景触发,具体可以看其他佬友的帖子【magic context介绍帖】为什么说magic context是值得研究的上下文工程和记忆实践


其他


其余的一些比如ctx_memory,ctx_note,ctx_aug,Dreamer这些功能,主要是长期记忆的一些工具,这里就不进行展开了。简单说一下功能,ctx_memory,一些可能长期复用的知识,由模型自主决定或者用户指定;ctx_note,类似于小便签,可以记录后续提醒、待办,或已完成的todo之类的;ctx_aug,配合一个sidekick专门检索记忆对prompt进行增强:Dreamer,在特定时段对记忆进行整理、分类、去重。


结尾


我认为magic context有一个缺点就是,虽然他把大部分数据都保存到了context.db,但人类要去读还是比较麻烦的,比如要去看具体drop了哪些,总结了什么。主要也是为了速度考虑。我对magic context的定位是单纯上下文管理工具,对于一些长期记忆的功能,前面也说了,人类要去整理还是比较麻烦的,不像什么MEMORY.md直接打开修改就行;并且对于ctx_note这类工具,主要是针对一个长期的大项目,但大项目的话,一般都会有详细的spec以及各类文档跟进,或者trellis这类专门特化的工具,我认为ctx_note的用处就不大了;ctx_aug也什么必要,还会产生额外的依赖和开销。


就写到这了,还有一些我没怎么用过的,比如grok build,如果有好的上下文管理思路,或者佬友们有什么自己的想法和见解,都欢迎补充!

最新回复 (4)
  • prosumer 08-09 12:35
    1

    codex压缩感受确实很好, compaction item其实是最后加入的一段内容, 前面压缩还是客户端压缩的话这个效果就很神奇了

  • 獸 禍威 08-13 01:20
    2

    magic-context的作者还有一个aft,不知道搭配起来怎么样,不过看样子 aft和 hashline-edit-pro以及pi-fff不兼容

  • Decidable6471 08-13 01:23
    3

    但是 你没有说出一个大问题

    那就是 codex 的远程压缩会强迫性的回复用户最后一句话

    如果你最后一句话是个莫名其妙的东西

    不是 /btw 跑 goal 的时候就会抽搐了哦

  • GloryDuck 08-13 01:31
    4

    oai不知道怎么做的 感觉多数情况无感压缩。


    我自己做的提示词压缩上下文就很简陋,效果不如人意,语义压缩经常少了关键东西,丢了也无法再恢复。


    感觉oai可能会和magic compact 有点类似吧,需要的时候按需展开。

* 帖子来源Linux.do
返回