# 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
唯一业务 Draft"] --> R["DiagnosisReleaseUseCase
发布编排所有者"] R --> E["EvidenceGuard
确定性引用验真"] E -->|"invalid"| X["EvidenceRepair
只修结构/引用"] X --> E2["EvidenceGuard Recheck"] E -->|"valid"| V["VerifiedEvidenceSnapshot"] E2 -->|"valid"| V V --> S["SemanticGuard
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)负责。