开源fast-jev-compaction:Jev评分替代摘要压缩的Claude Code插件


这个开源项目利用了typesafeai Jev模型,作为Claude中的一个插件,来审查所有不必要的工具调用,而且它只需1秒钟就能运行!

1秒钟就把Claude会话从近100万降到……86K个tokens!

Claude Code长对话压缩机制有个致命痛点:把工具调用和报错摘要成一段话,关键路径和命令直接丢失。fast-jev-compaction这个MIT开源插件换了一种玩法,让Jev模型给每个tool call打分,该留的原文一字不改,该删的直接扔。

fast-jev-compaction 是一个为 Claude Code 设计的上下文压缩插件,它用 Jev(一个轻量级 LLM)的逐条决策取代了传统的“摘要式”压缩,核心思路是只删除不再需要的工具调用和结果,所有保留的内容都保持原文逐字不改。

为什么需要它

传统的上下文压缩会让 LLM 对旧对话进行摘要,但摘要是有损的——一个文件路径、一个确切的错误信息或一条约束条件,可能在后续对话中仍然重要,却因为被摘要而丢失了。

fast-jev-compaction 的做法是:永远不重写任何内容,只根据 Jev 的判断删除不再需要的工具调用和工具结果,用户和助手的文本消息则原样、按顺序保留。


长对话越压越蠢,问题出在总结这一步

用过Claude Code写长项目的人,都撞过同一堵墙:聊到七八十轮之后,模型突然开始鬼打墙,你让它别改的文件它照改,报错行号张冠李戴,甚至把你上午定的接口签名忘得一干二净。

只要用Claude Code写过代码、调过接口,大概率都撞见过这个场景:一个任务聊到一半,助手突然像被人敲了脑门,把你十分钟前刚交代的“别动配置文件”忘得干干净净,转而把整段对话压缩成一份几百字的摘要,然后继续往下跑。

摘要这东西本身没什么错,问题是它有损。它丢掉的东西,往往不是冗余,而是精确信息。

你之前说的“测试环境跑npm test -- --grep=utils”,摘要可能缩成“运行了测试”;你贴出来的一段报错堆栈“Cannot read property 'map' of undefined at line 47”,摘要可能写成“遇到了一个类型错误”。文件路径丢了,命令参数丢了,报错行号也丢了。

每一个被摘要丢掉的细节,对写代码的人来说都可能是命根子。

更糟的是,摘要还会“错位”。有开发者反馈,Claude Code在上下文快满时自动触发压缩,过程中把“当前在哪个阶段、已经试过哪些方案、哪些方案失败了”这类工作状态抹掉了,只留下“整体目标”和“大致进展”。

这就是为什么你聊到后半段,助手会突然问你一个半小时前就回答过的问题。

罪魁祸首是一个叫context compaction(上下文压缩)的机制。

Claude Code的上下文窗口再大也有上限,一旦历史消息接近满,系统就得把旧对话"压缩"掉腾地方。

Claude Code 在上下文窗口用到大约八成五的时候会触发自动压缩,Claude Code 干的事情是把整段对话交给底层语言模型,让底层语言模型写一份摘要,然后用摘要替换掉原始记录。也就是说:请一个模型把老对话读一遍,写成一段自然语言的摘要,替换上下文替代原文。

问题就出在“替换”这两个字上,摘要是有损的,而且损失的东西你往往意识不到。

Claude Code 的官方文档自己承认了这一点:压缩会把较早的消息替换成摘要,所以对话早期那些具体指令可能不会被保留。你开任务时说的一句“注意:数据库密码在 .env.local 文件里,不要提交到 git”,到了摘要里可能只剩“注意了配置文件相关事项”。

文件路径丢了,环境变量名丢了,报错行号丢了,你明确划过的边界丢了,这些被丢掉的细节,恰好是写代码时最不能丢的东西。

有开发者做过一个统计:压缩摘要大约 3500 个 token,能保住主题和关键文件,但保不住细节,比如你之前争论过到底用 UTC 还是用本地时间这种决策。等 Claude Code 把这段摘要当成新的对话开头继续往下跑,Claude Code 会重新问你“时间格式你想用哪种”。


fast-jev-compaction 不写摘要,只问 Jev 该删哪条

fast-jev-compaction 做的事情跟摘要压缩完全不同,fast-jev-compaction 的核心逻辑只有一句话:不重写任何内容,只删除 Jev 判定不再需要的工具调用和工具结果。

它的核心动作不是"总结",是"评分"。

