聊聊 opencode 上下文压缩: 如何做到单会话 1 亿 token 不爆不丢,acp 模型主动压缩才是唯一正道

5 月 20 日
 ranxianglei

潜水 10 年:)

可以先把话撂在这里,acp 这个插件出现其他上下文压缩方式就可以谢幕了,甚至尝试继续扩大模型上下文的行为也变得无意义。

先说说上下文压缩插件 acp 是啥,这是一个 opencode 中的插件。为什么需要上下文压缩?用过 AI coding 的都知道,模型上下文都是有限的,哪怕你有 100w 上下文也无济于事,面对旷日持久的大项目也捉襟见肘(尤其是可怜的 glm5.1 啊啊啊)。

所以 gpt 和你聊天实际上是假聊天,你和他每次说的消息,包括他的输出,都作为上下文,每次都全部发给 gpt 处理,久而久之你们就攒满几十万甚至 100w 上下文,就要删去一些。

作为程序员,显而易见想到的方式就是把上下文提炼总结,替换掉原会话冗余的上下文。现在大部分上下文压缩都是这么做的,只不过有一些做的很粗糙(大部分),有的很精细(比如 claude 多级级联),有一些还甚至想出做向量存储相关性搜索等等。

不过最终面临的问题还是压缩效果不好的问题。

想法很简单,实现很简单:模型自己决定要不要压缩,怎么压缩

说起来一肚子气,本来这个插件是要提给原作者dcp的,我修了几十个 bug ,增加了类似 jvm 的垃圾回收算法,结果他看都不看全给我关了!!!

废话不多说先看看效果:最近上下文几乎都没有超过 50%!

模型 GLM-5.1 ( 204K context window ),ACP 阈值 55%。

指标 修复前 (5/14 - 5/19) 修复后 (5/19 - 5/20)
采样数 2,749 362
峰值 context 74.2% 45.3%
平均 context 35.9% 25.3%
context <40% 的占比 64.5% 87.0%
context >55% 的次数 130 次 (4.7%) 0 次 (0%)

主:修复前之前还有,再之前会话超过几百条就上下文丢干净了。

这是最近几天这个会话的 token

指标 数值
总 Input Tokens 35,232,595(约 3520 万)
总 Output Tokens 616,525(约 62 万)
总 Tokens (输入+输出) 约 35,849,120(约 3585 万)
消息总数 3,762 条
会话时长 约 6 天( 5/14 - 5/20 )
模型 GLM-5.1 ( 204,800 token 上下文窗口)
ACP 压缩阈值 55%( 112,640 tokens )

原理:把上下文压缩当做一个 skill

是的,就这么简单。你不需要像 claude 一样搞多级压缩(都是人工定死的,哪有模型灵活?)也不需要搞外部 api 专门压缩(外部 api 才不了解你需要哪些信息)。也不需要专门训练模型,只需要交给模型一个 skill ,他自己决定什么时候调用即可。

重点说明一些基本特性:

  1. 每条消息模型都可以压缩,或者解压缩(这很重要)
  2. 模型可以把连续 N 条消息压缩成 1 条消息。
  3. 模型可以删除消息。
  4. 模型可以修改消息。

系统做什么?

仅此而已。

最后说说我改了啥?和原生 dcp 有啥区别

原始 DCP 有个致命问题:上下文状态全靠 msgId 列表追踪,但这些 ID 不持久化,一重启就丢,35 个压缩块全部作废,559K 字符的摘要变成废纸,3175 条原始消息全部涌回上下文,GLM-5.1 直接返回 model_context_window_exceeded

我从头重写了核心架构,主要改了这些:

1. 独立 Block 架构 — 不再有巨型摘要

DCP 原始设计有个脑残的地方:每次压缩会把旧 block 的摘要内联展开到新 block 中。打个比方,就像你每次整理抽屉,都把之前所有整理记录的完整版塞进新记录里。经过 23 次压缩后,最新 block 的摘要膨胀到 90K 字符 — 整个会话历史的递归摘要。这个巨型摘要无法再压缩(它本身就是摘要),占据了上下文的大部分空间,会话卡在 70% context 无法继续。

ACP 改成独立 Block 架构:每个 block 独立存在,摘要只覆盖自己的范围。多个 active block 在上下文中同时存在。不再自动嵌套,(bN) 引用保留为文本标签。模型需要显式用 bN 作为 boundary 才会消费旧 block 。

2. 压缩块状态机(类似 JVM 分代 GC )

每个压缩块有 young → old 代的概念。新压缩的块是 young generation ,经历多次压缩周期后晋升为 old generation 。Old generation 的摘要会被 GC 自动截断(保留头部 + 尾部引用标记),防止老摘要无限膨胀吃掉上下文。

JVM GC ACP 触发条件
Young Gen 新创建的压缩块 每次压缩产生
Minor GC 合并最近的 young 块 55% 阈值触发
Old Gen 存活超过 N 次压缩周期的块 survivedCount ≥ 5 自动晋升
Major GC 截断 old-gen 块摘要 超过阈值自动执行
Age-based deactivation 超龄块自动停用 age > 15

实测效果:126 个 active blocks ( 63K tokens 死重)→ GC 自动清理到 10 个。

3. 34 个 Bug 修复

fork 以来修了 34 个 bug( 4 CRITICAL ,15 HIGH )。每个都是真实踩到的坑,不是坐着想出来的:

最离谱的几个:

完整的 bug 列表太多了就不贴了,感兴趣的看 GitHub 。

增加了 300 个测试

凸(艹皿艹 ),原来 dcp bug 那么多,总计 15 个测试只有 5 个能跑,让 glm5.1 帮我写了 300 个基准测试,

