Skip to content

fix(compaction): 自动压缩触发线单位对齐、pin 上限与紧急恢复迟滞 - #36

Closed
h3c-hexin wants to merge 2 commits into
pinvou3-cleanfrom
fix/compaction-auto-trigger-units
Closed

fix(compaction): 自动压缩触发线单位对齐、pin 上限与紧急恢复迟滞#36
h3c-hexin wants to merge 2 commits into
pinvou3-cleanfrom
fix/compaction-auto-trigger-units

Conversation

@h3c-hexin

Copy link
Copy Markdown

背景

长会话(~351 条消息、保守估算 ~247K token、预算 ~245K)观察到压缩退化为锯齿:每步都触发 Emergency context compaction started (preflight token budget),每次只前删 3 条最旧消息(351 → 348, ~247,159 → ~244,269 tokens, trimmed 3 oldest)就宣布恢复,下一步立刻再次越线。同时全程从未出现 Auto context compaction started。另有伴随症状:no-progress 停止消息恒报 elapsed 0s

根因

三层叠加缺陷 + 一个显示 bug:

  1. 触发线单位错配(自动压缩通道不可达)should_compact 用原始口径(chars/4)的估算与 token_threshold 比较,而阈值派生自保守口径预算(percent × input_budget_ceiling,即 ×3/2 缩放体系,与预检 estimate_input_tokens_conservative 同单位)。默认 75%/80% 触发线折算后高于预检预算线(需 <2/3 才可能先于预检),自动压缩数学上永远不可达,全部溢出都走 100% 线的紧急通道。TUI 页脚 will_auto_compact 却按保守口径对比,向用户预告了实际不会发生的自动压缩。
  2. pin 溢出(LLM 摘要整体空转)should_pin_message(working-set 路径提及 + error:/failed/panic/diff 标记)+ 外部 working_set.pinned_message_indices 在 debug 会话里几乎 pin 住全部消息 → summarize_indices 为空 → compact_messages 原样返回(完成文案无 retries、无 API pass failed 与此吻合),唯一剩下的手段就是无脑前删。
  3. 紧急恢复无迟滞trim_oldest_messages_to_budget 删到刚好低于预算即停,下一步新增输出(assistant + tool call + tool result,数千 token)立刻再次越线。每轮还静默吃掉最旧历史并使供应商 KV prefix cache 全量失效(观察到 42 次执行累计输入 4.4M token)。
  4. elapsed 0s:Warn 与 Stop 之间护栏恰好返回一次 Nonerepeats_after_warn_to_stop 先计 1 再计 2),该步把 no_progress_warning_started_at 重置为 None,Stop 时 get_or_insert_with(Instant::now) 现取时间 → 恒报 0s。

修复

  • should_compact:pinned/summarizable 双侧估算统一 ×3/2 保守缩放,与阈值、预检、页脚同一单位;token_threshold 字段补充单位契约文档。派生值、route_budget、UI 均不动(它们本来就是保守口径,改消费端影响面最小且不改变既有用户可配置行为)。
  • plan_compaction:新增 cap_pinned_token_share —— 大会话(原始估算 ≥16K token)pin 份额上限 1/2,超限从最旧开始释放;keep-recent 尾部与保留的 user query 永不逐出;逐出 call 连带其 pinned result(保留孤儿 result 会被 OpenAI 系后端拒绝)。小会话低于门槛完全不受影响,保留原启发式行为。
  • 紧急恢复 trim 目标降为预算 9/10(emergency_trim_budget),一次恢复买到数步余量。
  • no-progress 时钟:Warn 覆盖写入(新回合不沿用旧时钟),去掉 None 步重置,Stop 汇报真实 Warn→Stop 时长。

测试

新增 7 个回归测试:

  • should_compact_threshold_compares_in_conservative_units:阈值落在两口径之间必须触发(旧实现永不触发)。
  • should_compact_fires_below_the_preflight_budget_for_route:按 Kimi 路由真实预算/阈值构造「高于 80% 触发线、仍低于预算上限」的会话,断言自动压缩先于预检触发(原 bug 的端到端复现)。
  • plan_compaction_caps_pinned_token_share_for_large_sessions / plan_compaction_keeps_small_sessions_pin_heuristics / pinned_token_cap_evicts_call_and_result_pairs_together(含配对连带逐出与防双重记账)。
  • emergency_trim_budget_leaves_a_hysteresis_margin