插件把对话里每一次tool call(工具调用,比如Read文件、Bash执行命令)和对应的tool result(工具返回结果)拎出来,一个不落,交给一个叫Jev的模型打分。分数高于阈值的原封不动留下,分数低的直接删除或者截断头部300个字符。

请注意这里的分寸:留下的部分一个字都不改!用户说的话、助手说的话、以及被判定"还有用"的工具调用,全部逐字保留。压缩不再是把信息揉成一团面糊,而是精确地剪掉几段视频,剩下的画面像素不动。

这就和默认总结拉开了本质距离,一个是把书撕了写读后感,一个是把书里没用的章节撕掉但保留章节完整原文。谁更靠谱不用多说!


具体怎么跑,分四步:

第一步,把每个工具调用和对应的工具结果按编号配成对,第一条消息和最近几条消息里的调用会被固定,永远不动。

第二步,把整段对话打包成一个状态发给 Jev,工具结果在打包状态里被换成一行简短说明,比如“ok, 4213 chars (omitted)”,但工具输入和对话文本全部原样包含,不做任何摘要。

第三步,对每一个没被固定的工具调用,Jev 回答两个问题:调用本身还要不要留,对应的结果还要不要原文保留。每个问题 Jev 返回一个从零到一的概率数字,fast-jev-compaction 拿概率数字和 0.5 的阈值比较。

第四步,概率够高就全留,概率中等就保留调用、把结果截断到前 300 个字符,概率不够就整个删掉。用户和助手说过的每一句话,一个字不删。

所以你压缩完打开记录会发现:你说的“别动配置文件”原封不动在那里,你贴的报错信息如果 Jev 觉得还用得上就原文保留,早期读过但已经用完的文件内容才被删掉。


每次tool call被问两个问题,一票否决

具体到打分环节,Jev对每一个非置顶的工具调用要回答两个yes/no问题:这次调用本身还需不需要被知道?这次调用的结果内容还需不需要一字不差地保留?

两个问题分开投票,产出三种命运:结果保留概率高于keepThreshold(默认0.5),call和result一起留;只有call本身有价值但结果无所谓,那就留call,把result截断到前300字符加一行说明;两个分数都低,call和result一起删。

这个设计的精妙之处在于它承认了一个基本事实:工具调用和结果的价值是分离的。你可能需要知道"曾经Read过某个文件"这个事实(否则助手会重复读浪费token),但那个文件的具体内容早就不重要了。传统summary做不到这种颗粒度的裁剪,要么整块留要么整块摘。

而且Jev看到的不是孤立的一次调用,是整段对话的全景,只不过所有tool result的正文被替换成了ok, 4213 chars (omitted)这种简短占位。它是在"知道全局"的前提下判断每一个局部值不值得留。


25000token的分级压缩:不够就再压一层

Jev能看到的state(状态快照)不能无限大,插件默认给它25000 token的预算。历史一长,光是喂给Jev看的"精简版全景"都可能超预算,怎么办?

fast-jev-compaction用了一套五级递进的挤压策略:

  1. 先把tool的输入参数截断到1000字符,不够再截到200,还不够砍到60;
  2. 然后长文本消息abridge成"头+尾",最老的先动手;
  3. 再不够就把老消息整块折叠成[… N chars omitted …]
  4. 最后连续几条只有工具调用的老消息合并成一行t12 Read file_path=src/a.ts → ok 480ch

每一级只在上一级还塞不下的时候才启动。这种"最小干预"的哲学贯穿全程,能不动就不动,能少动就少动。而且首消息和最新的preserveRecentMessages条(默认6条)是pinned(钉住)的,Jev连问都不问,直接保留。

如果五级压完还塞不下,插件不会硬撑,直接抛异常,让调用方决定fallback。在Claude Code场景下就退回默认的summary。作者tamaratran显然懂"知道自己不知道"的重要性,不装能干!


Token估算不用tokenizer,土法炼钢反而更快

这里藏着一个反直觉的工程决策:整个插件估算token数量,压根没用真正的tokenizer库,用的是一套"每6个字母算一个词、每2个数字算一个token、其他符号大约一个一个"的土公式。

按主流认知,token数量必须精确,否则超限就会被API打回来。用真tokenizer才是行业规范!

但是,作者的选择是刻意校准这套土公式,让估算结果略微高于Jev实际返回的token数。这样只会保守估计,不会激进超限,代价是偶尔浪费一点空间,收益是每次估算快到几乎零开销,且没有tokenizer的依赖包袱。

