Skip to content

compress gate: 近阈值拒绝死循环 (4914/5000 chars 被拒 101 次) + 无终止语义 #101

Description

@ranxianglei

现象(来自 billion-context-omp #103 后续用户日志)

GLM-4.x 会话(128K, plugin 0.2.9)13:13:09→13:22:51 十分钟内,模型对同一个 compress 调用重试 101 次,每次返回同一错误:

Total compressible content too small (4914 chars across 1 range(s), min 5000). Combine more messages …
  • 4914/5000 = 98.3%,只差 86 字符;且该范围已覆盖会话全部剩余可压内容(前面全是活动块,后面是保护区)→ "Combine more messages" 在数学上不可满足。
  • 重试产生的 compress 调用本身按 ALWAYS_PROTECTED_TOOLS=["compress"] 保护、滞留不删(inMsgs 177→382),但 outMsgs/tokens 恒定(孤儿清理正常)→ 真实成本 = 101 次完整 LLM 往返 ≈ 1.8M input tokens。
  • 插件侧 loop-guard(streak=3 stop-directive / ≥4 suppressed)对 GLM 完全无效。
  • 用户感知:约 30% 上下文"永远压不掉"(搁浅的 4914 chars + 保护区)。

根因

src/compress.ts:230-259 门控只看 totalRangeChars < min:

  1. 不区分"模型能再凑"与"这就是全部可压内容";
  2. 拒绝文案是行动指令("Combine more"),但对结构性无解场景是死循环指令;
  3. 没有终止语义(告诉模型"这个会话已无可压,停")。

建议修复(同一个门控块内)

  1. 临界放行:totalRangeChars >= 0.9 × minCompressRange 且所请求范围已覆盖会话全部剩余可压内容 → 放行建块;
  2. 终止语义:会话总可压 < min → 返回 "nothing left to compress — stop calling compress";
  3. 文案可执行化:拒绝时列出可补充的具体 ref。

相关

  • 插件侧已验证: nudge 全程 idle(threshold 50000),不是 nudge 驱动;孤儿 compress 调用清理(hide-consumed)正常。
  • 错误文案循环证据:每次失败后 inMsgs+2(错误文本作为工具返回值进上下文,模型照建议重发)。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions