# 2026-05-29 Multi-Agent 分层诊断分类工作总结

这份文档记录当前 `multi-agent` 分支的本地提交状态、最近一组分类相关代码改动的含义、最新 evaluation 结果与 baseline 对比，以及下一步建议。它延续 `2026-05-26-multi-agent-work-summary.zh.md` 的记录口径：代码改动、评测产物、评分结论和后续动作分开说明。

## 1. 本地待提交状态

当前 `multi-agent` 分支已经和 `origin/multi-agent` 对齐，没有未推送 commit，也没有未提交的代码文件。

本地仍有两个未跟踪的 evaluation run 目录：

- `backend/data/eval_audits/evalrun-1823e5a4738e43f48bc2050fd5007161/`
- `backend/data/eval_audits/evalrun-f2e230424b32499290c74362099b30d1/`

这两个 run 都是 2026-05-27 的失败尝试，不建议纳入下一次提交：

| Eval run | Cases | 结果 | 失败原因 |
| --- | --- | --- | --- |
| `evalrun-1823e5a4738e43f48bc2050fd5007161` | `cpu-spike`, `expensive-query-spike` | 0 pass / 0 fail / 2 invalid | `agent_execution_timeout` |
| `evalrun-f2e230424b32499290c74362099b30d1` | `cpu-spike`, `expensive-query-spike` | 0 pass / 0 fail / 2 invalid | `artifact_resolution_failed`, `agent_execution_timeout` |

因此，如果今天要补一个提交，建议只提交这份文档，不提交上述 invalid evaluation 产物。

## 2. 最近分类相关代码改动的含义

最近的分类相关代码主要集中在两个提交：

| Commit | 标题 | 主要方向 |
| --- | --- | --- |
| `8f9ca71` | 收紧分层诊断分类契约 | 把 `diagnosis-analyst` 的分类输出从提示变成 schema / judge 可检查的结构化 contract。 |
| `b06f301` | 强化分层诊断容量分类约束 | 针对 CPU capacity / nodegroup peer comparison 场景，约束 diagnosis 不要把普通 TopSQL 排名误升为主因。 |

### 2.1 分类 contract 收紧

涉及文件：

- `agentkit/config/investibook/agents/diagnosis-analyst.md`
- `agentkit/config/investibook/schemas/diagnosis-analyst-output.schema.json`
- `backend/investibook/core/eval/eval_contracts.py`
- `backend/investibook/core/eval/eval_judge.py`
- `backend/investibook/core/eval/eval_runner.py`
- `backend/tests/test_diagnosis_orchestration_config.py`
- `backend/tests/test_eval_judge.py`
- `backend/tests/test_eval_runner.py`

这部分的核心不是新增更多单条诊断规则，而是让 diagnosis 阶段必须产出稳定的中间结构：

- `signal_tags`
- `fault_classification`
- `selected_diagnosis_skills`
- `critical_evidence_hints`

`signal_tags` 现在要求使用统一字段：

- `id`
- `source`
- `component`
- `metric_or_signal`
- `direction`
- `baseline`
- `severity`
- `confidence`
- `evidence_ref`

这解决了之前分类输出格式漂移的问题。以前 analyst 可能输出 `tag`、`metric`、`comparison`、`evidence_path` 等不稳定字段；现在 schema 和测试都要求使用 canonical 字段，evaluation 才能稳定读取。

`fault_classification` 也被收紧为固定结构：

- `primary_class`
- `candidates`
- `rejected_classes`
- `classification_confidence`
- `missing_evidence_for_higher_confidence`

目前支持的粗粒度分类包括：

- `sql_plan_or_query`
- `lock_or_transaction_contention`
- `tikv_read_hotspot`
- `tidb_cpu_gc_or_memory`
- `component_restart_or_panic`
- `resource_control_or_capacity`
- `storage_or_infra_io`
- `service_unavailability_or_worker_failure`
- `unknown_insufficient_evidence`

整体含义是：分类阶段不再只是自然语言里的“我觉得像 SQL 问题”，而是变成 evaluation 能验证的结构化诊断 contract。

### 2.2 Evaluation 对分类 contract 的可观测性

涉及文件：

- `backend/investibook/core/eval/eval_contracts.py`
- `backend/investibook/core/eval/eval_judge.py`
- `backend/tests/test_eval_judge.py`

新增的 routing facts 包括：

- `diagnosis_analyst_contract_present`
- `classification_contract_valid`
- `signal_tags_contract_valid`
- `fault_classification_contract_valid`
- `selected_diagnosis_skills_present`
- `primary_fault_class_*`

这意味着后续 case yaml 可以直接把“分类是否生效”写进 evidence routing 或独立评分项里。例如：

- analyst 是否写出了合法分类 contract
- primary fault class 是否等于预期
- 是否选择了专项 diagnosis skill
- 是否把错误分类放进 `rejected_classes`