对一个hook(钩子)来说这个trade-off很划算。压缩本身是延迟敏感的操作,用户按下/compact或者触发auto-compaction时正在等着继续对话,多花500ms调tokenizer是能被感知到的卡顿。宁可空间上保守10%,也要时间上快到无感!


一次请求装不下,就并发多请求装同一份state

对话如果很长,非置顶的tool call可能几十上百个,每个call两个问题,全塞进一个请求也会超Jev的32k request上限。

插件的处理方式是把questions拆成多个batch,每个batch保证state + questions总量在maxRequestTokens(默认30000)以内。多个请求并发发出去,答案回来后merge到一起。

但是这里有个代价必须点明:同一份state被重复发送了N次。历史越接近25k token上限,能装的questions越少,需要的请求数越多,state重复传输的开销越大。作者在Limitations部分自己承认了这一点:a history near the state ceiling costs one request per handful of questions(贴近状态上限的历史,每几个问题就要一次请求)。

这是拿钱换质量的设计。传统summary一次请求搞定,但输出有损;Jev方案N次请求,输出无损。对于用Claude Code写生产代码、每一行报错都可能价值几小时排查时间的人,这个账很好算。


和ZCode之流的插件比,它的独门操作是"逐字保留"

Claude Code生态里做上下文优化的工具不止一家,比如ZCode这类项目也在琢磨怎么让长对话不失忆。但fast-jev-compaction有一个能被亲手验证的独门动作:apply decisions之后你把新旧消息列表diff一下,留下来的每条消息都是同一个对象引用,一个字符都没变。

源码里applyDecisions函数明确保证:untouched messages are returned as the same objects(未触碰的消息返回同一个对象)。这不是宣传话术,是可以写测试断言的技术承诺。

对比之下,任何走summary路线的方案,无论prompt多聪明、模型多先进,输出必然是新生成的字符串,原文的哪怕一个变量名拼写、一个错误码数字,都存在被模型"改写"或"归纳"掉的可能。

这个差异翻译成用户体验就是:Jev压缩过的对话,你回头翻历史找当初报错的那行traceback,能一字不差找到;summary压缩过的对话,你只能找到一句"当时出现了模块导入错误"。差一个数量级!


阈值0.5和preserveRecentMessages=6,暴露了作者的保守立场

配置项里有两个默认值特别值得琢磨:keepThreshold默认0.5,preserveRecentMessages默认6。

keepThreshold=0.5意味着只要Jev觉得"有一半可能还需要",就留下来。这个阈值相当宽松,宁可错留不可错删。如果作者激进一点定成0.7甚至0.8,压缩率会更高,但风险是删掉一些其实还有用的东西。

preserveRecentMessages=6意味着最新的6条消息(加上永远pinned的第一条)根本不进入评分环节。这是承认一个现实:最近发生的对话,模型自己判断不了它有没有用,因为"用没用"这个判断本身可能就在下一轮才发生。

这两个默认值加在一起,勾勒出作者tamaratran的产品价值观:宁可少压一点,也不要误删。这跟summary派"能压多少压多少,反正大意不丢"的思路正好相反!


装它需要开一个隐藏开关,说明这功能还很early access

安装环节有个细节值得单独说:function hooks(函数钩子)是Claude Code 2.1.274才引入的early-access(早期体验)功能,必须在~/.claude/settings.json里手动打开CLAUDE_CODE_ENABLE_FUNCTION_HOOKS环境变量为1才能用。

json
{ "env": { "CLAUDE_CODE_ENABLE_FUNCTION_HOOKS": "1", "TYPESAFE_API_KEY": "" } }

然后通过claude plugin marketplace add tamaratran/fast-jev-compaction把这个仓库注册成插件市场,再claude plugin install fast-jev-compaction@fast-jev-compaction装上,最后重启或者/reload-plugins

装完之后你按/compact触发压缩,Claude Code的toast提示会变成fast-jev-compaction: kept N/M messages, no summary (…)。如果Jev发现压缩率不够(reductionRatio低于0.25就算不划算),它会主动放弃,弹出fallback to built-in summary (…)退回默认逻辑。

这个"不够划算就退回"的自知之明,比很多硬要证明自己有用的工具高明太多!


Jev是谁?为什么它能一次请求做几百个判断

反复出现的Jev到底是什么?根据插件配置,它是一个由TypeSafe公司(api.typesafe.ai)提供的模型,默认模型名叫jev-latest,走的是https://api.typesafe.ai/v1/systemone这个System One端点。

