diff --git a/mvp/engineering/harness/Harness LLM Judge 设计笔记-从不可信判定到可信裁决.md b/mvp/engineering/harness/Harness LLM Judge 设计笔记-从不可信判定到可信裁决.md new file mode 100644 index 0000000..38f2762 --- /dev/null +++ b/mvp/engineering/harness/Harness LLM Judge 设计笔记-从不可信判定到可信裁决.md @@ -0,0 +1,140 @@ +# Harness LLM Judge 设计笔记:从不可信判定到可信裁决 + +**更新日期**:2026-08-04 +**主题**:SemanticGuard + EvidenceRepair + GuardModelCall = LLM-as-a-judge 模式在证据安全链的完整落地(面试问答版) +**代码位置**:`src/main/java/com/superbiz/agent/harness/guard/semantic/` + `src/main/java/com/superbiz/agent/harness/release/EvidenceRepair.java` + +## 1. 定位:三个角色 + +```text +SemanticGuard → 典型 LLM judge:判「结论是否被已验证证据支持」,输出 verdict + reason +EvidenceRepair → judge 的修复延伸(rewriter):验真失败后「只修引用、不修结论」 +GuardModelCall → 受控 LLM 调用底座:judge 类调用的基础设施(共用) +``` + +## 2. 面试五段式回答稿(完整叙事) + +### ① 动机(先讲问题,不报组件名) + +> 我们的证据安全链里有一道「机械验真」——检查模型引用的每条证据是不是真实来自工具结果,这个用规则就能做。但光验真不够:模型可能引用真实的证据,结论却是「站不住」的——比如证据只支持 A 场景,它却拿去支撑 B 结论。这个「结论被没被证据支持」是**语义判断**,规则引擎做不了,必须靠模型。所以我们需要一个「裁判模型」来判——但裁判模型本身是不可信的,它可能乱判、可能输出奇怪的形状、可能跑很久。所以核心问题是:**怎么让一个不可信的模型做可信的判定**。 + +### ② 决策(方案 + 放弃了什么) + +> 我的方案是:用**隔离的轻量判定模型**——单轮、无工具、输出被强约束,跟主 Agent 的循环完全分离。这里放弃了两条路:第一,让主 Agent 自己判——不行,它已经写了自己的结论,有偏向;第二,纯规则判——语义判断规则做不到。同时有个关键决策:**判读的输入是「视图」不是原始内容**——裁判只看到用户将看到的内容和已验证证据,看不到内部 id 这些实现细节,防止信息污染影响裁判的客观性。 + +### ③ 实现(关键机制) + +> 三个关键机制: +> **输入视图化**:把 draft 投影成「用户可见视图」再交给裁判,剥离内部引用 id; +> **输出硬校验**:裁判的输出必须是恰好两个字段——verdict 和 reason,verdict 必须是合法枚举,reason 不能为空。多一个字段都不接受——我们不信任模型输出的形状,只信它在一个极小的空间里做选择; +> **受控调用**:裁判跑在独立线程、有硬超时、Run 取消能强杀它、它的输入输出都计入预算和 Token 账本——裁判的花费不是无底洞,它也是 Run 的一部分。 + +### ④ 边界(诚实说不做什么) + +> 裁判不判「内容对不对」——那是事实问题,由证据链负责;裁判不自己调工具,单轮无工具;裁判有硬截止线,超时就放弃判定;裁判失败走降级,**不阻塞主结论的发布路径**——我们宁可没有裁决,也不让裁决失败卡死整个流程。 + +### ⑤ 30 秒话术 + +> "LLM judge 的完整设计:**动机**是结论的支持度是语义判断、规则做不了,但裁判模型不可信,所以核心是让不可信的模型做可信的判定。**方案**是隔离的轻量判定模型——单轮、无工具、强约束输出。三个关键机制:输入视图化(裁判只看用户可见内容,防信息污染)、输出硬校验(恰好 {verdict, reason} 两字段,多一个都不接受)、受控调用(独立线程、硬超时、取消强杀、计预算记账)。**边界**:裁判不判事实、不调工具、超时即放弃、失败走降级不阻塞主路径。总结一句话——judge 不是追加一个模型调用,而是把『不可信判定』关进笼子里:限定输入、锁死输出、受控运行、失败降级。" + +## 3. 追问应对大全 + +### Q1:为什么 judge 不判事实?(最容易混的边界) + +```text +分工:事实由证据链保证,judge 只判「支持关系」 + 事实真伪 → 证据来自真实工具结果 + EvidenceGuard 验引用真实(根在数据源) + 支持关系 → SemanticGuard 判结论与证据的逻辑/相关性 + +judge 判不了事实的三个原因: + ① 没有事实源——它只看「视图 + 已验证证据」,不能查库,判事实只能猜 + ② 事实真伪需要权威源复核(真实值在哪),judge 拿不到 + ③ 如果 judge 判事实,它成了第二个事实来源——两个来源可能打架 + +例子 1(judge 能判的——判支持不是判真伪): + 证据:mysql 返回 count(*)=1000;结论:「user 表有 2000 条」 + EvidenceGuard 验引用真实 → 通过;SemanticGuard → UNSUPPORTED(数字不一致) + 注意:judge 不知道真实值是多少,它只发现「结论与证据不一致」 + +例子 2(支持关系成立,但事实未必对): + 证据:慢查询日志显示 DB 全表扫描;结论:「延迟由 DB 全表扫描导致」 + SemanticGuard → SUPPORTED(逻辑上站得住) + 但真实原因可能是网络抖动——judge 判不了(没有网络数据源) + → judge 只能保证「在现有证据下结论站得住」,不能保证「事实就是如此」 + +一句话:EvidenceGuard 保证「引用的证据是真的」,SemanticGuard 保证 + 「基于这些证据结论说得通」——事实的真伪从来不是 judge 的职责。 +``` + +### Q2:为什么重试 2 次(semanticGuard 策略)? + +```text +可重试性分析:语义审查单轮、无副作用(幂等)——多试几次不会造成破坏 +但也不能无限重试:判定有硬截止线(总超时耗尽即放弃) +→ 2 次 = 一次失败的成本 × 收益的平衡点;judge 失败走 Fallback,不影响主路径 +``` + +### Q3:语义不变性怎么保证(EvidenceRepair)? + +```text +双重锁死:prompt(只能改 analysis_id / tool_call_ids / based_on_analysis_ids 三字段) + + SemanticDraftView.hasSameUserVisibleSemantics(修复前后逐字段比对) + +关键:比的是「用户可读的内容」不是内部引用 id—— + Conclusion 只比 text,不比 basedOnAnalysisIds + Action 只比 action + requiresHumanConfirmation,不比 basedOnAnalysisIds + → 引用 id 允许变(这正是修复目标),用户看到的文字不许动(碰了判 SCHEMA_INVALID 重试) +``` + +### Q4:judge 判错了怎么办? + +```text +judge 不是最终真相源,是「安全链的一道闸」: + ① judge 判 UNSUPPORTED → 不发布,走 Fallback(宁可保守) + ② judge 判 SUPPORTED 但事实错 → 那是事实问题,不在 judge 职责(见 Q1) + ③ judge 自身失败 → 降级(不阻塞主路径) +→ 设计哲学:judge 的角色是「挡住明显不成立的结论」,不是「证明结论正确」 +``` + +### Q5:为什么独立线程 + 单独超时? + +```text +judge 调用不能阻塞主流程(主 Agent 循环) +独立线程 + future.get(timeout) = 硬超时截断 +Run 取消 → future.cancel(true) 强杀在途判定(judge 也是 Run 的一部分) +``` + +### Q6:输入为什么视图化? + +```text +judge 只看该看的:用户可见内容 + 已验证证据 +剥离内部 id(tool_call_id 等)——防止 judge 用内部信息做「看起来合理」的裁决 +(信息污染:judge 看到内部 id 可能产生不当关联,或泄露内部结构到裁决) +``` + +## 4. 通用 LLM Judge 设计要素(可迁移) + +| 通用要素 | 本项目实现 | +|---|---| +| 判什么(judgment task) | 结论是否被已验证证据支持 | +| 输入视图(该看什么) | SemanticDraftView(剥离内部 id)+ 已验证证据 | +| 输出约束(schema) | 恰好 {verdict, reason} + 枚举合法 + reason 非空 | +| 硬校验 | 字段集 equals({verdict, reason})——不多不少 | +| 隔离 | 单轮、无工具、独立线程——judge 不能自己调工具 | +| 硬超时 | 每次 attempt 剩余超时递减,总超时耗尽即放弃 | +| 重试策略 | 可重试性分析:单轮无副作用 → 2 次 | +| 失败降级 | judge 失败 → Fallback(不卡死主路径) | +| 可审计 | ModelCallLedger 记账 + semanticAttempt/evidenceRepairAttempt trace | +| 成本控制 | 输入/输出字节限制 + reserveRunBytes 计入 Run 预算 | + +## 5. 代码位置索引 + +| 类 | 文件 | +|---|---| +| `GuardModelCall` | `src/main/java/com/superbiz/agent/harness/guard/semantic/GuardModelCall.java` | +| `SemanticGuard` | `src/main/java/com/superbiz/agent/harness/guard/semantic/SemanticGuard.java` | +| `SemanticDraftView` | `src/main/java/com/superbiz/agent/harness/guard/semantic/SemanticDraftView.java` | +| `SemanticGuardInput` / `SemanticGuardLimits` | `src/main/java/com/superbiz/agent/harness/guard/semantic/` | +| `EvidenceRepair` | `src/main/java/com/superbiz/agent/harness/release/EvidenceRepair.java` | +| `EvidenceRepairLimits` / `EvidenceRepairPrompt` | `src/main/java/com/superbiz/agent/harness/release/` | +| 重试策略(semanticGuard/evidenceRepair) | `src/main/java/com/superbiz/agent/harness/retry/HarnessRetryPolicies.java` | diff --git a/mvp/engineering/harness/Harness组件学习路线-进度追踪.md b/mvp/engineering/harness/Harness组件学习路线-进度追踪.md index fcf8dc8..361ab99 100644 --- a/mvp/engineering/harness/Harness组件学习路线-进度追踪.md +++ b/mvp/engineering/harness/Harness组件学习路线-进度追踪.md @@ -111,6 +111,7 @@ | [Harness contract 状态流学习笔记-11个状态枚举的正交全景](Harness%20contract%20状态流学习笔记-11个状态枚举的正交全景.md) | 五层状态/四正交轴/纵向映射链/真实数据案例/面试叙事模板与追问应对 | ✅ 已沉淀 | | [Harness 面试复习笔记-五步复习与白板图沉淀](Harness%20面试复习笔记-五步复习与白板图沉淀.md) | **详细版**:30 秒陈述展开/三张白板图/九域五段式讲法(动机→决策→实现→边界→话术)/六易错点带原因/追问应对大全/支付超时案例/纠正认知清单/面试 Checklist | ✅ 已沉淀 | | [Harness agent 域学习笔记-从框架 ReAct 接入到受控停止](Harness%20agent%20域学习笔记-从框架%20ReAct%20接入到受控停止.md) | agent 域:装配(Factory 粘合点)/双拦截器(Model 预算+Token 审计、Tool 五道门)/UseCase 循环外壳/受控停止/双视图投影/串行工具 | ✅ 已沉淀 | +| [Harness LLM Judge 设计笔记-从不可信判定到可信裁决](Harness%20LLM%20Judge%20设计笔记-从不可信判定到可信裁决.md) | LLM-as-a-judge 模式:SemanticGuard(判支持度)+ EvidenceRepair(修引用)+ GuardModelCall(受控底座);面试五段式回答稿 + 六追问应对 + 通用要素 | ✅ 已沉淀 | ## 3. 一次请求的完整学习主线 @@ -138,8 +139,9 @@ flowchart LR 5. 2 分钟支付超时案例(复习笔记 §7) 可选深化(不阻塞面试): - 1. audit 域深化:RagLookupAuditEnricher 检索审计明细 - 2. 按需深入:模型步 hook 细节 / DiagnosisChatExecutor 恢复路径 / SSE 序列化 + 1. audit 域深化:RagLookupAuditEnricher 检索审计明细(已覆盖大半) + 2. LLM Judge 设计(已沉淀:面试问答 + 追问应对) + 3. 按需深入:模型步 hook 细节 / DiagnosisChatExecutor 恢复路径 / SSE 序列化 ``` ## 5. 建议每次学完一个域后更新