# 联合分析数据契约与治理规范

文档版本：v1.3 final
契约版本：`contract_version = 2.0`（**frozen**；序列化载荷中的取值，与文档版本独立，见第 1.2 节）
更新日期：2026-09-20
配套主文档：《Metric、Log、Trace、Profiling 联合分析研究报告（图解与案例版）》v1.6.1
适用对象：联合分析平台实现者、查询适配器开发者、诊断智能体接入方、POC 验收评审人

| 版本 | 日期 | 更新概要 |
| --- | --- | --- |
| v1.0 | 2026-09-20 | 自主报告 v1.3 拆分而来，收录字段级定义、状态取值、证据契约、查询预算、治理规则与验收指标清单 |
| v1.1 | 2026-09-20 | 终版审查收口：Evidence Contract 重构为 QueryResult（单源）与 EvidenceBundle（跨源）两层并统一为扁平字段名；新增契约版本与兼容策略（1.2 节）；修正证据等级前两级升级条件；权威源拆至字段级并消除 8.1/8.2 矛盾；限定 EXACT 在时间质量规则中的作用范围；修正 TIMEOUT 定义；时间戳改用 RFC 3339；拒答率纳入同步验收 |
| v1.2 | 2026-09-20 | **契约升至 `contract_version = 2.0`**，文档版本与契约版本解耦；关联状态由记录级下沉为关联边级（`correlations[]`）；来源标识拆为 `source_type` + `source_system` + `source_record_id` 三元组；为 `query_status`、`completeness` 增加 `UNKNOWN`、为 `evidence_level` 增加 `UNASSESSED`，使兼容策略可执行；`truncation_reason` 改为数组并增补预算维度；`hypothesis.status` 定义四值枚举；新增必填性字段表、`query_id` 与 `effective_time_range`；8.2 拆分表面冲突与真实冲突；验收统计单位改为关联边并按类型拆分 |
| v1.3 | 2026-09-20 | 冻结候选：定义关联边的 source 语义（隐含为父 Evidence）与三类 `type` 的 `target` Schema，增补不可传递复合规则；枚举兼容策略拆分为可降级枚举与结构性枚举并逐项归类；新增 `validation` 结构，闭合主报告 A.4 的可复查要求；`source_type` 增加 `CHANGE` 并定义优先级规则；清理 1.2 版本表的陈旧举例 |
| v1.3 final | 2026-09-20 | 冻结验收修订，无新增业务能力，仅消除 Schema 自身不一致：`validation.evidence_refs` 强制包内闭环；`truncation_reason`、`truncation_dimension`、`generated_by` 补入 `UNKNOWN` 使降级结果合法；`validation.conditions` 改为一律必填以消除 fallback 与必填规则的冲突；复合边状态由“按最弱环节取值”改为“不得高于最弱环节”；文字同步。**契约 `2.0` 自本版起冻结（frozen）** |

---

## 1. 定位与使用方式

### 1.1 与主报告的分工

主报告负责方法论与决策依据，本分册负责实现口径。两者的分工：

| | 主报告 | 本分册 |
| --- | --- | --- |
| 回答 | 为什么这样设计、有哪些边界和陷阱 | 字段叫什么、取什么值、怎么算 |
| 读者 | 架构评审、立项决策、跨团队沟通 | 实现者、接入方、验收人 |
| 变更节奏 | 随方法论认知更新 | 随实现细节迭代，通常更频繁 |

两份文档独立版本迭代。本分册中任何与主报告方法论冲突之处，以主报告为准，并应反向修订本分册。

**本分册给出的是字段与口径的建议基线，不是行业标准。**所有阈值均为首轮 POC 的讨论起点，冻结前须结合目标服务、数据规模、故障风险和现有基线调整。

### 1.2 契约版本与兼容策略

一旦工作台、查询适配器和诊断智能体围绕本契约开发，契约本身就必须可版本化演进。

**文档版本与契约版本相互独立。**本文档为 v1.3 final，其定义的载荷契约为 `2.0`。文档可因措辞、示例、验收阈值等原因单独修订而不改变契约；契约的 major 变化也不要求文档跟随同号。二者不得混用同一个版本号。

本次升至 `2.0` 的原因即在于此：Evidence Contract 由单层结构改为 `QueryResult` + `EvidenceBundle` 两层，关联状态由记录级下沉至关联边级，来源标识由二元组改为三元组，均属下表中的“层级调整”与“字段改名”，按本节规则必须升 major。

**每个顶层序列化载荷必须携带 `contract_version`**（语义化版本，如 `"2.0"`）。嵌套在 `EvidenceBundle` 内的 `QueryResult` 继承外层版本，不重复携带；若 `QueryResult` 独立传输（例如适配器直接返回单源结果），其自身必须携带 `contract_version`。实现方内部若另有存储结构版本，用 `schema_version` 单独标识，两者不得混用。

变更分级：

| 变更类型 | 版本位 | 说明 |
| --- | --- | --- |
| 新增可选字段 | minor | 向后兼容。消费方须忽略未知字段，不得因此报错 |
| 新增枚举取值 | minor | **条件性兼容**，见下 |
| 字段改名、删除、类型变更 | major | breaking change |
| 字段语义变更（名称不变但含义变） | major | 比改名更危险，须强制升 major |
| 必填性变更（可选转必填） | major | — |
| 层级调整（字段在结构中移位） | major | 见上文关于 `2.0` 的说明 |

> **枚举扩展的强制约束**：新增枚举取值只有在消费方遵守“**未知取值一律降级为该枚举的安全兜底值**”时才是向后兼容的。因此所有消费方实现必须对下列枚举设置默认分支，不得使用穷尽匹配。未遵守此约束的消费方，任何枚举扩展对它都是 breaking change。

**并非所有枚举都适用同一条兼容规则。**判定标准只有一条：

> 消费方在**不理解某个取值含义**的前提下，能否仍然做出安全且不误导的处理？

能，则该枚举可降级，新增取值属 minor；不能，则新增取值本身就是 breaking change。据此分为两类。

**第一类：可降级枚举。**必须定义安全兜底值，消费方必须实现默认分支。新增取值为 minor。

| 枚举 | 兜底值 | 说明 |
| --- | --- | --- |
| `query_status` | `UNKNOWN` | **与 `ERROR` 严格区分**：`ERROR` 是“查询确实失败了，原因归入其他”；`UNKNOWN` 是“无法判断这次查询处于什么状态”，典型来源是收到更高版本生产端的未知取值 |
| `data_status` | `UNKNOWN` | — |
| `completeness` | `UNKNOWN` | **消费方不得把 `UNKNOWN` 当作 `COMPLETE`** |
| `correlation_status` | `UNKNOWN` | — |
| `evidence_level` | `UNASSESSED` | 哨兵状态，**不是第六级** |
| `hypothesis.status` | `UNVERIFIED` | 未知取值按“尚未验证”处理，不得按“已验证”处理 |
| `truncation_reason` | `UNKNOWN` | 可降级：`completeness = PARTIAL` 已承载可行动信息，不理解具体原因也不会误判 |
| `truncation_dimension` | `UNKNOWN` | 同上，`QUERY_BUDGET` 已说明截断性质 |
| `validation.method` | `OTHER` | 不理解具体方法仍可如实展示，不影响 `status` 的判读 |
| `generated_by` | `UNKNOWN` | 未知来源按“血缘不明”处理，属保守方向 |

**第二类：结构性枚举。**取值决定了**其他字段如何解释**，因此不理解取值就无法安全处理。新增取值默认视为 breaking change，须升 major；除非该枚举在定义处明确写明“未知值可安全忽略”的策略。

| 枚举 | 为什么不可降级 |
| --- | --- |
| `source_type` | 决定该记录属于哪类数据、走哪条核验路径与展示逻辑 |
| `correlations[].type` | **直接决定 `target` 的 Schema**；不理解 `type` 就不知道 `target` 里的字段该如何解释 |
| `target.entity_type` | 决定 `entity_id` 指向什么 |
| `target.resource_type` | 决定 `resource_id` 指向什么 |

因此**不建议机械地给所有枚举都加 `UNKNOWN`**：给 `correlations[].type` 加兜底值毫无意义，消费方拿到未知 type 时连 `target` 都无法解析，此时正确行为是拒绝该条边并按 breaking 处理，而不是假装能理解。

兜底方向始终取保守一侧：不确定时降级，不得升级。

兼容窗口建议：major 版本发布后，生产端应在约定期限内同时支持新旧两个 major 版本，消费方完成迁移后再下线旧版本。跨 major 版本的历史诊断快照须保留其原始 `contract_version`，不得按新契约重新解释（参见主报告附录 A.4 的可复查要求）。

---

## 2. 查询结果状态契约

对应主报告 6.2 节。四个维度彼此正交，**必须分别返回，不得合并为单一枚举**。

**归属层级**（决定第 4 章的结构分层）：

| 维度 | 归属对象 | 出现位置 |
| --- | --- | --- |
| `query_status` | 一次对某个数据源的查询 | `QueryResult` |
| `data_status` | 一次对某个数据源的查询 | `QueryResult` |
| `completeness` | 一次对某个数据源的查询 | `QueryResult` |
| `correlation_status` | **一条关联边**（一条证据与某个目标之间的一条关系） | `Evidence.correlations[]` |

一次跨源诊断会产生多条 `QueryResult`、多条 `Evidence` 和更多条关联边，三者数量互不相等：一次查询可能产出多条证据也可能一条都没有；一条证据通常同时参与多条关联边。

**确定性属于关系，不属于记录。**同一条日志可以精确绑定到某个 Span（`EXACT`），而该 Span 归属哪个部署仍然只是候选（`CANDIDATE`）。给整条记录贴一个 `correlation_status` 无法表达这种情况，详见 2.4 节。

### 2.1 query_status：这次查询本身成功了吗

| 取值 | 含义 | 使用约束 |
| --- | --- | --- |
| `OK` | 查询正常完成 | — |
| `TIMEOUT` | 超出该源的超时预算 | `completeness` 置为 `PARTIAL`；若已获得结果则一并返回，若超时发生在首条结果之前则 `data_status = UNKNOWN` |
| `UNAVAILABLE` | 数据源不可达或拒绝服务 | 不得降级表达为“无记录” |
| `PERMISSION_DENIED` | 使用方对该源或该范围无权限 | **必须与 `NO_MATCH` 严格区分**，否则使用方无法判断是系统没有还是自己看不到 |
| `ERROR` | 其他查询失败 | 须附带来源侧错误标识，便于排查 |
| `UNKNOWN` | 无法判断查询状态 | 兜底值。**与 `ERROR` 区分**：`ERROR` 表示确定失败，`UNKNOWN` 表示状态不可判定。消费方不得视为 `OK` |

### 2.2 data_status：目标数据存在吗

| 取值 | 含义 | 使用约束 |
| --- | --- | --- |
| `FOUND` | 查到目标记录 | — |
| `NO_MATCH` | 查询成功且范围完整，确实无匹配记录 | 表示“查到了，没有”；仍不能推出“系统没问题” |
| `EXPIRED` | 目标已超出保留期 | **须有保留策略或保留审计作为依据**，不得由查不到推断 |
| `SAMPLED_OUT` | 目标在采样阶段被丢弃 | **仅当来源提供明确采样决策审计证据时才可填写**，否则返回 `UNKNOWN` |
| `NOT_COLLECTED` | 该字段/信号在该对象上未采集 | **仅当来源提供采集配置审计证据时才可填写**，否则返回 `UNKNOWN` |
| `UNKNOWN` | 无法区分上述情况 | 默认值。不确定时必须落在此项 |

> **硬约束**：绝大多数数据源不记录“这条我采样丢弃了”“这个字段我没采集”。`SAMPLED_OUT` 与 `NOT_COLLECTED` 若无审计证据支撑，就是带确定性外观的猜测。实现方在接入每一个数据源时，须先确认该源是否具备提供这两类证据的能力，并在适配器配置中显式声明；未声明的源一律只能返回 `UNKNOWN`。

### 2.3 completeness：拿到的是全部吗

| 取值 | 含义 |
| --- | --- |
| `COMPLETE` | 查询范围内结果完整 |
| `PARTIAL` | 因分页、预算、超时或源侧限制被截断 |
| `UNKNOWN` | 无法判断结果是否完整（兜底值） |

**消费方不得把 `UNKNOWN` 当作 `COMPLETE`**：在诊断语境下，“不知道是否完整”必须与“确认完整”分开处理，否则会退化为“没查到异常即无异常”。

`PARTIAL` 必须附带截断原因。由于一次查询可能同时触发多个限制（例如既超时又触及扫描预算），`truncation_reason` 定义为**数组**：

| 取值 | 含义 |
| --- | --- |
| `PAGE_LIMIT` | 分页上限 |
| `TIMEOUT` | 超时预算 |
| `SOURCE_LIMIT` | 源侧自身限制 |
| `QUERY_BUDGET` | 触及第 7 章定义的查询预算 |
| `UNKNOWN` | 已截断但原因不明，或消费方对未知取值的降级结果（1.2 节） |

出现 `QUERY_BUDGET` 时必须同时给出 `truncation_dimension`，取值为触顶的预算参数名：`max_time_range` / `max_entities` / `max_sources` / `max_scan_bytes` / `max_results` / `max_recursive_depth` / `UNKNOWN`；多个维度同时触顶时该字段亦为数组。

两处的 `UNKNOWN` 是 1.2 节兜底规则的**合法落点**：消费方执行降级后写回的值必须仍然符合本 Schema，否则就会出现“规范允许降级，Schema 不接受降级结果”的自相矛盾。生成端不应主动使用 `UNKNOWN`，除非确实无法判定截断原因。

这样设计是因为预算参数会随实现演进增减。把每个参数都做成一个 `truncation_reason` 取值会导致枚举频繁扩张，而 `QUERY_BUDGET` 加维度名的组合可以扩展而不破坏兼容。

### 2.4 correlation_status：这条关联关系确定吗

对应主报告 6.2、6.3 节与附录 B.1 的验收口径。

**该状态标注在关联边上，不标注在证据记录上。**一条证据同时参与多条关联，各边的确定性可以不同：

```text
证据 trace-span-S2  →  T7 / S2      （REQUEST_SPAN）      EXACT
证据 trace-span-S2  →  实例 I17      （ENTITY_RELATION）   CANDIDATE
```

若只给这条证据一个状态，`EXACT` 和 `CANDIDATE` 都是错的。因此 Schema 中采用 `correlations[]` 数组，逐边标注（见 4.3 节）。

#### 边的 source 语义

**一条边的 source 恒为其所属的父 `Evidence`，不在边内显式声明。**`target` 只允许一个明确目标。这条规则必须定死，否则实现者会在两种理解之间分裂：一种认为 source 永远是父 Evidence，另一种认为 source 可以是前一条边的 target（即把 `correlations[]` 当成链式关系包）。

由此得出一条推论：**边不可传递复合。**若要表达跨多跳的关系（例如“该日志所属实例的部署版本”），必须显式生成新的关联边，且：

> 复合边的 `status` **不得高于链路上最弱的一环**。链路最弱环节是确定性的**上限，而不是充分条件**。

具体说：`EXACT` 加 `CANDIDATE` 的上限只能是 `CANDIDATE`；即使上游各边均为 `EXACT`，仍须确认该关系具备可传递语义、各跳处于一致的事件时间与有效期区间，并由明确规则重新判定后，复合边才可标记为 `EXACT`。**不得仅以“上游边都是 `EXACT`”为由自动推导出复合边为 `EXACT`。**

#### type 与 target Schema

`type` 是结构性枚举（见 1.2 节），它**直接决定 `target` 的字段结构**：

| type | 含义 | `target` 必填字段 | 典型判定依据 |
| --- | --- | --- | --- |
| `REQUEST_SPAN` | 请求/执行关联：该记录属于某一次执行 | `trace_id` 必填；`span_id` 条件必填（关联到整条 Trace 而非具体 Span 时可省略） | Trace/Span 绑定或其他确定性执行键 |
| `ENTITY_RELATION` | 实体历史关系：该记录归属哪个逻辑实体 | `entity_type` + `entity_id` | 带有效期的历史关系 + 可信事件时间 |
| `RESOURCE_CONTEXT` | 资源上下文关联：该记录产生于哪个承载资源 | `resource_type` + `resource_id` | 进程身份、容器与主机关系 |

两个 target 枚举同样是结构性枚举：

| 枚举 | 取值 | 边界 |
| --- | --- | --- |
| `entity_type` | `SERVICE` / `DEPLOYMENT` / `INSTANCE` | 指向**逻辑实体** |
| `resource_type` | `POD` / `CONTAINER` / `HOST` / `PROCESS` | 指向**承载资源** |

`ENTITY_RELATION` 与 `RESOURCE_CONTEXT` 的分界即在于此：前者回答“属于哪个服务/部署/实例”，后者回答“跑在哪个 Pod/容器/主机/进程上”。二者不重叠，也不可互相替代。

`RESOURCE_CONTEXT` 对应 OTel Profiles 概念页区分的 resource-context correlation，使 Profile 到进程、容器、Pod、主机等**承载资源**的关联纳入同一模型，不必另设字段。注意逻辑实例 `INSTANCE` 属于 `entity_type`，不得放入 `resource_type`。

| 取值 | 含义 | 判定要求 |
| --- | --- | --- |
| `EXACT` | 确定性关联 | 依据请求上下文或唯一身份键直接命中；**不确定时必须降级，不得凑数** |
| `CANDIDATE` | 启发式候选 | 由时间窗口、实体范围、耗时特征等推出；界面与契约中必须标明为候选 |
| `AMBIGUOUS` | 存在多个候选且无法区分 | 例如误差窗口内有两个 Pod 候选、新旧身份空间无法判等 |
| `UNKNOWN` | 不具备判定依据 | 默认值 |

> **核心取舍**：对诊断系统而言，错误地声称 `EXACT` 比返回 `UNKNOWN` 危险得多。宁可不知道，也不能精确地关联错。

不同 `type` 的误判后果不同，验收时须分别统计（第 11.1 节）：`REQUEST_SPAN` 误判会把不相干的日志或样本塞进本次诊断；`ENTITY_RELATION` 误判会把证据归到错误的版本或实例上，进而误导版本对照。

### 2.5 典型组合

字段名使用第 4 章的扁平形式。注意 `correlation_status` 归属于**关联边**而非查询记录，此处并列仅为便于对照；实际载荷中它出现在 `Evidence.correlations[]` 内。

| 场景 | query_status | data_status | completeness | correlation_status |
| --- | --- | --- | --- | --- |
| 查询成功，范围完整，确实无记录 | `OK` | `NO_MATCH` | `COMPLETE` | — |
| Trace 保留审计确认已过期 | `OK` | `EXPIRED` | `COMPLETE` | — |
| 日志源超时，返回部分结果 | `TIMEOUT` | `UNKNOWN` | `PARTIAL` | — |
| 误差窗口内两个 Pod 候选 | `OK` | `FOUND` | `COMPLETE` | `AMBIGUOUS` |
| 扫描预算触顶 | `OK` | `FOUND` | `PARTIAL` | `EXACT` |
| 使用方无权限 | `PERMISSION_DENIED` | `UNKNOWN` | — | — |
| Exemplar 环形缓冲已覆盖 | `OK` | `UNKNOWN` | `COMPLETE` | `UNKNOWN` |

---

## 3. 证据等级

对应主报告 4.4 节。与 `correlation_status` 是两个不同维度：后者描述**一条关联边**的身份与关系确定性，前者描述**整组证据**的支撑程度。

| 取值 | 含义 | 上升到下一级所需的额外证据 |
| --- | --- | --- |
| `ENTITY_RELATED` | 同属一个实体 | **事件时间 + 历史生命周期与资源关系**（该实例当时运行在哪台主机、属于哪个部署） |
| `WINDOW_RELATED` | 同一时间窗口及相关资源 | **请求上下文**：Trace/Span 绑定，或其他确定性执行键 |
| `EXECUTION_RELATED` | 同一次执行 | 代码与版本差异分析 |
| `MECHANISM_HYPOTHESIS` | 形成可解释观测的机制假设 | 对照实验 |
| `MECHANISM_VERIFIED` | 控制条件下复现并验证 | — |
| `UNASSESSED` | **哨兵值，不是第六级**：尚无法评估证据等级 | 兜底值。证据包不含任何证据、或收到未知取值降级时使用 |

**每一级都不能由前一级自动推导。**输出诊断结论时必须标注当前等级；停在 `MECHANISM_HYPOTHESIS` 并说明还缺什么，优于直接输出 `MECHANISM_VERIFIED`。

注意前两级跃迁所需的证据类型不同，容易混淆：从 `ENTITY_RELATED` 到 `WINDOW_RELATED`，靠的是时间与历史资源关系；请求上下文属于再下一级的跃迁条件。在尚无请求身份时以“有 Trace 在同一窗口”为由标注 `EXECUTION_RELATED`，是本模型最常见的误用。

另有一条强制规则：

> **`evidence_level` 不得高于该证据包实际携带的证据所能支撑的等级。**

也就是说，若 `evidence[]` 为空，`evidence_level` 只能是 `UNASSESSED`。如果窗口级判断来自 K8s 历史关系、实体注册表或其他上下文事实，那么这些事实本身就是证据，必须作为条目进入 `evidence[]`，而不是以“背景信息”的名义支撑一个更高的等级。

---

## 4. 证据契约 Evidence Contract

统一的机器可读结构，人机双接口共用（主报告 7.4 节）。三条核心设计：

1. **分两层**：`QueryResult` 描述“对某个源的一次查询发生了什么”，`EvidenceBundle` 描述“这次诊断收集到的跨源证据整体”。
2. **确定性属于关系**：关联状态标注在 `Evidence.correlations[]` 的每条边上，不标注在证据记录上。
3. **事实与推断分离**：`evidence` 只承载观测到的记录，`hypothesis` 承载推断且带独立验证状态。

### 4.1 序列化约定

| 约定 | 规则 |
| --- | --- |
| 契约版本 | 顶层载荷必须带 `contract_version`；嵌套的 `QueryResult` 继承外层版本，独立传输时自身必须携带 |
| 字段命名 | **统一使用扁平字段名**（`query_status`、`data_status`、`completeness`、`correlation_status`），与第 2 章一致；不使用 `query.status` 这类嵌套形式 |
| 时间戳 | **RFC 3339**，含时区，如 `2026-09-20T10:04:00+08:00`。主报告的教学案例可继续显示短时间，实现必须用完整格式 |
| 记录标识 | 统一为 `source_type` + `source_system` + `source_record_id` 三元组（第 5 章），**不使用跨源拼接的裸字符串** |
| 空值语义 | 数组字段无内容时返回空数组，不得省略；省略表示“未检查”，空数组表示“已检查且无内容” |
| 未知字段 | 消费方必须忽略并继续处理 |
| 未知枚举值 | 消费方必须降级为该枚举的兜底值（见 1.2 节），不得升级 |

### 4.2 QueryResult：单源查询结果

```yaml
query_id: q-20260920-100501-log-01     # 指向 9.3 节审计记录中的实际查询条件
source_type: LOG
source_system: es-prod-cluster-a
query_status: OK
data_status: FOUND
completeness: COMPLETE
truncation_reason: []
effective_time_range:                   # 该源实际使用的窗口，可能因时钟偏差宽于 bundle 级 time_range
  start: "2026-09-20T10:03:30+08:00"
  end: "2026-09-20T10:06:30+08:00"
time_quality:
  clock_offset_estimate: "+120ms"
result_count: 3
```

`effective_time_range` 与 bundle 级 `time_range` 并存且可以不同。第 6 章规定“不得在扩大窗口后直接宣称精确关联”，若实际使用的窗口不落到 `QueryResult` 上，该规则事后无法审计——审计者看不到当时用的是哪个窗口。

### 4.3 EvidenceBundle：一次诊断的跨源证据包

```yaml
contract_version: "2.0"

subject:
  service_id: svc-042
  deployment_id: dep-240
  instance_id: I17

time_range:
  start: "2026-09-20T10:04:00+08:00"
  end: "2026-09-20T10:06:00+08:00"

query_results:
  - query_id: q-...-log-01
    source_type: LOG
    source_system: es-prod-cluster-a
    query_status: OK
    data_status: FOUND
    completeness: COMPLETE
  - query_id: q-...-trace-01
    source_type: TRACE
    source_system: apm-new-prod
    query_status: OK
    data_status: FOUND
    completeness: COMPLETE
  - query_id: q-...-profile-01
    source_type: PROFILE
    source_system: pyroscope-prod
    query_status: OK
    data_status: UNKNOWN
    completeness: COMPLETE

evidence:
  - source_type: LOG
    source_system: es-prod-cluster-a
    source_record_id: log-123
    correlations:
      - type: REQUEST_SPAN
        target:
          trace_id: T7
          span_id: S2
        status: EXACT
        rule_version: correlation-v3
      - type: RESOURCE_CONTEXT
        target:
          resource_type: CONTAINER
          resource_id: C17
        status: EXACT
        rule_version: entity-v7
  - source_type: TRACE
    source_system: apm-new-prod
    source_record_id: trace-span-S2
    correlations:
      - type: REQUEST_SPAN
        target:
          trace_id: T7
          span_id: S2
        status: EXACT
        rule_version: correlation-v3
      - type: ENTITY_RELATION
        target:
          entity_type: INSTANCE
          entity_id: I17
        status: CANDIDATE
        rule_version: entity-v7
        time_quality:
          time_uncertainty: "±300ms"

evidence_level: EXECUTION_RELATED

limitations: []

hypothesis:
  statement: "请求执行期间发生规则重新解析"
  status: UNVERIFIED
```

要点说明：

- `query_results` 中出现 `PROFILE` 且 `data_status = UNKNOWN`，而 `evidence` 中没有对应条目——这正是“查了但没拿到”与“拿到了证据”的区分，两者必须能分别表达。
- 每条边的 source 都是它所属的那条证据：`log-123` 的两条边分别断言“这条日志属于 T7/S2”和“这条日志产生于容器 C17”；`trace-span-S2` 的两条边分别断言“它就是 T7/S2”和“它当时归属实例 I17”。**没有任何一条边以另一条边的 target 为起点。**
- `trace-span-S2` 的两条边状态不同：请求身份是 `EXACT`，实体归属只到 `CANDIDATE`。这正是 2.4 节要解决的情况。
- 若要表达“这条日志归属哪个部署”，必须显式建一条 `log-123 → DEPLOYMENT dep-240` 的边，其状态按最弱环节取 `CANDIDATE`，不得由前两条边复合推出 `EXACT`。
- `time_quality` 出现在关联边上而非证据记录上，因为时间误差影响的是具体某条关系的判定（第 6 章）。
- `evidence_level` 与 `hypothesis` 属于整个包，描述这组证据整体支撑到了哪一级。

### 4.4 hypothesis.status 与状态迁移

`hypothesis.status` 为四值枚举：

| 取值 | 含义 | 允许的 evidence_level |
| --- | --- | --- |
| `UNVERIFIED` | 尚未做对照验证 | 最高 `MECHANISM_HYPOTHESIS` |
| `VERIFIED` | 对照验证通过 | `MECHANISM_VERIFIED` |
| `REFUTED` | 对照验证做了，假设被推翻 | **不得为 `MECHANISM_VERIFIED`**，应回落至 `EXECUTION_RELATED` |
| `INCONCLUSIVE` | 验证做了但结果不确定 | 保持 `MECHANISM_HYPOTHESIS`，并在 `limitations` 中说明缺口 |

只有 `UNVERIFIED` 与 `VERIFIED` 两个值时，对照实验失败后契约只能停在 `UNVERIFIED`，无法区分“还没验证”和“验证后被否定”——两者对下一步排查的指导完全相反。

**`REFUTED` 是有诊断价值的结论，不是失败。**它排除了一条机制假设，与 11.2 节“拒答不计为错误”属同一逻辑：`REFUTED` 不得计入错误结论分子。若不明确这一点，实现方会倾向于把被推翻的假设留在 `UNVERIFIED` 以免“显得做错了”，这恰好是该枚举要消除的行为。

状态迁移时，`hypothesis.status` 与 `evidence_level` **必须同时更新**。单独出现 `VERIFIED` 而证据等级未升，或 `REFUTED` 而等级仍为 `MECHANISM_VERIFIED`，均视为契约违例。

#### validation：验证过程与结论范围

仅有 `status` 无法满足主报告附录 A.4 的可复查要求——该节要求每份判断保存机制假设、支持与反证、缺口、**验证结果及结论范围**。若只记录 `VERIFIED`，系统可以声称已验证，却无法复现为什么已验证。因此 `hypothesis` 下增加 `validation`：

```yaml
evidence:
  # ...前述证据条目...
  - source_type: METRIC              # 验证实验本身也是证据，必须在包内
    source_system: prom-prod
    source_record_id: exp-run-01
    correlations: []

hypothesis:
  statement: "请求执行期间发生规则重新解析"
  status: VERIFIED
  validation:
    method: CONTROLLED_COMPARISON
    conditions: "同一输入、同一流量、同一资源规格；对比 v2.3 / v2.4 / 修复版"
    evidence_refs:
      - source_type: METRIC
        source_system: prom-prod
        source_record_id: exp-run-01
    result_summary: "仅 v2.4 复现，修复版恢复至基线"
    conclusion_scope: "订单服务 v2.4 优惠计算路径；未覆盖其他调用路径与其他服务"
```

**引用闭环是强制的：**

> `validation.evidence_refs` 中的每一项，必须引用**同一 `EvidenceBundle.evidence[]` 中存在的记录**。

若验证所依据的实验记录不在证据包内，就会出现“`status = VERIFIED`，但支撑它的实验记录不可访问甚至已过期”的情况，这正好抵消了 `validation` 要解决的可复查问题。因此验证实验本身必须作为证据条目进入 `evidence[]`（如上例的 `exp-run-01`），即使它没有任何关联边。

若将来确需引用包外证据，应单独定义 `external_evidence_refs` 字段，并强制携带快照、内容哈希与保留期约定；不得让 `evidence_refs` 同时承担包内引用和包外引用两种语义。该字段不在 `2.0` 范围内。

必填性规则：

| `hypothesis.status` | `validation` |
| --- | --- |
| `UNVERIFIED` | 可为空 |
| `VERIFIED` | **必填** |
| `REFUTED` | **必填** |
| `INCONCLUSIVE` | **必填** |

`validation.method` 为可降级枚举（1.2 节），兜底值 `OTHER`：`CONTROLLED_COMPARISON` / `ROLLBACK` / `FIX_VERIFICATION` / `LOAD_REPLAY` / `CONFIG_TOGGLE` / `OTHER`。

**只要 `validation` 存在，`conditions` 一律必填**，不区分 method 取值。原因是兼容性：未来 2.x 生产端若新增 `CHAOS_EXPERIMENT`，旧消费方会把它降级为 `OTHER`；若 `conditions` 仅在 `OTHER` 时必填，生产端并不知道自己会被降级，自然可能不发送该字段，于是一个声称 minor 兼容的枚举扩展经 fallback 后反而违反旧 Schema。统一必填既消除这个冲突，也让 `VERIFIED` / `REFUTED` / `INCONCLUSIVE` 三种结果都带有可复现的实验条件。

`conclusion_scope` 是其中最容易被省略、也最容易被误用的字段：**它划定结论的适用边界**。上例中验证只覆盖了优惠计算路径，不能据此断言该服务其他路径无同类问题。缺少这一字段时，一次局部验证很容易在传播中被当作全局结论。

`REFUTED` 同样必须填写 `validation`——被推翻的假设需要记录“用什么方法、在什么条件下推翻的”，否则下一次排查会重复同样的实验。

### 4.5 异常返回示例

```yaml
contract_version: "2.0"

query_results:
  - query_id: q-...-log-02
    source_type: LOG
    source_system: es-prod-cluster-a
    query_status: TIMEOUT
    data_status: UNKNOWN
    completeness: PARTIAL
    truncation_reason: [TIMEOUT, QUERY_BUDGET]
    truncation_dimension: [max_scan_bytes]

evidence: []

evidence_level: UNASSESSED

limitations:
  - "日志源超时且触及扫描预算，仅返回部分结果"
  - "时间误差窗口内存在两个 Pod 候选"
  - "Exemplar 留存窗口已过，无法反查指标样本"

hypothesis: null
```

此例中 `evidence[]` 为空，因此 `evidence_level` 只能是 `UNASSESSED`（第 3 章的等级上限规则）。若要声称已达 `WINDOW_RELATED`，支撑它的 K8s 历史关系或注册表事实必须作为证据条目出现在 `evidence[]` 中。

`limitations` 为空数组与不返回该字段含义不同：前者表示已检查且无限制，后者表示未检查。`evidence` 同理。

### 4.6 字段必填性

`QueryResult`：

| 字段 | 必填性 | 说明 |
| --- | --- | --- |
| `query_id` | 必填 | 审计与复查的锚点 |
| `source_type` / `source_system` | 必填 | — |
| `query_status` / `data_status` / `completeness` | 必填 | — |
| `truncation_reason` | 条件必填 | `completeness = PARTIAL` 时必填，否则返回空数组 |
| `truncation_dimension` | 条件必填 | `truncation_reason` 含 `QUERY_BUDGET` 时必填 |
| `effective_time_range` | 条件必填 | 与 bundle 级 `time_range` 不同时必填；相同时可省略 |
| `time_quality` | 可选 | 源端能提供时钟偏差估计时填写 |
| `result_count` | 可选 | — |
| `contract_version` | 条件必填 | 独立传输时必填；嵌套于 bundle 时省略 |

`EvidenceBundle`：

| 字段 | 必填性 | 说明 |
| --- | --- | --- |
| `contract_version` | 必填 | — |
| `subject` / `time_range` | 必填 | — |
| `query_results` | 必填 | 无查询时返回空数组 |
| `evidence` | 必填 | 无证据时返回空数组 |
| `evidence_level` | 必填 | 无证据时为 `UNASSESSED` |
| `limitations` | 必填 | 无限制时返回空数组 |
| `hypothesis` | 可选 | 未形成假设时为 `null` |

`Evidence` 条目：

| 字段 | 必填性 | 说明 |
| --- | --- | --- |
| `source_type` / `source_system` / `source_record_id` | 必填 | 三元组缺一不可 |
| `correlations` | 必填 | 无关联边时返回空数组（此时该证据只能支撑 `ENTITY_RELATED`） |
| `correlations[].type` / `target` / `status` | 必填 | `target` 的字段结构由 `type` 决定，见 2.4 节 |
| `correlations[].rule_version` | 必填 | 诊断可复现的前提 |
| `correlations[].time_quality` | 条件必填 | 该边的判定依赖时间时必填（典型为 `ENTITY_RELATION`） |

`hypothesis`：

| 字段 | 必填性 | 说明 |
| --- | --- | --- |
| `statement` / `status` | 必填 | `hypothesis` 非 `null` 时 |
| `validation` | 条件必填 | `status` 为 `VERIFIED` / `REFUTED` / `INCONCLUSIVE` 时必填 |
| `validation.method` / `result_summary` / `conclusion_scope` | 必填 | `validation` 存在时 |
| `validation.conditions` | 必填 | 不区分 method 取值，见 4.4 节的兼容性说明 |
| `validation.evidence_refs` | 必填 | 每项必须命中同一 bundle 的 `evidence[]`；无支撑证据时返回空数组，但此时 `status` 不应为 `VERIFIED` |

### 4.7 智能体输出的强制字段

诊断智能体基于上述证据包形成结论时，输出必须包含：

```text
conclusion          结论陈述
evidence_refs       引用的证据（source_type + source_system + source_record_id 三元组列表）
evidence_level      当前证据等级（第 3 章）
completeness        数据完整性汇总
counter_evidence    反证（观察到但与结论不符的证据）
missing_evidence    缺失证据及其影响
next_verification   下一步验证动作
```

`evidence_refs` 中的每一项必须能在同一证据包的 `evidence` 数组中找到对应记录；引用不存在的记录，计入主报告附录 B.1 的“智能体错误证据引用率”。

并强制区分四类陈述，不得混用：

| 类别 | 说明 | 反例 |
| --- | --- | --- |
| 事实 | 直接观测到的数据 | — |
| 关联 | 某条关联边所断言的关系，须带该边的 `status` | 把 `CANDIDATE` 的实体归属陈述为确定 |
| 假设 | 能解释观测的机制推断 | 把“同一窗口观察到 CPU 热点”写成“CPU 热点导致请求变慢” |
| 验证结论 | 经对照实验确认的机制 | 把未做对照的假设标为已验证 |

## 5. 血缘与来源字段

对应主报告附录 A.1。实体关系、别名映射和精确关联均须携带：

记录的全局引用由**三元组** `source_type` + `source_system` + `source_record_id` 承担。拆分的原因是：`log`、`trace` 这类值描述的是**数据类型**而非来源系统；企业同时运行两个日志集群时，`log` + `log-123` 并不唯一。

| 字段 | 说明 | 取值约束 |
| --- | --- | --- |
| `source_type` | 数据类型 | 封闭枚举（结构性，新增即 breaking）：`METRIC` / `LOG` / `TRACE` / `PROFILE` / `CHANGE` / `SERVICE_CATALOG` / `CMDB` / `K8S` / `RELEASE` / `CONFIG` / `RUNTIME` / `REGISTRY` |
| `source_system` | 提供该记录的具体系统实例 | **开放取值**，填企业注册的稳定系统标识（如 `es-prod-cluster-a`、`apm-new-prod`）；不是封闭枚举，新增系统不构成契约变更 |
| `source_record_id` | 在该系统中的原始记录标识 | 须可回溯查询；仅在同一 `source_system` 内保证唯一 |
| `observed_at` | 该事实被观察到的时间 | 与 `valid_from` 区分，观察时间可晚于生效时间 |
| `valid_from` | 关系生效时间 | 必填 |
| `valid_to` | 关系失效时间 | 未失效为空；**不得用当前时间填充** |
| `rule_version` | 产生该关系的规则或映射版本 | 诊断可复现的前提 |
| `confidence` | 确定性等级 | 复用 `correlation_status` 取值 |
| `generated_by` | 生成方式 | `auto_rule` / `manual_fix` / `source_direct` / `UNKNOWN`（血缘不明，亦为 1.2 节降级的合法落点） |

**关于变更事件的归类。**主报告 4.2 节把变更事件列为一级诊断维度，其来源涵盖发布、配置、扩缩容、路由流量调整、数据库与中间件变更、基础设施迁移等。这些记录必须有确定归属，否则各适配器会各自发明分类。规则如下，**按优先级从上到下**：

| 变更来源 | `source_type` |
| --- | --- |
| 应用发布、依赖升级（制品变更随发布记录） | `RELEASE` |
| 配置项变更、灰度配置调整 | `CONFIG` |
| 扩缩容、编排控制面内的路由与流量调整 | `K8S` |
| 其余变更记录：数据库与中间件变更单、基础设施迁移、网关或网格平台的独立变更单 | `CHANGE` |

即：**若变更由发布、配置或编排平台直接产生，用对应的具体类型；其余变更记录统一归入 `CHANGE`，具体领域由开放取值的 `source_system` 区分。**

这里没有采用"全部映射到已有类型、不设 `CHANGE`"的方案，原因是数据库变更单、基础设施迁移等确实在现有类型中无自然归属，强行映射到 `CMDB` 会把"资产登记"与"变更记录"混为一谈，反而制造新的语义争议。由于 `source_type` 是结构性枚举，此项必须在 `2.0` 冻结前定下——冻结后再加就是 breaking change。

关于人工修订：旧版本把 `manual` 放在来源枚举中并不合适——人工修订是**生成方式**而非数据源系统。正确表达是 `source_type = REGISTRY`（实体注册表自身）、`source_system` 填治理入口的系统标识，由 `generated_by = manual_fix` 承担“人工”语义。两个字段各司其职，不重叠。

---

## 6. 时间质量与误差模型

对应主报告附录 C.1。仅靠扩大查询窗口不足以支撑精确关联。

| 字段 | 说明 |
| --- | --- |
| `event_time` | 事件实际发生时间 |
| `observed_time` | 采集端观察到的时间（对应 OTel `ObservedTimestamp`） |
| `ingest_time` | 入库时间 |
| `clock_offset_estimate` | 该来源相对基准时钟的偏差估计 |
| `time_uncertainty` | 综合误差边界 |

判定规则建议：

需要区分两类关联，它们对时间质量的依赖完全不同：

| 关联类型 | 回答的问题 | 对时间的依赖 |
| --- | --- | --- |
| **请求/Span 身份关联** | 这条日志是不是属于这个 Span | 有请求上下文直接绑定时不依赖时间 |
| **实体关系关联** | 这个 Span 当时属于哪个部署、哪个 Pod、哪个版本 | **始终依赖时间**，因为实体关系带有效期 |

| 条件 | correlation_status 上限 |
| --- | --- |
| 有请求上下文直接绑定（仅限请求/Span 身份关联） | 可为 `EXACT`，不因时钟误差降级 |
| 无请求上下文，`time_uncertainty` 小于目标 Span 时长的约定比例 | 最高 `CANDIDATE` |
| 无请求上下文，误差窗口内存在多个候选 | `AMBIGUOUS` |
| 时间源不可信或偏差未知 | `UNKNOWN` |

误差边界必须随结果返回，不得在扩大窗口后直接宣称精确关联。

**即使请求身份关联已达 `EXACT`，涉及部署、实例生命周期和历史拓扑的实体关系仍须独立考虑时间质量**：请求上下文能证明“这条日志属于 T7/S2”，但不能证明“S2 当时跑在 dep-240 的 I17 上”——后者只能由带有效期的历史关系加上可信的事件时间共同确定。两类关联应分别标注 `correlation_status`，不得用前者的确定性覆盖后者。

---

## 7. 查询预算 Query Budget

对应主报告附录 C.1 与 7.4 节。应作为统一契约而非各适配器内部参数。

| 参数 | 说明 | 智能体额外约束 |
| --- | --- | --- |
| `max_time_range` | 单次查询的最大时间跨度 | 自主模式下应显著收紧 |
| `max_entities` | 单次查询涉及的最大实体数 | **未获对应策略授权时禁止自动横向扩展；获得目的绑定的授权后，仅允许在本预算范围内扩展** |
| `max_sources` | 单次查询涉及的最大数据源数 | — |
| `max_scan_bytes` | 各源扫描量上限 | 触顶即 `PARTIAL`，不得静默继续 |
| `max_results` | 返回记录数上限 | — |
| `timeout` | 各源超时预算 | — |
| `max_recursive_depth` | 自动下钻/展开的递归深度 | **必填**，防止一次告警触发跨四平台的大范围扫描 |

预算触顶必须体现在 `completeness` 与 `truncation_reason` 中（`QUERY_BUDGET` 加 `truncation_dimension`，见 2.3 节），不得以静默截断的方式返回看似完整的结果。

智能体的横向扩展遵循主报告 9.2 节的原则：**默认禁止，按需开通，目的绑定，范围受控**。这里的“受控”不等于“绝对禁止”——跨多应用巡检的诊断账号本就可能需要更大的横向范围，关键是该范围必须来自显式授权策略，并落在本章预算之内。

---

## 8. 权威源与冲突裁决

对应主报告 7.3 节。治理形态为**统一治理入口 + 多权威事实源**：注册表负责模型、规则版本、冲突裁决、历史快照与查询输出，不负责生产所有事实。

### 8.1 按字段划分权威

**不使用“业务归属”这类笼统说法，一律拆到字段。**笼统表述是跨文档矛盾的主要来源。

| 字段 | 权威源 | 说明 |
| --- | --- | --- |
| `service_identity` | 服务目录 | 逻辑服务身份 |
| `business_service_owner` | 服务目录 | 业务服务责任人 |
| `asset_owner` | CMDB | 资产责任人 |
| `cost_center` | CMDB | 成本中心 |
| `physical_location` | CMDB | 机房与网络位置 |
| Pod/容器/节点生命周期与有效期 | 编排控制面 | 时序信息，多数 CMDB 不保存 |
| 部署批次、版本、灰度范围 | 发布平台 | — |
| 配置项版本与生效时间 | 配置中心 | — |
| `process_identity` | 运行时 | 进程身份与启动时间 |
| `running_artifact` | 运行时 | 实际加载的制品，对“实际运行什么”强于登记信息 |

### 8.2 冲突裁决

**冲突不一定意味着数据错误。**它可能来自权威级别差异、数据新鲜度、迟到数据、配置偏差，也可能确实是真实的数据错误。裁决规则只负责给出当前应采信的值，不代表其余来源都无需修复。

需要先区分两类，处理方式完全不同。

**第一类：表面冲突。**看起来矛盾，拆到字段后自然消失。

```text
CMDB：   该实例的资产责任人属于 A 团队      （asset_owner）
运行时： 该进程实际加载 service-A 的构建产物 （running_artifact）
```

这两条根本没有冲突，只是回答了不同字段。**大量所谓的“数据不一致”属于这一类**，根因是用了“业务归属”这种笼统说法而非字段名。8.1 节把权威性拆到字段，正是为了消除这类伪冲突。

**第二类：真实冲突。**同一字段，两个来源给出不同值。

```text
service_catalog.service_identity = service-A
k8s.annotation.service_identity  = service-B
```

这才需要裁决。裁决规则按以下顺序应用：

1. **按字段确定权威源**（8.1 节表）——`service_identity` 的权威源是服务目录，服务目录胜出；K8s annotation 在此属于登记信息，对该字段不具权威性。
2. **同权威级别时按发生时间**——较新的事实优先，但须检查是否为迟到数据。
3. **仍无法裁决时置为 `AMBIGUOUS`**，不得任选其一。此项仅适用于两个**同等权威级别**来源相持不下的情形，权威级别明确时不得以 `AMBIGUOUS` 回避。
4. **无论裁决结果如何，保留冲突记录**，包含各来源取值、裁决依据与规则版本，供事后复查。

上例中虽然服务目录胜出，但 annotation 与服务目录长期不一致本身就是一个需要修复的配置问题，冲突记录是发现它的唯一途径。

禁止“谁先写谁赢”。

---

## 9. 数据新鲜度

实体映射不仅要正确，还要及时。“映射逻辑正确但事实已过时”是一类不易察觉的误诊来源。

各权威源须定义：

| 字段 | 说明 |
| --- | --- |
| `freshness_sla` | 约定的最大同步延迟 |
| `max_acceptable_lag` | 超过即触发告警并在查询结果中提示的阈值 |
| `last_updated_at` | 该源最近一次成功同步时间 |

超出 `max_acceptable_lag` 时，基于该源的关联结果应降级（`correlation_status` 不高于 `CANDIDATE`），并在 `limitations` 中说明。

---

## 10. 联合分析平台自身的可观测性

对应主报告附录 B.1。平台本身必须可观测，不能只依赖被它查询的四类系统。

| 指标 | 说明 |
| --- | --- |
| 查询成功率 | 按源、按使用方类型分别统计 |
| 查询时延 P95 / P99 | 按查询类型和负载分别统计 |
| 实体服务可用性 | 不可用时下游应按设计降级而非整体失败 |
| 映射同步延迟 | 按权威源分别统计，对应第 9 章 |
| `PARTIAL` 返回率 | 持续升高通常意味着预算设置不合理或源侧劣化 |
| `AMBIGUOUS` 比例 | 持续升高通常意味着实体模型或时间质量出现问题 |
| 错误关联数量 | 经事后核验发现的 `EXACT` 误判，应为零容忍项 |
| 查询超时率 | 按源统计 |
| 权威源数据新鲜度 | 对应 `last_updated_at` |
| 智能体错误证据引用率 | 引用了不存在或不相关证据的结论占比 |

---

## 11. POC 验收指标清单

拆为两组分别统计。**两组不得混合报告**，技术质量达标不等于运维价值实现。

### 11.1 技术质量组

| 指标 | 计算方法 | 讨论起点 |
| --- | --- | --- |
| 实体映射覆盖率 | 正确且唯一映射对象数 / 应覆盖对象数；另报流量加权 | ≥95%，关键对象单列 |
| EXACT 准确率 | 核验正确的 EXACT **关联边数** / 已核验 EXACT 关联边总数，**按 `type` 分别统计** | **任一 false positive 为阻断项**；须报样本量与统计不确定性 |
| CANDIDATE 质量 | Precision、Recall、Top-K 命中率，同样按 `type` 分别统计 | 单独统计，不与 EXACT 混合 |
| 请求上下文覆盖率 | 具备可用关联上下文的适用请求日志 / 适用请求日志 | ≥95%，不把系统日志混入分母 |
| 关联实际命中率 | 成功取得目标证据的关联尝试 / 全部合法关联尝试 | 原始与条件命中率须一起报告；Exemplar 留存作为独立变量 |
| Completeness | `COMPLETE` 返回占比 | 按源统计 |
| 查询时延 | 代表性并发与窗口下的 P95、超时率 | P95 ≤5 秒，须限定查询类型与负载 |
| 可用性 | 平台自身可用性与降级表现 | 与既有告警主路径解耦 |
| 采集与平台开销 | 应用 CPU/RSS/延迟、网络、存储、扫描量、运维工时 | **必须实测，不得引用宣传值** |

统计单位必须是**关联边**而非证据记录，且按 `type` 分开：`REQUEST_SPAN` 误判会把不相干的日志或样本塞进本次诊断，`ENTITY_RELATION` 误判会把证据归到错误的版本或实例上并误导版本对照。两者的后果、排查路径和阻断条件不同，混在一个比率里等于抹掉了 2.4 节建立的区分。

### 11.2 运维价值组

| 指标 | 说明 | 归因要求 |
| --- | --- | --- |
| MTTD | 故障发现时间 | — |
| MTTI | 定位时间中位数与 P90 | **必须同案例集对照，控制人员熟练度与案例记忆** |
| MTTR | 恢复时间 | 受修复方案影响大，不可单独归因本平台 |
| 跨平台跳转次数 | 单次排障中的平台切换次数 | 仅减少跳转不构成价值证明 |
| 人工查询次数 | 单次排障中的手工查询次数 | — |
| 错误结论率 | 错误根因结论占比 | 在已知真值且不向诊断者泄露的案例上评估 |
| 无法判断率（abstention rate） | 明确拒答占比 | 不计入“错误根因”分子，但**必须单独统计并与有效回答覆盖率同时验收**；不得通过扩大拒答比例换取表面准确率 |
| 假设推翻率 | `hypothesis.status = REFUTED` 的占比 | **不计入错误结论分子**（4.4 节）。推翻一条假设是有效的排除结论，不是诊断失败 |
| 有效回答覆盖率 | 给出明确结论（无论对错）的案例占比 | 与无法判断率互补，两者须成对报告 |

> **归因约束**：定位与恢复时间受人员熟练度、故障类型、当班经验影响很大。若未采用同案例集对照设计，改善不得归因于本平台。

---

## 12. 必测反例

实现方须对下列情形逐条执行并记录结果。用例表齐全不构成通过依据，执行记录与回归结果才是。

| 用例 | 期望表现 |
| --- | --- |
| 不同租户/环境同名服务 | 不串联、不泄露 |
| Pod 重建、IP 复用、PID 复用 | 旧数据仍关联原对象 |
| 服务改名、滚动发布、新旧版本并存 | 服务历史连续，部署与进程正确区分 |
| CMDB 与运行时冲突或迟到 | 按第 8 章裁决并保留冲突记录 |
| 日志乱序、时钟偏差、采集延迟 | 返回误差边界，不用到达顺序伪造执行顺序 |
| 消息重试、批处理、异步跨线程 | 不混淆尝试，不强拼单父链 |
| Trace 采样但日志保留 | 有 ID 无 Trace 时返回有依据的状态 |
| Exemplar 存在但 Trace 过期 | `EXPIRED`，不跳到其他 Trace |
| Exemplar 环形缓冲被覆盖 | 区分“无 Exemplar”与“有 Exemplar 但目标不可得” |
| 新旧 APM 身份空间无法判等 | `AMBIGUOUS`，不做截断或补零匹配 |
| 跨 Canonical Trace Context 切换点 | 提示关联降级，不把旧身份空间记录当 canonical 命中 |
| 使用方无权限 | `PERMISSION_DENIED`，不与无记录合并 |
| 数据源无采样审计能力 | `UNKNOWN`，不得推断为 `SAMPLED_OUT` |
| 同一条证据的两类关联状态不同 | `correlations[]` 中 `REQUEST_SPAN` 为 `EXACT`、`ENTITY_RELATION` 为 `CANDIDATE` 时可分别表达，界面与智能体输出不得取其一代表整条证据 |
| 消费方收到未知枚举值 | 按 1.2 节兜底表降级（`query_status`/`completeness`→`UNKNOWN`，`evidence_level`→`UNASSESSED`），不得报错中断，也不得升级为确定状态 |
| 证据包不含任何证据 | `evidence_level` 只能为 `UNASSESSED`，不得声称已达窗口级或更高 |
| 对照实验推翻假设 | `hypothesis.status = REFUTED`，`validation` 必填，`evidence_level` 回落至 `EXECUTION_RELATED`，且不计入错误结论 |
| 声称 `VERIFIED` 但未提供 `validation` | 拒绝该载荷为契约违例，不得接受 |
| `validation.evidence_refs` 指向包外记录 | 拒绝该载荷为契约违例；验证实验记录必须作为证据条目进入 `evidence[]` |
| `validation` 存在但缺 `conditions` | 拒绝该载荷为契约违例，不区分 method 取值 |
| 消费方降级后写回 `truncation_reason` / `generated_by` | 写回 `UNKNOWN` 必须仍通过 Schema 校验 |
| 上游各边均为 `EXACT` 的多跳复合 | 不得自动判为 `EXACT`；须确认可传递语义与时间区间一致后由明确规则重新判定 |
| 跨两跳的关联需求（日志→实例→部署） | 必须显式建边，状态按最弱环节取值；不得由两条边复合推出 `EXACT` |
| 收到未知的 `correlations[].type` 或 `source_type` | 按结构性枚举处理：拒绝该条边或该条证据并上报版本不兼容，**不得**降级为 `UNKNOWN` 后继续解析 `target` |
| 两个同等权威来源对同一字段给出不同值 | 置 `AMBIGUOUS` 并保留冲突记录；权威级别明确时不得以 `AMBIGUOUS` 回避 |
| 某源实际查询窗口宽于 bundle 级窗口 | `QueryResult.effective_time_range` 如实记录，不得仅上报 bundle 级 `time_range` |
| 智能体自主模式跨应用查询 | 无对应授权策略时拒绝或转人工确认；有目的绑定授权时仅在 Query Budget 范围内执行 |
| 递归深度触顶 | 停止下钻并返回 `PARTIAL` |
| Profile 有标记无样本、符号不全 | 显示限制，不输出虚假 Span 级归因 |
| 指标重置、单位差异、P99 跨实例聚合 | 正确处理或拒绝无效比较 |
| Collector 或实体服务不可用 | 原有监控继续运行，新增能力按设计降级 |

---

## 13. 与主报告的对应关系

| 本分册章节 | 主报告出处 |
| --- | --- |
| 第 1.2 节 契约版本 | 本分册独有，主报告不涉及 |
| 第 2 章 状态契约 | 6.2 节 |
| 第 3 章 证据等级 | 4.4 节、图 6 |
| 第 4 章 证据契约 | 6.2 节、7.4 节、附录 A.4 |
| 第 5 章 血缘字段 | 附录 A.1 |
| 第 6 章 时间质量 | 附录 C.1 |
| 第 7 章 查询预算 | 附录 C.1、7.4 节 |
| 第 8 章 权威源与冲突 | 7.3 节、2.3 节 |
| 第 9 章 数据新鲜度 | 7.3 节 |
| 第 10 章 平台自观测 | 附录 B.1 |
| 第 11 章 验收清单 | 附录 B.1 |
| 第 12 章 必测反例 | 附录 B.2 |

涉及 Canonical Trace Context 的迁移策略见主报告 6.3 节与图 9，本分册不重复。安全、权限与审计要求见主报告第 9 章。
