Skip to content

feat(#1997): throughput-adaptive nudge cadence (compress.nudgeAdaptive) - #2035

Open
ranxianglei wants to merge 3 commits into
masterfrom
wt-1997-ework-daemon
Open

ranxianglei wants to merge 3 commits into
masterfrom
wt-1997-ework-daemon

Conversation

@ranxianglei

@ranxianglei ranxianglei commented Oct 4, 2026 •

Copy link
Copy Markdown
Owner

Closes #1997

What

Opt-in throughput-adaptive nudge cadence. When enabled, the fixed compress.nudgeGrowthTokens step is replaced by an adaptive step derived from the session's recent per-call input throughput: large arrivals (bulk file/log reads) widen the step so folding does not interrupt the work; small arrivals (quiet interactive turns) narrow it so context stays lean. Clamped to [nudgeGrowthMin, nudgeGrowthMax] and seeded from nudgeGrowthTokens until three samples exist. The signal references growth magnitude and call count (a bounded window of recent requests) — not wall-clock time or cache occupancy (see the paper §6.3 note on the cache-occupancy denominator artifact).

Config surface (new fields — owner-approved pre-implementation)

Three optional fields under compress (three-level cascade global→provider→model, per-field deepest-wins, exactly like every other compress field):

  • nudgeAdaptive?: boolean — master switch. Default unset/false → byte-for-byte identical to today.
  • nudgeGrowthMin?: number — default 10000. Lower clamp.
  • nudgeGrowthMax?: number — default 200000. Upper clamp.

Why the existing surface cannot express this: nudgeGrowthTokens is a single STATIC step (it flattens the kernel's adaptive band to a constant); no field makes the step track observed throughput. Alternatives considered and rejected: re-introducing window-percentage scaling (forbidden by #379/#380), and cache-/occupancy-driven gating (owner rejected — a low-water fold's apparent miss is a denominator effect while the absolute re-computed tokens stay in the tens of thousands; the trade strongly favors compression). Internal tuning constants (window=8, min-samples=3, proportionality=1) are deliberately NOT exposed as config, to avoid surface bloat.

Behavior change (flagged)

  • Default OFF → zero behavior change. With nudgeAdaptive unset, applyAdaptiveNudgeStep returns the resolved config untouched — no allocation, no band change.
  • When ON → the per-request nudge.growthFloor/growthCap becomes a moving value S instead of the static step. This is the intended feature.

Both compression modes reasoned (§6):

Scope

bili-only — no acp-kernel change (verified kernel 0.0.100 resolveAdaptiveGrowth = min(growthCap, max(growthFloor, round(limit×growthRatio))), so pinning floor == cap == S drives the effective step exactly for any window). Files: new src/nudge-adaptive.ts (pure/deterministic step), src/config.ts (type + parser + misplaced-key hints), src/compress-settings.ts (merge picks + applyAdaptiveNudgeStep), src/server.ts (sample rawInputTokens into a bounded per-session ring persisted via session.metadata, then overlay the band at session bind), docs en/zh + paper §6.3, tests tests/nudge-adaptive.test.ts.

Verification

typecheck ✓ · build ✓ · deterministic unit+integration suite (65) ✓ · focused regression over changed-surface suites (store / e2e-compress-cascade / issue453-backstop / fork-ccr-adoption / cache-friendly-plugin / absorb-plugin / issue402 / issue532, 74) ✓. Rebased onto current master (clean, no conflicts). Full regression delegated to CI.

@github-actions

github-actions Bot commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

📦 Built Package Artifact

Branch: wt-1997-ework-daemon (3676e0f)

Option A — Install this exact build (recommended)

npm install -g billion-context@0.0.2400-pr.2035.db0d75b

This exact version is immutable and reproducible. The rolling pr tag tracks the newest test build across all PRs.

Option B — Download artifact

  1. Download the artifact from the Actions run
  2. Extract the tarball and install:
tar xzf billion-context-pr2035.tgz
npm install -g package

Install instructions are refreshed on each push.

ranxianglei pushed a commit that referenced this pull request Oct 4, 2026
New README.md FAQ continuation lines and the paper §6.3 paragraphs carried a
leading space inconsistent with surrounding style. Cosmetic only; renders
unchanged. Labeled incidental fix per the small-fixes-in-passing policy.
ranxianglei pushed a commit that referenced this pull request Oct 4, 2026
…ew fix)

