Archive pre-refactor interview notes and add current deep-dives on architecture evolution, issue-derived stories, and evidence gates.
30 KiB
主题深读:证据归因幻觉 + 现行质量门禁
用途:巩固知识 + 面试准备 + 举一反三
不是:逐字讲稿、旧 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
- 故事索引 → 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 |
四轴(现行)
- 谁写报告:仅 Diagnosis Agent
- 谁保证引用真:EvidenceGuard + Redis canonical(Harness only)
- 谁保证语义不越界:SemanticGuard(无 Tool、无记忆)
- 谁决定用户看见什么: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 在系统中的位置
POST /api/chat (SSE)
→ ChatApplicationUseCase
→ Intent Router → DIAGNOSIS
→ Diagnosis ReAct Agent
只读 Tool × 3,观察的是 projection
产出 DiagnosisDraft
→ DiagnosisReleaseUseCase
EvidenceGuard →(可选 EvidenceRepair 一次)→ SemanticGuard → Release
→ 公开 content | SAFE_FALLBACK | failure
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」的差(质量视角)
纯 ReAct: Model ↔ Tools → 文本直接给用户
现行: Model ↔ ToolBoundary/projection → DiagnosisDraft
→ EvidenceGuard → SemanticGuard → Release → SSE
+ run 预算/取消 + Timeline(EVIDENCE/SEMANTIC/RELEASE 事件)
2. 现行契约:DiagnosisDraft 如何逼归因诚实
2.1 结构(代码事实)
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[]
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 关键口径(面试可直接引用思想,不必背原文):
- 唯一报告作者;内部规划,不对外输出 CoT。
- PreviousTurn 不是本 Run 证据,不能引用其 tool_call_id。
- 只有
evidence_status=EVIDENCE_FOUND|NO_EVIDENCE且带真实tool_call_id的观察才能当证据。 - NORMAL 只能引 FOUND;NEGATIVE_OBSERVATION 只能引 NO_EVIDENCE。
- NO_EVIDENCE ≠ 系统健康 / 已排除根因。
- Tool ERROR 不是证据,禁止引用。
- 证据不足:
conclusion=null,写 scope/missing_info,禁止编根因。 - 不暴露 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:
- 用
runId + toolCallId生成 key,查 Redis canonical - 找不到 →
INVOCATION_MISSING(典型:编造 id) - id 不一致 →
INVOCATION_ID_MISMATCH !isReferencableBy(runId)或 agent_result 空 →INVOCATION_NOT_REFERENCABLE
(跨 Run、未 READY、不可引用状态)analysis.kind.accepts(invocation.evidenceStatus)- NORMAL ↔ EVIDENCE_FOUND
- NEGATIVE_OBSERVATION ↔ NO_EVIDENCE
否则EVIDENCE_KIND_MISMATCH
- 按 tool 反序列化 agent_result 投影(不是让模型再读 raw 讲故事):
lookup_knowledge/query_logs/query_mysql
- 投影内
tool_call_id、evidence_status与 canonical 一致 - FOUND 必须有可展示证据条目;NO_EVIDENCE 必须空列表且 count=0
- 通过则写入
VerifiedEvidence,汇总为VerifiedEvidenceSnapshot
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(高频考点)
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):
只给:
原始 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 |
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) - 同输入有限重试;仍失败 →
semanticUnavailablefallback(有 snapshot 时仍可带已验证事实) - 不把内部异常原文甩给用户
5. Release:Draft 与公开通道切断
5.1 DiagnosisReleaseUseCase 决策序
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 边界:归因幻觉的上游防线
门禁是下游闸门;上游仍要减少「胡拼素材」。
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_knowledgequery_logsquery_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 信息量换体验 |
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 |
三个不一样(现行表述)
- 引用是 Run 级所有权问题,不是文案装饰。
- 物理与语义拆分,失败阶段可进 Trace / fallback。
- 公开通道与生成通道切断,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 现场设计题
- 给「工单退款 Agent」设计等价于
tool_call_ids+ EG 的字段。 - 若增加第四个 Tool,EG 要补哪些分支?kind/status 如何扩展?
- Knowledge Query 路径(非完整 DIAGNOSIS)如何复用「引用必须真实」而不照搬整份 SemanticGuard?
- 如何用 Timeline 事件向面试官演示一次 UNSUPPORTED 的失败阶段?
12. 原则清单(现行)
- 唯一报告作者,多个确定性关卡
- Draft 是 staging,SSE 是 production
- 引用主键 = 本 Run 的 framework tool_call_id
- NORMAL / NEGATIVE 与 FOUND / NO_EVIDENCE 强匹配
- NO_EVIDENCE 不是健康证明
- ERROR 与跨 Run id 绝不能当证据
- PreviousTurn 不是证据
- SemanticGuard 冻结 snapshot,无 Tool
- Reasoning 不是证据
- 历史 Issue 论证问题,现行代码定义答案
13. 白板默画(只画现行)
13.1 图 A · 60 秒主链
Diagnosis Agent → DiagnosisDraft
↓
EvidenceGuard (0 LLM, tool_call_id)
↓ verified snapshot
SemanticGuard (no tools)
↓ SUPPORTED?
Release → SSE or SAFE_FALLBACK
13.2 图 B · 45 秒闭包
conclusion.based_on → analysis_id → tool_call_ids
↓
Redis canonical (this runId)
↓
FOUND / NO_EVIDENCE
↓
kind must match
13.3 图 C · 30 秒历史锚点(可选)
早期发现:有 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. 自测(必须能用现行组件名回答)
- 画出 Draft 从产生到 SSE 的完整门禁序,并标出哪步 0 LLM。
NORMAL与NEGATIVE_OBSERVATION分别能绑哪种evidence_status?- EvidenceGuard 如何发现「编造 tool_call_id」?
- 为什么 conclusion 要
based_on_analysis_ids而不是直接绑 tool? - SemanticGuard 的输入有哪三样?为什么不能有 Tool?
- Evidence 失败时 Repair 最多几次?仍失败用户看到什么产品语义?
- PreviousTurn 为什么不能提供可引用的 tool_call_id?
- Reasoning 能否帮助 Draft 过 EG/SG?
- 用 20 秒说明早期归因幻觉与现行三道门的关系(动机 vs 实现)。
- 举一个「EG 通过但 SG 应 UNSUPPORTED」的例子。
17. 和旧版材料的关系
若你曾按「五段流水线 + excerpt 外键」准备:
- 保留:归因幻觉定义、物理/语义拆分、发布切断思想
- 替换:所有主路径类名、Tool 列表、verdict 枚举、引用主键、API
- 降级:Executor/Gatekeeper/Composer 仅出现在「历史动机」小节
面试默认叠词顺序
1. 现行:Agent → Draft → EG → SG → Release
2. 机制:tool_call_id、kind/status、snapshot、fallback
3. 动机:早期归因幻觉(可选一句)
4. 演进:正确性模型保留,装载进 Harness(若追问重构)