Files
SuperBizAgent-java/interview/topic-evidence-attribution-and-gates.md
T
zhuyongxin de5a5b09d9 docs(interview): refresh materials for single-agent harness narrative
Archive pre-refactor interview notes and add current deep-dives on
architecture evolution, issue-derived stories, and evidence gates.
2026-07-24 18:14:49 +08:00

740 lines
30 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.
# 主题深读:证据归因幻觉 + 现行质量门禁
**用途**:巩固知识 + 面试准备 + 举一反三
**不是**:逐字讲稿、旧 Issue 复述、过时五段流水线说明书
**材料日期**:2026-07-24
**叙事原则**:**以现行设计为主讲;早期 Issue 只说明「问题从哪来」**
**主题定位**:质量辨识度主故事——「工具调了,结论为何仍不能直接给用户」
**现行依据(面试默认口径)**
| 层级 | 路径 |
|---|---|
| 架构 | `mvp/architecture/current-mvp-architecture.md` |
| 编排 | `mvp/architecture/agent-orchestration.md` |
| 门禁 | `mvp/architecture/harness-quality-gates.md` |
| Draft 契约 | `.../harness/contract/DiagnosisDraft.java`、`AnalysisKind.java` |
| 物理验真 | `.../harness/guard/evidence/EvidenceGuard.java` |
| 语义审查 | `.../harness/guard/semantic/SemanticGuard.java`、`semantic-guard-prompt.md` |
| 发布 | `.../harness/release/DiagnosisReleaseUseCase.java`、`SafeFallbackFactory.java` |
| Agent 规则 | `src/main/resources/prompts/diagnosis-agent-prompt.md` |
**历史依据(只作起源,不代表 runtime)**
- `mvp/issues/archived/executor-evidence-attribution-hallucination.md`(2026-07-07,旧 Executor 链路)
- Phase2 Gatekeeper / `executor_evidence_v2` 归档文档
**配套**
- 演进骨架 → [architecture-evolution-deep-dive.md](architecture-evolution-deep-dive.md)
- 故事索引 → [issues-interview-stories.md](issues-interview-stories.md) §3.1
---
## 0. 怎么用 / 怎么讲
| 目标 | 用法 |
|---|---|
| 巩固 | 先背现行三道门与 `DiagnosisDraft` 引用闭包,再看历史问题为何逼出这套设计 |
| 面试 | **先讲现在怎么拦,再补一句早期怎么发现**;禁止把主链路讲成 Planner→Executor→Verifier |
| 举一反三 | 用现行组件名迁移到客服/代码/合规场景 |
**开场主线(先背这句,现行口径)**
> 当前系统里,Diagnosis Agent 是唯一报告作者,它在 ReAct 里查只读工具并写出结构化 `DiagnosisDraft`。
> 但 Draft **默认不能出门**:必须先过 **EvidenceGuard(0 LLM,验 tool_call 归属与投影)**,再过 **SemanticGuard(隔离单轮,判是否越证)**,最后由 **Release Policy** 决定公开报告还是 SAFE_FALLBACK。
> 这套门禁要防的核心失败,是早期线上已经见过的 **证据归因幻觉**:有工具调用,却把 runbook/常识写成「本 Run 已证实事实」。
**禁止的过时口径**
| 不要说 | 要说 |
|---|---|
| 我们主链路是 Planner→Executor→Gatekeeper→Verifier→Composer | 单 Diagnosis Agent + Harness 门禁 |
| Gatekeeper 验 `source_invocation_id + raw_path + excerpt` | EvidenceGuard 验 `tool_call_id` + 当前 Run canonical/projection |
| Verifier 输出 LOW_CONFID/PASS | SemanticGuard 输出 `SUPPORTED` / `UNSUPPORTED` |
| 工具有 metrics/Prometheus | 现行诊断 Tool:`lookup_knowledge` / `query_logs` / `query_mysql` |
| 引用主键是 DB `tool_invocation.id` | 框架 `tool_call_id`;完整调用在 Redis canonical |
**四轴(现行)**
1. **谁写报告**:仅 Diagnosis Agent
2. **谁保证引用真**:EvidenceGuard + Redis canonical(Harness only)
3. **谁保证语义不越界**:SemanticGuard(无 Tool、无记忆)
4. **谁决定用户看见什么**:Release Policy(Draft ≠ 公开 SSE)
**图目录**
| 图 | 位置 | 白板优先级 |
|---|---|---|
| 现行主链(质量视角) | §1 | ★★★ |
| DiagnosisDraft 引用闭包 | §2 | ★★★ |
| EvidenceGuard 校验步骤 | §3 | ★★★ |
| AnalysisKind × EvidenceStatus | §3.3 | ★★ |
| SemanticGuard 输入冻结 | §4 | ★★★ |
| Release / Repair / Fallback | §5 | ★★★ |
| 数据三层如何服务验真 | §6 | ★★ |
| 历史问题 → 现行映射(30 秒) | §8 | ★★ |
| 白板速画 | §13 | ★★★ |
---
## 1. 现行设计:质量门禁长什么样
### 1.1 在系统中的位置
```text
POST /api/chat (SSE)
→ ChatApplicationUseCase
→ Intent Router → DIAGNOSIS
→ Diagnosis ReAct Agent
只读 Tool × 3,观察的是 projection
产出 DiagnosisDraft
→ DiagnosisReleaseUseCase
EvidenceGuard →(可选 EvidenceRepair 一次)→ SemanticGuard → Release
→ 公开 content | SAFE_FALLBACK | failure
```
```mermaid
flowchart TB
Q["query + safe previous_turn"] --> A["Diagnosis Agent<br/>唯一报告作者 · ReAct"]
A <--> TB["ToolBoundary"]
TB --> Redis["Redis canonical<br/>Harness only"]
TB --> Proj["bounded agent_result"]
Proj --> A
A --> Draft["DiagnosisDraft<br/>内部制品"]
Draft --> RU["DiagnosisReleaseUseCase"]
RU --> EG["EvidenceGuard · 0 LLM"]
EG -->|invalid| RP["EvidenceRepair 最多一次"]
RP --> EG
EG -->|valid snapshot| SG["SemanticGuard · 隔离单轮"]
SG -->|SUPPORTED| Out["Release SUCCESS<br/>公开 typed report"]
SG -->|UNSUPPORTED / 不可用| FB["SAFE_FALLBACK"]
EG -->|仍 invalid| FB
```
**面试一句话**
> Agent 负责「尽量基于证据写对」;Harness 负责「写错了也不能当成功答案发出去」。
### 1.2 职责切分(现行,必背)
| 组件 | 做 | 不做 |
|---|---|---|
| **Diagnosis Agent** | 规划、调 Tool、写完整 Draft、证据不足时写 limitations | HTTP/SSE、预算、物理验真、语义终审、发布 |
| **ToolBoundary** | schema/只读/预算、写 canonical、projector、只回有界观察 | 业务推理 |
| **EvidenceGuard** | Draft 结构、analysis 闭包、`tool_call_id` 属本 Run、READY/可引用、kind↔status、投影可解析 | 调模型、改报告语义 |
| **EvidenceRepair** | 在证据失败时尝试一次结构化修复 | 无限重试、绕过验真 |
| **SemanticGuard** | 在 verified snapshot 上判断整份报告是否被支持 | Tool、记忆、Redis、改写报告、部分放行 |
| **Release** | SUPPORTED 才公开 Draft;否则固定 fallback/failure | 把未验证 Draft 流式出去 |
对应代码入口:`DiagnosisReleaseUseCase.execute(run, query, draft)`。
### 1.3 和「纯 ReAct Demo」的差(质量视角)
```text
纯 ReAct: Model ↔ Tools → 文本直接给用户
现行: Model ↔ ToolBoundary/projection → DiagnosisDraft
→ EvidenceGuard → SemanticGuard → Release → SSE
+ run 预算/取消 + Timeline(EVIDENCE/SEMANTIC/RELEASE 事件)
```
---
## 2. 现行契约:`DiagnosisDraft` 如何逼归因诚实
### 2.1 结构(代码事实)
```text
DiagnosisDraft
conclusion?
text
based_on_analysis_ids[] ← 必须指向本 Draft 内 analysis_id
analysis[]
analysis_id ← 唯一
kind: NORMAL | NEGATIVE_OBSERVATION
text
tool_call_ids[] ← 至少一个;必须是本 Run 真实 id
action_plan[]
action
based_on_analysis_ids[]
requires_human_confirmation
recommendations[]
text
based_on_analysis_ids[]
limitations ← 必填 scope(EvidenceGuard 校验)
scope
missing_info[]
```
```mermaid
flowchart TB
C["conclusion / actions / recommendations"] -->|"based_on_analysis_ids"| A["analysis_id"]
A -->|"tool_call_ids"| T["本 Run tool_call_id"]
T --> Canon["Redis canonical<br/>runId + toolCallId"]
Canon --> Status["evidence_status<br/>FOUND / NO_EVIDENCE"]
A --> Kind["kind NORMAL / NEGATIVE"]
Kind -.->|"must match"| Status
```
**设计意图(对着归因幻觉)**
| 约束 | 防什么 |
|---|---|
| 每条 analysis 必须带 `tool_call_ids` | 「凭空结论」「常识当观测」 |
| conclusion 不直接绑 tool,只绑 analysis | 结论必须落在已声明的分析链上,形成闭包 |
| `limitations` 强制存在 | 证据不足时不能装成完整结案 |
| `kind` 二分 | 负向观察与正向命中不能混用同一类证据状态 |
### 2.2 Agent Prompt 里的硬规则(现行)
`diagnosis-agent-prompt.md` 关键口径(面试可直接引用思想,不必背原文):
1. **唯一报告作者**;内部规划,不对外输出 CoT。
2. **PreviousTurn 不是本 Run 证据**,不能引用其 tool_call_id。
3. 只有 `evidence_status=EVIDENCE_FOUND|NO_EVIDENCE` 且带真实 `tool_call_id` 的观察才能当证据。
4. **NORMAL** 只能引 FOUND;**NEGATIVE_OBSERVATION** 只能引 NO_EVIDENCE。
5. **NO_EVIDENCE ≠ 系统健康 / 已排除根因**。
6. Tool **ERROR 不是证据**,禁止引用。
7. 证据不足:`conclusion=null`,写 scope/missing_info,**禁止编根因**。
8. 不暴露 raw、凭据、内部错误、hidden reasoning。
**金句**
> Prompt 负责教 Agent「怎么写才诚实」;EvidenceGuard 负责「写得不诚实就过不了」。两者缺一不可,但**安全边界在代码**。
### 2.3 和早期「分区文案」的关系(30 秒)
早期 Issue 要求 Executor 输出「已证实 / 推测 / 缺口 / 动作」分区。
现行不是同一套 JSON 字段名,但**语义被结构吸收了**:
| 早期分区意图 | 现行落点 |
|---|---|
| 已证实事实 | `analysis`(NORMAL + FOUND) |
| 负向观察 | `analysis`(NEGATIVE_OBSERVATION + NO_EVIDENCE) |
| 证据缺口 | `limitations.missing_info` + 可空 `conclusion` |
| 建议动作 | `action_plan` / `recommendations`(必须 based_on analysis) |
| 禁止常识当事实 | Guard 不认无 tool_call 的 analysis;Semantic 审越证 |
---
## 3. EvidenceGuard:现行物理验真(0 LLM)
### 3.1 它在防什么
不是「这句话好不好听」,而是:
> 报告里声明依赖的每一个 tool_call,是否真的在本 Run 发生过、状态是否可引用、投影是否自洽、analysis kind 是否与 evidence_status 匹配,以及 conclusion/action 是否只引用存在的 analysis。
### 3.2 校验流水(按代码路径讲)
`EvidenceGuard.validate(RunContext, DiagnosisDraft)` 大致两段:
**A. Draft 结构与报告闭包**
- draft / analysis 非空
- `analysis_id` 存在且唯一
- `kind`、`text` 必填
- 每条 analysis 的 `tool_call_ids` 非空
- conclusion / action_plan / recommendations:text 非空,且 `based_on_analysis_ids` 非空、id 都认识
- `limitations.scope` 必填
**B. 逐 tool_call 验真**
对每个 `tool_call_id`:
1. 用 `runId + toolCallId` 生成 key,查 **Redis canonical**
2. 找不到 → `INVOCATION_MISSING`(典型:编造 id)
3. id 不一致 → `INVOCATION_ID_MISMATCH`
4. `!isReferencableBy(runId)` 或 agent_result 空 → `INVOCATION_NOT_REFERENCABLE`
(跨 Run、未 READY、不可引用状态)
5. `analysis.kind.accepts(invocation.evidenceStatus)`
- NORMAL ↔ EVIDENCE_FOUND
- NEGATIVE_OBSERVATION ↔ NO_EVIDENCE
否则 `EVIDENCE_KIND_MISMATCH`
6. 按 tool 反序列化 **agent_result 投影**(不是让模型再读 raw 讲故事):
- `lookup_knowledge` / `query_logs` / `query_mysql`
7. 投影内 `tool_call_id`、`evidence_status` 与 canonical 一致
8. FOUND 必须有可展示证据条目;NO_EVIDENCE 必须空列表且 count=0
9. 通过则写入 `VerifiedEvidence`,汇总为 `VerifiedEvidenceSnapshot`
```mermaid
flowchart TB
Draft["DiagnosisDraft"] --> S["结构 + analysis 闭包"]
S --> Loop["foreach tool_call_id"]
Loop --> Key["key = runId + toolCallId"]
Key --> Redis["Canonical store"]
Redis -->|missing| V1["INVOCATION_MISSING"]
Redis -->|not referencable| V2["NOT_REFERENCABLE"]
Redis -->|ok| K["kind vs evidence_status"]
K -->|mismatch| V3["KIND_MISMATCH"]
K --> P["Parse projection"]
P -->|invalid| V4["PROJECTION_*"]
P --> Snap["VerifiedEvidenceSnapshot"]
```
### 3.3 `AnalysisKind` × `EvidenceStatus`(高频考点)
```text
NORMAL → 只能绑 EVIDENCE_FOUND
NEGATIVE_OBSERVATION → 只能绑 NO_EVIDENCE
ERROR → 根本不是证据(Agent Prompt + 边界)
```
**面试例子**
| Agent 想说 | 错误绑法 | Guard |
|---|---|---|
| 「CPU 告警 92%」 | 编造 tool_call_id | MISSING |
| 「未查到池耗尽日志」 | kind=NORMAL 却绑 NO_EVIDENCE | KIND_MISMATCH |
| 「已排除内存泄漏」 | 仅 NO_EVIDENCE 却写排除结论 | 物理可能过,**Semantic 应 UNSUPPORTED** |
| 引用上一轮 previous_turn 的 id | 非本 Run canonical | MISSING / NOT_REFERENCABLE |
### 3.4 为什么验的是 projection 且 canonical 在 Redis
| 设计 | 原因 |
|---|---|
| Agent 只见 projection | 有界 ACI;降低上下文里的 debug 噪声与胡拼素材 |
| Guard 读 canonical 元数据 + 校验投影 | 确认「Agent 引用的 id」对应真实调用,且投影自洽 |
| Agent 不能访问 Redis | 防止自己翻 raw 再编第二套故事 |
| MySQL `tool_invocation` 只 metadata | 长期审计 ≠ 验真主存;完整 raw 短 TTL |
**与早期 Gatekeeper 的差异(讲清楚就加分)**
| 早期 Gatekeeper | 现行 EvidenceGuard |
|---|---|
| 多角色流水线中的一环 | Harness Release 路径上的确定性步骤 |
| `source_invocation_id` + `raw_path` + `excerpt` 字符串闭合 | `tool_call_id` + Run ownership + 投影结构/状态闭合 |
| 面向 `executor_evidence_v2` claims | 面向 `DiagnosisDraft` analysis 闭包 |
| 工具集合含 metrics 等 | 现行三 Tool;投影类型 Rag/Logs/Mysql |
语义继承:**都是 0 LLM 的物理/契约验真**;协议与装载层已现代化。
### 3.5 失败码怎么用于口述
挑几个最能讲故事的 `EvidenceViolationCode`:
| Code | 一句话 |
|---|---|
| `TOOL_REFERENCE_MISSING` | analysis 根本没绑工具 |
| `INVOCATION_MISSING` | 引用了不存在的 tool_call(归因造假) |
| `INVOCATION_NOT_REFERENCABLE` | 调用存在但不可作为证据(错 Run/未就绪/空结果) |
| `EVIDENCE_KIND_MISMATCH` | 负向/正向证据用错 kind |
| `ANALYSIS_REFERENCE_UNKNOWN` | 结论引用了不存在的 analysis_id |
| `PROJECTION_INVALID` | 投影与契约不一致,不能当干净证据 |
---
## 4. SemanticGuard:现行语义保险丝
### 4.1 输入被故意冻死
`SemanticGuardInput.from(query, draft, verifiedSnapshot)`:
```text
只给:
原始 query
+ 完整 Draft 视图
+ EvidenceGuard 产出的 verified snapshot
明确没有:
Tool / 记忆 / Redis / 主 Agent 回调 / 诊断历史
```
Prompt(`semantic-guard-prompt.md`)要求:
- 查:evidence 是否支持各 analysis;analysis 是否支持 conclusion;action/recommendation 是否越界;limitations 是否如实
- **禁止**改写、纠正、摘要扩展、**部分批准**
- 只返回 `{"verdict":"SUPPORTED|UNSUPPORTED","reason":"..."}`
### 4.2 它专门接住 EvidenceGuard 接不住的归因幻觉
EvidenceGuard 通过只说明:
> 「你引用的调用是真的,投影也合法。」
仍可能:
| 漏洞 | 例子 | 谁拦 |
|---|---|---|
| 真日志推不出该根因 | 只有超时日志 → 写「确定是死锁」 | SemanticGuard |
| 负向观察说成排除 | NO_EVIDENCE → 「不可能是池耗尽」 | SemanticGuard + Agent 规则 |
| 结论超出 analysis 集合语义 | analysis 只谈 A,conclusion 谈 B | SemanticGuard |
| 建议动作无分析支撑 | 乱给变更建议 | SemanticGuard + 结构上 based_on |
```mermaid
flowchart LR
EG["EvidenceGuard<br/>物理真"] --> SG["SemanticGuard<br/>语义立"]
SG -->|SUPPORTED| R["可发布"]
SG -->|UNSUPPORTED| F["Fallback<br/>保留 observed_facts"]
```
### 4.3 为什么必须隔离、且无 Tool
| 若 SemanticGuard 能再查库 | 后果 |
|---|---|
| 自建第二证据世界 | 与主 Agent / snapshot 不一致 |
| 「审稿时补证」 | 绕过用户可见的排查过程 |
| 又变成带 Tool 的第二 Executor | 归因问题换个角色重演 |
**金句**
> SemanticGuard 是保险丝,不是第二名侦探。
### 4.4 技术失败策略(现行)
- 输入/输出字节上限(`SemanticGuardLimits`)
- 同输入有限重试;仍失败 → `semanticUnavailable` fallback(有 snapshot 时仍可带已验证事实)
- 不把内部异常原文甩给用户
---
## 5. Release:Draft 与公开通道切断
### 5.1 `DiagnosisReleaseUseCase` 决策序
```text
1) EvidenceGuard.validate
2) 若失败 → EvidenceRepair 一次 → 再 validate
3) 仍失败 → EVIDENCE_VALIDATION_FAILED fallback
4) SemanticGuard.review(query, draft, snapshot)
5) SUPPORTED → SUCCESS(公开 candidate Draft + snapshot 元数据路径)
6) UNSUPPORTED → SEMANTIC_UNSUPPORTED fallback(可带 observed_facts)
7) Semantic 技术不可用 → SEMANTIC_UNAVAILABLE fallback
```
Timeline 会记:`EVIDENCE_GUARD_INITIAL` / `RECHECK` / semantic / release 决策(普通 Trace 可回放阶段,不靠「感觉」)。
### 5.2 SAFE_FALLBACK 在防什么
不是空白 500,而是**可信的不完整**:
| Fallback 类型 | 用户侧含义(思想) |
|---|---|
| 证据校验失败 | 引用/结构没过,不能确认根因;可带 validation 问题方向 |
| 语义不支持 | 已有可验证事实,但撑不起当前根因结论 |
| 语义不可用 | 有事实,但审不过/审不了,暂不发根因 |
`SafeFallbackFactory` 会从 snapshot 抽取有界 `observed_facts` / sources(有上限),并给出 `failure_stage`、`next_steps` 等——**在不泄 Prompt/raw/内部 Draft 细节的前提下**尽量可操作。
**金句**
> 我们宁可发布「已经核实到什么、卡在哪」,也不发布「流畅但未过门禁的完整故事」。
### 5.3 PreviousTurn 与门禁的衔接
- 仅 **同 Session、最近一次 DIAGNOSIS + SUCCESS + published_result** 可进下一轮
- Fallback/失败/raw **不进** PreviousTurn
- Prompt 明确:previous_turn **不可当本 Run 证据**
防止「上一轮没过门禁的句子」在下一轮被当成已证实事实——这是归因幻觉的跨轮版本。
---
## 6. Tool 边界:归因幻觉的上游防线
门禁是下游闸门;上游仍要减少「胡拼素材」。
```text
framework tool_call_id
→ exact Run / schema / read-only / budget
→ 执行
→ Redis canonical(request/raw/agent_result/status)
→ projector → bounded agent_result
→ 只把 projection 给 Agent
→ MySQL 仅 metadata audit
```
现行 Agent 可见 Tool 固定三个:
- `lookup_knowledge`
- `query_logs`
- `query_mysql`
**与归因的关系**
| 机制 | 作用 |
|---|---|
| 投影有界 | 少把 rerank/debug 大字段留给模型拼案情 |
| 统一 evidence_status | FOUND/NO_EVIDENCE/ERROR 语义稳定,供 kind 匹配 |
| 每调必有 tool_call_id | Draft 绑定有稳定主键 |
| 禁止 Agent 见 Redis | 不能「翻完整 raw 再假装引用」 |
---
## 7. Reasoning 明确不是证据(现行安全边界)
架构硬约束:
- Provider reasoning 若存在,进独立 `agent_reasoning_audit`
- **不进**普通 SSE、Trace 正文、Evidence Snapshot、业务判断
- **不能**绕过 EvidenceGuard / SemanticGuard
- 未返回则记 unavailable,**禁止伪造**
面试若被问「你们保存思考过程吗」:
> 审计与事实分离。思考不是 tool evidence,更不能当发布依据。
---
## 8. 历史问题:只用来回答「为什么要这套现行设计」
### 8.1 早期现象(30–45 秒够)
2026-07 旧 Chat 链路(Planner/Executor/Verifier…)上:
- 多次 E2E:`lookup_* / logs / metrics` 有调用,仍大量 `LOW_CONFID`
- 答案出现工具 raw 中不存在的「精确事故事实」(OOM 次数、慢 SQL 秒数等)
- 根因命名:**证据归因幻觉**——把 runbook/常识/他服事实写成「本会话已证实」
- 曾伴随摘要失真、自证闭环(自己总结再自己绑引用)
**正确用法**
> 这段证明「只靠模型自觉 + 事后 LLM Verifier」不够,必须把物理引用做成确定性约束,并把发布权从生成模型手里拿走。
**错误用法**
> 把整场面试讲成旧五段角色和 `executor_evidence_v2` 字段细节,却说不清现在的类名与 API。
### 8.2 语义迁移表(历史 → 现行)
| 历史概念 | 现行概念 | 说明 |
|---|---|---|
| Executor 综合答案 | Diagnosis Agent 写 Draft | 仍是模型生成,但是唯一作者 |
| claim + excerpt binding | analysis + `tool_call_ids` | 主键协议变更 |
| Gatekeeper | EvidenceGuard | 仍 0 LLM;装入 Release 用例 |
| Verifier LOW_CONFID | SemanticGuard UNSUPPORTED | 隔离输入;二元 verdict |
| Composer 控表达 | Draft 结构 + Release/Fallback | 表达权在 Agent,发布权在 Harness |
| 调低阈值换 PASS | **明确不做** | 用 fallback 信息量换体验 |
```mermaid
flowchart LR
H1["早期:归因幻觉被发现"] --> H2["正确性模型:物理验真+语义审+控表达"]
H2 --> H3["ISS-014:装载到单 Agent + Harness"]
H3 --> Now["现行:Draft→EG→SG→Release"]
```
### 8.3 一句话定位两阶段
> 早期 Issue 解决的是 **「要什么正确性」**;
> 现行架构解决的是 **「正确性如何成为默认运行路径」**。
---
## 9. 和业界主流的区别(用现行组件说)
| 常见做法 | 缺口 | 本项目现行 |
|---|---|---|
| 纯 ReAct 直接吐最终答案 | 无发布闸 | Draft 默认内部,Release 才公开 |
| 答案末尾 sources 角标 | 角标可假 | `tool_call_id` 必须在本 Run canonical 可解析 |
| 单一 LLM-as-Judge | 真伪与语义混判、不可复现引用检查 | EG 代码 + SG 隔离模型 |
| 质检 Agent 再带 Tool | 第二证据世界 | SG 无 Tool,冻结 snapshot |
| 离线 RAGAS | 不挡单次错误出门 | 在线门禁 + Timeline + eval 夹具 |
| 只靠更强模型 | 无工程边界 | 与模型代际正交的 Harness |
**三个不一样(现行表述)**
1. **引用是 Run 级所有权问题**,不是文案装饰。
2. **物理与语义拆分**,失败阶段可进 Trace / fallback。
3. **公开通道与生成通道切断**,SUPPORTED 才是成功产品语义。
---
## 10. 决策环(填的是现行答案)
| # | 问题 | 现行答案 |
|---|---|---|
| 1 | 威胁? | 带 tool 外观的完整假案情进入 SSE |
| 2 | 谁强制? | EG 强制引用;SG 强制语义;Release 强制发布 |
| 3 | 数据面? | canonical / projection / metadata audit;reasoning 另表 |
| 4 | 失败用户看到? | SAFE_FALLBACK(阶段、有界事实、下一步),非假成功 |
| 5 | 如何证明? | 单元/契约测 violation;E2E release_outcome;Timeline 事件 |
| 6 | 演进? | 误杀先查投影与 schema/Repair;不给 SG 加 Tool;停止策略见 ISS-015 |
---
## 11. 面试题库(默认用现行答)
### 11.1 90 秒主叙述(推荐背这个版本)
> 故障诊断里最危险的不是完全不查工具,而是查了一点真实信号就补成完整事故——我们早期在旧链路上把它定义为证据归因幻觉。
> **现在**的做法是:唯一 Diagnosis Agent 用 ReAct 查三个只读工具,只看投影,输出结构化 DiagnosisDraft;每条分析必须绑定本 Run 的 tool_call_id。
> Draft 先过 EvidenceGuard:纯代码检查结构闭包、调用是否存在于当前 Run 的 Redis canonical、证据状态是否与 NORMAL/负向观察匹配、投影是否自洽。
> 通过后生成 verified snapshot,再交给无工具的 SemanticGuard 做整份语义是否越证的审查。
> 只有 SUPPORTED 才经 Release 进入 SSE;否则走 SAFE_FALLBACK,宁可告诉用户已核实事实和卡住的阶段,也不发未验证根因。
> 所以质量辨识度是三句话:**可调用 ≠ 可归因;可归因 ≠ 语义成立;语义成立才可发布。**
### 11.2 为什么题
| 问题 | 现行得分点 |
|---|---|
| 怎么防幻觉? | Draft 绑定 tool_call_id → EG → SG → Release |
| 为什么 EG 不用模型? | 归属与状态是确定性的;要可回归 |
| 为什么还要 SG? | 真调用推不出假根因;负向≠排除 |
| 为什么 SG 不能有 Tool? | 冻结证据集,防第二世界 |
| 引用主键为什么是 tool_call_id? | 框架协议 id;与当次调用一致;不靠「最新 DB 行」 |
| Agent 能看 raw 吗? | 不能;只看 projection;canonical Harness only |
| 证据不足怎么办? | conclusion 可空 + limitations;或 fallback;不编根因 |
| 和旧 Gatekeeper 啥关系? | 语义祖先;现装在 Harness,协议已换 |
### 11.3 对抗题
**Q:这不就是多 Agent 质检吗?**
A:不是。业务侧只有一个带 Tool 的 Diagnosis Agent。SG 是无 Tool 的隔离单轮审查,属于 Harness 控制面,不是协作同事。
**Q:你们重构掉多角色后质量是不是弱了?**
A:弱的是重复的 LLM 角色编排;强的是默认路径上的确定性 EG + 发布切断。正确性模型保留,装载点从流水线角色变成 Release 用例。
**Q:投影校验不看 raw 原文子串,会不会漏?**
A:现行 EG 强调 **调用所有权 + 状态 + 投影结构自洽 + kind 匹配**,再交给 SG 做语义。上游靠 projector 把可引用证据做成稳定结构。若追问 excerpt 级闭合,可承认协议从早期 raw_path/excerpt 演进到投影契约,并强调 **不能引用 ERROR/跨 Run/不可引用调用** 仍是硬的。
**Q:用户体验会不会总是 fallback?**
A:体验做在「信息化 fallback + 成功路径的 limitations」,不是放宽 Guard。ISS-015 继续收敛停止策略与 fallback 信息量。
### 11.4 现场设计题
1. 给「工单退款 Agent」设计等价于 `tool_call_ids` + EG 的字段。
2. 若增加第四个 Tool,EG 要补哪些分支?kind/status 如何扩展?
3. Knowledge Query 路径(非完整 DIAGNOSIS)如何复用「引用必须真实」而不照搬整份 SemanticGuard?
4. 如何用 Timeline 事件向面试官演示一次 UNSUPPORTED 的失败阶段?
---
## 12. 原则清单(现行)
1. **唯一报告作者,多个确定性关卡**
2. **Draft 是 staging,SSE 是 production**
3. **引用主键 = 本 Run 的 framework tool_call_id**
4. **NORMAL / NEGATIVE 与 FOUND / NO_EVIDENCE 强匹配**
5. **NO_EVIDENCE 不是健康证明**
6. **ERROR 与跨 Run id 绝不能当证据**
7. **PreviousTurn 不是证据**
8. **SemanticGuard 冻结 snapshot,无 Tool**
9. **Reasoning 不是证据**
10. **历史 Issue 论证问题,现行代码定义答案**
---
## 13. 白板默画(只画现行)
### 13.1 图 A · 60 秒主链
```text
Diagnosis Agent → DiagnosisDraft
↓
EvidenceGuard (0 LLM, tool_call_id)
↓ verified snapshot
SemanticGuard (no tools)
↓ SUPPORTED?
Release → SSE or SAFE_FALLBACK
```
### 13.2 图 B · 45 秒闭包
```text
conclusion.based_on → analysis_id → tool_call_ids
↓
Redis canonical (this runId)
↓
FOUND / NO_EVIDENCE
↓
kind must match
```
### 13.3 图 C · 30 秒历史锚点(可选)
```text
早期发现:有 tool 仍假案情
→ 要物理验真 + 语义审 + 发布权
→ 现装在 EG / SG / Release
```
### 13.4 红线
- 画出 Planner/Executor/Composer 当主路径
- 说 Verifier 输出 LOW_CONFID 当现行 API
- SG 带检索箭头
- Draft 直连用户
- 说 metrics Tool 仍是诊断三件套之一(现行是 knowledge/logs/mysql)
---
## 14. 复习路径(偏现行)
| 步骤 | 动作 |
|---|---|
| 1 | 读 `diagnosis-agent-prompt.md` + `DiagnosisDraft` / `AnalysisKind` |
| 2 | 通读 `EvidenceGuard.validate` 与 `EvidenceViolationCode` |
| 3 | 读 `DiagnosisReleaseUseCase` + `semantic-guard-prompt.md` |
| 4 | 对照 `harness-quality-gates.md` 默画 §13 图 A/B |
| 5 | 用 §11.1 录音;再花 20 秒提早期归因幻觉作动机 |
| 6 | 扫一眼归档 Issue 标题与现象表即可,不背旧字段 |
---
## 15. 源文件索引
### 现行(主)
| 内容 | 路径 |
|---|---|
| 架构总览 | `mvp/architecture/current-mvp-architecture.md` |
| 执行序列 | `mvp/architecture/agent-orchestration.md` |
| 门禁 | `mvp/architecture/harness-quality-gates.md` |
| Draft | `src/main/java/.../harness/contract/DiagnosisDraft.java` |
| Kind/Status | `AnalysisKind.java` / `EvidenceStatus.java` |
| EG | `.../guard/evidence/EvidenceGuard.java` |
| SG | `.../guard/semantic/SemanticGuard.java` |
| Release | `.../release/DiagnosisReleaseUseCase.java` |
| Fallback | `.../release/SafeFallbackFactory.java` |
| Agent Prompt | `src/main/resources/prompts/diagnosis-agent-prompt.md` |
| SG Prompt | `src/main/resources/prompts/semantic-guard-prompt.md` |
### 历史(辅)
| 内容 | 路径 |
|---|---|
| 归因幻觉发现 | `mvp/issues/archived/executor-evidence-attribution-hallucination.md` |
| 自证闭环笔记 | `mvp/issues/design-notes/executor-self-evidence-loop-design-note.md` |
| 旧证据契约 | `mvp/architecture/archive/2026-07-22-legacy/executor-evidence-pipeline-refactor.md` |
| 重构承接 | `mvp/issues/archived/ISS-014-...` |
| 运行质量后续 | `mvp/issues/active/ISS-015-...` |
---
## 16. 自测(必须能用现行组件名回答)
1. 画出 Draft 从产生到 SSE 的完整门禁序,并标出哪步 0 LLM。
2. `NORMAL` 与 `NEGATIVE_OBSERVATION` 分别能绑哪种 `evidence_status`?
3. EvidenceGuard 如何发现「编造 tool_call_id」?
4. 为什么 conclusion 要 `based_on_analysis_ids` 而不是直接绑 tool?
5. SemanticGuard 的输入有哪三样?为什么不能有 Tool?
6. Evidence 失败时 Repair 最多几次?仍失败用户看到什么产品语义?
7. PreviousTurn 为什么不能提供可引用的 tool_call_id?
8. Reasoning 能否帮助 Draft 过 EG/SG?
9. 用 20 秒说明早期归因幻觉与现行三道门的关系(动机 vs 实现)。
10. 举一个「EG 通过但 SG 应 UNSUPPORTED」的例子。
---
## 17. 和旧版材料的关系
若你曾按「五段流水线 + excerpt 外键」准备:
- **保留**:归因幻觉定义、物理/语义拆分、发布切断思想
- **替换**:所有主路径类名、Tool 列表、verdict 枚举、引用主键、API
- **降级**:Executor/Gatekeeper/Composer 仅出现在「历史动机」小节
**面试默认叠词顺序**
```text
1. 现行:Agent → Draft → EG → SG → Release
2. 机制:tool_call_id、kind/status、snapshot、fallback
3. 动机:早期归因幻觉(可选一句)
4. 演进:正确性模型保留,装载进 Harness(若追问重构)
```