竞品对比

ACP DCP 原版 Morph opencode 内置
压缩方式 模型自己决定 模型自己决定 外部专用压缩 API 被动全量摘要
额外 LLM 调用 idle 分析可选(费 token ) 需要 API Key 1 次摘要调用
触发时机 45% 建议,55% 必须 可配置 70% 95%(基本已经来不及了)

PS:实际上 glm5.1 本身就是压缩大师了:)。

安装

opencode plugin opencode-acp@latest --global

或者

{
  "plugin": ["opencode-acp@latest"]
}

配置文件 acp.jsonc

{
  "maxContextLimit": "55%",  // 触发压缩的阈值
  "gc": {
    "enabled": true,
    "interval": 300  // 每 5 分钟 GC 检查一次
  }
}

链接


吐槽归吐槽,DCP 原作者的设计思路我是认可的(好吧,这句话是模型给我加的,原作者思路是对的,只不过并没有发挥到登峰造极) — 让模型自己决定压缩,而不是搞一堆规则和外部 API 。只是在工程实现上踩了太多坑,我花了几周把这些坑都填上了。

如果你也在用 opencode 做大项目,试试看。 几周 session 不用重开。

遇到 bug 就提 github 吧,我自己高频用,应该还会发现很多 bug 。你发现了在 issue 说,直接提 pr 即可。

4746 次点击
所在节点    推广
26 条回复
Tmss2
5 月 20 日
我看了一下 dcp 作者回你的消息:pr 太庞大,想让你拆分一下。人还是看了的呀,还提了一些 pr 的意见。
win8en
5 月 20 日
@Tmss2 是的,我正想说呢,人家确实看了
wsbqdyhm
5 月 20 日
貌似很高级
tlerbao
5 月 20 日
貌似很高级,不太懂

请教下:opencode 本身就带自己的压缩吧,我感觉还挺好用呢,
还有现在 gpt5.5 这种是不是都模型侧压缩了,账号好像有 compact 属性呢

不用国产垃圾模型,你这插件在洋人模型下有效果吗?
songray
5 月 20 日
这种依赖模型自主性的方式,一定会碰到各个模型的差异...
类似工作还是适合供应商干而不是插件干。
ranxianglei
5 月 20 日
回复 1 楼,我后面又拆分了 20 个 issue ,作者看都不看给我关闭了 https://github.com/Opencode-DCP/opencode-dynamic-context-pruning/issues/537
ranxianglei
5 月 20 日
@tlerbao 1.opencode 本身就带自己的压缩吧 是的自带压缩,但是一旦触发压缩,基本上关键上下文丢一大半,没办法持续工作。
2.还有现在 gpt5.5 这种是不是都模型侧压缩了,账号好像有 compact 属性呢。任何外部压缩效果都不如当前模型好。
3.不用国产垃圾模型,你这插件在洋人模型下有效果吗?你觉得 glm5.1 垃圾你的事情,我是 codex 换 glm5.1 的。
ranxianglei
5 月 20 日
@songray 这种依赖模型自主性的方式,一定会碰到各个模型的差异... 建议配合 gpt5.4 以上,glm5.1 以上使用,qwen3.6 我试过,大概还可以。
Zeaxion
5 月 20 日
这插件 skill ,cc 能用不?
ranxianglei
5 月 20 日
@Zeaxion 暂时不能,我在考虑如何迁移过去。迁移好了通知你
abc0123xyz
5 月 20 日
等我有空试试看
zbinlin
5 月 20 日
我比较好奇这些压缩过的上下文对于 DeepSeek v4 这种高命中缓存的模型来说是不是降低的命中率了?
imnpc
5 月 20 日
因为担心压缩以后丢失东西太多
我目前的做法依然是每次一个小功能开发 尽量避免丢失上下文
如果大模块用 omo 规划以后开发,尽量避免压缩的出现,而且 plan 规划好的随时会读取修正
juzisang
5 月 20 日
我发现 opencode 的缓存命中率很低,kimi gpt 都只有 10~20%左右。放到 codex 里缓存命中率能达到 90%+,不知道咋回事...
ranxianglei
5 月 20 日
@zbinlin
看情况,第一种情况压缩大多数合并最近的消息,这时候基本 95%以上命中率,大多数是这个情况,因为模型不会等 45%才压缩,而且每个会话都考虑压缩,我问过为啥,他说他觉得早压缩更好。第二种情况,到达 45%以后的压缩,这时候会比较大,但是一般会压缩到 16%左右,这 16%有概率全新,有概率旧的。达到 45 %的情况不多,只有 10%。第三种,如果没用这个插件的时候,到 80%多外部模型压缩,然后到 16 %,这个时候和第二种情况差不多,但是如果你上下文比较多,会频繁 full 压缩,实际上更加费 token
ranxianglei
5 月 20 日
@imnpc
配合 omo 非常合适,当然我的 omo 也裁剪过,开始会话上下文只有 9%,我记得原来是 15%左右。你不用避免压缩出现,acp 会一边工作一边压缩,类似于 c 语言手动垃圾回收,实际上没有费太多 token ,因为 95%以上是命中缓存的?我修了 dcp 的 bug ,原版会卡一个点导致命中率很低。
ranxianglei
5 月 20 日
@juzisang
肯定是 bug ,可以给我配置我给你看下。不过大概率是插件的 bug ,正常命中是 95 以上
izzzz
5 月 20 日
能复用你的逻辑魔改一下进我自己的 agent 做会话压缩吗?
JasperHale
5 月 20 日
甚感兴趣, 等待 codex 版本
ranxianglei
5 月 20 日
@izzzz
软件开源的,随便改

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1213953

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX