# 结构性缺陷验证报告

**日期**：2026-05-06
**对照数据源**：
- `outputs/qwen3-30b-tp1-v5-optD-baseline-rerun/`（v5 KVC kv-aware Option D，2P6D，**3 次同配置 rerun**）
- `outputs/qwen3-30b-tp1-exps/exp1_8way_dp_cache_aware_summary.json`（同 trace 8DP CA）
- `outputs/qwen3-30b-tp1-v5-optD-baseline-rerun/.../logs/decode-{0..5}.log`、`prefill-{0,1}.log`
**模型**：Qwen3-30B-A3B（TP1），单机 8×H100 80GB，trace `qwen35-swebench-50sess.jsonl`（4449 reqs / 52 sessions）。
**报告作用域**：验证 `docs/AGENTIC_FIT_ANALYSIS_ZH.md` §1-§7 提出的结构性 claim 是否真实存在；量化影响。

> ⚠️ **环境限制**：本轮缺 GPU 访问，未跑新 sweep。所有数据来自已存在的 v5 rerun + 8DP baseline。Backpressure 代码已实现但**未端到端验证**——下文标注为"预期收益（pending GPU smoke）"。

---

## 0. 实验有效性锚点：N=1 不可信

3 次 v5 baseline EXP2（**完全相同配置**）的 errors 漂移：

| Run | Errors | Lat P50 | Lat P90 | TTFT P50 |
|---|---:|---:|---:|---:|
| run1 | **372** | 1.11s | 8.65s | 0.147s |
| run2 | **912** | 0.94s | 7.68s | 0.071s |
| run3 | **396** | 1.22s | 8.43s | 0.183s |

errors 漂移 **2.5×**（372 → 912），P50 latency 漂移 **30%**。**任何 N=1 比较 < 30% 差异都不可信。** 后续所有"同 trace 不同配置 / 不同代码"的对比，都需要 N≥3 才有意义。

**对 KVC vs DP 的 headline 数据，3 次 KVC 的最佳值（P50=0.94s）仍然是 DP（P50=0.65s）的 1.45×**——8 way DP 的优势远超 single-run variance 范围，这一头条结论不受 variance 影响。

---

## §1. Session 永久 pin 到 D + 容量盲选 → 极端双峰 ✅ 完全成立

### Claim
KvAwarePolicy 评分以 hash overlap 为主，没有 D 容量项。Session 第一次落到某 D 后被永久 pin。导致大 session 在已满 D 上反复 admission 拒绝，小 session 在原 D 上 100% 走 direct-to-D。

### 数据

**(a) Session 永久绑定，跨 3 次 rerun 一致**：

```
run1: 52 sessions, avg distinct-D-per-session = 1.00
run2: 52 sessions, avg distinct-D-per-session = 1.00
run3: 52 sessions, avg distinct-D-per-session = 1.00
```

每个 session 在整个运行中只访问 **1 个** D worker，3 次独立 run 完全一致。**不是巧合，是结构。**

**(b) Direct-to-D 命中率呈极端双峰**：

| Direct-to-D rate | run1 | run2 | run3 |
|---|---:|---:|---:|
| 0-20%（饿死） | 15 | 18 | 16 |
| 20-40% | 7 | 6 | 7 |
| 40-60% | 11 | 7 | 9 |
| 60-80% | 5 | 6 | 4 |
| 80-100%（顺利） | 14 | 15 | 16 |

中间态稀少，两端拥挤。

**(c) 跨 3 次 run 一致饿死的 session 与 session 大小强相关**：

```
13 sessions starved (<20% direct-to-D) in ALL 3 runs.
  avg peak input of consistently-starved sessions: 62043 tokens
  avg peak input of consistently-lucky sessions:   31344 tokens
  ratio: 1.98× — starved sessions are exactly 2× larger.
```

**13/52 = 25% 的 session 在 3 次独立 run 中都被饿死，且这些 session 的 peak input 恰好是顺利 session 的 2 倍。** 这排除了"运气"假说，证实是大 session 在容量过载 D 上结构性失败。

### 影响量化
- 25% session 几乎每个 turn 都走 fallback 路径，相对 direct-to-D **TTFT 慢 100×、E2E 慢 6×**（数据点：fallback path mean lat ~3.5s vs direct ~0.5s）
- 对应这些 session 的用户体验是"系统性糟糕"，而不是"偶尔慢"
- **SLO 视角下 P99 完全由这 13 个 session 拉高**

### 结论
**完全成立**。修复方向（不在本轮）：policy score 加 capacity penalty + 允许 session 跨 D 迁移，或 D 端引入 hot session retract。

---

