Replies: 2 comments
Verified reference implementation for the reported checkpoint re-compaction loop (not an upstream merge). Source baseline: b150a55 Implementation
Causal regression coverage
Verification
Scope
|
|
从「压缩手段反复失败→反复重压」这层看,有一个绕法:dsh-dcp 用确定性代码抽取替换「让模型总结」这一环(压缩零 LLM 调用),同样的输入永远得到同样的检查点,不存在「摘要无用→再来一轮」的失败模式,一次成活。触发/保留/事务锁继承官方,检查点格式兼容: dsh plugin add @aiwayds/dsh-dcp边界说明:你观察到的「不可压缩 checkpoint 被反复压缩」如果根因在 checkpoint 语义层(比如保留尾巴本身构成了环),那是另一层问题,dcp 解决的是「摘要这一环反复失败」;建议两个层面分开验证。 |
Uh oh!
There was an error while loading. Please reload this page.
BUG 报告:compaction 反复压缩不可压缩 checkpoint 导致死循环
概要
selectCompactableRange不排除 checkpoint 节点 + pressure 路径无 summary 失败退避问题现象
长时间运行的 agent session 在后期出现以下症状:
summary is not smaller than the shadowed content (1981 estimated framed tokens >= 1981)错误compaction/start事件后中断,无对应compaction/end(未闭合的压缩锁)实际数据(来自真实 session 日志)
Session 概况
session-af3b7d70-f2e9-471a-86fc-a8dbe8ace5c1E:\code\xxxxxvcode-gateway / Ali-DeepSeek-V4-Flash-1mcompaction/start(seq 240311),session 在压缩过程中中断Compaction 事件统计
compaction/startcompaction/endcompaction/summary(成功落地)summary is not smaller)Request was aborted)失败率 89.6%(221 次尝试仅 19 次成功)。
失败原因分布
summary is not smaller than the shadowed contentRequest was aborted180/198 次失败的 framed token 与 shadowed token 完全相等——摘要正文长度与原文几乎一致,framing 开销恰好抵消了仅有的缩短。
失败的 token 规模
198 次失败的 shadowedTokenCount 全部集中在 1981-2158 的极窄区间:
被压缩区只有约 2000 token(按 4 字符/token 约 8000 字符),是一段小而密的结构化内容。
触发频率:turn 105 后飙升
turn 105 之后,单个 turn 内反复触发压缩几十次。
无退避的连续失败
turn 119 尾部的事件序列(seq 几乎连续):
start 和 end 的 seq 几乎连续,中间没有任何 step/tool 事件——同一个 region 被反复选中、反复尝试、反复失败,无任何退避。
关联错误:pi-ai detected context overflow
由于 compaction 失败导致 surface 无法缩减,input token 持续超过 contextWindow,触发 6 次 "pi-ai detected context overflow" 错误(turn 113-118)。
每次 overflow 后触发 context-overflow 恢复压缩,6 次全部失败(同样是
summary is not smaller),形成 overflow → 恢复压缩失败 → surface 不变 → 下一个 turn 又 overflow 的死循环。根因分析
根因一:
selectCompactableRange不排除 checkpoint 节点packages/compaction/compaction-basic/src/region.ts的selectCompactableRange函数(第 98-134 行)从surfaceNodes[0]开始选可压缩区:它不检查
surfaceNodes[0]是否是 compaction checkpoint。checkpoint 是compactCheckpointSource来源的 user/message,内容是CHECKPOINT_PREAMBLE + <compacted-summary>...</compacted-summary>,是一段高度结构化的摘要,几乎不可再压缩。isCompactCheckpointSource(packages/compaction/compaction/src/checkpoint.ts:49)已经存在,可以检测 checkpoint:但
selectCompactableRange没有使用它。死循环机制
从成功压缩的数据发现真相——每次压缩的都是前一次压缩产生的 checkpoint 消息本身:
shadowedSeqs是单个 seq,就是上一次 summary 的落地 user/message。token 值递减是因为偶尔压缩成功(摘要短了几个 token),产生一个新的稍小的 checkpoint,下一个 step 又尝试压缩这个新 checkpoint。完整循环:
根因二:pressure 路径无 summary 失败退避
packages/compaction/compaction-basic/src/index.ts的agent/pre-step监听器(第 147-165 行)每个 step 都跑一次 pressure 检查:失败只
warn不阻断,不记录哪个 region 失败过,不跳过失败的 region。下一个 step 又触发agent/pre-step,又跑 pressure 检查,totalTokens仍超阈值(surface 没变),selectCompactableRange选出同一个 checkpoint,又失败。对比
context-overflow路径有overflowRetries计数器(WeakMap<Agent, number>),pressure 路径没有任何类似的退避机制。warnedPressureConfigTargets只对TargetPressureConfigError(配置错误)去重,对summary is not smaller这种普通 Error 不生效。根因三:被压缩区过小
~2000 token 的 region 信息密集(结构化摘要),模型无法在保留必需事实的同时明显缩短。
COMPACTION_INSTRUCTION要求保留 "exact file paths, commands, error strings, identifiers, numeric values, function signatures",对短而密的 checkpoint 而言,合规摘要几乎等于原文。frameSummary()的 framing 开销:CHECKPOINT_PREAMBLE:约 300 字符 ≈ 75 token<compacted-summary>开闭合标签:约 40 字符 ≈ 10 tokenBLOCK_OVERHEAD:4 + 4 = 8 tokenROLE_OVERHEAD:4 token摘要正文只要比原文短不到约 97 token 就会触发断言失败。对 1981 token 的 checkpoint,这是一个极窄的窗口。
根因四(配置层面):contextWindow 配置缺失
settings.yaml里vcode-gateway的模型只声明了id和name,没有contextWindow字段,导致走llm-pi-ai的DEFAULT_CONTEXT_WINDOW = 262_144(256K)默认值。模型名Ali-DeepSeek-V4-Flash-1m里的 "1m" 只是显示文本,DSH 不从名字解析容量。这导致
thresholdTokens = floor(262144 × 0.8) = 209,715,session 后期 surface 容易超过这个阈值,频繁触发 pressure 压缩。影响面
建议修复方案
修复一:
selectCompactableRange跳过头部 checkpoint 节点(根因修复)在
selectCompactableRange确定保留尾部边界后、返回范围前,从头部向前跳过所有连续的 checkpoint 节点。使用已有的isCompactCheckpointSource检测 checkpoint。修复二:pressure 路径加 summary 失败退避(缓解)
在
compactIfNeeded的 pressure 路径for循环里,compactRegion抛出summary is not smaller错误时 break 而非继续重试。修复三:给被压缩区设最小 token 门槛(缓解)
selectCompactableRange加minShadowedTokens检查,选出的 region token 总量小于阈值(如 4000)时返回 null。修复四:配置层面(用户侧)
在
settings.yaml里给每个模型补上contextWindow字段,避免走 256K 默认值。1M 模型设为 1000000,其余按实际容量填写。相关代码位置
packages/compaction/compaction-basic/src/region.tsselectCompactableRange:不排除 checkpointpackages/compaction/compaction-basic/src/region.tssummary is not smaller断言packages/compaction/compaction-basic/src/region.tssummarizeCompaction:framing 后比价packages/compaction/compaction-basic/src/index.tsagent/pre-step监听器:每 step 触发,无退避packages/compaction/compaction-basic/src/index.tspackages/compaction/compaction/src/checkpoint.tsisCompactCheckpointSource:已存在但未被 region 使用packages/compaction/compaction-basic/src/summarizer.tsCHECKPOINT_PREAMBLE:约 300 字符固定开销packages/compaction/compaction-basic/src/summarizer.tsframeSummary:preamble + 标签 + summarypackages/llm/token-meter/src/estimate.tspackages/llm/llm-pi-ai/src/config.tsDEFAULT_CONTEXT_WINDOW = 262_144packages/llm/llm-pi-ai/src/catalog.ts复现方法
thresholdTokens(contextWindow × 0.8)selectCompactableRange反复选中 checkpoint,summary is not smaller错误循环出现All reactions