# M2 真实数据压测:场景矩阵与数据清单

> **定位**: M2 压测 harness 设计文档的直接输入(调研产物,非设计本身)。依据: 2026-07-20 三项目工作负载调研(subagent,证据为 reference/ 文件:行号)+ 远端数据勘察(iomgaa@10.77.0.40)+ 已拉取语料的实测统计。

## 0. 决策记录(2026-07-20 人类拍板)

**不自研独立 mock 网关,压测直接打真实网关 + 真实数据**。理由: 调研证实无现成开源方案覆盖关键需求(静默缺 [DONE]/usage 精确可控),自研则"为醋包饺子";协议级故障的确定性测试已由 M1 进程内 MockTransport 单测钉死且长期保留,独立 mock 的边际价值不足。真实环境下的故障不靠造,靠**故障源混编**(§3)。成本上贵一点接受(预算上限 M2 设计定稿,人类签字)。

## 1. 语料盘点

| 语料 | 状态 | 体量 | 用途 |
|---|---|---|---|
| VT `generate_questions_telemetry.db` | ✅ 已拉取 `data/soak/` | 189MB,**376 条真实调用,messages p50=313KB / p95=1.2MB / max=2.5MB**(内嵌帧的多模态重载) | 直接回放: 真实重载 payload 分布,不用编造 |
| VT `harness.db` | ✅ 已拉取 | 50MB,traces/predictions 表(agent 搜索完整轨迹) | 挖取 S1 形态: system+多轮累积的真实长上下文序列 |
| VT `store/videos/*/frames/` | ⏳ 按需拉 | 300 视频 × ~90 帧 × ~190KB(全量 3.4G) | 多模态请求组装(1-4 帧/call 与 5-6 帧/call 两档);**建议先拉 20 个视频 ≈ 350MB** |
| CHS `data/real_data/` 等 | ⏳ 按需拉 | 4298 张超声 jpg(~150-230KB);假名,无需脱敏,拉取时顺手按索引匿名重命名 | 单图+固定短指令的无状态负载;**建议先拉 500 张 ≈ 100MB** |
| CHS `app/providers/vascular_assets/` | 本地 reference/ 已有 | 13 张参考图 | 拼接图场景(若纳入) |
| GovDoc | 无数据(人类确认) | — | 纯文本场景用 VT harness traces 的文本部分替代 |

## 2. 压测场景矩阵(6 个执行场景,收敛自调研的 9 形态)

> 原则: 场景以"请求生成器形态 × 治理能力覆盖"划分,全部经 `GatewayClient.chat()` 走完整洋葱;业务编排(arq/phase/租户配额)不在库内,不复刻。

| # | 场景 | 请求形态(数据源) | 并发/节奏 | 覆盖的治理能力 |
|---|---|---|---|---|
| P1 文本长上下文回放 | harness.db traces 还原 system+多轮累积(2K→数万 token 渐增),session_id/parent_call_id 链路照原样 | 16-32 并发,链式(单链串行 ≤40 步 × 多链并行) | 多轮大 prompt 吞吐、遥测链路、SQLite 持续写 |
| P2 多模态重载回放 | telemetry.db 376 条原样回放 + frames 组装新请求(1-4 帧与 5-6 帧两档) | 16 并发批(VT `TREE_BUILD_API_CONCURRENCY` 同量级) | 大 payload(300KB-2.5MB)下 SSE 稳定性、多模态摘要(缓存 key/遥测)开销、TPM 预扣结算 |
| P3 单图短指令高频 | CHS 超声图 + 12 字段 EXTRACT/CLASSIFY 固定 instruction,structured=pydantic 真实 schema | 50-100 并发(用户指定的目标强度主场景) | 高并发限流六道闸、结构化阶梯真实成功率、无 session 确定性负载 |
| P4 缓存双向 | 同 messages 重复(CHS 固定指令天然重复形态)+ VT cache_salt 破缓存;per-call namespace 隔离 | 混入 P3 流量 20-30% | 命中率统计、salt 强制 miss、namespace 隔离、Redis 持续读写 |
| P5 故障源混编 | P3 流量打到多源池: 健康源 + §3 故障源 | 50 并发持续 | 换源/退避/熔断开路-半开-恢复/冷却备忘/per_source_reasons,全部真实触发 |
| P6 混合浸泡(终测) | P1-P5 按比例混合(比例 M2 设计定),上千次请求、50-100 并发、2-3 小时(人类指定强度) | 持续 2-3h | §4 全部不变量 + 成本/延迟基线产出 |

## 3. 故障源混编清单(真实故障,零 mock)

多源池中混入**故意配坏的源**,故障在真实协议下自然发生:

| 故障源 | 配法 | 触发的真实路径 |
|---|---|---|
| 坏凭据源 | 真实网关 + 错误 API_KEY | 401 → SourceDeadError → force_open → 立即换源 |
| 黑洞源 | base_url 指向不可达地址(如防火墙 DROP 的内网 IP) | 连接超时 → TransientError(timeout)→ 退避重试 |
| 慢源 | 真实源 + 极紧 ttft/inter_token(如 1s/0.5s) | 看门狗真实触发 → 流中断 → 重试换源 |
| 紧闸源 | 真实源 + rpm=5 / max_concurrency=1 | 限流闸真实排队/fail-fast;RPM 窗口行为 |
| 间歇故障 | 真实网关自身的 429/5xx/空补全(M1 验收已证明会自然出现) | 免费的真实混沌 |

## 4. 不变量与记分板

压测记分板 = **我们自己的遥测库**(llm_calls,吃自己的狗粮)。结束时断言的硬不变量:

1. 记账归零: 全部源 inflight == 0;无泄漏租约;探针不悬挂(gate 可再准入)。
2. 遥测完备: llm_calls 行数 == 请求数(±取消双记的已知语义);call_id 无重复;error 分布与注入比例吻合。
3. 限额从未击穿: 按遥测时间戳重算,任意 60s 窗口内单源请求数 ≤ RPM 配置(故障源的闸)。
4. 内存平稳: 进程 RSS 首末差 < 阈值(窗口计数器/租约表无无界增长)。
5. 结构化成功率: P3 的 structured 档最终成功率(含重问)≥ 基线(首跑建立)。
6. 产出基线: 成本(usage 汇总)、延迟分位(p50/p95 ttft 与总时长)入 `tests/outputs/soak/` 报告。

## 5. 留给 M2 设计文档定稿的项

预算上限与网关保护(挑时段/全局限速值,人类签字);P6 混合比例;harness 落点(`tools/` 脚本,不入 pytest 门);CHS 图像子集拉取数量与匿名重命名脚本;traces→messages 还原器的实现;报告模板。

## 6. 数据拉取(2026-07-20 全部完成)

- [x] VT 两个遥测库 → `data/soak/`(239MB)
- [x] VT frames 子集 → `data/soak/vt_frames/`(20 视频、2480 帧、234MB)
- [x] CHS 超声子集 → `data/soak/chs_images/`(全库 4226 张均匀采样 469 张、84MB;患者姓名路径已平铺为 `chs_NNNN.jpg` 匿名索引,不留映射)

**测试隔离约定(harness 必须遵守)**: Redis 固定用专用 db3,每轮开跑前 `FLUSHDB`(仅 db3,我们独占;严禁碰 db0 共享库)+ 每轮独立 `cache_namespace=run_id` 双层隔离;回放场景每请求掺 run 级 cache_salt 破缓存(缓存行为归 P4 单测)。