从"System One"这个命名能嗅出味道:卡尼曼(Daniel Kahneman)在《思考,快与慢》里把人的认知分成System 1(快思考,直觉)和System 2(慢思考,理性推理)。TypeSafe把这个模型命名叫System One,暗示它就是设计来做快速直觉判断的,不做深度推理,只做yes/no投票。

这解释了为什么整个流程可以做到"快":Jev不需要理解你的代码逻辑,不需要生成任何自然语言,它只回答一堆结构化的keep/drop问题。这跟让GPT-4写一段总结的算力开销完全不在一个量级。

Jev本身有32k的request上限(略高于插件设定的30k安全线),说明它就是为这种"大批量小问题"场景优化的模型,而不是通用对话模型。fast-jev-compaction本质上是把Claude Code的记忆管理,从"通用大模型摘要"外包成了"专用小模型投票"!


作者tamaratran和Devin AI的合作,暴露了这个项目的诞生方式

翻commit记录会发现一个有意思的细节:几乎每个commit的Co-authored-by都挂着devin-ai-integration[bot]。Devin是Cognition Labs出的AI软件工程师,能自主执行编码任务。

也就是说,fast-jev-compaction这个613星的项目,很可能是作者tamaratran用Devin辅助搭出来的。这本身就是一个AI用来管AI记忆的元层套娃:她大概率是在自己用Claude Code和Devin写长项目时,被默认summary坑得受不了,才动手做了这个插件。

这个诞生背景让整个项目的价值主张更硬核了——设计者自己就是最激烈的痛苦承受者,她知道丢一个file_path意味着什么、忘掉一句"不要动generated目录"意味着什么。

MIT协议、公开源码、demo里那个SwiftUI做的macOS原生动画演示app,都指向一件事:这不是一个商业产品的宣发,是一个工程师被自己工具坑烦了之后的正面反击。


613个星背后:一个技术共识正在形成

2026年9月发布,短时间攒到613 star、25 fork、20个branch,这个数字在GitHub开发工具类项目里算相当可观。它反映的不是这个插件本身多完美,而是背后一个正在形成的共识:LLM应用的长期记忆管理,不该继续依赖粗暴的summary。

行业里同期出现的memory类项目,比如各种vector store(向量存储)方案、mem0这种memory layer框架,都在尝试同一个方向——把"记忆"从LLM自身的上下文里剥离出来,用更结构化、更可控的方式存取。

fast-jev-compaction走的是同一条路的另一个分支:不引入外部存储,只是在原生上下文的裁剪算法上做手术。它证明了即便不动架构,只要把"总结"换成"决策",效果差异就足够肉眼可见。

值得留意的是这个项目的Limitations部分特别诚实地写了一句:a probability is not a proof that a result is safe to delete. The assistant can always re-run the tool(一个概率不是可以安全删除结果的证明,助手随时可以重跑工具)。这句话把话说死了——Jev打的分只是启发式,不是保证。假如你的助手不擅长在需要时重跑工具,或者重跑成本很高(比如那次调用花了5分钟才返回),fast-jev-compaction给你带来的"记忆完整感"就可能在某个具体call上塌房,而你还不知道到底哪一个决策错了!

不删用户的话,但对话本身会撑爆窗口

fast-jev-compaction 有一个设计上的硬边界:只处理工具调用和工具结果,用户和助手的文本消息在输出中一个字不删、一个字不改。

只处理工具调用和工具结果的设计,是 fast-jev-compaction 的最大优点,也是 fast-jev-compaction 的最大限制。

优点很明确:你跟 Claude Code 说过的每一句约束、每一个决策、每一条要求,压缩后永远都在,你不会再看到助手把你说的“别动配置文件”忘得一干二净。

限制也很明确:如果对话文本本身就占满了上下文窗口,fast-jev-compaction 能帮你省下来的空间是有限的,fast-jev-compaction 只能删工具结果,删不了你说的话。

你越是把 fast-jev-compaction 当作“永不丢信息”的保险,就越倾向于在一个对话里塞越来越多的东西。对话越长,文本本身占的比例越大,fast-jev-compaction 能删的工具结果占比越小。

然后在某个时刻,对话长得连 Jev 的 32000 token 窗口都装不下了,fast-jev-compaction 会直接抛错,退回 Claude Code 自带的摘要压缩。

那一天你丢掉的东西,和从来没用过 fast-jev-compaction 时一模一样。

Jev 这个名字来自杰文斯悖论:效率提高之后,总消耗反而增加。fast-jev-compaction 让压缩变得更快、更不卡,所以你更不愿意主动重开对话、更倾向于让上下文无限膨胀。效率的提升催生了更大的消耗,然后消耗撞上了上限。