## §2. D 端 LRU 只 evict idle session → 跟不上压力 ✅ 完全成立

### Claim
`scheduler.py:2040` 的 `evict_idle_streaming_sessions_lru` 只能 evict "所有 req 都 finished + streaming 模式"的 session。高并发下 hot session 永远不 idle，LRU 找不到东西可踢。结果 D 顶到 100% 然后撞 mooncake transfer timeout。

### 数据（v5 baseline rerun run1）

| D worker | Trim 事件 | KVTransferError | 峰值 token_usage |
|---|---:|---:|---:|
| decode-0 | 9 | 0 | 0.99 |
| decode-1 | 43 | 4 | 0.99 |
| decode-2 | 16 | 153 | 0.97 |
| decode-3 | 37 | 29 | 0.99 |
| decode-4 | 28 | 90 | **1.00** |
| decode-5 | 30 | 93 | **1.00** |

**6 个 D 全部峰值 ≥ 0.97**，其中 2 个直接顶到 1.00（KV 池完全耗尽）。**LRU 触发 9-43 次，远不及 transfer 错误的 90-153 次。**

decode-2 极端：trim 16 次 vs error 153 次 = LRU 比错误慢 **9.5×**。

### 影响量化
- 单 run 累计 369 KVTransferError（总 6 个 D 之和）
- 对应 ~8% 的请求失败率（v5 errors 9/372/912 三次平均 ~430/4449 = 9.7%）
- **每次 mooncake timeout 是 32s**——对 P99 latency 直接贡献几十秒尾巴

### 结论
**完全成立**。修复方向（不在本轮）：分层 eviction——除 idle 外加冷 session retract、按访问频率/时序加权。Backpressure（本轮代码）只是把"D 满"的雪崩从"timeout 错误"转成"主动等待"，**不是真正解决容量问题**。

---

## §3. 没有 D→Replay backpressure 通道 ✅ 成立（已实现修复）

### Claim
D 端 transfer queue 堆 → 32s timeout → KVTransferError，没有"D 过载请慢点"信号反向到 replay；concurrency 一直 32 不降。

### 数据
- §2 的 369 KVTransferError 全部为 32s mooncake timeout（日志中均为 `Failed to send kv chunk` 或 `Decode instance could be dead`）
- 错误集中在运行后半段（按现有 `KVC_DEBUG_JOURNEY_V1_TO_V5.md` §v4：错误均在 run 的 44.8% 之后开始累积）
- 表明：**前期 D 容量充裕时正常，达到容量上限后所有后续请求集中失败**——典型无 backpressure 系统行为

### 修复（本轮已实现，待 GPU smoke 验证）

代码改动：
1. `third_party/sglang/python/sglang/srt/managers/io_struct.py`：`DirectAppendAdmissionReqOutput` 增加 `recommended_pause_ms` 字段
2. `third_party/sglang/python/sglang/srt/managers/scheduler.py:admit_direct_append`：基于 `transfer_queue_depth`、`retracted_queue_depth`、`token_usage_after` 计算 hint
   ```python
   def _compute_backpressure_pause_hint(...):
       if retracted_queue_depth > 0: return 1500
       if token_usage_after >= 0.90: return max(200, min(2000, overshoot * 5))
       if transfer_queue_depth >= 8: return min(2000, transfer_queue_depth * 100)
       return 0
   ```
3. `src/agentic_pd_hybrid/replay.py`：
   - `DecodeResidencyState.pause_until_s: dict[str, float]`
   - `_query_decode_direct_admission` 解析 hint 更新 `pause_until_s`
   - 新增 `_wait_for_decode_pause`，在 `_invoke_router` / `_invoke_session_direct` 入口检查
4. CLI flag：`--enable-backpressure`、`--backpressure-max-pause-s 2.0`（默认关闭）
5. 结构性日志：`structural/admission-events.jsonl`、`backpressure-events.jsonl`、`session-d-binding.jsonl`

### 预期收益（pending GPU smoke E2 vs E1）
- KVTransferError 应从 ~370 / 4449 跌到 < 50 / 4449
- P99 应改善（消除 32s timeout 尾巴）
- 整体 latency mean 可能**略升**（被强制 pause），但 P99 应大幅降
- backpressure-events.jsonl 应显示 D-4 / D-5 累积大量 pause 事件（与 §2 数据吻合）

### 结论
**Claim 成立；修复已实现，待 smoke 验证**。注意：backpressure 是**降级**机制，不是性能优化——它把"硬错误"换成"主动等待"，整体 throughput 不会因此提升。

---

## §4. Admission RPC 与 scheduler 主循环耦合 ⚠️ 间接证据，本轮未直接验证

