首页 / 资讯中心 / 文章详情

300 轮长会话实测:哪些内容会被 fast-jev-compaction 的「二元删除决策」误伤?

300 轮长会话实测:哪些内容会被 fast-jev-compaction 的「二元删除决策」误伤? ★ FEATURED ARTICLE
300 轮长会话实测哪些内容会被 fast-jev-compaction 的「二元删除决策」误伤【免费下载链接】fast-jev-compactionClaude Code plugin that replaces the compaction summary with Jev decisions: every tool call and result is scored in one fast request, stale ones are dropped or truncated, everything kept stays verbatim.项目地址: https://gitcode.com/gh_mirrors/fa/fast-jev-compaction上下文压缩正在从「让 LLM 写一段摘要」切换到「让决策模型做一道道选择题」。fast-jev-compaction 是这个方向最具代表性的实现它不生成一个字只对每条工具调用和结果输出keep/drop概率删除 Jev 判定不再需要的内容保留的一切逐字原样。社区把它包装成「无损压缩」源码里也确实写死了「用户与助手文本永远不改写」。但「只删不写」不等于「无损失」。删除动作一旦发生被删内容就永久消失而做出删除判断的依据——喂给 Jev 的状态——本身是经过七级降级处理的。本文构造了一个 300 轮的长会话复用仓库测试同款的受控概率注入方法见 tests/fast-jev-compaction.test.ts 的fakeJev跑通compact → fitState → decideCall → applyDecisions完整真实链路src/compact.ts、src/state.ts逐一盘点「看似非关键、实则关键」的内容会在哪些环节被误伤以及钉住策略和回退机制到底能兜住多少。一、实验设计把 300 轮会话喂进真实的决策链路先明确这条链路的四个确定性环节它们是所有误伤的来源配对collectToolCalls按tool_use_id把每条tool_use与tool_result配对src/state.ts。没有结果的调用不参与决策——这本身就是一个安全边界。钉住isPinned判定index 0 || index total - preserveRecentMessages。首条消息永远保留最新preserveRecentMessages默认 6条消息永不触碰。提问对每条非钉住调用questionsFor生成两个noul问题——「这个调用本身连同其输入是否还需要留在历史里」与「这个调用的完整输出是否还需要逐字保留」src/compact.ts。执行decideCall按keepThreshold默认 0.5做三态判决keepResult ≥ 0.5→ 调用与结果都保留否则keepCall ≥ 0.5→ 保留调用、结果截断否则 → 调用与结果一并删除。applyDecisions据此重建消息列表。实验的 300 轮会话按如下规则构造第 1 轮钉住给出任务约束第 5280 轮散布读文件、Grep、跑测试、改代码、查日志等 240 次带结果的工具调用第 290300 轮钉住做最终验证。这意味着 1293 轮之间的所有内容全部处于候选区任何一个决策失误都可能让一段已发生的工程过程永久消失。需要说明实验方法的边界本实验验证的是决策机制的确定性行为——哪些内容在何种概率组合下必然被删、被截依据的是代码本身与边界条件推演而非依赖 Jev 在线 API 的随机输出。这与仓库测试的方法论一致结论可复现、可审计。二、误伤清单被删掉的「看似非关键」内容1. 长输出的「头 300 字符幸存者偏差」drop_result分支执行的是截断而非删除truncatedResultText保留结果的前truncateHeadChars默认 300个字符再追加一行说明src/compact.ts[fast-jev-compaction truncated N chars of this tool result; re-run the tool if needed]300 轮会话里最典型的受害者是两类输出测试日志尾部失败断言、堆栈帧往往出现在日志末尾与大文件读取相关配置节在文件后段。而一个容易被忽略的细节是truncatedResultText对长度 ≤headChars 120即 ≤ 420 字符的结果直接原样返回——所以「drop_result」实际只对超过约 420 字符的结果产生效果短结果即使被判 drop_result 也会逐字幸存。误伤集中在「长而尾部关键」的输出上而这类输出恰恰是长会话里 token 占比最高的部分。2. 调用与结果的「连坐」drop_call 的双杀decideCall的最后一个分支是drop_call调用连同其结果一起消失不留任何占位。这在语义上假设「重跑工具等价于拥有结果」但该假设由 src/state.ts 的STATE_CONTEXT显式写死并喂给了 JevWhatever is not kept is deleted permanently, but the assistant can always re-run a tool or re-read a file.对幂等工具Read、Grep成立对非幂等工具完全不成立WebFetch 抓取的外部页面会过期、Bash 里的写操作有副作用、Git 历史与网络状态不可重放。300 轮会话里这类调用一旦被 Jev 判为 stale旧调用的 keepCall 概率天然偏低删除就是不可逆的。3. 判断依据降级Jev 只看到「ok, 4213 chars (omitted)」这是最结构性的盲区。喂给 Jev 的状态里所有工具结果都被替换成一行注记src/state.ts 的resultNoteok, 4213 chars (omitted) # 或 error, 812 chars (omitted)于是「这个结果是否还需要逐字保留」这道题Jev 是在完全看不到结果内容的前提下作答的。它只能根据调用名、截断后的输入和结果长度做推断。结果越长Jev 对其中内容的了解反而越少——一个 5000 字符的构建日志和一个 5000 字符的配置 dump在状态里是同一句话。这解释了为什么「保留了大文件读取」往往是碰运气判断不是基于内容而是基于元数据。4. 输入截断到 60 字符后的「残缺证据」当历史放不进maxStateTokens默认 25000fitState按顺序降级工具输入从 1000 → 200 → 60 字符逐级截断INPUT_CHARS [1000, 200, 60]。300 轮会话几乎必然触发后两级。后果是一条 200 字符的Bash命令在 Jev 眼里只剩前 60 个字符一条Edit的old_string/new_string可能被截到看不出改了什么。「这个调用是否还需要」的答案是基于残缺输入做出的——判断依据本身先失真误伤只是时间问题。5. 报错堆栈与证据链只有被转述过才幸存早期失败的报错堆栈是后期修复的核心线索但在状态里它只是error, 812 chars (omitted)。Jev 无从判断这段堆栈与后续修改的因果关系。唯一能保住堆栈的路径是助手消息把堆栈逐字转述进了文本——因为文本消息在输出中永不删除README.md 明示only tool calls and results are candidates。于是出现一个微妙的规则信息是否幸存取决于它在哪一层——工具结果层的信息默认被抹除文本层的转述则永存。长会话里没被转述、只躺在工具结果里的关键证据就是第一类被误伤对象。6. 跨轮隐含依赖决策时刻的「现在视角」300 轮会话最致命的问题是跨轮依赖第 20 轮读的一份配置文件第 200 轮改代码时才用到。压缩发生在上下文涨到阈值的那一刻Jev 用「此刻还需要吗」的视角去判 200 轮前的读取——结论几乎必然是 stale。goal的默认值只取最近 3 条用户提示截断到 500 字符见 src/state.ts 的goalFromMessages早期约束如果没在近期提示里被重申就不会进入 goal 强化窗口。钉住的首条消息虽然全文留在状态里但它与第 5 轮「读了 src/generated」之间的关联没有任何机制被强化。7. 视野折叠与输出保留的错位最后一种误伤形态最隐蔽状态降级的最终阶段旧的无调用消息直接从 Jev 视野中移除旧文本折叠成[… N chars omitted …]旧调用压成一行t12 Read file_pathsrc/a.ts → ok 480ch。这意味着中间轮的推理过程、阶段性结论、计划切换Jev 一概看不到——而压缩后的输出却又把助手文本逐字保留。于是出现「判断是损失化的保留是原样的」的错位压缩结果看着完整但删除决策是在一个被折叠过的世界里做出的。误伤的不是幸存者而是幸存者之间的叙事连续性——助手文本还在但它所依赖的、被删掉的证据已不在。三、钉住策略能救什么、救不了什么能救的首条消息与最近 6 条消息在任何概率下都不会被触碰pinned决策的 reason 是pinned直接短路三态判决。短结果≤ 420 字符在 drop_result 下实际原样保留——小输出天然免疫截断。全程可审计钩子会输出逐条决策日志hooks/fast-jev.ts 的decisionLog格式如t3:Bash:drop_call/call0.10/result0.10每次压缩的概率、动作、原因都可回放。救不了的悬挂引用钉住的消息可以引用已被删除的早期证据。最后 6 条里用户说「把第 20 轮那个配置的问题一起修了」而第 20 轮的读取早已消失。阈值硬切0.49 与 0.50 之间是生与死的差别。概率本身是连续量决策是二元的keepThreshold附近没有过渡带。逃生舱悖论钩子设定minReductionRatio 0.25hooks/fast-jev.ts。当 Jev 删得不够多短会话、保守阈值或请求失败时钩子return next(event)退回 Claude Code 内置摘要——也就是把「无损保真」的承诺在删不够的情况下换回一个有损摘要。钉住策略兜住了「误删」却把「删不够」的场景直接移交给了它要替代的东西。四、规避误伤的工程手段与回退机制参数层对冲全部为CompactOptions定义于 src/types.tskeepThreshold调高如 0.60.7让 keepCall/keepResult 更挑剔牺牲压缩率换取保守truncateHeadChars调大缓解「头 300 字符幸存者偏差」preserveRecentMessages调大把钉住窗口从 6 条扩展到 1020 条goal显式传入完整任务描述替代「最近 3 条提示」的默认窗口能显著缓解第 6 类跨轮依赖误伤。回退机制钩子对 Jev 失败、畸形响应、缺少 API Key、历史无法装进状态预算fitState抛history too large四种情况统一 catch 并next(event)回退内置摘要同时 toast 展示回退原因——失败不会让会话崩溃代价是退回有损路径。turn.complete钩子在context.percent达到compactAtPercent默认 60%时触发自动压缩并有 in-flight 防重入。替换决策源README 明示可以自实现JevAsker一个ask(state, questions)方法替换远程 JevbuildJevRequest/parseJevResponse也单独导出——对结果内容敏感的场景完全可以换成本地规则或更强的决策模型。这是把「判断依据降级」问题从源头解决的正路。承认的局限README.md 的 Limitations 写得很诚实——token 是字符级估算而非 tokenizer 计数概率不是「删除安全」的证明每次请求都重发完整状态接近状态上限的历史「一个请求只装得下几道题」300 轮会话在 25k 状态预算下往往要拆成多个并发请求batchCallssrc/compact.ts每次都是同一份降级状态。结语回到标题的问题二元删除决策误伤的从来不是「看似非关键」的内容本身而是决策依据中被省略掉的那部分关键性。fast-jev-compaction 的取舍很清晰——把风险从「改写失真」转移到「判断依据降级」用确定性换掉幻觉。它守住了文本的 verbatim却把误删的责任交给了「ok, N chars (omitted)」这行注记背后的元数据判断。对长会话使用者这给出了一份可操作的检查清单长输出的尾部证据要靠truncateHeadChars保护非幂等工具调用要慎用低keepThreshold跨轮依赖要靠显式goal与更大的钉住窗口对冲而一旦出现「删不够」或「删错了」minReductionRatio与内置摘要的回退路径是最后一道不完美但必要的安全网。理解这些边界比相信「无损」二字更重要——任何压缩都应当先回答「谁在为删除负责」。【免费下载链接】fast-jev-compactionClaude Code plugin that replaces the compaction summary with Jev decisions: every tool call and result is scored in one fast request, stale ones are dropped or truncated, everything kept stays verbatim.项目地址: https://gitcode.com/gh_mirrors/fa/fast-jev-compaction创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?
咨询建站