来源:#351 线程拆分(https://github.com/ranxianglei/billion-context/issues/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
ranxianglei commentedon Sep 3, 2026
[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 失败现状均无兜底:src/loop/core.ts:515-521—— 启动横幅硬编码 raw-HTTP-capture(on) 而真开关是 ACP_DUMP_BODY(默认关):日志永远撒谎,取证被带偏过 #413 零副作用重试失败后仅记upstream stream truncated+ emitError 返回,会话状态不变;src/server.ts:2408-2411直接 throw,无任何记录;后果与 #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」一类。
所以门不能是单一绝对百分比,建议双信号任一满足:
4. 方案对比与建议
lastInputTokensfloor 到有效窗口 → 下一轮 kernel 紧急 nudge + preflight 强制压缩。复用现有 400 自愈的武装路径(server.ts:2507-2519);preflight 判据currentTokens = max(lastInputTokens, payloadEstimate) ≥ limit即触发(preflight.ts:236-262)learnedContextLimits(标 weak 来源)server.ts:2489),误诊会一直持续直到人工干预建议:A 为主、B 为辅,且两者触发严格度不同:
min(现值, 失败 payload 估算 × 0.9)写入learnedContextLimits(按模型 scope,与硬学习一致),日志明确标注weak-signal (non-400 inference),后续 400 硬信号直接覆盖它。其余设计点:
core.ts截断重试失败后 +server.tsfetch 失败处;客户端主动断开(IDE cancel)不计入(用clientAbort.signal.aborted区分);单次网络抖动已被 启动横幅硬编码 raw-HTTP-capture(on) 而真开关是 ACP_DUMP_BODY(默认关):日志永远撒谎,取证被带偏过 #413 重试吸收、不进计数。max(session.stats.lastInputTokens, estimateCoreMessages(payload))(与 preflight 同一估算口径,preflight.ts:90)。lastInputTokensfloor 生效,无需双份实现。v1 范围覆盖 proxy 模式流式路径 + fetch 前失败;plugin pipe 与非流式 JSON 复用同一判定函数,随后补上。@ranxianglei 请拍板:(1) 「A 即时触发 + B 三次/30分钟」是否同意?(2) 参数(绝对底线 50%、尺寸连续性 90%、B 系数 0.9)是否可接受?确认后我进入实现(typecheck/test/build + e2e preflight 验证)。
Merge pull request #504 from ranxianglei/2026-09-03_fix-498-weak-over…
fix: learn from weak overflow signals, persist prefix-affinity, warn …
fix: client abort miscounted as weak overflow; opencode-local system …