### Claim
`admit_direct_append` 进 scheduler 主循环遍历 session slot，admission RPC 频率 16+/s 时与 decode 抢调度。

### 现有间接证据
- `docs/V5_PROFILE_INVESTIGATION_ZH.md`：仅加 1Hz `/server_info` polling 就让 EXP2 errors 从 9 涨到 415（46×）；但 v6 P0 三次 baseline 不开 polling 同样得到 372/912/396——**polling 不是唯一原因，主循环负载本身就敏感**。

### 本轮未做
- 没有"admission probe 拆 fast/slow"的对照实验。需要 SGLang 较深的改动（提供 lock-free snapshot），不在 KISS 边界。

### 结论
**Claim 间接成立，本轮未直接验证**。Backpressure 实现里 admission RPC 的频率没有变（仍每个 turn 一次），只是结果会触发 sleep。如果这条 claim 成立，加 backpressure 后 admission RPC 数量大致不变但每次响应里的 `pause_ms` 会非零——**新增的 admission-events.jsonl 可在 GPU smoke 后用来直接验证此现象**。

---

## §5. P-side round-robin 不感知 D 健康 ✅ 成立

### Claim
`pd_router.py:_select_decode_index` 是裸 round-robin。任一 P 撞到 hot D 时反复失败，另一 P 完全不受影响。

### 数据（v5 baseline rerun run1）

| Worker | KVTransferError | "Decode could be dead" |
|---|---:|---:|
| prefill-0 | **367** | 361 |
| prefill-1 | **2** | 0 |

prefill-0 的请求量从 summary 看是 2225 vs prefill-1 的 2224——**请求量近乎对半，错误率差 180×**。

### 影响量化
- 失败请求集中在 P-0 → 某个 hot D 的链路上（日志中反复出现 `to 10.45.80.47:XXXXX`）
- 单 P 的"死亡链路"贡献了 **99%** 的全部 KVTransferError
- 如果 P 选择能避开"正在和 hot D 死磕"的链路，**理论上可消除单 P 故障的雪崩效应**

### 备注
- 此现象**未在 v6 P0 的 3 次 rerun 中横向验证**——只有 run1 的日志可读。需要在新 sweep 的 prefill-{0,1}.log 上重复确认，避免 N=1 嫌疑。

### 结论
**单 run 数据成立，多 run 一致性未验证**。修复方向（不在本轮）：router 选 P 时考虑 (P 当前 inflight transfer 数, 目标 D 健康度)。

---

## §6. （已撤回）Replay 端 session footprint 估算膨胀

写计划时仔细看代码后撤回——`_estimate_session_resident_tokens` 返回 full prompt，但所有需要"增量"的 call site (`replay.py:1247-1254`、`:1393-1394`、`:1490-1491`) 都已用 `target - current` 减法处理。**不是 bug**。

---

## §7. time-scale=10 把 inter-turn gap 压到 1/10 ✅ 完全成立

### 数据

```
原始 trace inter-turn gap (n=4397):
  p10=1.6s   p50=2.5s   p90=7.8s   p99=25.1s   max=261s

time-scale=10 实际 replay gap:
  p10=0.16s  p50=0.25s  p90=0.78s  p99=2.5s    max=26s
```

真实 agentic 用户/agent 在 turn 之间停 2-8 秒（思考、打字、tool call、agent reasoning）。time-scale=10 把这些窗口压到 0.16-0.78 秒——**人为消除了 D 的自然 idle 时间**，正好是 KVC 想利用的"session 短暂 idle 时 LRU 可以 evict、其他 session 可以 admit"机会。

### 测量学影响
- 所有 v3-v6 数据基于 time-scale=10
- 意味着所有"KVC 在 SWE 上输给 baseline"的结论**可能被 benchmark 放大了**
- §1 的 25% session 永久饿死现象，在 time-scale=1 下可能因为 D 有更多 drain 时间而显著缓解

### 本轮未做
- 没跑 time-scale=1 baseline。这是项目当前**最重要但缺失的验证**。
- Smoke sweep 脚本（`scripts/sweep_backpressure_smoke.sh`）E3、E4 包含了 time-scale=1 的 KVC + DP 短 trace 对比，等 GPU 时跑。

### 结论
**Claim 完全成立；time-scale=1 验证为 P0 待办**。

---

## 头条对比（同 trace、同硬件）