KDD#7 said the proxy 'suppresses tool + nudge injection' in plugin mode. Since
#451 the proxy-side nudge IS injected in BOTH modes (ephemeral trailing user
message; the agent has no nudge channel of its own) — see src/server.ts
'Inject in BOTH modes (#451)' and TECHNICAL-NOTES.md 'Two compression modes'
table row 'Proxy-injected nudge | yes | yes'. The stale wording is the source
of the incorrect plugin-mode claim in the #2035 PR description. Factual doc fix
only; no behavior change.
@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 Review 完成 — 可合并(需人工合并:feat + 新配置面,按 §7.4 不满足 auto-merge,留 human)。

查重:无重复——本 PR 即 #1997 的实现(设计已经 owner floor 12「同意开工」预批),字段名/默认值/三级级联与批准稿完全一致。

结构化七问:

  1. 是什么问题:Supports dynamic compress.nudgeGrowthTokens | 支持动态compress.nudgeGrowthTokens #1997 需求——nudge 节奏现为静态 nudgeGrowthTokens 步长,批量读文件/日志时折叠打断工作、安静交互时上下文偏肥;需要可 opt-in 的吞吐自适应步长。
  2. 什么原因导致的:静态步长无法表达「步长跟随到达节奏」——nudgeGrowthTokens 把内核自适应 band 扁平化为常数,现有配置面无任何字段能让步长跟踪观测吞吐(window-percentage 缩放被 fix: merge all system/developer messages into one leading message (SGLang #377) #379/探索是否可以集成进sglang,以做到透明管理 #380 禁止;缓存占用闸门被 owner 否决为分母假象)。
  3. 怎么修复的:纯函数 computeAdaptiveStep 取近 8 次调用 Δtokens(每次调用到达率,clamp≥0、非有限值先滤除)的均值作步长 S,夹到 [nudgeGrowthMin,nudgeGrowthMax],样本 <3 时回退 nudgeGrowthTokens ?? 50000 种子;然后写 nudge.growthFloor = growthCap = S。已对安装的 acp-kernel 0.0.101 dist 验证 resolveAdaptiveGrowth = min(cap, max(floor, round(limit×ratio))) ⇒ floor==cap==S 时任一窗口有效步长恰为 S(PR 正文写 0.0.100,实际 master pin 是 0.0.101,公式一致且本 PR 不动 pin)。bili-only,零内核改动。
  4. 是否有回归问题:无。逐项审过的既有行为:① 默认 OFF → applyAdaptiveNudgeStep 返回同一对象引用,config/wire 逐字节不变(有同引用恒等测试钉住);② 前缀缓存([Bug] ACP: ≥2 compressed blocks render as multiple system messages → SGLang (vLLM-compatible) upstreams return 400 #377):仅 band 变,head/system 字节不动;③ plugin 模式:nudge 双模式都注入(0.1.69 回归: plugin 模式 nudge 注入被 !pluginMode 静默禁用 — INJECT 日志照打但从不投递, 自动压缩只剩 preflight 应急 #451),自适应节奏同样生效(carrier 仍是 ephemeral trailing user message,不进 agent 持久历史 → 无漂移);④ preflight 硬兜底(plugin mode: no hard backstop when the agent ignores the compress nudge — context grows past the window and upstream 400s #453/track: plugin-mode proxy-side emergency truncation (last-resort backstop, deferred from #467) #470)依赖 modelContextLimit,与 band 无关;⑤ emergency nudge 基于 usage,与 band 无关;⑥ 内核 state.nudge 引用值来自持久化 state 而非 band 推导,band 按请求变化安全;⑦ 新 metadata 键 nudgeInputHistory(≤8 个数)即使 OFF 也会写入——与 rawInputTokens([Feature] Default state file cleanup strategy #1082)既有的无条件写 metadata 先例一致,有界、加性键,判定可接受,在此明示;⑧ 配置解析对非法类型严格拒绝,与兄弟字段纪律一致。本地定向验证:typecheck ✓、新套件 20 ✓、9 个变更面回归套件共 97 测试 ✓(store / e2e-compress-cascade / issue453-backstop / fork-ccr-adoption / cache-friendly-plugin / absorb-plugin / issue402 / issue532 / issue833-panel-effective-config)。CI 原 head 16/16 全绿;我补的 2 个 docs commit 后正在重跑(docs-only)。
  5. 是否破坏缓存:不涉及——前缀缓存锚点(head system 字节)未动;CCR/content-store、更新节流、去重键均未触碰。
  6. 是否改了设计方向:无转向——复用既有三级级联 + session.metadata 持久化通道;信号选择(每次调用到达率而非墙钟/缓存占用)正是 owner 裁定。
  7. 是否引入了新的配置:是,3 个 opt-in 字段,无需迁移:compress.nudgeAdaptive(bool,unset/false = 现状)、nudgeGrowthMin(默认 10000)、nudgeGrowthMax(默认 200000);内部常量(window=8 / min-samples=3 / proportionality=1)刻意不暴露,防表面膨胀。CONFIGURATION.md 中英条目照旧补齐。

行为变更 flag:默认 OFF → 零行为变化;ON → 每请求 growthFloor/growthCap 变为移动值 S(即功能本身,已在正文与文档披露)。

review 中发现的小问题,已直接修(不止报告):

判定:方向正确(预批设计原样落地)、无回归、测试有牙(边界/clamp/同引用/级联/解析拒绝均有断言)、diff 干净——可合并,请人工合并:#2035

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 CI 补报(windows-22 单测 flake,已重跑转绿)

上一轮回复末尾说"正在重跑",结果如下:

现象:两个 review 小修 commit 推上去后(doc-only),主 CI workflow 仅 test (windows-latest, 22) 一个 job 红,其余(version-guard、ubuntu-22/24、windows-24、全部 e2e/overlay/artifact lane)全绿。失败的是单个测试:

not ok 3158 - Responses WS: a title-persona side request rides the lane transport instead of 502ing
tests/responses-websocket.test.ts — AssertionError: input did not match /side request \(agent=title\)/
suite: 3954 tests / 3939 pass / 1 fail / 14 skipped

判定为环境性 flake,非本 PR 回归,证据链:

  1. diff 不可能影响:相对上一个全绿 head(6b6a9b1),新增的只有两个 markdown commit(文档行首空格 + AGENTS.md KDD#7 措辞),JS 行为零变化。
  2. 同版本对照:同一 Node 22 测试集在 ubuntu-22 通过,windows-24 也通过 → Windows 特有的时序问题,不是代码路径问题。
  3. 断言机制:该测试在代理启动后 ~93ms 就断言日志里出现 side request (agent=title);抓到的日志只有启动行 + 约 30 条 [instances] another bili instance is running(无单实例守卫:多个 bili 代理进程并存共写 sessions 目录,伴随孤儿 ref、有注册表无 transcript、计数自相矛盾的污染簇 #394 多实例警告,来自共享 runner 上并行跑的其它测试进程)——典型的共享 Windows runner 高负载下日志竞态。
  4. 本地定向复跑:node --import tsx --test tests/responses-websocket.test.ts Linux 上 36/36 通过(2.7s)。

处置:已触发 failed-job 重跑,整个 run 现已 completed success(head abc1586)。无需为这个失败改任何代码;该 flake 无既有 issue 记录,按机制粒度规则不单独立项(单次瞬态、无复现证据链支持系统性缺陷),若后续在 CI 里再次出现同测试失败,再按"重复出现"立项跟进。

结论不变:可合并(需人工 merge——feat + 新配置面,§7.4 人工通道)。

@ranxianglei

Copy link
Copy Markdown
Owner Author

Related: the default-cadence side of this landed as #2278 (window-scaled default step, 20k..50k band — fixes the #2110 RC2 starvation for sub-1M windows). #2035 remains complementary: it adds the opt-in throughput-adaptive cadence (nudgeAdaptive / nudgeGrowthMin / nudgeGrowthMax) on top of the scaled default, with nudgeGrowthTokens as the seed. No overlap in behavior — happy to rebase if desired.

ework-agent added 3 commits October 7, 2026 00:33
Adds an opt-in adaptive nudge growth step driven by the session's recent
per-call input throughput: large arrivals (bulk file/log reads) widen the step
so folding does not interrupt the work; small arrivals (quiet interactive
turns) narrow it so context stays lean. Clamped to [nudgeGrowthMin,
nudgeGrowthMax] and seeded from nudgeGrowthTokens until three samples exist.
The signal references growth magnitude AND call count (a bounded window of
recent requests), not wall-clock time or cache occupancy.

New opt-in fields under compress (default OFF = byte-for-byte unchanged):
nudgeAdaptive (bool), nudgeGrowthMin (num, default 10000), nudgeGrowthMax
(num, default 200000). Pure/deterministic step computation lives in the new
src/nudge-adaptive.ts; the band is overlaid onto the already-resolved
per-request config in compress-settings.ts (applyAdaptiveNudgeStep); server.ts
samples rawInputTokens into a bounded per-session ring (persisted via
session.metadata) at session bind and applies the band.

Docs: CONFIGURATION en/zh field reference, README en/zh FAQ, paper §6.3 en/zh
(throughput signal vs the cache-occupancy denominator artifact). Tests:
tests/nudge-adaptive.test.ts (deterministic unit + integration).

Closes #1997
New README.md FAQ continuation lines and the paper §6.3 paragraphs carried a
leading space inconsistent with surrounding style. Cosmetic only; renders
unchanged. Labeled incidental fix per the small-fixes-in-passing policy.
…ew fix)

KDD#7 said the proxy 'suppresses tool + nudge injection' in plugin mode. Since
#451 the proxy-side nudge IS injected in BOTH modes (ephemeral trailing user
message; the agent has no nudge channel of its own) — see src/server.ts
'Inject in BOTH modes (#451)' and TECHNICAL-NOTES.md 'Two compression modes'
table row 'Proxy-injected nudge | yes | yes'. The stale wording is the source
of the incorrect plugin-mode claim in the #2035 PR description. Factual doc fix
only; no behavior change.
@ranxianglei
ranxianglei force-pushed the wt-1997-ework-daemon branch from abc1586 to 3676e0f Compare October 6, 2026 16:38
@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 收到,已核对并顺手把 #2035 rebase 到当前 master(零冲突)。先一个事实更正,再给交互分析:

① 事实更正:#2278 尚未 landed。API 核实:#2278 fix(kernel): window-scale the default nudge growth step (50k flat -> 20k..50k band) 当前 state=open, merged=false(分支 2026-10-07_nudge-growth-window-scaled,kernel 0.0.104→0.0.105)。master 上 AGENTS.md KDD#8 仍写 flat-50K 契约——#2278 是那个契约的唯一待决变更。所以目前是两个 open PR,不是"已落地+互补",合并顺序需要定一下(见 ③)。

② 机制层交互分析(结论:不冲突,顺序二选一均可):

③ 建议合并顺序:#2035 先合(seed 零接缝),#2278 后合时我补 seed 对齐 follow-up;若 owner 更想先落 #2278(修 #2110 starvation 紧迫性更高),也可以——代价只是上述瞬态 seed 不一致,我同样可以先把 seed 对齐 commit 加进 #2035 再等 #2278。请拍板顺序。

④ Rebase 报告(新 head 3676e0f1,原 abc15864 落后 master 410 提交):

  • rebase 零文本冲突;diff 保持原样(12 文件 +349/−8,含 review 小修)。
  • 语义审计(热点文件 server.ts 这 410 提交里大改过):overlay 点仍在所有 prepare* 路径之前;master 新增的 output-headroom modelContextLimit 收缩发生在 overlay 之后,但对 floor==cap pin 无效(公式保证),动态步长不受影响;storeEffectiveConfig 面板 stamp 行为不变。
  • 本地验证(rebased head):typecheck ✓ · build ✓ · nudge-adaptive 20/20 ✓ · 聚焦回归 8 套件 74/74 ✓。全量回归交 CI,结果出来我再补报。

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 CI 补报:rebased head 3676e0f1 全绿 ✅

全部 13 条 workflow lane completed success:CI(ubuntu-22/24 + windows-22/24 + version-guard)、Theorem Lane、E2E real codex、e2e-native-dsh / e2e-native-pi / e2e-native-opencode、e2e-dsh-plugin-lane、e2e-pi-codex-ws、e2e-fake-codex、e2e-local-registry、ci-image、PR Build Artifact、CI Windows codex overlay。

当前状态汇总:

This branch has not been deployed

No deployments
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.

Supports dynamic compress.nudgeGrowthTokens | 支持动态compress.nudgeGrowthTokens

1 participant