这一步非常关键，因为它让我们可以区分：

- evidence 没收齐
- signal tag 打错
- fault class 分错
- skill routing 错
- diagnosis 阶段推翻或漂移

之前这些失败都混在最终 diagnosis 分数里，现在开始可以被拆出来治理。

### 2.3 Bounded fallback 也必须遵守分类 contract

涉及文件：

- `backend/investibook/core/eval/eval_runner.py`
- `backend/tests/test_eval_runner.py`
- `agentkit/config/investibook/agents/diagnosis-orchestrator.md`

之前 `bounded_timeout_salvage` 能避免“evidence 已经采完但 analyst 超时”导致 invalid，但它写出的 fallback diagnosis 也必须遵守同一套 schema。

现在 capacity 和 expensive-query fallback 都会产出 canonical `signal_tags` 和 `fault_classification`，避免 fallback 成为一个绕过分类 contract 的特殊路径。

这保证了无论是真实 analyst 输出，还是非常窄口径的 bounded fallback，最终 evaluation 看到的结构都是一致的。

### 2.4 Capacity / nodegroup peer comparison 约束

涉及文件：

- `agentkit/config/investibook/agents/diagnosis-analyst.md`
- `agentkit/config/investibook/agents/diagnosis-orchestrator.md`
- `backend/tests/test_diagnosis_orchestration_config.py`

这部分针对 `cpu-spike-during-incident-window` 一类问题。之前 evidence 已经能看到 capacity / nodegroup 对比，但 diagnosis 容易被 TopSQL 排名吸走，最后把 SQL digest 当成主因。

现在 prompt 明确要求：

- nodegroup peer comparison 也属于 capacity evidence。
- 如果较小的 `db` TiDB nodegroup 已经 80-90% 饱和，而较大的 peer 或 `*-ac-*` nodegroup 在相近 workload 下正常，应优先分类为 `resource_control_or_capacity`。
- 如果只有多个低占比 TopSQL 贡献者，而没有单个 digest 能解释大部分 CPU，不应把排名第一的 digest 提升为 primary root cause。
- `sql_plan_or_query` 可以作为 secondary / rejected class 记录，但 primary 应该保持 capacity path。

这其实是在把“分类不能被 diagnosis 悄悄推翻”做成更明确的约束：可以推翻，但必须有强证据；没有强证据时，diagnosis 要服从分类和 peer comparison。

## 3. 最新完整 Evaluation 结果

最近一次完整 10 case pod run：

- Run ID：`evalrun-7c93a2e56a72442d8c1620759db2a141`
- Suite：`core-daily-demo @ 319095b2475f`
- Image / commit：`b06f3014fc1661986b0853df950ceb645deb58b3`
- 模型：`chat/claude-standard`
- Status：completed
- Counts：6 pass / 4 fail / 0 invalid
- 平均分：70.7
- Baseline：`baseline-evalrun-b92aea0782574c129e3a933d662adcdd`
- Baseline 平均分：61.6

与 baseline 的阶段对比：

| Stage | 最新结果 | Baseline | 变化 |
| --- | ---: | ---: | ---: |
| Workflow | 100 / 100 | 100 / 100 | 0 |
| Evidence collection | 50 / 50 | 50 / 50 | 0 |
| Critical evidence | 266 / 300 | 216 / 300 | +50 |
| Evidence routing | 50 / 50 | 50 / 50 | 0 |
| Diagnosis | 217 / 500 | 196 / 500 | +21 |
| Total | 707 / 1000 | 616 / 1000 | +91 |

结论：

- 相对 baseline，当前分支整体提升明显，平均分从 61.6 到 70.7，提升 9.1。
- 最大收益仍然来自 evidence，尤其是 critical evidence，从 216/300 到 266/300。
- Diagnosis 也有提升，但只有 +21/500，说明分类与专项诊断的收益还没有完全释放。

## 4. Case 级别对比

| Case | 最新分 | Baseline | 变化 | 主要差异 |
| --- | ---: | ---: | ---: | --- |
| `component-restart-during-incident-window` | 70 | 28 | +42 | critical evidence 从 0/30 到 30/30，但 diagnosis 仍未命中 known-bug restart chain。 |
| `expensive-query-spike-during-incident-window` | 100 | 72 | +28 | evidence 和 diagnosis 都显著改善，完整命中 SQL plan / expensive query path。 |
| `expensive-query-alert-correlation-during-incident-window` | 92 | 70 | +22 | diagnosis 从 17/50 到 42/50，能更好排除背景噪声。 |
| `cpu-spike-during-incident-window` | 50 | 30 | +20 | critical evidence 从 10/30 到 30/30，但 diagnosis 仍把主因误收敛到 SQL path。 |
| `sql-tail-latency-spike-during-incident-window` | 58 | 48 | +10 | critical evidence 从 20/30 到 30/30，但 diagnosis 没有吃到 heap / range-split internal path。 |
| `tail-latency-surge-during-incident-window` | 100 | 100 | 0 | 保持满分。 |
| `service-degradation-during-incident-window` | 75 | 75 | 0 | 持平。 |
| `panic-in-recoverable-goroutine` | 70 | 70 | 0 | evidence 满分，但 diagnosis 分数低于 5/26 阶段性结果。 |
| `system-unavailability-during-incident-window` | 44 | 54 | -10 | critical evidence 从 20/30 降到 10/30，是当前 evidence 主要缺口。 |
| `sustained-cpu-pressure-during-incident-window` | 48 | 69 | -21 | critical evidence 持平 16/30，但 diagnosis 从 35/50 降到 12/50。 |

