docs(mvp): add harness design and progressive guides
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
# 04 验证与发布:让未经证明的结论无法越过出口
|
||||
|
||||
这一篇只回答一个问题:**Agent 写完诊断草稿以后,为什么还不能直接返回给用户?**
|
||||
|
||||
## 1. 先看一个具体问题
|
||||
|
||||
Agent 在报告中写道:
|
||||
|
||||
> 10:03 出现数据库连接池耗尽,因此支付接口大量超时。
|
||||
|
||||
它同时引用了一次日志 Tool Call。系统即使确认这个调用真实存在、属于当前 Run,而且日志中确实出现“connection timeout”,仍然不能直接发布上述结论。
|
||||
|
||||
因为这里至少有三个不同问题:
|
||||
|
||||
1. Agent 引用的 Tool Call 是不是真的?
|
||||
2. 真实日志是否足以证明“数据库连接池耗尽导致支付超时”?
|
||||
3. 验证失败后,系统最终应该向用户发布什么?
|
||||
|
||||
它们分别属于 EvidenceGuard、SemanticGuard 和 Release。
|
||||
|
||||
## 2. 草稿怎样通过发布链
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
D["DiagnosisDraft<br/>Agent 写出的草稿"] --> E["EvidenceGuard<br/>机械验证引用和归属"]
|
||||
E -->|"结构或引用可修复"| R["EvidenceRepair<br/>只修引用,不改语义"]
|
||||
R --> E
|
||||
E -->|"得到已验真证据"| S["SemanticGuard<br/>判断证据是否支持报告"]
|
||||
S -->|"SUPPORTED"| P["Release SUCCESS<br/>发布原始安全 Draft"]
|
||||
E -->|"无法验真"| F["SafeFallback"]
|
||||
S -->|"UNSUPPORTED 或不可用"| F
|
||||
F --> O["Release FALLBACK"]
|
||||
```
|
||||
|
||||
第一次阅读只需记住:
|
||||
|
||||
- **EvidenceGuard 验真**:证明引用确实来自当前 Run 的已完成 Tool 调用。
|
||||
- **SemanticGuard 验义**:判断这些真实证据是否支持报告中的结论。
|
||||
- **Release 决定出口**:只发布验证通过的原 Draft,或者发布确定性的 SafeFallback。
|
||||
|
||||
## 3. EvidenceGuard:代码能证明的事交给代码
|
||||
|
||||
EvidenceGuard 会检查:
|
||||
|
||||
- Draft 结构和 analysis ID 是否合法;
|
||||
- 所有结论分析是否形成完整引用闭包;
|
||||
- `tool_call_id` 是否属于当前 Run;
|
||||
- canonical invocation 是否为 `READY`;
|
||||
- `EvidenceStatus` 与引用类型是否一致;
|
||||
- 引用的 evidence 是否确实存在于标准化 Tool 结果中。
|
||||
|
||||
这些都是确定性问题,不需要模型判断。让模型验证自己的引用,会让同一个非确定性来源同时当作者和裁判。
|
||||
|
||||
验证通过后,EvidenceGuard 生成 `VerifiedEvidenceSnapshot`。它只包含 SemanticGuard 所需的已验真证据、来源和 scope,不包含 Tool raw response。
|
||||
|
||||
主要代码入口:`EvidenceGuard`、`EvidenceGuardResult`、`VerifiedEvidenceSnapshot`、`EvidenceViolation`。
|
||||
|
||||
## 4. EvidenceRepair:为什么允许修,又为什么只能修一次
|
||||
|
||||
有些 Draft 的业务语义可能没有问题,只是引用结构出错,例如漏写一个 analysis ID。直接丢弃会浪费一次昂贵诊断,因此系统允许一次 EvidenceRepair。
|
||||
|
||||
但 Repair 不是第二个报告作者。它必须满足:
|
||||
|
||||
```text
|
||||
修复前用户可见语义 == 修复后用户可见语义
|
||||
```
|
||||
|
||||
它只能修结构和引用,不能新增事实、改变结论或补写建议;修复后还必须重新经过 EvidenceGuard。若语义视图发生变化,Repair 立即失败。
|
||||
|
||||
只允许一次,是为了避免形成“修复失败再修复”的隐式 Agent loop,也让成本和执行路径保持可解释。
|
||||
|
||||
## 5. SemanticGuard:引用真实不等于推论成立
|
||||
|
||||
一条真实的 `connection timeout` 日志,可能来自下游网络问题,也可能只是故障结果,不能自动证明数据库连接池耗尽。
|
||||
|
||||
这类支持关系无法完全用规则判断,因此 SemanticGuard 使用一次隔离的模型调用,只接收:
|
||||
|
||||
- 用户原始问题;
|
||||
- Draft 的用户可见语义;
|
||||
- EvidenceGuard 产生的 verified snapshot。
|
||||
|
||||
它没有 Tool、没有记忆、没有 ReAct loop、不访问 Redis,也不能改写报告。输出只有 `SUPPORTED / UNSUPPORTED` 和审计原因。
|
||||
|
||||
这个设计没有追求一个看似精确的置信度分数。首版真正需要的是发布门禁,而未经校准的 0.73 并不能形成比二元判定更可靠的协议。
|
||||
|
||||
主要代码入口:`SemanticGuard`、`SemanticGuardInput`、`SemanticGuardDecision`、`GuardModelCall`。
|
||||
|
||||
## 6. Release:为什么必须只有一个出口
|
||||
|
||||
如果 Agent、Guard、Application 都能各自构造最终结果,会出现:
|
||||
|
||||
- 同一验证失败被不同层解释成不同文案;
|
||||
- 某个分支忘记经过 SemanticGuard;
|
||||
- 迟到的 Draft 绕过已经确定的取消或失败;
|
||||
- Fallback 混入未经验证的 Agent 内容。
|
||||
|
||||
`DiagnosisReleaseUseCase` 因此拥有唯一发布决策。它协调 EvidenceGuard、可选 Repair、SemanticGuard 和 SafeFallbackFactory,但自己不写新根因。
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
V{"可以安全发布正常报告吗?"}
|
||||
V -->|"引用真实且语义受支持"| OK["ReleaseOutcome.SUCCESS<br/>发布原 Draft"]
|
||||
V -->|"证据不足或验证不通过"| FB["ReleaseOutcome.FALLBACK<br/>发布 SafeFallback"]
|
||||
V -->|"无法形成安全业务结果"| ER["ReleaseOutcome.FAILED"]
|
||||
```
|
||||
|
||||
这里必须区分:
|
||||
|
||||
```text
|
||||
RunState.SUCCESS + ReleaseOutcome.FALLBACK
|
||||
```
|
||||
|
||||
这表示系统完整、安全地处理了请求,但证据不足以发布正常诊断结论。Fallback 是安全产品结果,不等于执行失败。
|
||||
|
||||
## 7. SafeFallback:不是让另一个模型重新回答
|
||||
|
||||
验证失败后再次让模型“写得保守一点”,仍然可能产生新事实。SafeFallback 因此由代码确定性构造,只允许使用:
|
||||
|
||||
- 已验真的来源和检查范围;
|
||||
- 可以安全表达的观察事实;
|
||||
- 当前证据的限制;
|
||||
- 面向补充数据的下一步建议;
|
||||
- 稳定、可公开的问题类型。
|
||||
|
||||
它不会包含 Agent 原 Draft 的未验证结论、SemanticGuard 内部原因、Provider 错误或异常栈。
|
||||
|
||||
主要代码入口:`DiagnosisReleaseUseCase`、`EvidenceRepair`、`SafeFallbackFactory`、`DiagnosisReleaseResult`。
|
||||
|
||||
## 8. Contract:为什么状态和结果必须类型化
|
||||
|
||||
发布链横跨模型 JSON、Java、Redis、数据库和 SSE。如果各层都使用自由字符串 `SUCCESS`,很快就无法区分:
|
||||
|
||||
- Tool 调用完成;
|
||||
- Tool 找到候选证据;
|
||||
- 语义审查通过;
|
||||
- Run 执行成功;
|
||||
- 最终发布正常报告。
|
||||
|
||||
Contract 包用不同类型保持这些问题正交,例如 `InvocationStatus`、`EvidenceStatus`、`SemanticVerdict`、`RunState` 和 `ReleaseOutcome`。类型多不是为了复杂,而是为了阻止一个含糊的 `status` 穿过整个系统。
|
||||
|
||||
## 9. 为什么没有采用其他方案
|
||||
|
||||
| 方案 | 没有采用的原因 |
|
||||
|---|---|
|
||||
| 只检查 Tool Call ID 存在 | 只能证明引用存在,不能证明归属、状态和内容一致 |
|
||||
| 一个 Verifier Agent 同时验引用和语义 | 混合确定性与非确定性判断,失败后难以定位责任 |
|
||||
| Guard 自动改写报告 | Guard 会变成第二个作者,并可能引入未验证事实 |
|
||||
| SemanticGuard 使用 Tool 再查证 | 会形成第二条诊断链,预算和证据归属变复杂 |
|
||||
| 验证失败直接抛技术错误 | 证据不足是正常业务结果,用户仍需要安全说明 |
|
||||
| 用置信度阈值发布 | 未校准分数不能充当可靠安全协议 |
|
||||
|
||||
## 10. 先记住这些
|
||||
|
||||
1. Draft 是候选结果,不是已发布报告。
|
||||
2. EvidenceGuard 用代码证明引用真实,SemanticGuard 判断证据是否支持语义。
|
||||
3. Repair 只能修引用,不能改变用户可见语义,而且只尝试一次。
|
||||
4. Release 是唯一出口,只发布原 Draft 或确定性 SafeFallback。
|
||||
5. `FALLBACK` 可以对应一次正常完成的 Run。
|
||||
|
||||
四篇组件导读到这里结束。需要回看整体路径时返回[组件学习地图](README.md)。
|
||||
|
||||
需要深入验证规则时,阅读[证据安全链专题](../Harness证据安全链-从引用真实到结论可发布.md);需要查所有 contract 类型时,阅读[组件全景](../Harness组件全景-职责-设计原因与边界.md)。
|
||||
Reference in New Issue
Block a user