Files
xsl 0652c2f0e6 research(hunt-sim): 10 人并发在线模拟×多 agent 设计迭代——研究设施与全套档案
研究命题(00-plan):验证并打磨《围猎》联机纯音频体验——不是逻辑能跑通,
而是真人玩起来是什么体验、愿不愿意再来一局。

设施 web/harness/hunt-sim/(可复用,零依赖):
- arena.mjs 编排器:起真实服务器(随机端口+临时数据目录隔离),10 个拟人客户端
  经真实 WS 同时在线,错峰组队、真实时长对局、局间状态跨轮维护
- sim-client.mjs:game.js 行为移植(协议+手势路由+R1-R8 音频调度逐字节对齐),
  产出「玩家耳朵听到什么」的感知时间线(语音全文/loop 方位音量档/喊话/震动)
- brain.mjs:人设大脑——察觉延迟/前后判读(以真值为条件的正确率模型)/掩蔽放大/
  冷却乱滑/技能囤积/呼喊倾向/分心断网,全部随机判定用按实体 id 哈希的独立抽签
  (mulberry32 流会被刷怪座位轮转结构带偏,实测造成系统性镜像反转,已证伪)
- recorder.mjs:每人每局 events.jsonl 全量 + percept.md 感知转写 + 对局速览

研究档案 research/hunt-sim/:
- players/:10 人配置(LLM 亲笔)+策略自述+逐局去留决策+人设终评+退游者重访
- rounds/:v1 三轮(R0 教学+R1/R2 真人快速)+ v2 复测两轮,共 48 局全记录
- analysis/01-04:数值曲线/音频信息设计/协作社交/上手留存四视角报告
- synthesis.md(S1-S9 裁定)、metrics.md/metrics-v2.md(两版对比总账)、report.md(总报告)

v1 核心发现:10 人 3 轮 28 局 100% 胜率 26S/营地零损耗;击杀垄断(专家单人 30/39);
呼喊 3 次/28 局应答 0;N/S 前后判读仅 63/69% 可命中;四种退游死因
(失衡/社交真空/交互摩擦/观光无聊)→ 详见 report.md
2026-09-19 11:06:07 +08:00

