Files
SuperBizAgent-java/mvp/engineering/harness/Harness LLM Judge 设计笔记-从不可信判定到可信裁决.md
T

8.9 KiB
Raw Blame History

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. 定位:三个角色

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 不判事实?(最容易混的边界)

分工:事实由证据链保证,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 策略)?

可重试性分析:语义审查单轮、无副作用(幂等)——多试几次不会造成破坏
但也不能无限重试:判定有硬截止线(总超时耗尽即放弃)
→ 2 次 = 一次失败的成本 × 收益的平衡点;judge 失败走 Fallback,不影响主路径

Q3:语义不变性怎么保证(EvidenceRepair)?

双重锁死: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 判错了怎么办?

judge 不是最终真相源,是「安全链的一道闸」:
  ① judge 判 UNSUPPORTED → 不发布,走 Fallback(宁可保守)
  ② judge 判 SUPPORTED 但事实错 → 那是事实问题,不在 judge 职责(见 Q1)
  ③ judge 自身失败 → 降级(不阻塞主路径)
→ 设计哲学:judge 的角色是「挡住明显不成立的结论」,不是「证明结论正确」

Q5:为什么独立线程 + 单独超时?

judge 调用不能阻塞主流程(主 Agent 循环)
独立线程 + future.get(timeout) = 硬超时截断
Run 取消 → future.cancel(true) 强杀在途判定(judge 也是 Run 的一部分)

Q6:输入为什么视图化?

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