```
8-way DP cache-aware (TP1):
  errors=  0 | latency mean=1.426s p50=0.654s p90=3.609s
              | TTFT  mean=0.123s p50=0.093s p90=0.256s

KVC v5 2P6D (3 reruns, no polling):
  run1: errors=372 | mean=3.50s p50=1.11s p90=8.65s | TTFT mean=2.13s
  run2: errors=912 | mean=3.00s p50=0.94s p90=7.68s | TTFT mean=1.64s
  run3: errors=396 | mean=3.42s p50=1.22s p90=8.43s | TTFT mean=2.07s
```

KVC 三次 run 全输 DP，且差距远超 single-run variance：
- Latency mean：DP 优 **+110%**（KVC 平均 3.30s vs DP 1.43s）
- Latency P50：DP 优 **+65%**（KVC 平均 1.09s vs DP 0.65s）
- TTFT mean：DP 优 **+1500%**（KVC 平均 1.95s vs DP 0.12s——慢 17×！）
- Errors：DP 0 vs KVC 平均 ~560

**这是这个项目当前最严肃的事实**——所有 KVC 复杂度回报为负。

---

## 综合结论

按"是否结构性 + 影响大小"的二维分类：

| Claim | 结构性 | 影响 | 本轮验证 | 修复（KISS 内） | 修复（KISS 外） |
|---|---|---|---|---|---|
| §1 Session pin + 容量盲选 | 强 | 大（25% session 饿死） | ✅ 3 run 一致 | ❌ | capacity-aware policy + 跨 D 迁移 |
| §2 LRU 跟不上 | 强 | 大（每次 ~370 KVTransferError） | ✅ 6 D 数据 | ❌ | 分层 eviction、hot retract |
| §3 无 backpressure | 强 | 中-大（消除 32s timeout 雪崩） | ⚠️ 已实现，待 smoke | ✅ **本轮交付** | – |
| §4 admission RPC 干扰 | 弱-中 | 中 | ⚠️ 间接 | ❌ | probe / commit_evict 拆分 |
| §5 P-side 不感知 D 健康 | 中 | 中（单 P 错误率差 180×） | ✅ N=1，需 N≥3 复核 | ❌ | router P 选择带 D 健康反馈 |
| §6 estimate 膨胀 | – | – | ❌ 已撤回 | – | – |
| §7 time-scale=10 失真 | 强（测量学） | 大（可能颠覆所有 KVC vs DP 结论） | ✅ 数据明确 | ✅ 改 flag | – |

### 最关键的两个 takeaway

1. **§7 time-scale=1 是当前项目所有结论的前置依赖**——必须先做。如果 time-scale=1 下 KVC 与 DP 接近，前面所有 v3-v6 的"KVC 输得彻底"诊断都需要重新解读。
2. **§1 + §2 是双胞胎结构性问题**——session 被永久 pin 在某个 D + D 不能 evict 已满 = 大 session 永久卡死。任何不动 policy + 不动 LRU 的修复（包括本轮的 backpressure）只能让症状好看，不能消除根因。

---

## 本轮代码改动汇总（git diff 范围）

```
src/agentic_pd_hybrid/replay.py        # +结构性日志 + backpressure pause 检查 + admission 增强
src/agentic_pd_hybrid/cli.py           # +CLI flags
src/agentic_pd_hybrid/benchmark.py     # +CLI flags 透传
third_party/sglang/python/sglang/srt/managers/io_struct.py
third_party/sglang/python/sglang/srt/managers/scheduler.py
                                       # +recommended_pause_ms 字段 + hint 计算
scripts/sweep_backpressure_smoke.sh    # 4-run smoke sweep（待 GPU 跑）
scripts/analysis/analyze_backpressure_smoke.py
                                       # 配套分析器
docs/REFACTOR_PLAN_ZH.md               # 计划文档
docs/STRUCTURAL_VALIDATION_REPORT_ZH.md
                                       # 本报告
```

代码默认行为**不变**（`enable_backpressure=False`）——所有现有脚本/配置无影响。

---

## 待 GPU 时执行

```bash
bash scripts/sweep_backpressure_smoke.sh
python3 scripts/analysis/analyze_backpressure_smoke.py outputs/sweep_backpressure_smoke
```

预算：4 个 run × 30-60 min ≈ 3-4h GPU 时间。

按 §3 的预期：E2 (KVC + backpressure) 相对 E1 (KVC baseline) 应有 errors 降 70%+；P99 改善；TTFT P50 持平或略升。E3 (KVC + backpressure @ time-scale=1) vs E4 (DP @ time-scale=1) 是验证 §7 的关键对照。

如果 E2 vs E1 的 errors 没有显著下降，说明 backpressure hint 公式调得不对（`_compute_backpressure_pause_hint` 阈值可调），或 §3 实际不是雪崩主因（更可能是 §2 D-side LRU 才是）。
