You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Progress on the bili-side half of this fix family — PR #504 is up for review (branch 2026-09-03_fix-498-weak-overflow):
高 usage 下的非 400 失败(流截断/超时)无任何 overflow 兜底 —— 会话直接死 #498 weak-overflow learning — noteWeakOverflow: stream-truncation / upstream-error evidence at ≥90% window usage; 3 events in 15 min learn a shrink-only conservative window (per-model when the model is known) and arm the emergency shrink. Hooked into both loop adapters' truncation + UpstreamHttpError paths and both plugin passthrough pipes. Strong 4xx signals remain the primary teacher.
P1a prefix-affinity persistence — snapshot ${XDG_STATE_HOME}/billion-context/prefix-affinity.json (atomic write, 5 s debounce, flush on shutdown, hydrate on startup) → proxy restart no longer orphans anonymous sessions (the 0.1.63 应用在DeepSeek Harness超出 固定阈值没进行压缩 #351 pfa-8588 raw-458K-resend case).
P1b cache-collapse warn — after a ≥50% hit on a ≥8k request, 5 consecutive ~0% hits log ONE diagnostic warning with the likely causes; no behavior change.
Pre-flight: typecheck ✅, tests 1018/1020 (2 known env fails, unrelated), build ✅. 20 new tests.
The kernel-side half (first-sight mass nudge bypass) is acp-kernel PR #196, awaiting human merge → kernel 0.0.51 → then a separate pin-bump release PR here (NOT bundled into #504, per release rules).
Activity
ranxianglei commentedon Sep 3, 2026
[bot] 🏷 查重结论:非重复。 与 #248(prompt_cache_key identity)、#286/#287/#288(会话身份)、#309/#310/#311(匿名前缀亲和)、#316/#318(codex turn 分区 + tail-window)同族但角度不同——那些解决的是进程生命周期内的身份稳定性;本 issue 是跨重启的身份丢失,此前未覆盖。
机制已在代码层验证,根因明确:
trackedChains是纯内存 Map(src/prefix-affinity.ts:158),重启即清空。{ depth, tailHash }写进session.metadata.anonymousPrefixAffinity(src/server.ts:1134)并随会话落盘——但启动时没有任何代码把它读回来。initSessions()(src/session.ts:137)只加载会话状态,不重建路由表。(tailHash, depth)确定性派生(src/prefix-affinity.ts:258)。重启后客户端重发的历史 depth 已增长 → 派生出新 id(pfa-8588…vspfa-bc1e…)→ 落盘的压缩状态成为孤儿 → 代理转发未折叠的原始 1746 条历史 → 与上游缓存里已折叠的 128 条视图字节不一致 → 全 miss,且因窗口错配 + growth floor(acp-kernel#194)压缩也不触发,0% 持续整个会话。三个方向的评估与建议:
persist.ts明示承诺"rebuild the folded view after a restart",对匿名客户端该承诺被打破。建议立即做:把itemHashes一并持久化到metadata.anonymousPrefixAffinity,给 resolver 加reseed(),initSessions()启动时对全部落盘会话重播种 + 单测。修复后 #351 场景:重启首请求经前缀匹配回到原 session,折叠生效,wire 回到 ~128 条折叠视图,缓存命中恢复 95%+。我现在按方向 1 开工(bug fix),完成后回贴 PR 链接。
ranxianglei commentedon Sep 3, 2026
[bot] 🏷 方向 1 已完成,PR:#502
实现过程中发现了第二个 bug,比预想的更严重——单修路由表救不回来:
Bug A(数据丢失):
migrateLegacyIds()每次启动都在销毁匿名会话。 #309 的匿名会话统一带展示标签"prefix-affinity"≠ 其pfa-id,恰好违反了 #286 迁移的自终止不变量(label === id)。于是每次 bili 启动,迁移都会把所有pfa-会话 rekey 成同一个 id"prefix-affinity",并删除除最新之外的全部兄弟文件。已实测验证:落盘 2 个 pfa 会话 → 迁移后只剩 1 个。也就是说旧版里匿名会话的压缩状态每次重启就被静默清掉,即使路由表能恢复也无状态可恢复。修复:迁移跳过pfa-前缀 id(真·legacy 会话的 rekey 行为不变,有回归测试)。Bug B(本 issue 主因):亲和路由表纯内存、启动不重建。 按此前分析落实为三处小改动:
src/server.ts:metadata.anonymousPrefixAffinity增持久化sessionId+itemHashes(滚动窗口客户端重启后的 tail-window 重挂也靠它);src/prefix-affinity.ts:新增reseed(records)——逐条校验形状、itemHashes 截断到MAX_STORED_ITEMS、走note()保持 LRU/TTL 语义不变;src/session.tsinitSessions():对全部落盘会话重播种(不止装进内存上限的那批——被 LRU 驱逐的会话按需重载,必须仍可路由),并打日志[prefix-affinity] re-seeded N tracked chain(s) from persisted sessions。无 schema/version 变更:记录在自由
metadata字段里,老文件没有该字段就跳过重播种(等同现状)。#351 场景下的效果:重启后重连经前缀匹配回到
pfa-bc1e…→ 持久化的 blocks 把 1746 条重放折叠回 ~128 条折叠视图(即上游已缓存的前缀)→ 首个请求缓存命中即恢复到重启前 95%+ 水平。验证:新增 5 个测试(重播种路由回原会话 / tail-window 恢复 / 畸形记录跳过 / 迁移不碰 pfa 且仍 rekey 真 legacy /
initSessions全链路:落盘→清内存→启动→状态往返+增长重放回原会话)。typecheck ✅ · 全量测试 951/952 ✅(唯一失败是launcher.test.ts的 codex PATH 解析,master 上在该环境同样失败,与本改动无关,已 stash 验证)· build ✅。请求管线本身未动(仅增量 metadata 写入 + 启动重播种 + 迁移守卫),e2e codex 路径走 client-provided identity 不进匿名分支;本环境无本地上游可跑ACP_TEST_E2E。遗留:方向 3(面板/日志暴露 cache hit 回归)属 feature,等你拍板做不做、随哪个 release;方向 2 由 acp-kernel#194 覆盖,本仓库无独立工作。
ranxianglei commentedon Sep 3, 2026
[bot] 🏷 Review:机制复核 + 修复路径分级(基于 #351 线程原始日志分析 + 代码取证)
机制修正:#499 的"0%"是三个问题叠加的症状,不只是亲和断裂
重新核对 #351 日志后,原 issue 描述的机制("重启→会话 id 变化→前缀不一致")只解释了第一次 miss,解释不了每次请求都 0%。完整因果链:
src/prefix-affinity.ts:LRU 256 条 + 7 天 TTL,重启即清空)。0.1.63 应用在DeepSeek Harness超出 固定阈值没进行压缩 #351 的会话pfa-8588…正是匿名前缀亲和建的——bili 重启后哈希链没了,同一段历史重发也只能开新会话,压缩状态全部归零,首转发 = 全量 raw 458K。growth 17721 < floor 22500,934K ready 的 T1 干等。窗口分母又被注册表虚标成 1M(0.1.63 应用在DeepSeek Harness超出 固定阈值没进行压缩 #351 主因,已另行跟踪),75%/95% 线永远到不了。upstream stream truncated,高 usage 下的非 400 失败(流截断/超时)无任何 overflow 兜底 —— 会话直接死 #498)。失败请求在上游不产生任何 KV 缓存 → 后续同前缀请求也全 miss。这才是"每请求 cached=0"的直接原因——不是缓存键问题,是请求根本没成功到能暖缓存的程度。重启前 95–98% 是旧会话压缩视图字节稳定的结果;重启后三个问题互相锁死,0% 只是账单上的表现。
修复路径(按优先级)
P0(已在跟踪,是本 issue 的前置)
P1(bili 侧新工作,建议做)
cacheHitPct(src/server.ts:3169),补一条"大输入 + 命中率断崖"的 warn 日志,让成本异常可发现(issue 方向 3)。不做:会话亲和猜测(#499 方向 1 的"映射回原 session")。身份 verbatim 规则(#286)是有意设计,内容匹配做身份有碰撞面;正确方向是快速收敛(一次压缩回到稳定前缀),而不是猜身份。
prompt_cache_key转发上游维持现状(仅路由粘性,单实例上游无意义)。结论
#499 本身降级为跟踪项:P0 两项(kernel#194、#498)落地后,重启的最坏代价从"每请求全量付费直到会话死"收敛为"首请求压缩一次付全量";P1 的亲和持久化再消掉匿名客户端的开新会话问题。需要时我可以直接开做 P1 两项。
ranxianglei commentedon Sep 3, 2026
Progress on the bili-side half of this fix family — PR #504 is up for review (branch
2026-09-03_fix-498-weak-overflow):noteWeakOverflow: stream-truncation / upstream-error evidence at ≥90% window usage; 3 events in 15 min learn a shrink-only conservative window (per-model when the model is known) and arm the emergency shrink. Hooked into both loop adapters' truncation +UpstreamHttpErrorpaths and both plugin passthrough pipes. Strong 4xx signals remain the primary teacher.${XDG_STATE_HOME}/billion-context/prefix-affinity.json(atomic write, 5 s debounce, flush on shutdown, hydrate on startup) → proxy restart no longer orphans anonymous sessions (the 0.1.63 应用在DeepSeek Harness超出 固定阈值没进行压缩 #351 pfa-8588 raw-458K-resend case).Pre-flight: typecheck ✅, tests 1018/1020 (2 known env fails, unrelated), build ✅. 20 new tests.
The kernel-side half (first-sight mass nudge bypass) is acp-kernel PR #196, awaiting human merge → kernel 0.0.51 → then a separate pin-bump release PR here (NOT bundled into #504, per release rules).
fix: exclude anonymous pfa sessions from legacy-id migration (#499 pr…
ranxianglei commentedon Sep 3, 2026
[bot] 🏷 收到,已对照 #504(64d605f)逐文件核过。三点:
1. 重叠部分我已主动收缩。 我此前的 #502 用"会话 metadata 存链快照 +
initSessions()重播种"实现同一目标;#504 P1a 的独立快照表(affinity-persist.ts,hydrate/flush)是更完整的机制——保留lastSeen(TTL 跨重启生效)、不依赖会话文件生命周期、覆盖整张表。两套并存就是同一张表的两个持久化路径,没必要。#502 已 force-push 收缩为只剩它独有的那一块(见下),标题改为 "fix: stop legacy-id migration destroying anonymous pfa sessions (#499 prerequisite)"。2. #504 没有覆盖的坑:
migrateLegacyIds()每次启动都在销毁匿名会话的状态文件——P1a 单独上线救不回 #351。 #504 的 diff 不含src/persist.ts。而 #286 的一次性迁移会把"label ≠ id"的会话 rekey 成 label 并删除同 label 下除最新外的全部兄弟文件;匿名会话恰好共享 label"prefix-affinity"≠pfa-id,全部命中该规则。实测:后果链:重启 → 迁移先于任何请求执行,删掉 N-1 个匿名会话状态、把幸存者改 key → P1a hydrate 恢复的路由指向
pfa-8588…→getSession找不到文件、新建空会话 → 无 blocks 可折叠 → 原始历史照发 → 缓存命中照样 0%。路由恢复了,状态没了。 两个修复互补:P1a 恢复路由,这个 guard 保住路由指向的状态。收缩后的 #502 就是一行 guard(迁移跳过pfa-前缀)+ 回归测试(两个 pfa 会话 + 一个真 legacy 会话:pfa 全部原样存活、legacy 照常 rekey)。typecheck ✅、persist 测试 22/22 ✅、全量 1000/1001(唯一失败是本环境 codex-on-PATH 的既有 launcher 用例)。与 #504 零文件重叠,先后顺序无所谓,建议随 #504 一起合。3. P1b = 我之前提的方向 3,认可其形态(≥50% 基线后连续 5 次 ~0% 打一条诊断 WARN、不改行为)——观测性到位即可,不再另提。
另记一个 P1a-only 的已知小边界,供参考不必处理:首次升级(旧版本从未写过
prefix-affinity.json)的那一次重启,跨越升级边界的活跃匿名会话仍会分裂一次(新进程 hydrate 为空);首个匿名请求 ~5s 后快照落盘即自愈。若在意可在 hydrate 发现文件缺失时回退读会话 metadata 兜底——我的判断是不值得为此留双路径,接受一次性代价。Merge pull request #504 from ranxianglei/2026-09-03_fix-498-weak-over…
Merge pull request #502 from ranxianglei/2026-09-03_pfa-restart-reseed
fix: learn from weak overflow signals, persist prefix-affinity, warn …
merge: resolve conflicts against master (kernel 0.0.53 + #559 polling)
merge: resolve conflicts against master (kernel 0.0.53 + #559 polling)
fix(cache): stop conflating unmeasured cache reports with 0% misses; …
fix(wire): keep redacted_thinking blocks verbatim on the Anthropic st…