169 lines
20 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 02 · 音频信息流设计分析(Q2 方位判读 / Q3 信道拥挤)· Phase 2
> 视角:纯音频信息流——耳朵在每一秒能听到什么、能不能判读、被谁掩蔽。
> 数据:`metrics.md`(28 局总账)+ 4 份抽样精读转写(P02/P08 · R1-quick,P07 · R2-quick,P10 · R0-teach)+ P01/P02/P09/P10 终评。
> 硬约束(全程自检):**R6 红线——方位信息永远由声像承载,语音不得报方位**;语音资产每句重新 TTS 有成本。
> 改动面缩写:**G** = `web/hunt/game.js`,**S** = `web/server/hunt-room.js`,**V** = `docs/design/23-hunt-voice-ids.md`(含 `tools/gen_voice_dual.py` 两表一致铁律),**D** = `docs/design/23-hunt.md` 同步。
## 0. 总判断
信息流层面三个结构性断点,且互相放大:**① 前后轴只有"音量 ×0.6"一条线索,且与距离档共用响度维度(混淆是数学上注定的)**;**② 出击冷却 2.5s 对玩家是纯黑箱(666 次乱滑被拦,无一毫秒就绪信息)**;**③ up/down 手势"有怪=出击、无怪=菜单/状态链"的语境折中,把方位判读弱点直接兑换成误触 UI 与语音轰炸**。R1-R8 机制本身全部成立(无叠播、无超限),但 R2 的"占线丢弃"打在唯一正确的时机上(波始),bot 喊话在快速局近乎绝迹。掩蔽的代价不在判读准确率(10% vs 10%,无差异),而在**漏检与迟到**——当前指标没有这个口径,复测需补。
---
## A1 · 前后轴(N/S 家族)判读 63-69%:单线索、且与距离轴共维度
### 现象(数据+转写引用)
- 混淆账(821 次登记,按 90° 弧可命中口径):**N 63%(n=120)/ S 69%(n=85)**,而 E/W 100%、四斜向 96-99%。弱点严格落在 "pan=0、只能靠音量分前后" 的家族;N 同时是尝试量最大的桶(玩家默认往前滑)。
- 转写翻车样本(全部是前后翻转,不是左右混淆):
- P02 R1 `00:45.7`「以为左前实在正左」、`01:11.4`「上滑出击→以为正前的狼(其实在正后)」、`01:18.9`/`01:44.7`「以为正后(其实在正前)→下滑变菜单」;
- P07 R2 `01:44.2`「以为正前其实在正后」、`01:51.3`「以为左前其实在正前——挥空」;
- P10 R0 `01:53.9`「闷声以为左前,实在左后」、`02:29.1`「以为右后,其实在正前」。
- 终评三方盖章:P01(资深,斜向混淆 <8% 的人设)「单音量线索在旁白轰炸下等于无线索」;P02「三局五验同判不同果,等于不可用」;P10「三局了这条规则从没进过我脑子」。
### 根因(三层,一层比一层深)
1. **几何层**:`pan = sin(az)`,N/S 同为 0——StereoPanner 前后同位是已知物理边界,设计已用 ×0.6 补偿;但 **-4.4dB 是一个响度差**。
2. **维度混淆层(设计根源)**:距离三档 0.30/0.55/0.80 与前后系数 ×0.6 **共用同一个响度维度**,且量级相当(档间差 -5.2dB / -3.2dB vs 前后差 -4.4dB)。具体对撞:前方中档 0.55 vs 后方贴脸档 0.80×0.6=**0.48**——差 0.5dB,人耳不可分;后方中档 0.33 与前方远档 0.30 直接重叠。**"多远"和"前还是后"两个信息轴被压进同一根响度刻度**,这不是练不练的问题,是解码不出来。
3. **实现与教学层**:
- game.js `_ensureMobLoop` 里"闷"= `vol × 0.6` 纯增益(G:961),**没有任何低通滤波**——设计词面"身后发闷"承诺的是音色,实现只给了响度;
- 教学零覆盖:hunt-room.js `tickTeach` 的教学狼全在前半平面(stage1 `dir:'N'`、stage2 `N+NE`,S:591/606-607),`tut_rule` 台词不含闷约定(voice-ids 头注写"由音效演示承担"——**演示狼根本不从身后来**)。P10 终评 P0-1 原话:「闷=身后零教学」。
### 修复提案(全部不越 R6 红线:方位仍由声像/音色承载,无语音报方位)
| # | 修法 | 机制/参数 | 改动面 | 新台词? |
|---|---|---|---|---|
| F1 | **后半平面低通第二线索(音色轴)** | loop 链路 `src→gain→BiquadFilter(lowpass, f≈900Hz, Q≈0.7)→panner→sfxBus`,后半平面启用、前半平面直通;与 ×0.6 音量并行。音色与响度正交,距离档不再污染前后判读。loop 走游戏层自建节点(abyss 先例),**不动引擎面** | G(`_ensureMobLoop`) | 否 |
| F2 | **前后系数加深** | ×0.6→**×0.5**(-6dB),配合 F1 后响度只做辅助确认 | G(一行常量)+D §8.3 同步 | 否 |
| F3 | **教学补"身后"课** | T1 杀掉 N 狼后补放一只 `dir:'S', eta:12` 教学狼(同样零惩罚、可挥空重教 ≤2 次);新台词点破:`tut_back`「这一声发闷——它在身后。声音发闷,就在背后。」;同时**换写既有 `tut_rule`**:「野兽会从八个方向围上来。声音在哪边,就往哪边滑。听着发闷的,在身后。」(重生成,不加 id) | S(`tickTeach`)+V(+1 id)| **是:新增 `tut_back` 1 id ×2 性别;`tut_rule` + key 变体各重生成 ×2**。预算说明:旁白 60 句已打满,+1 需设计裁决(扩到 61,或砍 1 句低价值句腾位;候选:`st_wave_c`「最后一波了」与 `wave_band5` 语义重复度最高,但状态链在用——建议直接扩容并更新 D §10.3/自检 #5 口径) |
| F4 | **挥空增亮反馈(错向的真话由声像自己说)** | `ev miss` 后 0.3s 内,把当前内圈 eta 最小怪的 loop 短暂增亮 0.6s(vol×1.3;若在身后,这 0.6s **暂时解除低通**)——玩家当场听到"真实方位",形成前后轴的即时纠错学习回路 | G(`_onMiss` → `_highlightNearest`) | 否 |
| F5 | (P2 备选)呼啸 howl 同样加低通 | howl 走引擎 `opts.pan` one-shot,插滤波需引擎面例外——与 §9"引擎改动唯一一处"冲突,暂缓;外圈损失由中圈 loop(已带 F1)接管 | (引擎例外,暂缓) | 否 |
### 预期指标变化
- N/S 可命中率 63%/69% → **≥85%**(R4 同口径复测);斜向 96-99% 与 E/W 100% 不回归(F1 不动 pan)。
- 前后翻转型挥空(转写归因"以为前/其实在后"类)样本占比下降 ≥60%;P02 类"同判不同果"主观报告消失(F1 使线索可解码,F3/F4 使其可学习)。
- P10 类新手「闷声判反两次」→ 教学局内至少一次正确应对身后世狼。
---
## A2 · 冷却黑箱:666 次乱滑被拦,就绪状态零外显
### 现象
- 全场 666 次冷却乱滑被拦;P07 R2 一局 **138 次**(`01:48.3-01:49.7` 十一连滑、`02:21.1-02:22.7` 又十余次,每 100-300ms 一记 error 震,间夹 threat 重听);P09 三局局局「回神头两秒全在吃震」,P10「三局从没数清过一次冷却」「一度以为是我滑法不对」。
- 实现现状(G):`_attackDir` 冷却内 `vibrate('error'); return;`——**2.5s 冷却没有任何音/震/节拍外显**;旋风斩 8s 有 `skill_ready` 语音,但 `queue:true` 排队在拥挤信道里迟到:P07 R2 `02:25.3` 战斗最烈时排不上,**`02:32.7` 局已进入结算才播出来**(`_enterEnd` 未清 `_skillReadyTimer`,跨局泄漏,bug 级)。
- 附带偏差(需核查):P02 R1 `00:37.2/00:38.1/00:39.0` 三次被拦右滑,转写播了 `gd_invalid`「这个操作现在用不上」——设计口径是"冷却仅 error 震、gd_invalid onceKey 同局 1 次"。sim 感知模型与 game.js 两处不符(或真实客户端存在异常路径),实施前先对 harness 复刻核对。
### 根因
就绪状态只存在于客户端 `_atkReadyAt` 时间戳,**没有任何模态把它交给耳朵/手掌**;error 震语义是"不行"而不是"还差多久";冷却被拦与无效手势用同一种震(P10 三局分不清是冷却还是手势错);`skill_ready` 绑在语音队列上,信道越挤越迟到。
### 修复提案
| # | 修法 | 机制/参数 | 改动面 | 新台词? |
|---|---|---|---|---|
| F6 | **出击就绪双信号** | 冷却到期播 0.2s 合成短音 `atk_ready`(SYN 高频双 blip,中央,vol 0.5,R5 合规 <1s)+轻触觉(`vibrate('confirm')`)。**节流:仅当本冷却窗内发生过被拦尝试才播**(没人急着出手时不出声,避免每 2.5s 空响成噪声) | G(`_attackDir` 内 arm setTimeout,复用 `_armSkillReady` 模式)+1 条程序合成 sfx | 否(SYN 无 TTS) |
| F7 | **被拦反馈分级** | 同一冷却窗内首次被拦:专用触觉模式(0.4s 双脉冲,与"无效手势"单短震区分);第 2 次起合并静默至窗尾(消灭 P07 式连震风暴)。冷却与无效手势两种 error 从此可分辨 | G(`_attackDir` + `_invalid`) | 否 |
| F8 | **skill_ready 去语音化+修跨局泄漏** | 改合成 earcon(上行双音 0.3s)+success 轻震;`_enterEnd/_teardownRound/stop` 一律清 `_skillReadyTimer`;`skill_ready` 语音 id 保留给 TEACH 讲解用 | G(`_armSkillReady`、`_enterEnd`)+1 条 SYN sfx | 否 |
| F9 | (P2 可选)冷却进度 tick | 被拦时按剩余时间给 1-3 声短 tick(P02 要的"还差半秒");有 F6 后收益边际化 | G | 否 |
### 预期指标变化
- 冷却乱滑被拦 **666 → ≤200**(就绪可听后 spam 自熄:P07 类玩家的连滑在第一声 ready 后失去动机)。
- P09「回神头两秒吃震」消除;P10「扣的那 1 分」收回;`skill_ready` 跨局/迟到播报归零(语音打断 37 次中 skill_ready 相关子项清空)。
---
## A3 · down/up 双语义陷阱:误触菜单与状态链误播
### 现象
- **下滑=菜单误触,6/10 玩家中招**:P01 R1 连环 14 次(终评:「下滑退出和下滑打正后是同一根手指,三次下滑全弹回菜单」);P07 R2 `01:15.6-01:17.6` 菜单乒乓 6 轮(每轮 `sys_menu→回到猎场→settings_hint` 三句链互相打断,"回到猎场"四度播不完);P02 R1 五次(`01:18.9/01:44.7/02:20.9-02:21.3` 连环);P08 R1 三次;P09 三局同坑(进菜单后 `01:09.3` 盲单击又误触"重新听帮助")。
- **上滑=状态链误播**:错向挥出的上/下滑恰好制造"弧内无怪"→ 变成播报。P02 R1 `01:11.4`「以为正前其实在正后→上滑变成 4 句状态链」;P07 R2 `01:44.2` 同型。状态链 4 句 ≈5-6s 占满语音通道(见 A4)。
- 菜单打开战斗照走(E3 豁免本意),P01 终评 P0-1 定性:「操作本身就是在跟系统打架」。
### 根因
23-hunt §2.1 表格自身双重定义(四向滑=出击 vs 上滑=状态链/下滑=菜单);game.js 文件头"语境折中"用 `_arcHasTarget()`(客户端本地、eta≤4、事件流瞬时空窗都影响判定)做分流——**把"打不打得到"这个毫秒级动态事实当作"出招还是开 UI"的开关**。且误触率与 A1 前后弱点正相关:错向 → 无怪 → 菜单。退出菜单仍是下滑,同一根手指解自己上的锁。
### 修复提案
| # | 修法 | 机制/参数 | 改动面 | 新台词? |
|---|---|---|---|---|
| F10 | **WAVE 态下滑恒=出击** | 无目标也走完整出击:`atk_whoosh`+服务器 miss → `atk_swish` 真话+error 震(与左/右滑同待遇)。菜单入口改为:①**波间休整**(`wave_clear` 后 8s 窗)内下滑=菜单;②战斗中**长按**=菜单(WAVE 态旁白极少,快进让渡成本最低——需宪法裁决登记 E3 修订);若裁决不动长按,则战斗中无菜单、退出只在波间 | G(`_onWaveGesture`)+D §2.1/§2.4/§2.5 豁免表同步 | 否 |
| F11 | **上滑状态链两条护栏** | ①链播中再上滑=**立即停止**(现在会再排 4 句,P02 R0 23 秒里"营地固若金汤"五遍);②**危急截断**:任一怪进入贴脸档(eta<2.5s)时,正在播的状态/菜单/帮助/watchdog 链立即截断(`_chainSeq++`+`stopVoice`;警报/教学/结算链豁免)——即 P02 点名的"危急时刻静默通道",实现"loop 优先"而不动引擎 ducking | G(`_statusChain`、`_refreshLoops` 内挂钩) | 否 |
| F12 | **状态链瘦身** | 4 句→按变化播:耐久档+击杀档恒播;波次档仅波次变更后首次播;猎人数仅 roster 变更后播(P08 是状态链重度用户且终评正向——保留主干只砍复读) | G(`_statusIds`) | 否 |
### 预期指标变化
- 战斗中 menu-open 误触:P01 的 14 次/局 → **0**(结构性消除)。
- 语音打断 37 → **≤15**(大头是菜单乒乓互切,见 A4 清单)。
- "状态链压贴脸脚步"型掩蔽案例(P02 R2 01:21.0-01:26.8)归零;P01 P0-1/P0-2 直接收口。
---
## A4 · 语音通道 × 战斗信息冲突账
### 掩蔽源清单(按占用时长与伤害排序;样本来自 4 份精读转写)
| 源 | 单次占用 | 样本频次 | 价值裁定 |
|---|---|---|---|
| 误触菜单链(`sys_menu`+`sys_menu_resume`+`settings_hint`) | 3 句 ≈5s | P02 R1 ×5、P07 R2 菜单乒乓期近连续、P08 R1 ×3 | **负价值**;F10 后归零 |
| `help_main` 战斗中整播 | ≈8s | P08 R1 `01:33.4/02:02.3`、P07 R2 `01:40.9`、P01 R1 ×2 | **负价值**(P01:「对老手重播基础教学=战斗中没收我的耳朵」);F10 后结构性消失(菜单不在战斗态) |
| 状态链 4 句 | ≈5-6s | P08 R1 主动使用 ×6;P02 R0 23s 五遍复读 | **中价值**(P08/P06 靠它感知战局)——瘦身保留(F11/F12),不删功能 |
| watchdog 两级(idle 提示音+`gd_idle`) | ≈2s×2 | 全场 93 次 ≈3.3 次/局;P10 R2 七次全误伤(在等怪不是挂机) | **救命索但触发条件错**(P09 走神 9 次全被拽回)。修:inner-ring 有怪、或 5s 内有击杀/被拦/呼喊记录时不触发;第 1 级降为纯震+短音,第 2 级才上语音(G `_rearmWatchdog` 条件) |
| 波次/威胁/警报(`wave_band*`/`threat_*`/`breach*`) | 各 1 句 | 合法占用 | **警报可达性 PASS**:28 局 6 次 breach 全部按时到达并正确顶掉状态类(R1 实测有效);`threat_*` 重听是 P07 的双击主出口,属宪法语义,保留 |
| `skill_ready` | 1 句 | P07 R2 高频 | F8 去语音化后退出竞争 |
### 掩蔽 × 误判的诚实读数
metrics:掩蔽下误判率 10%(n=297)vs 干净时 10%(n=524)——**判读准确率不受掩蔽影响**(拟人模型"听到后"的判读与掩蔽无关)。但掩蔽的真实代价是**漏检/迟到**:P01「menu 播报正好盖掉右后闷脚步→四连打空」、P02「状态链四句压着贴脸脚步」。当前混淆账不测这个。**复测新增指标:「掩蔽窗漏检率」=语音在播期间进入内圈的怪,玩家首次针对性出手延迟 >1.5s 的比例**(基线值 R4 复测时先量再比)。
### bot 喊话饿死(结构性对撞)
- R2 客户端规则"旁白在播→延迟 ≤2s→丢弃",撞上服务端触发时机:`greet` 排在开局(`GREET_FIRST_MS` 起,S:352-353)、`wave` 排在波始(S:371)——**这两个时刻旁白必播**(`room_start`+`wave_band1` / `wave_bandN`),氛围句 2s TTL 必死。
- 实测:P10 R0 教学局 12 条 botline 中 **7 条被挤掉**(`00:04.6` greet、`01:07.3/02:11.5/02:43.1` 恰为波始),5 条可闻(help_call/help_ack/cover/kill×2——priority 句 4s TTL 兑现了"呼救不可被顶");**P02/P07/P08 三份快速局转写可闻 bot 喊话 0 条**。
- 注意另一根因:P01 R1 是 4 真人房(0 bot),bot 喊话结构性为零——"真人房社交声道缺失"要靠呼喊链路补(全场呼喊仅 3 次/28 局,且 P02 R1 `02:12.6` 呼喊的本地反馈在转写里呈现为旁白"重新听帮助。",同房 P01 也没报告听到空间化喊话——**呼喊播放链路需专项核查**)。
**修法 F13(S+G,无新台词)**:服务端 `wave`/`greet` botline 延迟 3.5s 投递(等 `wave_band` 播完;BOTLINE 预算 10s≤2 不变);客户端非优先句 TTL 2s→4s。预期:快速局人均可闻喊话 0→3-5 条;教学局挤掉 7→≤2。
---
## A5 · R1-R8 拥挤规则在真实并发下的表现
| 规则 | 机制是否成立 | 真实并发下的表现与裁定 |
|---|---|---|
| R1 旁白单句队列 | ✅ | 全程无叠播;警报顶状态类实测有效(breach 到达 6/6)。代价=队列拥堵与打断(37 次,主因菜单乒乓,F10 后大减);**发现的 bug:`skill_ready` 排队跨入结算链**(P07 R2 02:32.7),F8 修复 |
| R2 喊话 sfx ≤1+延迟丢弃 | ✅(但丢弃打错时机) | 上限无违例;丢弃全落在 greet/wave 两类"旁白必播时刻"——**修触发时机(F13),不放宽 ≤1 上限**。help_call/help_ack 优先级兑现(教学局 5 条可闻里 3 条是 priority) |
| R3 loop ≤3 路 | ✅ 且无害 | 无"第 4 近怪因静默突入"的可归因案例(快速局 6 局全零 breach);被裁剪怪的方位由击杀倒地音(带 pan)部分补偿。**维持原样** |
| R4 one-shot ≤2+呼啸可弃 | ✅ | 波 4-5 生成潮确有呼啸被弃(外圈方向预告缺失),但中圈起 loop 接管,本数据集无可归因损失。可选微调 HOWL_QUEUE_MAX 2→3 / TTL 1.5→1.2s——收益小,列 P2 |
| R5 非语音 <1s | ✅ | 无违例 |
| R6 方位由声像承载 | ✅ | 全程无语音报方位。**这是立身之本:本报告所有修法(低通/增亮/earcon)均让声像自己说话,明确拒绝"语音报前后"类越线捷径** |
| R7 电平分层 | ⚠️ 半成立 | BGM duck 语音方向正确;但"语音 vs loop"无互让——用 F11 危急截断在游戏层实现"贴脸 loop 优先",不动引擎 |
| R8 ≤7 声流 | ✅ | 转写无超限/叠爆证据 |
---
## 修复优先级表
| 级 | 编号 | 修法 | 改动面 | 新台词(TTS 成本) | 预期指标 |
|---|---|---|---|---|---|
| **P0** | F1 | 后半平面 loop 低通(音色第二线索) | G | 否 | N/S 63/69% → ≥80%(配合 F3 冲 ≥85%) |
| **P0** | F6 | 出击就绪 earcon+触觉(有被拦才播) | G+1 SYN sfx | 否(程序合成) | 乱滑被拦 666 → ≤200 |
| **P0** | F10 | WAVE 下滑恒出击;菜单改波间窗/长按 | G+D(E3 修订) | 否 | 战斗中误触菜单 →0;语音打断 37 → ≤15 |
| **P0** | F3 | 教学补身后期:S 向教学狼+`tut_back`+`tut_rule` 换写 | S+V | **是:+1 id×2 性别;`tut_rule`(含 key 变体) 重生成 ×4**;60 句预算需裁决扩容 | 新手前后翻车率减半;「闷」从零教学变一课学会 |
| **P1** | F11 | 危急截断+链中再上滑即停 | G | 否 | 状态链压贴脸脚步案例归零 |
| **P1** | F7 | 被拦反馈分级(冷却≠无效手势,连震合并) | G | 否 | P10 类"自我怀疑"消除 |
| **P1** | F13 | botline 波始延迟 3.5s+客户端 TTL 2→4s;呼喊链路专项核查 | S+G | 否 | 快速局可闻喊话 0→3-5 条/人 |
| **P1** | F8 | skill_ready 去语音化+局末清 timer | G+1 SYN sfx | 否 | 跨局泄漏/迟到归零 |
| **P1** | F12 | 状态链按变化播(4 句→2-4 句) | G | 否 | "固若金汤"复读投诉归零,状态感知功能保留 |
| **P2** | F4 | 挥空增亮(0.6s 突出真方位 loop) | G | 否 | 前后错向即时纠错学习回路 |
| **P2** | F2 | 前后系数 0.6→0.5 | G+D §8.3 | 否 | F1 的响度辅助确认 |
| **P2** | F5/F9 及 watchdog 降级、HOWL 队列微调 | 见 A4/A5 | G/S | 否 | 边际改善 |
**汇总账**:12 项修法中仅 F3 需要新台词(1 个新 id + 1 个既有 id 换写重生成,合计 6 条 TTS:`tut_back`×2 性别、`tut_rule`×2 性别+key 变体×2);其余全部是 game.js / hunt-room.js 逻辑与程序合成 sfx,零 TTS 成本。全部修法通过 R6 红线自检(无一句语音报方位)。
**复测口径提醒(给 synthesis)**:①混淆账同口径重跑(N/S 家族单列);②新增"掩蔽窗漏检率";③新增"战斗中 menu-open 次数""被拦后至就绪出手的中位延迟""bot 喊话可闻条数/人/局";④先核对 sim 感知模型两处偏差(冷却被拦播 `gd_invalid`、呼喊本地反馈异常),确保 R4 对比不被 harness 噪声污染。