Skip to content

高 usage 下的非 400 失败(流截断/超时)无任何 overflow 兜底 —— 会话直接死 #498

Closed

Description

@ranxianglei

来源:#351 线程拆分(#351 ,三个加固项之一)。

现象

#351 的致死组合:窗口被高估(解析 967232 vs 上游真实 ~477K)+ 上游以非 400 方式失败(03:55:26 upstream stream truncated)→ self-heal 学不到真实窗口、也没有强制压缩兜底 → 会话死亡。

现状

窗口自愈只认 HTTP 400(明确的 overflow 信号);流截断 / 超时 / 连接重置一律不算。

建议方向(需谨慎设计)

高 usage(如 ≥90% 解析窗口)下的流截断/超时作为弱 overflow 信号:

  • 触发一次性窗口自学习探测(下调一档试转发)或强制压缩兜底。
  • 截断原因多样(网络抖动、上游 bug、真 overflow),不能一律当 overflow:建议仅「高 usage + 短时间窗内重复 ≥N 次」才降窗/强制压缩,且日志明确标注这是弱信号推断,避免把网络问题误诊为 overflow 把好窗口改小。

Activity

  1. ranxianglei commented on Sep 3, 2026

    @ranxianglei
    OwnerAuthor

    🤖 ework agent · qwen3.8-27b

    [bot] 🏷 收到。已按流程先做重复判定 + 审核验证,再给方案对比与明确建议;实现等 @ranxianglei 拍板后进行。

    1. 重复判定

    检索了全部 issue/PR(含已关闭):无重复。本条即 #351 线程拆分的第 2 个加固项(#351 floor 5 记录发现、floor 14 列出拆分计划),兄弟项为 acp-kernel#194(growth floor)与 #499(重启后 cache 0%),与任何现有 PR 无重叠。

    2. 审核:现象成立(逐行核对过代码)

    自愈确实只认 400/413:inspectContextOverflow(src/util.ts:152)对非 400/413 直接返回非 overflow;唯一调用点在 forward() 的 !upstream.ok 分支(src/server.ts:2460-2521)。三类非 400 失败现状均无兜底:

    1. 流截断(200 但无 completion event):src/loop/core.ts:515-521 —— 启动横幅硬编码 raw-HTTP-capture(on) 而真开关是 ACP_DUMP_BODY(默认关):日志永远撒谎,取证被带偏过 #413 零副作用重试失败后仅记 upstream stream truncated + emitError 返回,会话状态不变;
    2. 超时/连接重置(未到响应头):src/server.ts:2408-2411 直接 throw,无任何记录;
    3. 非流式 JSON 路径的 arrayBuffer 失败同理。

    后果与 #351 一致:下一轮客户端重发同尺寸上下文 → 再失败 → 会话死。另注:「零内容到达客户端」的情形 #413 已有一次透明重试吸收,本项设计不与其重叠。

    3. 关键发现:issue 里「≥90% 解析窗口」的门会漏掉动机事件本身

    #351 致死会话截断时 usage 仅为解析窗口的 49%(476383/967232)——因为解析窗口约为真实值 2 倍。一般化:若解析窗口是真实的 f 倍,真实 overflow 时刻观测到的 usage ≈ 1/f(f=2 → 50%,f=5 → 20%)。固定 90% 门只能覆盖窗口高估 <~1.1× 的情形,恰好漏掉最致命的「注册表 1M vs 实际 200–500K」一类。

    所以门不能是单一绝对百分比,建议双信号任一满足:

    • 绝对底线:本次估算输入 ≥ 解析有效窗口的 50%(小上下文很少 overflow,控制误报率);
    • 尺寸连续性:本次估算输入 ≥ 上次失败估算输入的 90%(确定性 overflow 会在相近尺寸反复失败,网络抖动不会)。

    4. 方案对比与建议

    A. 强制压缩兜底 B. 窗口自学习(降一档探测)
    机制 把 lastInputTokens floor 到有效窗口 → 下一轮 kernel 紧急 nudge + preflight 强制压缩。复用现有 400 自愈的武装路径(server.ts:2507-2519);preflight 判据 currentTokens = max(lastInputTokens, payloadEstimate) ≥ limit 即触发(preflight.ts:236-262) 写试探值进 learnedContextLimits(标 weak 来源)
    误诊成本(实为网络问题) 低:只是提前压一次——压缩本就是产品常规功能且可逆;下一轮真实 usage 上报即自动清除武装 高:把好窗口改小,而现有自愈「只缩不放」(server.ts:2489),误诊会一直持续直到人工干预
    治本 否(窗口仍虚高,之后每次撞真实上限都会再压一次,形成事实上的动态天花板——可用但浪费) 是

    建议:A 为主、B 为辅,且两者触发严格度不同:

    • A 在首个符合条件的事件即触发。理由:A 不改窗口,误诊代价只是一次多余压缩;反过来,0.1.63 应用在DeepSeek Harness超出 固定阈值没进行压缩 #351 的客户端在第一次截断后就死了——若要求「重复 ≥N 次」才动作,这类会话永远没机会被救(客户端不重试,N 就攒不起来)。
    • B 要求 30 分钟内 ≥3 次符合条件的失败,取 min(现值, 失败 payload 估算 × 0.9) 写入 learnedContextLimits(按模型 scope,与硬学习一致),日志明确标注 weak-signal (non-400 inference),后续 400 硬信号直接覆盖它。

    其余设计点:

    @ranxianglei 请拍板:(1) 「A 即时触发 + B 三次/30分钟」是否同意?(2) 参数(绝对底线 50%、尺寸连续性 90%、B 系数 0.9)是否可接受?确认后我进入实现(typecheck/test/build + e2e preflight 验证)。

  2. added a commit that references this issue on Sep 4, 2026
    0a64aa3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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