Every 测试里 1/7 的漏检率,对应着 fast-jev-compaction 里被误删的工具结果、被误判成“不再需要”的文件内容。fast-jev-compaction 跑得快,快到你忘了 fast-jev-compaction 也会犯错,而 1/7 什么时候落到你头上,你永远不会提前知道。

fast-jev-compaction 能不能替你把对话记住,取决于你愿不愿意接受 fast-jev-compaction 偶尔忘掉一件你还需要的事。

网友锐评

这就是个糟糕透顶的压缩方案,作者根本没搞懂压缩和上下文管理到底怎么工作。

很多人看迷糊了,我拆开讲。

第一,压缩不是过滤器。压缩的作用是清理历史,让 agent 保持专注,不是单纯删噪音。压缩应该少用,只在上下文太长的时候用,不是天天拿来把上下文压小。

第二,Jev 根本不知道自己正在决定什么。大模型做摘要时,靠的是整个对话线程的上下文来决定留什么删什么。fast-jev-compaction 是按“逐行”、也就是每个工具调用单独判断留还是删。Jev 只有 32k 上下文,对之前发生过什么知道得很少,而且在这个实现里,Jev 连工具调用的结果是什么都不知道。随机删这些东西,模型就不知道自己试过什么,最后一定会掉进“愚蠢循环”,反复试同一个东西。

第三,你把推理过程全扔了。OpenAI、Anthropic、XAI、Google 的前沿模型,不会通过 API 把推理轨迹明文给你,给的是加密载荷,Jev 看不到,而且经常会把加密载荷丢掉。Anthropic 更严格,要求你必须保留完整历史,才能拿到推理数据。结果就是,在 Claude Code 里用 fast-jev-compaction,模型一定会表现得笨很多。

第四,模型是在自己的压缩流程上训练过的。过去一年,前沿实验室把压缩和长任务运行放进了训练过程。这些模型学会的压缩方式,比任何粗糙方案都更有效。一个好玩的事实:你在 Codex 里切换模型,如果此时需要压缩,压缩会在原来那个线程用过的模型上跑。

第五,缓存写入比缓存读取贵得多。对 agent 来说,缓存写入是最大头的成本。作者个人用 Claude Code 和 Codex 时,缓存写入成本经常超过总 LLM 花费的 60%。历史里越靠前的数据被改动,缓存写入就越贵,因为顶部一变,旧缓存就失效了。每一次历史编辑,都会导致编辑点之后的所有数据需要重写缓存。假设历史是 1、2、3、4、5、6,你删掉 2,你就得重写 3、4、5、6。这比把 2 留着更贵。好消息是:既然这个愚蠢压缩策略已经把推理 token 全杀了,重写成本反而不会太高,因为模型已经缺了太多数据。

第六,实现是热垃圾。原文自己说:“任何不保留的内容永久删除,但助手可以随时重新运行工具或重新读取文件。”祝你好运。

说清楚:这是个很酷的实验,作者也觉得真有意思。但如果你认为这种基于概率阈值的垃圾过滤真的是压缩策略,作者强烈建议你直接用 Claude Code 和 Codex 这类工具的默认设置。这样你更不容易把自己搞伤。

这些批评对吗

大方向对,但语气偏激。

第二点最扎心。Jev 看不到工具结果原文,只知道结果被省略了,这个在 fast-jev-compaction 的设计里确实是事实。让 Jev 在不知道结果内容的情况下判断“结果还要不要”,这个判断质量很难保证。

第三点也成立。Claude Code 的推理数据是加密的,Jev 看不到。fast-jev-compaction 删掉历史,等于把推理轨迹一起丢了。模型后面表现变笨,这个风险真实存在。

第五点是工程硬账。删历史中间一段,后面的缓存全部失效,缓存写入成本会飙升。这个成本很多人不会算,但 agent 跑久了就是真金白银。

第一点和第四点有道理,但偏绝对。压缩确实不该当过滤器天天用,前沿模型也确实在自己的压缩流程上训练过。但 fast-jev-compaction 的定位本来就不是取代默认压缩,而是给“工具结果巨大、用户文本必须逐字保留”的场景多一个选项。

第六点情绪化。永久删除加重新运行工具,对有副作用的工具是灾难,对只读文件是小事。原文一棍子打死,过头了。

所以结论是:把 fast-jev-compaction 当默认压缩方案,不对。把它当实验,当特定场景的补充,可以。大多数人直接用 Claude Code 和 Codex 的默认设置,确实更稳。