验证与披露

  • cargo test -p codewhale-tui --lib -- compaction:: core::engine:: core::route_budget:582 通过。
  • cargo test --workspace --all-features --locked:除 5 个与本次无关的既有失败(config 命令 4 个 + skill 卸载 1 个)外全绿;这 5 个在干净基线上以相同方式失败(本地环境相关),已逐一对照验证。
  • clippy:本次修改的 4 个文件零告警;本地 clippy 1.95 对基线既有代码报 34-35 个新 lint(如 question_markcollapsible_match),stash 对照确认非本次引入,未在 PR 中顺手修改。
  • cargo fmt --all -- --check 干净。

影响面

  • 行为变化:自动压缩现在会在到达预算线之前真实触发(此前从不触发);debug 型大会话中超过 1/2 份额的旧 pin 会被摘要而非永久保留(continuation block 仍覆盖全量转录);紧急恢复单次删得更深但不再每步抖动。
  • 兼容性:CompactionConfig::default() 的 800K 兜底阈值语义变为保守口径(直接构造配置的路径触发提前 ~1/3),route 派生路径与用户可配置项(auto_compact_threshold_percent)不受影响。

pinvou3-dev added 2 commits September 1, 2026 09:58
长会话诊断出三层叠加缺陷,压缩退化为「每步触发、每次只前删几条最旧
消息」的锯齿(351→348 条、~247K→~244K token 反复越线):

1. 单位错配:should_compact 用原始口径估算(chars/4)与 token_threshold
   比较,而阈值派生自保守口径预算(percent × input_budget_ceiling,×3/2
   缩放)。默认 75%/80% 触发线折算后高于预检预算线(需 <2/3 才可达),
   自动压缩在紧急通道之前数学上不可达,全部溢出都走 preflight 紧急
   恢复;TUI 页脚的 will_auto_compact 却按保守口径对比,向用户预告了
   实际不会发生的自动压缩。修复:pinned/summarizable 双侧估算统一
   ×3/2 保守缩放,与阈值、预检、页脚同一单位。

2. pin 溢出:working-set 路径提及 + error/patch 标记 + 外部 pin 在
   debug 会话里几乎 pin 住全部消息,summarize_indices 为空使
   compact_messages 原样返回,LLM 摘要整体空转,只剩前删一条路。
   修复:大会话(原始估算 ≥16K token)pin 份额上限 1/2,超限从最旧
   开始释放;尾部与保留的 user query 永不逐出,逐出 call 连带其
   result,避免保留孤儿 result。

3. 无迟滞:trim_oldest_messages_to_budget 删到刚好低于预算即停,下一
   步新增输出立刻再次越线,每步一次紧急压缩。修复:紧急 trim 目标
   降到预算的 9/10(emergency_trim_budget)。

Signed-off-by: pinvou3-dev <dev@pinvou3.local>
Warn 与 Stop 之间护栏恰好返回一次 None(repeats_after_warn_to_stop
先计 1 再计 2),该步对 no_progress_warning_started_at 的重置把告警
时钟清掉,Stop 时 get_or_insert_with 重新取当前时间,elapsed 恒报
0s。改为 Warn 覆盖写入时钟、去掉 None 步重置,Stop 汇报真实的
Warn→Stop 时长;Warn 覆盖同时保证新告警回合不沿用旧回合的时钟。

Signed-off-by: pinvou3-dev <dev@pinvou3.local>
@h3c-hexin

Copy link
Copy Markdown
Author

错误目标:此修复属上游原生缺陷(相对 Hmbown/CodeWhale v0.9.5 逐字节验证),按 fork 政策应从最新 upstream main 提 Hmbown/CodeWhale,由后续上游 release 同步回流 fork。将关闭并在上游重提。

@h3c-hexin h3c-hexin closed this Sep 1, 2026
@h3c-hexin
h3c-hexin deleted the fix/compaction-auto-trigger-units branch September 1, 2026 03:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant