Files
SuperBizAgent-java/mvp/engineering/harness/Harness证据安全链-从引用真实到结论可发布.md
T

277 lines
13 KiB
Markdown
Raw 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.
# Harness 证据安全链:从引用真实到结论可发布
**更新日期**:2026-07-29
**主题**:EvidenceGuard、EvidenceRepair、SemanticGuard 与 Diagnosis Release
**术语与状态**:[CONTEXT.md](CONTEXT.md) · [Harness生命周期与状态.md](Harness生命周期与状态.md)
**前置阅读**:[Harness-Tool双视图-从原始结果到可验证证据.md](Harness-Tool双视图-从原始结果到可验证证据.md)
## 1. 证据存在,不代表结论成立
诊断报告至少可能出现三类不同错误:
1. **伪造引用**:Draft 引用了不存在、失败或属于其他 Run 的 Tool Call。
2. **引用断裂**:analysis 有证据,但 conclusion、action plan 或 recommendation 没有绑定对应 analysis。
3. **牵强推论**:引用和结构都真实,但证据并不足以支持所写根因。
例如,系统确实查到了一条支付超时日志,但报告把根因写成“数据库连接池耗尽”。此时 Tool Call 真实、日志真实、引用格式也正确,结论仍然不成立。
因此“报告是否安全”不能由一个笼统的 Verifier 回答。它至少包含两个不同命题:
```text
P1:引用的证据是否真实、属于当前 Run,并形成结构闭包?
P2:这些真实证据是否足以支持报告中的结论?
```
P1 可以由代码确定性证明;P2 是语义判断。把两者都交给模型,会让本可机械验证的身份和状态也变成概率结果;全部交给规则,则规则代码会逐渐变成另一套业务诊断引擎。
## 2. 设计目标:验证链不能产生新事实
证据安全链遵守四个约束:
- Diagnosis Agent 是唯一报告作者;
- EvidenceGuard 只做确定性验真;
- SemanticGuard 只做受限语义审查;
- Release 是唯一公开决策点。
任何 Guard 都不能调用业务 Tool、探索新证据或重写最终报告。否则验证链会变成第二条隐式 Agent 链,重新引入多 Agent 架构的问题。
```mermaid
flowchart LR
D["DiagnosisDraft<br/>唯一业务 Draft"] --> R["DiagnosisReleaseUseCase<br/>发布编排所有者"]
R --> E["EvidenceGuard<br/>确定性引用验真"]
E -->|"invalid"| X["EvidenceRepair<br/>只修结构/引用"]
X --> E2["EvidenceGuard Recheck"]
E -->|"valid"| V["VerifiedEvidenceSnapshot"]
E2 -->|"valid"| V
V --> S["SemanticGuard<br/>SUPPORTED / UNSUPPORTED"]
S -->|"SUPPORTED"| OK["发布原始或等义修复 Draft"]
E -->|"repair 失败"| F["SafeFallback"]
E2 -->|"invalid"| F
S -->|"UNSUPPORTED / unavailable"| F
```
## 3. 第一层:EvidenceGuard 证明引用真实性
`EvidenceGuard` 不调用模型。它从当前 `RunContext` 出发,对 Draft 进行两阶段检查。
### 3.1 Draft 结构与引用闭包
首先检查报告自身结构:
- Draft、analysis、analysis ID、kind 和正文是否存在;
- analysis ID 是否重复;
- 每条有结论分析是否绑定 Tool Call;
- conclusion、action plan 和 recommendation 是否绑定已存在的 analysis ID;
- limitations 是否存在并声明报告范围。
引用链的目标不是让 JSON 看起来完整,而是形成下面的闭包:
```text
Conclusion / Action / Recommendation
|
v
Analysis Item
|
v
framework tool_call_id
|
v
Canonical READY Invocation
```
只要其中一跳缺失,公开报告就不能证明其来源。
### 3.2 Canonical Invocation 验真
对每个 `tool_call_id`,Guard 使用当前 `runId` 重新生成 canonical key,然后检查:
1. ID 格式是否合法;
2. canonical record 是否存在;
3. record 内 ID 是否与引用一致;
4. record 是否属于当前 Run;
5. invocation 是否 READY;
6. analysis kind 是否接受该 evidence status;
7. Tool 类型是否受支持;
8. `agent_result` 是否能严格解析为对应 typed result;
9. typed result 中的 ID、状态、count 和集合是否自洽。
因此,即使模型猜中了另一个 Run 的 Tool Call ID,也无法通过当前 Run key 和 ownership 检查。
### 3.3 正向证据和负向观察不能混用
`AnalysisKind` 与 `EvidenceStatus` 的匹配是关键约束。
一条 READY + NO_EVIDENCE 日志结果,只能支持如下陈述:
> 在企业 A、时间窗 T、服务 S、查询条件 Q 下没有匹配事件。
它不能支持:
> 系统没有发生故障。
EvidenceGuard 会把空结果投影为带 source 和 scope 的负向观察,而不是把它提升为支持任意根因的正向证据。
## 4. VerifiedEvidenceSnapshot 为什么是必要中间产物
EvidenceGuard 验证通过后不会把 canonical raw 直接交给 SemanticGuard,而是生成 `VerifiedEvidenceSnapshot`。它保存:
- analysis ID、正文和 kind;
- 已验证证据的 source type、source、scope、timestamp、excerpt;
- Tool-specific 的有限结构化 values;
- 去重后的 verified sources。
Snapshot 的作用是形成一条新的最小信任边界:SemanticGuard 不需要访问 Redis,也不能看到 request、raw response 或内部错误。它只判断“这组已经验真的事实是否支持 Draft”。
这也避免了语义审查阶段重新解释 backend 私有格式。
## 5. 第二层:EvidenceRepair 只允许修引用
### 5.1 为什么需要 Repair
模型可能生成业务语义正确、但引用结构存在局部问题的 Draft,例如:
- conclusion 漏写 `based_on_analysis_ids`;
- analysis 引用了错误的 Tool Call ID;
- 输出字段结构不符合严格 Schema。
如果所有结构错误都直接 Fallback,会丢掉部分可修复结果。但让 Repair 自由重写又会产生更严重的问题:最终结论不再来自 Diagnosis Agent,Repair 事实上成为第二个报告作者。
### 5.2 Repair 的不变量
`EvidenceRepair` 只获得:
- 原始 query;
- 原始 Draft;
- EvidenceGuard 给出的 violations。
它没有 Tool,也不能获取新证据。修复输出必须再次严格解析,并满足:
```text
SemanticDraftView(original)
==
SemanticDraftView(repaired)
```
即用户可见的 analysis、conclusion、action plan、recommendations 和 limitations 语义不变,只允许修复引用和结构。之后必须重新执行 EvidenceGuard,不能因为“已经 Repair”就跳过验真。
当前策略只给 EvidenceRepair 一次 attempt。Repair 失败、改变语义或复检仍不通过,直接进入 `EVIDENCE_VALIDATION_FAILED` Fallback。
## 6. 第三层:SemanticGuard 判断结论支持度
EvidenceGuard 证明的是“证据是真的”,SemanticGuard 判断的是“推论是否成立”。为了不让它变成第二个 Agent,系统主动削减了它的能力:
| 约束 | 目的 |
|---|---|
| 无 Tool | 不能自行寻找新事实 |
| 无会话记忆 | 不能引入当前输入之外的信息 |
| 单轮调用 | 不形成新的 ReAct loop |
| 输入固定 | 只接收 query、Draft 和 VerifiedEvidenceSnapshot |
| 输出严格 | 只能返回 `verdict + reason` |
| 二值 verdict | 只允许 `SUPPORTED / UNSUPPORTED` |
| 无发布权限 | 不能改写或直接返回用户报告 |
技术超时、传输失败、解析失败和 Schema 非法可以按完全相同输入进行一次显式重试。仍失败时不假设“可能支持”,而是 fail closed,发布 `SEMANTIC_UNAVAILABLE` Fallback。
这里需要准确描述“确定性边界”:SemanticGuard 的语义判断仍然是概率性的;确定的是它的权限、输入、输出、成本、重试次数和失败后的发布行为。
## 7. Release 为什么必须是唯一发布入口
如果 Application、EvidenceGuard 和 SemanticGuard 都能构造公开结果,同一种失败会出现多个含义,甚至可能绕过前置校验。`DiagnosisReleaseUseCase` 集中处理以下分支:
| 输入情况 | 验证路径 | 发布结果 |
|---|---|---|
| Draft 有 conclusion | EvidenceGuard -> 可选 Repair/Recheck -> SemanticGuard | 原 Draft/等义修复 Draft,或安全 Fallback |
| Draft 无 conclusion,有已验真进展 | 只验证其中已有 Tool 引用 | `INSUFFICIENT_EVIDENCE` |
| Draft 无 conclusion,无进展但声明缺失信息 | 检查引用边界 | `MISSING_REQUIRED_CONTEXT` |
| Harness 受控停止,有已验真进展 | ProgressSnapshot | `INSUFFICIENT_EVIDENCE` |
| Draft 非法,但有已验真进展 | 丢弃非法 Draft,使用 ProgressSnapshot | `INSUFFICIENT_EVIDENCE` |
| Draft 非法且没有安全进展 | 无可发布事实 | FAILED,保持 fail closed |
有结论时,只有 `SUPPORTED` 可以发布 Draft。发布的是 Diagnosis Agent 原始 Draft,或者经证明用户可见语义相同的 Repair Draft,不让 SemanticGuard生成“更好的答案”。
## 8. `conclusion=null` 为什么不进入完整语义审查
当模型明确表示无法确认根因时,不存在需要验证的根因结论。继续调用 EvidenceRepair 或 SemanticGuard,不仅浪费 Token,还可能让验证模型反向补出一个原本不存在的结论。
因此无结论路径只做必要的引用真实性检查,然后根据安全进展决定:
```text
有 verified progress
-> INSUFFICIENT_EVIDENCE
没有 progress,但有 limitations.missing_info
-> MISSING_REQUIRED_CONTEXT
两者都没有
-> 不是合法业务结果,fail closed
```
这项设计把“没有找到根因”从模型失败中分离出来,但没有降低证据要求。
## 9. SafeFallback 不是通用错误文案
SafeFallback 是结构化、确定性的发布结果。它可以包含:
- `verified_sources`:通过当前 Run 验真的来源;
- `observed_facts`:有界、去重后的事实或负向观察;
- `limitations`:为什么不能确认根因;
- `next_steps`:下一步需要补充的信息或检查;
- `validation_issues`:稳定违规码和目标字段;
- `failure_stage`:失败发生在收集、证据还是语义阶段。
它不能包含 Prompt、原始 Draft、raw Tool payload、内部异常或模型 reasoning。Fallback 不是“把错误吞掉”,而是只发布系统已经能够证明的部分。
## 10. 为什么不采用其他方案
### 10.1 单个 Verifier Agent
优点是实现表面简单,缺点是把 ID、Run ownership、Schema 和业务支持度混在同一次模型判断中。本来可以 100% 用代码拒绝的跨 Run 引用,也会变成概率审查。
### 10.2 Guard 自动改写报告
可以提高表面成功率,但破坏唯一作者原则。Guard 为了“修好”结论,往往会引入新解释;此时必须重新验证新内容,最终形成循环。
### 10.3 只做引用存在检查
能阻止伪造 ID,却无法阻止“真实证据 + 错误根因”。这正是 SemanticGuard 存在的原因。
### 10.4 语义失败直接技术报错
丢失了已经验真的事实,也把“结论不受支持”错误表达为基础设施故障。SafeFallback 可以保留过程价值,同时拒绝发布根因。
## 11. 真实问题如何改变设计
| 问题 | 暴露的设计缺陷 | 修正 |
|---|---|---|
| 旧 Verifier 同时验引用、判断语义和改写答案 | 验证职责没有边界 | EvidenceGuard、SemanticGuard、Release 三段拆分 |
| Tool 结果面向开发者,含 raw 和重复正文 | Guard 与 Agent 没有独立事实面 | 先建立 canonical truth 和 verified snapshot |
| `NO_EVIDENCE` 被当成一般失败或正向证据 | 负向观察没有范围约束 | AnalysisKind 与 EvidenceStatus 匹配校验 |
| Repair 可能改变结论 | 修复者变成新的报告作者 | SemanticDraftView 前后等义检查 |
| 非法 Draft 使已完成 Tool 检查全部丢失 | Draft 是唯一可发布价值来源 | 只在 ProgressSnapshot 已验真时允许过程型 Fallback |
| SemanticGuard 技术失败时行为不一致 | 各层自行决定降级 | Release 统一生成 `SEMANTIC_UNAVAILABLE` |
## 12. 代价与剩余风险
1. SemanticGuard 增加一次模型调用和延迟;这是语义安全与成本之间的明确取舍。
2. Semantic verdict 不是形式化证明,仍可能误判;当前设计限制的是权限和失败影响,而不是宣称模型绝对正确。
3. EvidenceGuard 需要理解每种 Tool 的 typed result;新增 Tool 必须同步增加验证规则。
4. Repair 的语义等价由结构化视图定义,无法证明两个自然语言文本在所有解释下完全等价,因此 Repair 能力被刻意限制。
5. Canonical record TTL 到期后无法重新完成完整验真,所以公开决策必须在当前 Run 内完成。
## 13. 如何验证
| 需要证明 | 代表性验证 |
|---|---|
| 缺失、重复、未知 analysis 引用被拒绝 | `EvidenceGuardTest` |
| cross-run、非 READY、ID/status 不一致被拒绝 | `EvidenceGuardTest` + canonical fixtures |
| NO_EVIDENCE 只能形成限定负向观察 | Evidence kind focused cases |
| Repair 不得改变用户可见语义 | `DiagnosisReleaseUseCaseTest`、Repair tests |
| Semantic 非法输出只有限重试并安全降级 | `SemanticGuardTest` |
| Guard 不改写报告,Release 只发布原 Draft 或 Fallback | `DiagnosisReleaseUseCaseTest` |
| 无结论、非法 Draft、受控停止正确映射 | `DiagnosisChatExecutorTest`、Release focused cases |
| exact-run Trace 能解释每一道门禁 | live E2E Timeline |
## 14. 与前后链路的关系
证据安全链依赖 [Tool 双视图](Harness-Tool双视图-从原始结果到可验证证据.md)提供独立 canonical truth;它解决的是“哪些内容能够发布”,不负责判断 Agent 是否还应该继续取证。正常收敛由[Harness信息增益停止-让无证据诊断正常收敛.md](Harness信息增益停止-让无证据诊断正常收敛.md)负责。