## 5. 与 2026-05-26 阶段性结果的关系

`2026-05-26-multi-agent-work-summary.zh.md` 中的阶段性口径平均分是 74.5，而这次完整 pod run 是 70.7，低 3.8 分。

差异集中在：

- `sustained-cpu-pressure`：88 -> 48
- `sql-tail-latency-spike`：69 -> 58
- `panic-in-recoverable-goroutine`：79 -> 70
- `service-degradation`：83 -> 75

不过口径也不同：5/26 文档是多轮本地补跑后整理出的阶段性结果，这次是单轮完整 pod run，更接近日常回归真实表现。因此当前结论应该是：

- 相对 baseline，分层架构确实有明显收益。
- 相对 5/26 阶段性最好结果，还有回退，主要需要修 diagnosis 稳定性。
- evidence harness 已经比较成熟，classification / diagnosis harness 还需要继续补。

## 6. 当前分类机制是否已经生效

从最新 10 个 case 的 `diagnosis-analyst.json` 看，分类机制已经结构性生效：

- 10/10 都产出了 `signal_tags`
- 10/10 都产出了 `fault_classification`
- 10/10 都产出了非空 `selected_diagnosis_skills`

但分类质量还不稳定，典型问题包括：

- `cpu-spike` 应该走 `resource_control_or_capacity`，但容易被 SQL / TopSQL path 吸走。
- `sql-tail-latency-spike` ground truth 是 TiDB internal range-split heap retention，但容易被误分类为普通 `sql_plan_or_query`。
- `sustained-cpu-pressure` 和 `system-unavailability` 仍存在 critical evidence 或 diagnosis 传递不足。

因此，当前状态可以概括为：

```text
分类字段已经有了
分类输出能被 evaluation 读取了
但分类还没有稳定约束最终 diagnosis
```

## 7. 这次提交的建议内容

如果现在要提交，建议提交：

- `docs/plans/2026-05-29-multi-agent-classification-work-summary.zh.md`

不建议提交：

- `backend/data/eval_audits/evalrun-1823e5a4738e43f48bc2050fd5007161/`
- `backend/data/eval_audits/evalrun-f2e230424b32499290c74362099b30d1/`

原因是这两个 run 是失败尝试，且已经被后续完整 pod run 与之前已提交的有效 eval artifacts 覆盖。继续把它们放进仓库只会制造噪声。

## 8. 下一步建议

短期建议按这个顺序推进：

1. 先把分类 contract 的 evaluation 规则接到 case yaml 中，让分类正确性不再只靠人工读 `diagnosis-analyst.json`。
2. 优先修 `cpu-spike`：让 `resource_control_or_capacity` 的分类和 diagnosis 保持一致，避免被 TopSQL 排名劫持。
3. 修 `sustained-cpu-pressure`：这是当前相对 baseline 最大回退点，优先看 diagnosis 为什么从 35/50 掉到 12/50。
4. 修 `system-unavailability`：这是当前 critical evidence 唯一明显没满的 case。
5. 为 LogStats 设计日志聚合索引接入方案，把 Loki 宽扫降级为精确补样，减少 evidence-agent 在日志阶段的耗时和不稳定。
6. 增加 phase-level telemetry，记录 orchestrator、evidence-agent、collector、diagnosis-analyst 各自耗时、token、工具调用和产物状态，方便定位“时间花在哪里”。

## 9. 验证状态

本次文档提交没有改动代码，因此没有重新跑测试。

最近相关代码提交中已有测试覆盖：

- `backend/tests/test_diagnosis_orchestration_config.py`
- `backend/tests/test_eval_judge.py`
- `backend/tests/test_eval_runner.py`
- `backend/tests/test_collect_evidence_tikv_thread_cpu.py`

最新完整 pod run 的核心结论是：

| 指标 | 结果 |
| --- | ---: |
| 平均分 | 70.7 |
| Baseline 平均分 | 61.6 |
| 平均分提升 | +9.1 |
| Pass / Fail / Invalid | 6 / 4 / 0 |
| Critical evidence | 266 / 300 |
| Diagnosis | 217 / 500 |

