1813 lines
60 KiB
Markdown
1813 lines
60 KiB
Markdown
# Executor Structured Output V2 可执行设计与实施 Issue
|
||
|
||
**状态**:阶段三实施中
|
||
**严重程度**:高
|
||
**创建日期**:2026-07-07
|
||
**最后更新**:2026-07-08
|
||
**文档类型**:可执行设计 issue / 分阶段实施说明
|
||
**范围**:Chat Executor 输出结构、Gatekeeper、Verifier、Composer 数据契约调整
|
||
**关联问题**:
|
||
|
||
- `executor-evidence-attribution-hallucination`
|
||
- `executor-self-evidence-loop-design-note`
|
||
- `chat-verifier-agent`
|
||
- `executor-evidence-output-contract`
|
||
|
||
---
|
||
|
||
## 0. 给后续实现 Agent 的执行入口
|
||
|
||
这份文档是 `Executor Structured Output V2` 的可执行设计 issue,不是单纯的数据结构草案。后续 agent 接手时,先按本节确认背景、当前阶段、执行纪律和验收边界,再进入后面的字段契约。
|
||
|
||
### 0.0 当前接手快照
|
||
|
||
截至 2026-07-08,阶段状态如下:
|
||
|
||
| 阶段 | 状态 | 记录 |
|
||
|---|---|---|
|
||
| 阶段一:Executor V2 输出契约 | 已完成、已归档、已提交 | `050cbc8 feat(agent): add executor evidence v2 contract` |
|
||
| 阶段二:Gatekeeper 接入 VerifierInputHook | 已完成、已归档、已提交 | `c5e496e feat(agent): add executor gatekeeper hook` |
|
||
| 阶段三:Verifier V2 可推导性校验 | 实施中 | active change:`openspec/changes/executor-verifier-claim-checks` |
|
||
| 阶段四:Composer 输出最终答案 | 未开始 | 等阶段三归档并提交后再启动 |
|
||
| 阶段五:回归评测与审计闭环 | 未开始 | 等 Composer 链路稳定后补齐 |
|
||
|
||
接手时的第一动作:
|
||
|
||
1. 先运行 `git status --short`,确认是否已有阶段三未提交改动。
|
||
2. 如果 `openspec/changes/executor-verifier-claim-checks` 仍存在,继续阶段三,不要跳到 Composer。
|
||
3. 阶段三完成后,必须先验证、归档 OpenSpec、回填 devflow、单独 commit,再进入阶段四。
|
||
|
||
推荐阅读顺序:
|
||
|
||
1. 先读 `0.1 - 0.10`:理解为什么要改、当前问题在哪里、目标链路是什么、哪些事情明确不做,以及如何按 OpenSpec/devflow 分阶段落地。
|
||
2. 再读 `20 - 25`:理解代码影响面、阶段拆分、每阶段验收、回滚策略和最终完成标准。
|
||
3. 最后按需查阅 `2 - 19`:这些是实现时使用的详细数据契约、Gatekeeper 规则、Verifier/Composer 输入输出。
|
||
|
||
一句话目标:
|
||
|
||
```text
|
||
把 Executor 从“诊断 + 表达”收敛为“证据收集 + 结构化事实输出”,
|
||
在 Verifier 前加 Gatekeeper 做确定性拦截,
|
||
让 Verifier 只判断 claim 是否能由证据合理推出,
|
||
最终用户答案交给 Composer 生成。
|
||
```
|
||
|
||
执行约束:
|
||
|
||
- 必须按阶段实施,不允许把五个阶段揉成一个大改动。
|
||
- 每个阶段都要先有 OpenSpec change,再实现、验证、归档到 devflow,并单独提交。
|
||
- 阶段未归档、未提交前,不进入下一阶段。
|
||
- 验收失败如果是代码问题,agent 自行修复;如果是设计决策不明确,停下来问用户。
|
||
- 本 issue 不要求一次性完成所有阶段;后续 agent 应从当前 git/OpenSpec/devflow 状态继续推进。
|
||
|
||
交付纪律:
|
||
|
||
- 每个阶段只解决该阶段的问题,不顺手做下一阶段。
|
||
- 每个阶段至少要有主成功路径和新增失败路径测试。
|
||
- 提交前必须确认 diff 只包含当前阶段内容。
|
||
- 不提交、不推送,除非用户明确要求;本 issue 默认只指导阶段实现和本地提交。
|
||
|
||
---
|
||
|
||
## 0.1 背景
|
||
|
||
近期 Chat 复杂诊断链路中,多条诊断会话被 Verifier 判为 `LOW_CONFID`。这些会话并不是没有调用工具;Executor 通常已经调用了 `lookup_knowledge`、`query_metrics`、`query_logs` 等 evidence tools。
|
||
|
||
真正的问题是:Executor 在综合输出阶段把三类内容混在一起写成“当前事故结论”:
|
||
|
||
1. 本轮工具真实返回的事实。
|
||
2. runbook、skill 或知识库里的通用模式。
|
||
3. 模型基于经验补全的推断。
|
||
|
||
当前 Executor 输出中还包含最终面向用户的自然语言字段:
|
||
|
||
```text
|
||
diagnosis_summary
|
||
user_facing_answer
|
||
```
|
||
|
||
这会诱导 Executor 提前进入“诊断报告 / 用户表达”模式,在证据不充分时把“可能方向”写成“确认结论”。后续 Verifier 虽然会拦截,但它面对的是自然语言答案和结构化字段混在一起的输出,校验边界不稳定。
|
||
|
||
典型风险包括:
|
||
|
||
- Executor 编造不存在的 `source_invocation_ids`。
|
||
- Executor 使用真实 invocation id,但 `evidence_excerpt` 与真实工具输出不一致。
|
||
- Executor 把 hypothesis 写成 confirmed claim。
|
||
- Executor 在 `user_facing_answer` 里夹带 claims 中没有的根因、错误码、指标值或修复建议。
|
||
- Verifier 被迫从自然语言里逐字抽事实,校验边界不稳定。
|
||
|
||
因此,本次改造不是为了“多加几个 Agent 显得完整”,而是为了收紧证据归因链路:Executor 只产出可校验材料,Verifier 只判断材料是否被证据支撑,最终表达交给 Composer。
|
||
|
||
---
|
||
|
||
## 0.2 当前问题定义
|
||
|
||
本 issue 要解决的是:
|
||
|
||
```text
|
||
Executor 证据归因幻觉
|
||
```
|
||
|
||
更具体地说,Executor 已经调用了工具,但在最终输出时没有严格区分:
|
||
|
||
| 类型 | 定义 | 应进入哪里 |
|
||
|---|---|---|
|
||
| direct evidence | 本轮工具直接观测到的事实 | `claims` |
|
||
| reasonable inference | 能由证据合理推出但不是逐字出现的判断 | `claims`,但需要标注 `indirect`,并由 Verifier 判断是否说重 |
|
||
| hypothesis | 值得排查但尚未被当前证据确认的方向 | `hypotheses` |
|
||
| missing evidence | 还缺少哪些证据才能确认 | `missing_info` |
|
||
| user expression | 面向用户的自然语言答案 | Composer 输出,不由 Executor 输出 |
|
||
|
||
如果不拆开这些边界,Verifier 会长期承担两个不同任务:
|
||
|
||
1. 从自然语言中抽事实。
|
||
2. 判断事实是否有证据支撑。
|
||
|
||
这会让 Verifier 的职责过重,也让最终答案容易夹带未经校验的内容。
|
||
|
||
---
|
||
|
||
## 0.3 当前实现
|
||
|
||
当前代码中的复杂 Chat 链路是三 Agent 顺序执行:
|
||
|
||
```text
|
||
chat_planner -> chat_executor -> chat_verifier
|
||
```
|
||
|
||
对应入口在:
|
||
|
||
```text
|
||
ChatService.executeChatComplex(...)
|
||
```
|
||
|
||
当前数据流:
|
||
|
||
```text
|
||
用户问题
|
||
-> Planner 生成 planner_plan
|
||
-> Executor 调用工具并输出 executor_evidence_v1
|
||
-> VerifierInputHook 构造 verifier payload
|
||
-> Verifier 输出 facts_checked/verdict
|
||
-> ChatService 根据 verdict 生成最终 answer
|
||
```
|
||
|
||
当前关键问题在 Executor 与 Verifier 之间:
|
||
|
||
```text
|
||
Executor 同时输出结构化 claims 和 user_facing_answer
|
||
Verifier 既要校验 structured output,又要扫描自然语言答案
|
||
```
|
||
|
||
这会让最终答案和证据归因之间出现缝隙。
|
||
|
||
---
|
||
|
||
## 0.4 目标设计
|
||
|
||
目标链路仍保持单条顺序链路,不新增 Controller,不改 Planner:
|
||
|
||
```text
|
||
chat_planner
|
||
-> chat_executor
|
||
-> VerifierInputHook 内 Gatekeeper
|
||
-> chat_verifier
|
||
-> chat_composer
|
||
-> final answer
|
||
```
|
||
|
||
职责边界:
|
||
|
||
| 组件 | 职责 |
|
||
|---|---|
|
||
| Planner | 选择 skill、拆解计划;本次不改 |
|
||
| Executor | 调用工具收集证据,输出结构化 claims/hypotheses/actions/missing_info |
|
||
| Gatekeeper | 在 Verifier 前做确定性校验,拦截伪造 ID、工具名不匹配、明显张冠李戴、非法 schema |
|
||
| Verifier | 判断 claims 是否能由证据合理推出,不再逐字扫描最终答案 |
|
||
| Composer | 只根据 Verifier 允许的材料生成最终用户答案 |
|
||
|
||
目标数据流:
|
||
|
||
```text
|
||
Executor raw JSON
|
||
-> parse JSON
|
||
-> Gatekeeper.validate(...)
|
||
-> Verifier claim-level validation
|
||
-> ChatService 过滤 allowed_claims/allowed_hypotheses
|
||
-> Composer 生成 user_facing_answer
|
||
```
|
||
|
||
核心原则:
|
||
|
||
- 单智能体优先:不为了抽象而新增 Controller;只有已经明确的职责边界才拆 Agent。
|
||
- Planner 不改:本 issue 的问题不在计划拆解,而在 Executor 输出和 Verifier 校验边界。
|
||
- Gatekeeper 不做诊断:只做确定性校验,拦截伪造 ID、字段退化、明显张冠李戴。
|
||
- Verifier 不再逐字扫描最终答案:主校验对象是结构化 `claims`。
|
||
- Composer 不补事实:只把 Verifier 允许的材料组织成中文答案。
|
||
|
||
---
|
||
|
||
## 0.5 分阶段实施总览
|
||
|
||
| 阶段 | 名称 | 目标 | 状态 | 退出条件 |
|
||
|---|---|---|---|---|
|
||
| 1 | Executor V2 输出契约 | 移除 Executor 最终表达字段,只输出结构化诊断材料 | 已完成并归档 | `executor_evidence_v2` 可运行,最终用户不再看到 raw JSON |
|
||
| 2 | Gatekeeper 接入 VerifierInputHook | 在 Verifier 前加入确定性拦截与审计 | 已完成并归档 | `gatekeeper_result` 进入 Verifier payload 和 `self_evaluation` |
|
||
| 3 | Verifier V2 可推导性校验 | 从 `facts_checked` 转向 `claim_checks`,判断 claims 是否可由证据推出 | 实施中 | Verifier 不再从自然语言答案抽取额外事实 |
|
||
| 4 | Composer 最终表达 | 由 Composer 根据 Verifier 允许材料生成最终用户答案 | 未开始 | PASS/LOW_CONFID/REJECT 都不读取 Executor `user_facing_answer` |
|
||
| 5 | 回归评测与审计闭环 | 用测试和 eval fixtures 证明幻觉拦截链路有效 | 未开始 | 伪造 ID、张冠李戴、hypothesis 写成事实等场景都有回归覆盖 |
|
||
|
||
更详细的实施拆解见 `21. 实施阶段`。
|
||
|
||
阶段推进顺序必须串行:
|
||
|
||
```text
|
||
阶段一 archive + commit
|
||
-> 阶段二 archive + commit
|
||
-> 阶段三 archive + commit
|
||
-> 阶段四 archive + commit
|
||
-> 阶段五 archive + commit
|
||
```
|
||
|
||
不能在阶段三未归档提交时启动 Composer,也不能在 Composer 未稳定时把回归评测阶段提前做成大杂烩。
|
||
|
||
---
|
||
|
||
## 0.6 范围与非目标
|
||
|
||
### In scope
|
||
|
||
- 调整 Executor 输出契约,从 `executor_evidence_v1` 演进到 `executor_evidence_v2`。
|
||
- 在 `VerifierInputHook` 中接入 Gatekeeper。
|
||
- 将 Gatekeeper 结果写入 Verifier payload 和 `diagnosis_session.self_evaluation`。
|
||
- 将 Verifier 主输出从 `facts_checked` 迁移到 `claim_checks`,兼容期保留旧字段。
|
||
- 新增 Composer 表达层,最终用户答案只来自 Verifier 允许材料。
|
||
- 补齐关键回归测试和 eval fixtures。
|
||
|
||
### Out of scope
|
||
|
||
本 issue 第一版明确不做以下事情:
|
||
|
||
- 不改 Planner 的输入输出。
|
||
- 不引入 Controller 或新的编排层。
|
||
- 不改变现有 retry 机制。
|
||
- 不要求 Gatekeeper 失败后自动回退 Executor 重试。
|
||
- 不新增数据库表。
|
||
- 不让 Gatekeeper 做根因判断。
|
||
- 不让 Composer 调用工具或重新诊断。
|
||
|
||
---
|
||
|
||
## 0.7 关键设计决策
|
||
|
||
| 决策 | 结论 | 原因 |
|
||
|---|---|---|
|
||
| 是否改 Planner | 不改 | 当前问题主要在 Executor 输出和 Verifier 校验边界 |
|
||
| Gatekeeper 放在哪里 | 放在 `VerifierInputHook` | 保持当前 workflow,不改 Executor hook 和编排 |
|
||
| Gatekeeper 失败是否重试 | 第一版不重试 | 先建立拦截和审计闭环,避免扩大改动面 |
|
||
| Executor 是否继续输出最终答案 | 不输出 | 防止未经 Verifier 的自然语言结论泄露 |
|
||
| Verifier 是否逐字扫描答案 | 不扫描 | 主校验对象改为 `claims` |
|
||
| Composer 是否可以新增事实 | 不可以 | Composer 只是表达层,不是诊断层 |
|
||
| `facts_checked` 是否保留 | 兼容期保留 | 当前低置信模板、retry_context、评测可能依赖 |
|
||
|
||
---
|
||
|
||
## 0.8 实现约束
|
||
|
||
实现时必须遵守:
|
||
|
||
- 优先保持现有三 Agent 复杂链路可运行。
|
||
- 每个阶段都应能单独测试和回滚。
|
||
- 第一版以“降低幻觉风险”为目标,不追求一次性重构所有历史字段。
|
||
- 所有最终用户可见答案都必须来自 Verifier 允许输出的材料。
|
||
- 所有 Gatekeeper 校验结果都必须进入审计,至少能回溯到 `self_evaluation.verifier_evaluation.gatekeeper_result`。
|
||
- 如果结构化输出 malformed,不允许回退到自然语言逐字抽取后给 PASS。
|
||
|
||
---
|
||
|
||
## 0.9 OpenSpec / devflow 落地要求
|
||
|
||
这个 issue 后续按 OpenSpec-first 的方式实施。每个阶段都必须留下可审计痕迹:
|
||
|
||
```text
|
||
OpenSpec proposal/design/spec/tasks
|
||
-> 实现代码
|
||
-> 最小验证
|
||
-> archive 到 openspec/changes/archive
|
||
-> 回填 devflow/projects
|
||
-> git commit
|
||
```
|
||
|
||
阶段交付物:
|
||
|
||
| 交付物 | 要求 |
|
||
|---|---|
|
||
| OpenSpec change | 每个阶段一个独立 change,不能复用上个阶段的 active change |
|
||
| tests | 至少覆盖该阶段的新增失败路径和主成功路径 |
|
||
| devflow | 记录 brief、decisions、evidence、acceptance |
|
||
| commit | 每个阶段单独提交,提交前确认 diff 只包含本阶段内容 |
|
||
|
||
推荐验证命令:
|
||
|
||
```powershell
|
||
mvn "-Dtest=VerifierInputHookTest,ChatServiceSequentialAgentTest" test
|
||
cmd /c openspec validate --specs
|
||
```
|
||
|
||
如阶段新增专门测试,应把测试类加入 Maven `-Dtest` 列表。
|
||
|
||
后续 agent 每个阶段的固定执行清单:
|
||
|
||
1. 确认当前阶段:读取本 issue、`git status --short`、`openspec status`。
|
||
2. 确认 OpenSpec:没有 active change 时先创建;已有当前阶段 active change 时继续使用。
|
||
3. 实现阶段内代码:只改该阶段要求的 prompt/service/hook/test,不夹带下一阶段。
|
||
4. 验证:运行该阶段建议测试和 `cmd /c openspec validate ...`。
|
||
5. 回填任务:更新 OpenSpec `tasks.md`、`decisions.md`,记录实际验证命令和结果。
|
||
6. 归档:创建或更新 `devflow/projects/<date>-<change-id>/`,然后 `cmd /c openspec archive <change-id> -y`。
|
||
7. 复验:归档后再次运行最小测试和 `cmd /c openspec validate --specs`。
|
||
8. 提交:检查 `git diff --cached --stat`,确认只包含当前阶段,再单独 commit。
|
||
|
||
若步骤 4 或 7 失败:
|
||
|
||
- 代码或测试问题:自行修复并重新验证。
|
||
- 阶段边界或协议设计问题:停止,向用户说明阻塞点,不进入下一阶段。
|
||
|
||
---
|
||
|
||
## 0.10 推荐实现切片
|
||
|
||
建议不要把所有改动塞进一个大 PR。推荐拆成以下可独立验收的切片:
|
||
|
||
| 切片 | 内容 | 可单独合入条件 |
|
||
|---|---|---|
|
||
| PR-1 | Executor V2 prompt + parse-only 边界调整 | Executor 能输出 V2 JSON;不会再通过 Executor 直接生成最终用户答案 |
|
||
| PR-2 | Gatekeeper 基础框架 + schema/invocation 规则 | Verifier payload 和审计中出现 `gatekeeper_result`;伪造 invocation id 可被拦截 |
|
||
| PR-3 | Verifier V2 claim_checks + facts_checked 兼容 | Verifier 主校验 `claims`;现有低置信模板和 retry_context 不坏 |
|
||
| PR-4 | Composer 接入 + 最终答案渲染 | PASS/LOW_CONFID/REJECT 最终答案都不再读取 Executor `user_facing_answer` |
|
||
| PR-5 | eval fixtures + 回归测试补齐 | 覆盖伪造 ID、工具名不匹配、excerpt 张冠李戴、hypothesis 写成事实 |
|
||
|
||
每个切片都应保留当前复杂 Chat 链路可运行。若某个切片失败,优先回滚该切片,不要连带回滚已经稳定的前置切片。
|
||
|
||
---
|
||
|
||
## 0.11 为什么这样拆阶段
|
||
|
||
本 issue 的改造目标不是一次性重写 Chat 诊断链路,而是逐步切断“证据不足但自然语言先下结论”的通道。阶段拆分按风险来源分层:
|
||
|
||
| 阶段 | 切断的风险 | 为什么不能合并 |
|
||
|---|---|---|
|
||
| 阶段一 | Executor 提前输出最终诊断话术 | 先移除污染源,否则后面 Verifier/Composer 仍会被旧答案影响 |
|
||
| 阶段二 | 伪造 ID、工具名不匹配、非法 schema 等物理级幻觉 | 这类问题不需要 LLM 判断,先用代码低成本拦截 |
|
||
| 阶段三 | claim 与证据之间是否可推导 | 这是 Verifier 的语义职责,必须在 Gatekeeper 之后处理 |
|
||
| 阶段四 | 最终用户答案夹带未验证事实 | 只有 Verifier 输出稳定后,Composer 才知道哪些材料可用 |
|
||
| 阶段五 | 新链路是否真的防住历史幻觉场景 | 等链路完整后再做系统化回归,避免测试绑死临时实现 |
|
||
|
||
每个阶段的设计都必须满足两个条件:
|
||
|
||
- 可以独立验证,不依赖后续阶段已经完成。
|
||
- 失败时可以独立回滚,不破坏已经归档提交的前置阶段。
|
||
|
||
因此后续实现时不要把“顺手移除临时 renderer”“顺手扩展 Gatekeeper 规则”“顺手接 Composer”混进阶段三。阶段三只解决 Verifier `claim_checks` 和有效 verdict guardrail。
|
||
|
||
---
|
||
|
||
## 1. 设计目标
|
||
|
||
当前 `executor_evidence_v1` 同时包含结构化诊断材料和自然语言表达字段:
|
||
|
||
- `diagnosis_summary`
|
||
- `user_facing_answer`
|
||
|
||
这两个字段容易让 Executor 提前进入“总结报告 / 用户表达”模式,并可能把未证实内容写成确认结论。
|
||
|
||
V2 的目标是让 Executor 只输出结构化诊断材料,不负责最终用户表达。
|
||
|
||
---
|
||
|
||
## 2. 输出结构
|
||
|
||
```json
|
||
{
|
||
"answer_version": "executor_evidence_v2",
|
||
"claims": [
|
||
{
|
||
"claim_id": "claim-1",
|
||
"claim_type": "symptom",
|
||
"claim_text": "payment-service 出现请求超时日志。",
|
||
"support_level": "direct",
|
||
"evidence_bindings": [
|
||
{
|
||
"source_type": "tool_trace",
|
||
"source_id": "trace-1",
|
||
"tool_name": "query_logs",
|
||
"source_invocation_ids": [394],
|
||
"evidence_excerpt": "request timeout"
|
||
}
|
||
]
|
||
}
|
||
],
|
||
"hypotheses": [
|
||
{
|
||
"hypothesis_text": "连接池压力可能参与了超时问题。",
|
||
"basis": "当前已有超时现象,但缺少连接池 active/idle/pending 指标。",
|
||
"needed_evidence": ["HikariCP active/idle/pending 指标"]
|
||
}
|
||
],
|
||
"recommended_actions": [
|
||
{
|
||
"action_text": "补充查询连接池 active/idle/pending 指标。",
|
||
"reason": "用于确认连接池是否达到上限。",
|
||
"evidence_bindings": []
|
||
}
|
||
],
|
||
"missing_info": [
|
||
"缺少连接池指标,无法确认连接池耗尽是根因。"
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 字段定义
|
||
|
||
| 字段 | 类型 | 必填 | 定义 |
|
||
|---|---|---:|---|
|
||
| `answer_version` | string | 是 | 固定为 `executor_evidence_v2` |
|
||
| `claims` | array | 是 | 已确认或有明确支撑的事实断言 |
|
||
| `hypotheses` | array | 否 | 合理怀疑但未被当前证据确认的方向 |
|
||
| `recommended_actions` | array | 否 | 建议补证据、排查或处理动作 |
|
||
| `missing_info` | array | 否 | 当前无法确认结论所缺少的证据 |
|
||
|
||
## 4. claims
|
||
|
||
```json
|
||
{
|
||
"claim_id": "claim-1",
|
||
"claim_type": "symptom",
|
||
"claim_text": "payment-service 出现请求超时日志。",
|
||
"support_level": "direct",
|
||
"evidence_bindings": []
|
||
}
|
||
```
|
||
|
||
| 字段 | 类型 | 必填 | 定义 |
|
||
|---|---|---:|---|
|
||
| `claim_id` | string | 是 | claim 唯一标识 |
|
||
| `claim_type` | string | 是 | claim 类型,例如 `symptom` / `root_cause` / `impact` / `risk` |
|
||
| `claim_text` | string | 是 | 已确认或有限确认的事实断言 |
|
||
| `support_level` | string | 是 | `direct` / `indirect` |
|
||
| `evidence_bindings` | array | 是 | 支撑该 claim 的证据绑定,不能为空 |
|
||
|
||
## 5. evidence_bindings
|
||
|
||
```json
|
||
{
|
||
"source_type": "tool_trace",
|
||
"source_id": "trace-1",
|
||
"tool_name": "query_logs",
|
||
"source_invocation_ids": [394],
|
||
"evidence_excerpt": "request timeout"
|
||
}
|
||
```
|
||
|
||
| 字段 | 类型 | 必填 | 定义 |
|
||
|---|---|---:|---|
|
||
| `source_type` | string | 是 | 证据来源类型,例如 `tool_trace` |
|
||
| `source_id` | string | 否 | evidence block id、trace_ref 或可定位标识;第一版不作为硬校验依据 |
|
||
| `tool_name` | string | 是 | 来源工具名 |
|
||
| `source_invocation_ids` | array | 是 | 来源 `tool_invocation.id` |
|
||
| `evidence_excerpt` | string | 是 | 从工具返回中摘取的原话、指标值、日志片段或关键数据 |
|
||
|
||
## 6. hypotheses
|
||
|
||
```json
|
||
{
|
||
"hypothesis_text": "连接池压力可能参与了超时问题。",
|
||
"basis": "当前已有超时现象,但缺少连接池 active/idle/pending 指标。",
|
||
"needed_evidence": ["HikariCP active/idle/pending 指标"]
|
||
}
|
||
```
|
||
|
||
| 字段 | 类型 | 必填 | 定义 |
|
||
|---|---|---:|---|
|
||
| `hypothesis_text` | string | 是 | 未证实但值得排查的方向 |
|
||
| `basis` | string | 是 | 它基于哪些已知证据,以及为什么仍只是推测 |
|
||
| `needed_evidence` | array | 是 | 要确认该假设还缺少的证据 |
|
||
|
||
## 7. recommended_actions
|
||
|
||
```json
|
||
{
|
||
"action_text": "补充查询连接池 active/idle/pending 指标。",
|
||
"reason": "用于确认连接池是否达到上限。",
|
||
"evidence_bindings": []
|
||
}
|
||
```
|
||
|
||
| 字段 | 类型 | 必填 | 定义 |
|
||
|---|---|---:|---|
|
||
| `action_text` | string | 是 | 建议动作 |
|
||
| `reason` | string | 是 | 建议原因 |
|
||
| `evidence_bindings` | array | 否 | 建议动作关联的证据绑定,可为空 |
|
||
|
||
## 8. 移除字段
|
||
|
||
V2 移除以下字段:
|
||
|
||
| 字段 | 移除原因 |
|
||
|---|---|
|
||
| `diagnosis_summary` | 容易让 Executor 提前总结诊断故事 |
|
||
| `user_facing_answer` | 容易夹带 claims 之外的确认式事实 |
|
||
|
||
最终用户答案应由 ChatService 或后续专门渲染阶段基于结构化字段生成。
|
||
|
||
---
|
||
|
||
## 9. Gatekeeper 设计
|
||
|
||
Executor 输出后、Verifier 接收前,增加一层确定性校验:
|
||
|
||
```text
|
||
Executor raw output
|
||
-> parse JSON
|
||
-> Gatekeeper
|
||
-> Verifier payload
|
||
```
|
||
|
||
Gatekeeper 的目标是拦截物理级幻觉,而不是替代 Verifier 做语义判断。
|
||
|
||
### 9.1 职责边界
|
||
|
||
| 组件 | 职责 |
|
||
|---|---|
|
||
| Gatekeeper | schema、引用 ID、工具名、excerpt、黑名单措辞等确定性校验 |
|
||
| Verifier | 判断 claim 是否被证据语义支撑、是否说重、是否缺关键证据 |
|
||
|
||
Gatekeeper 不判断根因是否正确,也不做新的检索。
|
||
|
||
### 9.2 接入位置
|
||
|
||
V1 接入位置保持在 `VerifierInputHook` 内,不改 Executor hook,不改 Chat workflow 编排:
|
||
|
||
```text
|
||
parseExecutorOutput(...)
|
||
-> buildVerifierTraceSummary(...)
|
||
-> Gatekeeper.validate(...)
|
||
-> verifierInput.put("gatekeeper_result", ...)
|
||
```
|
||
|
||
`VerifierInputHook` 的职责收敛为:
|
||
|
||
- 解析 Executor raw output。
|
||
- 构造 `executor_output_parse_status`。
|
||
- 构造 `tool_trace_summary`。
|
||
- 调用 Gatekeeper。
|
||
- 组装 Verifier payload。
|
||
|
||
除 JSON parse 与最小可解析性判断外,`VerifierInputHook` 不再维护独立校验规则。
|
||
|
||
后续持久化时,将 `gatekeeper_result` 写入:
|
||
|
||
```text
|
||
diagnosis_session.self_evaluation.verifier_evaluation.gatekeeper_result
|
||
```
|
||
|
||
---
|
||
|
||
## 10. Gatekeeper 规则加载
|
||
|
||
Gatekeeper 采用“规则代码稳定、规则配置可调”的设计。
|
||
|
||
```text
|
||
Rule Index
|
||
-> Rule Metadata
|
||
-> Rule Implementation
|
||
```
|
||
|
||
### 10.1 索引层
|
||
|
||
索引层列出所有可用规则名和一句话描述,用于加载、展示和审计。
|
||
|
||
```yaml
|
||
rules:
|
||
- id: schema.executor_v2
|
||
name: Executor V2 Schema
|
||
description: 校验 Executor 输出是否符合 V2 schema
|
||
enabled: true
|
||
|
||
- id: evidence.invocation_ref
|
||
name: Invocation Reference Check
|
||
description: 校验 source_invocation_ids 是否属于当前 session 且工具名匹配
|
||
enabled: true
|
||
|
||
- id: evidence.excerpt_similarity
|
||
name: Excerpt Similarity Check
|
||
description: 校验 evidence_excerpt 与真实工具输出是否相似
|
||
enabled: true
|
||
|
||
- id: claim.hallucination_phrases
|
||
name: Hallucination Phrase Check
|
||
description: 禁止 confirmed claims 使用经验化、推测化措辞
|
||
enabled: true
|
||
|
||
- id: claim.evidence_utilization
|
||
name: Evidence Utilization Check
|
||
description: 检查 claim_text 与 evidence_excerpt 的关键词覆盖率
|
||
enabled: false
|
||
```
|
||
|
||
### 10.2 元数据层
|
||
|
||
元数据层定义规则参数、阈值、严重级别和适用字段。调整阈值或黑名单时只改元数据,不改代码。
|
||
|
||
```yaml
|
||
ruleMetadata:
|
||
schema.executor_v2:
|
||
severity: error
|
||
verdictImpact: low_confid_on_fail
|
||
params:
|
||
requiredFields:
|
||
- answer_version
|
||
- claims
|
||
deprecatedFields:
|
||
- diagnosis_summary
|
||
- user_facing_answer
|
||
|
||
evidence.excerpt_similarity:
|
||
severity: error
|
||
verdictImpact: low_confid_on_fail
|
||
params:
|
||
passThreshold: 0.5
|
||
warnThreshold: 0.3
|
||
compareAgainst:
|
||
- output_preview
|
||
- retrieval_details
|
||
- tool_trace_summary
|
||
|
||
claim.hallucination_phrases:
|
||
severity: error
|
||
verdictImpact: low_confid_on_fail
|
||
params:
|
||
targetFields:
|
||
- claims[].claim_text
|
||
blacklist:
|
||
- 通常情况下
|
||
- 根据经验
|
||
- 一般来说
|
||
- 我认为
|
||
- 理论上
|
||
- 可能是
|
||
- 推测
|
||
allowInFields:
|
||
- hypotheses[].hypothesis_text
|
||
- hypotheses[].basis
|
||
|
||
claim.evidence_utilization:
|
||
severity: warning
|
||
verdictImpact: warn_only
|
||
params:
|
||
minKeywordCoverage: 0.5
|
||
|
||
evidence.invocation_ref:
|
||
severity: error
|
||
verdictImpact: reject_on_fail
|
||
```
|
||
|
||
### 10.3 规则实现层
|
||
|
||
规则实现使用代码中的固定接口。新增全新类型规则需要代码;调整阈值、词表、启用状态不需要改代码。
|
||
|
||
```text
|
||
GatekeeperRule
|
||
id()
|
||
validate(context, metadata)
|
||
-> RuleResult
|
||
```
|
||
|
||
### 10.4 与 VerifierInputHook 原有校验的关系
|
||
|
||
V2 中,`VerifierInputHook` 原有的结构判断只保留 parse-only 能力:
|
||
|
||
```text
|
||
raw text
|
||
-> sanitized JSON
|
||
-> JsonNode / Map
|
||
-> parse status
|
||
```
|
||
|
||
以下校验统一迁移到 Gatekeeper:
|
||
|
||
- schema 字段是否完整。
|
||
- `answer_version` 是否正确。
|
||
- `claims` 是否为空或类型错误。
|
||
- `claims[].evidence_bindings` 是否为空。
|
||
- `source_invocation_ids` 是否存在。
|
||
- `tool_name` 是否匹配。
|
||
- `evidence_excerpt` 是否可信。
|
||
- 是否出现已移除字段 `diagnosis_summary` / `user_facing_answer`。
|
||
|
||
这样避免 `VerifierInputHook` 和 Gatekeeper 出现两套规则、两套错误口径。
|
||
|
||
---
|
||
|
||
## 11. 推荐规则集
|
||
|
||
### 11.1 Schema 校验
|
||
|
||
规则 id:
|
||
|
||
```text
|
||
schema.executor_v2
|
||
```
|
||
|
||
校验内容:
|
||
|
||
- 输出必须是 JSON object。
|
||
- `answer_version` 必须为 `executor_evidence_v2`。
|
||
- `claims` 必须存在且为 array。
|
||
- `hypotheses`、`recommended_actions`、`missing_info` 缺省时按空数组处理。
|
||
- 不允许出现 `diagnosis_summary`。
|
||
- 不允许出现 `user_facing_answer`。
|
||
|
||
Gatekeeper 输出给 Verifier 前应 normalize 结构:
|
||
|
||
```text
|
||
missing hypotheses -> []
|
||
missing recommended_actions -> []
|
||
missing missing_info -> []
|
||
```
|
||
|
||
也就是说,Verifier 和 Composer 可以假定这三个字段存在且为数组。
|
||
|
||
### 11.2 Invocation 引用校验
|
||
|
||
规则 id:
|
||
|
||
```text
|
||
evidence.invocation_ref
|
||
```
|
||
|
||
校验内容:
|
||
|
||
- `claims[].evidence_bindings` 不能为空。
|
||
- `source_invocation_ids` 必须为非空数组。
|
||
- 每个 invocation id 必须属于当前 session。
|
||
- `tool_name` 必须和对应 `tool_invocation.tool_name` 匹配。
|
||
- 不允许引用其它 session 的工具调用。
|
||
|
||
该规则只检查 `claims[].evidence_bindings`。`recommended_actions[].evidence_bindings` 可以为空,不参与硬失败判定。
|
||
|
||
### 11.3 Excerpt 相似度校验
|
||
|
||
规则 id:
|
||
|
||
```text
|
||
evidence.excerpt_similarity
|
||
```
|
||
|
||
校验内容:
|
||
|
||
- 将 `evidence_excerpt` 与真实工具输出进行相似度比对。
|
||
- 可比较来源包括:
|
||
- `tool_invocation.output_preview`
|
||
- `tool_invocation.retrieval_details`
|
||
- `tool_trace_summary.output_summary`
|
||
|
||
建议阈值:
|
||
|
||
| 相似度 | 结果 |
|
||
|---:|---|
|
||
| `>= 0.5` | pass |
|
||
| `0.3 - 0.5` | warn |
|
||
| `< 0.3` | fail |
|
||
|
||
如果只是 excerpt 相似度低,第一版判为 `LOW_CONFID` 级风险,不直接 `REJECT`,因为 `output_preview` 可能被截断,`tool_trace_summary` 也可能是摘要。
|
||
|
||
只有以下情况才进入 `REJECT` 级别:
|
||
|
||
- invocation id 不存在。
|
||
- invocation id 不属于当前 session。
|
||
- `tool_name` 与 invocation 的真实工具名不匹配。
|
||
- invocation 存在但 excerpt 明显指向另一个服务、错误码或指标值。
|
||
|
||
### 11.4 幻觉措辞校验
|
||
|
||
规则 id:
|
||
|
||
```text
|
||
claim.hallucination_phrases
|
||
```
|
||
|
||
校验内容:
|
||
|
||
- `claims[].claim_text` 不允许出现经验化、推测化措辞。
|
||
- `hypotheses` 中允许出现推测措辞。
|
||
|
||
默认黑名单:
|
||
|
||
```json
|
||
[
|
||
"通常情况下",
|
||
"根据经验",
|
||
"一般来说",
|
||
"可能是",
|
||
"推测",
|
||
"我认为",
|
||
"理论上"
|
||
]
|
||
```
|
||
|
||
### 11.5 证据利用率校验
|
||
|
||
规则 id:
|
||
|
||
```text
|
||
claim.evidence_utilization
|
||
```
|
||
|
||
校验内容:
|
||
|
||
- 提取 `claim_text` 中的实词。
|
||
- 提取 `evidence_excerpt` 中的实词。
|
||
- 计算关键词覆盖率。
|
||
|
||
伪代码:
|
||
|
||
```python
|
||
def check_evidence_utilization(claim_text, excerpt):
|
||
claim_keywords = extract_keywords(claim_text)
|
||
excerpt_keywords = extract_keywords(excerpt)
|
||
overlap = claim_keywords & excerpt_keywords
|
||
utilization = len(overlap) / len(claim_keywords)
|
||
return utilization >= 0.5
|
||
```
|
||
|
||
第一版建议作为 warning,不作为硬拒绝条件。
|
||
|
||
---
|
||
|
||
## 12. Gatekeeper 输出
|
||
|
||
Gatekeeper 第一版只保留必要审计字段。
|
||
|
||
```json
|
||
{
|
||
"gatekeeper_result": {
|
||
"status": "fail",
|
||
"failed_rules": ["evidence.invocation_ref"],
|
||
"warnings": ["evidence.excerpt_similarity"],
|
||
"errors": [
|
||
{
|
||
"rule_id": "evidence.invocation_ref",
|
||
"target": "claims[0].evidence_bindings[0]",
|
||
"message": "source_invocation_ids not found in current session"
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
### 12.1 字段定义
|
||
|
||
| 字段 | 类型 | 定义 |
|
||
|---|---|---|
|
||
| `status` | string | `pass` / `warn` / `fail` |
|
||
| `failed_rules` | array | 失败规则 id 列表 |
|
||
| `warnings` | array | warning 规则 id 列表 |
|
||
| `errors` | array | 最小错误明细 |
|
||
| `errors[].rule_id` | string | 失败规则 id |
|
||
| `errors[].target` | string | 出问题的字段路径 |
|
||
| `errors[].message` | string | 人类可读原因 |
|
||
|
||
### 12.2 总状态计算
|
||
|
||
```text
|
||
任一规则 fail -> gatekeeper status = fail
|
||
否则任一规则 warn -> gatekeeper status = warn
|
||
否则 gatekeeper status = pass
|
||
```
|
||
|
||
第一版不记录 `version`、`rule_set`、`summary`、`severity`、`details`、`checked_at` 等字段,避免审计结构过重。
|
||
|
||
Verifier 如需判断 Gatekeeper fail 是否应导致 `REJECT`,使用规则元数据中的 `verdictImpact`,不依赖落库字段。
|
||
|
||
---
|
||
|
||
## 13. 审计落点
|
||
|
||
Gatekeeper 结果需要进入两处:
|
||
|
||
### 13.1 Verifier payload
|
||
|
||
```json
|
||
{
|
||
"executor_structured_output": {},
|
||
"executor_output_parse_status": {},
|
||
"tool_trace_summary": [],
|
||
"gatekeeper_result": {}
|
||
}
|
||
```
|
||
|
||
Verifier 可根据 `gatekeeper_result` 调整判定,尤其是:
|
||
|
||
- `gatekeeper_result.status=fail` 时,不应输出 `PASS`。
|
||
- schema 或 invocation 引用失败时,应倾向 `LOW_CONFID` 或 `REJECT`。
|
||
|
||
### 13.1.1 parse status 与 schema status 边界
|
||
|
||
`executor_output_parse_status` 只表示 JSON 解析结果:
|
||
|
||
| parse status | 含义 |
|
||
|---|---|
|
||
| `valid` | raw output 可解析为 JSON object |
|
||
| `missing` | raw output 为空或不是 JSON |
|
||
| `malformed` | raw output 像 JSON 但解析失败 |
|
||
|
||
schema 合法性不再由 parse status 表达,统一交给 Gatekeeper:
|
||
|
||
```text
|
||
JSON parse valid but schema invalid
|
||
-> executor_output_parse_status.status = valid
|
||
-> gatekeeper_result.status = fail
|
||
-> failed_rules contains schema.executor_v2
|
||
```
|
||
|
||
### 13.2 self_evaluation
|
||
|
||
持久化位置:
|
||
|
||
```text
|
||
diagnosis_session.self_evaluation.verifier_evaluation.gatekeeper_result
|
||
```
|
||
|
||
审计链路:
|
||
|
||
```text
|
||
executor_raw_output
|
||
-> executor_output_parse_status
|
||
-> gatekeeper_result
|
||
-> verifier_evaluation
|
||
-> final_answer
|
||
```
|
||
|
||
---
|
||
|
||
## 14. Verifier 输入结构
|
||
|
||
V2 中 Verifier 以结构化输出为主输入,不再逐字抽取自然语言事实。
|
||
|
||
```json
|
||
{
|
||
"original_query": "用户原始问题",
|
||
"executor_raw_output": "{raw executor output, debug/fallback only}",
|
||
"executor_structured_output": {
|
||
"answer_version": "executor_evidence_v2",
|
||
"claims": [],
|
||
"hypotheses": [],
|
||
"recommended_actions": [],
|
||
"missing_info": []
|
||
},
|
||
"executor_output_parse_status": {
|
||
"status": "valid",
|
||
"detail": "parsed executor evidence contract"
|
||
},
|
||
"tool_trace_summary": [],
|
||
"gatekeeper_result": {
|
||
"status": "pass",
|
||
"failed_rules": [],
|
||
"warnings": [],
|
||
"errors": []
|
||
},
|
||
"retry_context": null
|
||
}
|
||
```
|
||
|
||
| 字段 | 用途 |
|
||
|---|---|
|
||
| `original_query` | 判断结构化诊断是否围绕用户问题 |
|
||
| `executor_raw_output` | 原始输出,仅用于 debug/fallback,不作为主校验来源 |
|
||
| `executor_structured_output` | Verifier 主校验对象 |
|
||
| `executor_output_parse_status` | 判断结构化输出是否可用 |
|
||
| `tool_trace_summary` | 证据索引 |
|
||
| `gatekeeper_result` | Gatekeeper 物理级校验结果 |
|
||
| `retry_context` | 第二轮时判断缺口是否被补足 |
|
||
|
||
兼容期可以保留旧字段名:
|
||
|
||
```text
|
||
executor_final_answer
|
||
```
|
||
|
||
第一期 payload 可以同时包含 `executor_raw_output` 和 `executor_final_answer`,二者内容相同。`executor_final_answer` 语义上视为 deprecated;Verifier 不应在结构化输出合法时从该字段抽取额外事实。
|
||
|
||
---
|
||
|
||
## 15. Verifier 字段使用规则
|
||
|
||
### 15.1 executor_structured_output
|
||
|
||
Verifier 主要校验:
|
||
|
||
- `claims`
|
||
- `hypotheses`
|
||
- `recommended_actions`
|
||
- `missing_info`
|
||
|
||
重点是 `claims`:
|
||
|
||
```text
|
||
claim_text 是否能由 evidence_bindings 指向的证据合理推出?
|
||
support_level 是否说重?
|
||
是否引入证据外的新实体、新指标、新错误码或新根因?
|
||
```
|
||
|
||
### 15.2 tool_trace_summary
|
||
|
||
`tool_trace_summary` 用于判断 evidence binding 是否有语义支撑。
|
||
|
||
Verifier 不要求 claim 与证据逐字一致,而是判断可推导性:
|
||
|
||
```text
|
||
证据:payment-service CPU 92%,线程数 245
|
||
claim:payment-service 当前存在 CPU 使用率过高现象
|
||
=> direct_observation
|
||
|
||
claim:CPU 过高可能导致支付接口超时
|
||
=> reasonable_inference
|
||
|
||
claim:CPU 过高是支付超时的唯一根因
|
||
=> overstated
|
||
```
|
||
|
||
### 15.3 gatekeeper_result
|
||
|
||
Gatekeeper 负责物理合法性,Verifier 消费结果:
|
||
|
||
```text
|
||
status = pass
|
||
-> 正常校验 claims
|
||
|
||
status = warn
|
||
-> 正常校验 claims,但 rationale 可提及 warning
|
||
|
||
status = fail
|
||
-> 不允许 PASS
|
||
-> 相关 claim 至少应判 unsupported / external_unknown / contradicted
|
||
```
|
||
|
||
### 15.4 executor_output_parse_status
|
||
|
||
```text
|
||
status = valid
|
||
-> 使用结构化可推导性校验
|
||
|
||
status = missing / malformed
|
||
-> 不再回退到自然语言逐字抽取
|
||
-> verdict = LOW_CONFID
|
||
-> groundedness_score = 0
|
||
```
|
||
|
||
### 15.5 executor_raw_output
|
||
|
||
`executor_raw_output` 只用于审计和 debug。
|
||
|
||
当 `executor_structured_output` 合法时,Verifier 不应从 `executor_raw_output` 中抽取额外事实。
|
||
|
||
---
|
||
|
||
## 16. Verifier 输出结构
|
||
|
||
V2 Verifier 输出从 `facts_checked` 转向 `claim_checks`。
|
||
|
||
```json
|
||
{
|
||
"verdict": "LOW_CONFID",
|
||
"groundedness_score": 0.62,
|
||
"claim_checks": [
|
||
{
|
||
"claim_id": "claim-1",
|
||
"verification": "direct_observation",
|
||
"detail": "query_metrics 显示 CPU 使用率为 92%,可直接支撑 CPU 过高现象。",
|
||
"evidence_refs": [
|
||
{
|
||
"trace_ref": "trace-1",
|
||
"tool_name": "query_metrics",
|
||
"source_invocation_ids": [394],
|
||
"note": "指标摘要包含 CPU=92%"
|
||
}
|
||
]
|
||
}
|
||
],
|
||
"hypothesis_checks": [
|
||
{
|
||
"hypothesis_index": 0,
|
||
"verification": "reasonable_hypothesis",
|
||
"detail": "该假设明确标注为未证实,并列出需要补充的证据。"
|
||
}
|
||
],
|
||
"rationale": "已有证据支持超时现象,但根因仍缺少直接证据。"
|
||
}
|
||
```
|
||
|
||
兼容期建议同时保留 `facts_checked`:
|
||
|
||
- `claim_checks` 作为 V2 主字段。
|
||
- `facts_checked` 由 `claim_checks` 映射生成,供现有 Trace Workbench、eval 和降级输出继续使用。
|
||
- 待前端和评测全部迁移后,再移除 `facts_checked`。
|
||
|
||
### 16.1 字段定义
|
||
|
||
| 字段 | 类型 | 定义 |
|
||
|---|---|---|
|
||
| `verdict` | string | `PASS` / `LOW_CONFID` / `REJECT` |
|
||
| `groundedness_score` | number | 结构化 claim 的证据支撑评分 |
|
||
| `claim_checks` | array | 对 `claims` 的可推导性校验结果 |
|
||
| `claim_checks[].claim_id` | string | 被校验的 claim id |
|
||
| `claim_checks[].verification` | string | 可推导性判定 |
|
||
| `claim_checks[].detail` | string | 判定说明 |
|
||
| `claim_checks[].evidence_refs` | array | 使用的证据引用 |
|
||
| `hypothesis_checks` | array | 对 hypotheses 的边界校验 |
|
||
| `hypothesis_checks[].hypothesis_index` | number | hypothesis 数组下标 |
|
||
| `hypothesis_checks[].verification` | string | `reasonable_hypothesis` / `unsupported_hypothesis` / `overstated_as_fact` |
|
||
| `hypothesis_checks[].detail` | string | 判定说明 |
|
||
| `rationale` | string | 总体判定理由 |
|
||
|
||
### 16.1.1 facts_checked 兼容映射
|
||
|
||
兼容期内,ChatService 需要从 `claim_checks` 生成旧字段 `facts_checked`。
|
||
|
||
映射规则:
|
||
|
||
| `claim_checks[].verification` | `facts_checked[].verification` |
|
||
|---|---|
|
||
| `direct_observation` | `direct_evidence` |
|
||
| `reasonable_inference` | `indirect_support` |
|
||
| `overstated` | `indirect_support` |
|
||
| `unsupported` | `no_evidence` |
|
||
| `external_unknown` | `no_evidence` |
|
||
| `contradicted` | `contradicted` |
|
||
|
||
`facts_checked[].fact` 建议格式:
|
||
|
||
```text
|
||
{claim_id}: {claim_text}
|
||
```
|
||
|
||
`facts_checked[].is_critical` 规则:
|
||
|
||
```text
|
||
claim_type in ["root_cause", "symptom", "impact", "risk"] -> true
|
||
其它 -> false
|
||
```
|
||
|
||
`facts_checked[].evidence_refs` 直接复用 `claim_checks[].evidence_refs`。
|
||
|
||
### 16.2 claim verification 枚举
|
||
|
||
| verification | 含义 |
|
||
|---|---|
|
||
| `direct_observation` | 证据直接观测到该事实 |
|
||
| `reasonable_inference` | 证据没有逐字说明,但可以合理推出 |
|
||
| `overstated` | 有部分依据,但 claim 说得太满 |
|
||
| `unsupported` | 证据不足 |
|
||
| `external_unknown` | 引入证据外的新服务、数值、错误码、根因等 |
|
||
| `contradicted` | 与证据冲突 |
|
||
|
||
`external_unknown` 需要区分严重程度:
|
||
|
||
| 场景 | 建议 verdict |
|
||
|---|---|
|
||
| 引入证据外的核心服务名、订单号、错误码、关键指标值、根因 | `REJECT` |
|
||
| 引入非核心背景实体或表达不清 | `LOW_CONFID` |
|
||
|
||
---
|
||
|
||
## 17. Verifier 判定矩阵
|
||
|
||
### PASS
|
||
|
||
必须同时满足:
|
||
|
||
- `gatekeeper_result.status != fail`
|
||
- 所有核心 claims 均为 `direct_observation` 或 `reasonable_inference`
|
||
- 至少一个关键 claim 为 `direct_observation`
|
||
- 不存在 `overstated`
|
||
- 不存在 `unsupported`
|
||
- 不存在 `external_unknown`
|
||
- 不存在 `contradicted`
|
||
|
||
### LOW_CONFID
|
||
|
||
满足任一条件:
|
||
|
||
- 存在 `unsupported`
|
||
- 存在 `external_unknown`
|
||
- 存在 `overstated`
|
||
- 所有核心 claims 都只是 `reasonable_inference`
|
||
- `executor_output_parse_status.status` 为 `missing` 或 `malformed`
|
||
- `gatekeeper_result.status = fail`,但失败不属于严重伪造
|
||
|
||
### REJECT
|
||
|
||
满足任一条件:
|
||
|
||
- 任一核心 claim 为 `contradicted`
|
||
- Gatekeeper 发现严重伪造:
|
||
- 编造 `source_invocation_ids`
|
||
- 跨 session 引用
|
||
- `tool_name` 与 invocation 不匹配
|
||
- 使用真实 id 但 excerpt 明显张冠李戴
|
||
- Verifier 发现核心 `external_unknown`:
|
||
- 新增证据外服务名
|
||
- 新增证据外订单号
|
||
- 新增证据外错误码
|
||
- 新增证据外关键指标值
|
||
- 新增证据外根因
|
||
|
||
严重伪造由 Gatekeeper 规则元数据中的 `verdictImpact=reject_on_fail` 定义。
|
||
|
||
---
|
||
|
||
## 18. 与 V1 Verifier 的差异
|
||
|
||
| 项目 | V1 | V2 |
|
||
|---|---|---|
|
||
| 主校验对象 | `executor_final_answer` 和 structured output | `executor_structured_output` |
|
||
| 校验方式 | 从自然语言中抽取 facts 并逐条校验 | 对 claims 做可推导性校验 |
|
||
| 原始文本作用 | 事实抽取来源之一 | debug/fallback only |
|
||
| 输出字段 | `facts_checked` | `claim_checks` |
|
||
| Gatekeeper | 无 | 前置物理级校验 |
|
||
| malformed 输出 | 可回退自然语言校验 | 直接 LOW_CONFID |
|
||
|
||
---
|
||
|
||
## 19. Composer 设计
|
||
|
||
Composer 是最终表达层。它不负责诊断,不调用工具,不补充新事实。
|
||
|
||
```text
|
||
Executor structured output
|
||
-> Gatekeeper
|
||
-> Verifier
|
||
-> Composer
|
||
-> final answer
|
||
```
|
||
|
||
### 19.1 职责边界
|
||
|
||
| 组件 | 职责 |
|
||
|---|---|
|
||
| Executor | 输出结构化诊断材料 |
|
||
| Gatekeeper | 做物理级确定性校验 |
|
||
| Verifier | 判断 claims 是否可由证据合理推出 |
|
||
| Composer | 将 Verifier 允许输出的材料组织成用户可读中文答案 |
|
||
|
||
Composer 禁止:
|
||
|
||
- 调用工具。
|
||
- 重新诊断。
|
||
- 重新判断根因。
|
||
- 新增服务名、订单号、时间、指标值、错误码、根因。
|
||
- 把 hypothesis 写成 confirmed claim。
|
||
- 把相关性写成因果。
|
||
|
||
### 19.2 Composer 输入
|
||
|
||
Composer 输入应由 ChatService 根据 Executor、Gatekeeper、Verifier 的结果过滤得到。
|
||
|
||
```json
|
||
{
|
||
"original_query": "用户原始问题",
|
||
"verdict": "LOW_CONFID",
|
||
"allowed_claims": [
|
||
{
|
||
"claim_id": "claim-1",
|
||
"claim_type": "symptom",
|
||
"claim_text": "订单123支付失败期间出现 ERR_TIMEOUT,请求耗时 5.3 秒。"
|
||
}
|
||
],
|
||
"allowed_hypotheses": [
|
||
{
|
||
"hypothesis_text": "网关超时阈值可能偏低。",
|
||
"needed_evidence": ["网关超时阈值配置"]
|
||
}
|
||
],
|
||
"missing_info": [
|
||
"缺少网关侧具体配置参数,无法确认阈值是否过低。"
|
||
],
|
||
"recommended_actions": [
|
||
{
|
||
"action_text": "检查网关侧超时阈值配置",
|
||
"reason": "当前缺少网关配置参数,需确认阈值是否低于实际请求耗时。"
|
||
}
|
||
],
|
||
"rationale": "已有证据支持请求超时现象,但根因仍缺少直接证据。"
|
||
}
|
||
```
|
||
|
||
| 字段 | 类型 | 定义 |
|
||
|---|---|---|
|
||
| `original_query` | string | 用户原始问题 |
|
||
| `verdict` | string | Verifier verdict |
|
||
| `allowed_claims` | array | Verifier 允许作为确认事实输出的 claims |
|
||
| `allowed_hypotheses` | array | Verifier 允许作为合理推测输出的 hypotheses |
|
||
| `missing_info` | array | 证据缺口 |
|
||
| `recommended_actions` | array | 允许输出的建议动作 |
|
||
| `rationale` | string | Verifier 总体判定理由 |
|
||
|
||
Composer 不应接收 raw tool output,也不应接收未经筛选的完整 Executor 输出。
|
||
|
||
### 19.2.1 Composer 输入组装规则
|
||
|
||
ChatService 根据 Verifier 输出组装 Composer 输入:
|
||
|
||
| Verifier 结果 | Composer 输入 |
|
||
|---|---|
|
||
| `claim_checks[].verification = direct_observation` | 放入 `allowed_claims` |
|
||
| `claim_checks[].verification = reasonable_inference` | 可放入 `allowed_claims`,但表达时不得写成唯一根因 |
|
||
| `claim_checks[].verification = overstated` | 不放入 `allowed_claims`,可降级为 `allowed_hypotheses` 或 `missing_info` |
|
||
| `claim_checks[].verification = unsupported` | 不输出为事实,放入 `missing_info` |
|
||
| `claim_checks[].verification = external_unknown` | 不输出,必要时放入 `missing_info` |
|
||
| `claim_checks[].verification = contradicted` | 不输出,触发 `REJECT` 降级表达 |
|
||
|
||
`allowed_hypotheses` 只允许来自:
|
||
|
||
- 原始 `hypotheses`。
|
||
- `hypothesis_checks` 判为 `reasonable_hypothesis` 的条目。
|
||
- 被 Verifier 判为 `overstated` 后降级的 claim。
|
||
|
||
Composer 执行条件:
|
||
|
||
```text
|
||
Verifier 输出有效 verdict 后才执行 Composer。
|
||
executor_output_parse_status = missing/malformed 时,可跳过 Composer,直接使用固定低置信模板。
|
||
gatekeeper_result.status = fail 且 verdict = REJECT 时,Composer 只能接收 allowed_claims、missing_info、recommended_actions,不接收 allowed_hypotheses。
|
||
```
|
||
|
||
REJECT 场景下:
|
||
|
||
- `allowed_hypotheses` 必须为空。
|
||
- `user_facing_answer` 不得出现根因结论。
|
||
- 推荐动作只能是补证据或人工复核类动作。
|
||
|
||
### 19.3 Composer 输出
|
||
|
||
Composer 输出严格 JSON。
|
||
|
||
```json
|
||
{
|
||
"answer_summary": "当前已确认订单123支付失败期间出现 ERR_TIMEOUT,请求耗时 5.3 秒;网关阈值是否过低尚未确认。",
|
||
"recommended_actions": [
|
||
{
|
||
"action_text": "检查网关侧超时阈值配置",
|
||
"reason": "当前缺少网关配置参数,需确认阈值是否低于实际请求耗时。"
|
||
}
|
||
],
|
||
"user_facing_answer": "您的订单123在支付失败期间出现了请求超时,记录显示 ERR_TIMEOUT,耗时约 5.3 秒。目前还不能确认网关阈值配置就是根因,因为缺少网关侧具体配置参数。建议下一步检查网关超时阈值,并与该请求耗时进行比对。"
|
||
}
|
||
```
|
||
|
||
| 字段 | 类型 | 定义 |
|
||
|---|---|---|
|
||
| `answer_summary` | string | 一句话摘要,只能总结 Verifier 允许输出的内容 |
|
||
| `recommended_actions` | array | 面向用户的建议动作 |
|
||
| `recommended_actions[].action_text` | string | 建议动作 |
|
||
| `recommended_actions[].reason` | string | 建议原因 |
|
||
| `user_facing_answer` | string | 最终面向用户的中文答案 |
|
||
|
||
### 19.4 Verdict 表达规则
|
||
|
||
#### PASS
|
||
|
||
- 可以表达确认结论。
|
||
- 只能使用 `allowed_claims` 和 `recommended_actions`。
|
||
- 只有当 `allowed_claims` 中存在 root cause 类型 claim 时,才能使用“根因已确认”类表述。
|
||
|
||
#### LOW_CONFID
|
||
|
||
- 必须说明当前证据仍有缺口。
|
||
- 必须区分“已确认信息”和“可能方向”。
|
||
- 不得把 `allowed_hypotheses` 写成确认结论。
|
||
- 必须包含至少一个证据缺口或下一步建议。
|
||
|
||
#### REJECT
|
||
|
||
- 必须说明当前无法基于已获取证据生成可靠结论。
|
||
- 不得输出根因结论。
|
||
- 只输出已确认信息和下一步建议。
|
||
|
||
### 19.5 Composer Prompt 草案
|
||
|
||
```text
|
||
你是 Answer Composer。你的职责是把 Verifier 允许输出的结构化材料,组织成用户可读的中文答案。
|
||
|
||
边界:
|
||
- 你不负责诊断。
|
||
- 你不调用工具。
|
||
- 你不补充新事实。
|
||
- 你不重新判断根因。
|
||
- 你只能使用输入中的 allowed_claims、allowed_hypotheses、missing_info、recommended_actions、rationale。
|
||
|
||
输入字段:
|
||
- original_query:用户原始问题
|
||
- verdict:PASS / LOW_CONFID / REJECT
|
||
- allowed_claims:允许作为确认事实输出的结论
|
||
- allowed_hypotheses:允许作为合理推测输出的内容
|
||
- missing_info:证据缺口
|
||
- recommended_actions:建议动作
|
||
- rationale:Verifier 判定理由
|
||
|
||
表达规则:
|
||
1. 如果 verdict=PASS:
|
||
- 可以表达确认结论。
|
||
- 只能使用 allowed_claims 和 recommended_actions。
|
||
2. 如果 verdict=LOW_CONFID:
|
||
- 必须说明当前证据仍有缺口。
|
||
- 必须区分“已确认”和“可能方向”。
|
||
- 不得把 allowed_hypotheses 写成确认结论。
|
||
3. 如果 verdict=REJECT:
|
||
- 必须说明当前无法基于已获取证据生成可靠结论。
|
||
- 不得输出根因结论。
|
||
- 只输出已确认信息和下一步建议。
|
||
|
||
禁止:
|
||
- 禁止新增输入中不存在的服务名、订单号、时间、指标值、错误码、根因。
|
||
- 禁止把相关性写成因果。
|
||
- 禁止把假设写成事实。
|
||
- 禁止输出 Markdown。
|
||
- 禁止输出 JSON 之外的任何文字。
|
||
|
||
输出格式:
|
||
{
|
||
"answer_summary": "...",
|
||
"recommended_actions": [
|
||
{
|
||
"action_text": "...",
|
||
"reason": "..."
|
||
}
|
||
],
|
||
"user_facing_answer": "..."
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 20. 当前代码影响面
|
||
|
||
本 issue 按当前三 Agent 实现增量落地,不改 Planner,不引入 Controller。
|
||
|
||
| 模块 | 当前职责 | 本次改动 |
|
||
|---|---|---|
|
||
| `chat-planner-prompt.md` | 选择 skill、拆解计划 | 不改 |
|
||
| `chat-executor-prompt.md` | 执行工具并输出 `executor_evidence_v1` | 改为输出 `executor_evidence_v2`,移除最终表达字段 |
|
||
| `VerifierInputHook` | 解析 Executor 输出、构造 Verifier payload | 收敛为 parse + trace summary + Gatekeeper + payload |
|
||
| `ToolTraceSummaryService` | 从 `tool_invocation` 汇总证据索引 | 原则上不改;只有 Gatekeeper 缺少比对字段时才补最小字段 |
|
||
| `chat-verifier-prompt.md` | 校验 `executor_final_answer` 与证据 | 改为校验 `executor_structured_output.claims` 的可推导性 |
|
||
| `ChatService.parseVerifierDecision(...)` | 解析 `facts_checked` | 增加 `claim_checks` 解析和 `facts_checked` 兼容生成 |
|
||
| `ChatService.persistVerifierEvaluation(...)` | 写入 verifier 审计结果 | 增加 `gatekeeper_result`、`claim_checks`、Composer 结果 |
|
||
| `ChatService` 最终答案渲染 | PASS 使用 `user_facing_answer`,LOW_CONFID/REJECT 用模板 | 改为由 Composer 或固定降级模板生成最终答案 |
|
||
|
||
第一版落地原则:
|
||
|
||
- Planner 不改。
|
||
- Gatekeeper 仍放在 Verifier 的 hook,即 `VerifierInputHook`。
|
||
- `VerifierInputHook` 原有结构校验迁移到 Gatekeeper,hook 自身只保留 JSON parse。
|
||
- 兼容期保留 `executor_final_answer`,但只作为 raw debug 字段。
|
||
- 兼容期保留 `facts_checked`,但由 `claim_checks` 映射生成。
|
||
|
||
---
|
||
|
||
## 21. 实施阶段
|
||
|
||
每个阶段都必须作为独立 OpenSpec change 落地。后续 agent 不应直接从本节复制代码实现,而应把本节转成该阶段的 `proposal.md`、`design.md`、`spec.md` 和 `tasks.md`。
|
||
|
||
### 阶段一:Executor V2 输出契约
|
||
|
||
状态:已完成并归档。
|
||
|
||
已完成记录:
|
||
|
||
- OpenSpec archive:`openspec/changes/archive/2026-07-07-executor-v2-output-contract`
|
||
- devflow:`devflow/projects/2026-07-07-executor-v2-output-contract`
|
||
- commit:`050cbc8 feat(agent): add executor evidence v2 contract`
|
||
|
||
目标:先让 Executor 不再输出最终诊断话术,只输出结构化诊断材料。
|
||
|
||
改动范围:
|
||
|
||
- 更新 `chat-executor-prompt.md`。
|
||
- `answer_version` 从 `executor_evidence_v1` 改为 `executor_evidence_v2`。
|
||
- 移除 `diagnosis_summary` 和 `user_facing_answer`。
|
||
- 保留 `claims`、`hypotheses`、`recommended_actions`、`missing_info` 的整体形状。
|
||
|
||
验收标准:
|
||
|
||
- Executor 示例输出可被 JSON 解析。
|
||
- 输出中不再出现 `diagnosis_summary`、`user_facing_answer`。
|
||
- `claims[].evidence_bindings` 仍要求非空。
|
||
- 现有流程即使尚未接入 Composer,也不会把 Executor raw JSON 直接当最终答案泄露给用户。
|
||
|
||
已知阶段性债务:
|
||
|
||
- `ChatService` 仍有临时 V2 renderer,用于 Composer 上线前避免 raw JSON 外泄。
|
||
- Verifier 仍以 `facts_checked` 为主,`claim_checks` 等待阶段三。
|
||
|
||
### 阶段二:Gatekeeper 接入 VerifierInputHook
|
||
|
||
状态:已完成并归档。
|
||
|
||
已完成记录:
|
||
|
||
- OpenSpec archive:`openspec/changes/archive/2026-07-07-executor-gatekeeper-hook`
|
||
- devflow:`devflow/projects/2026-07-07-executor-gatekeeper-hook`
|
||
- commit:`c5e496e feat(agent): add executor gatekeeper hook`
|
||
|
||
目标:在 Verifier 之前用确定性规则拦截物理级幻觉。
|
||
|
||
改动范围:
|
||
|
||
- 新增 Gatekeeper 校验服务,第一版先覆盖必要规则。
|
||
- 在 `VerifierInputHook` 中调用 Gatekeeper。
|
||
- 将 `gatekeeper_result` 写入 Verifier payload。
|
||
- 将 `gatekeeper_result` 持久化到 `self_evaluation.verifier_evaluation`。
|
||
- 更新 `chat-verifier-prompt.md`,明确 Gatekeeper fail 时不得 PASS。
|
||
|
||
第一版规则范围:
|
||
|
||
| 规则 | 必须实现 | 说明 |
|
||
|---|---:|---|
|
||
| `schema.executor_v2` | 是 | 校验 V2 基础字段、移除字段、数组字段 |
|
||
| `evidence.invocation_ref` | 是 | 校验 invocation id 存在于当前 session 且工具名匹配 |
|
||
| `evidence.excerpt_similarity` | 否 | 可作为后续阶段或 warn-only 增强 |
|
||
| `claim.hallucination_phrases` | 否 | 可作为后续配置化规则增强 |
|
||
| `claim.evidence_utilization` | 否 | 第一版不建议硬启用,避免中文分词误杀 |
|
||
|
||
验收标准:
|
||
|
||
- JSON parse 成功但 schema 不合法时,`executor_output_parse_status.status=valid`,`gatekeeper_result.status=fail`。
|
||
- 编造不存在的 `source_invocation_ids` 时,`failed_rules` 包含 `evidence.invocation_ref`。
|
||
- `tool_name` 与真实 invocation 不匹配时,Gatekeeper fail。
|
||
- 缺省 `hypotheses`、`recommended_actions`、`missing_info` 时,传给 Verifier 前会 normalize 为 `[]`。
|
||
- 旧的 `VerifierInputHook` 结构校验不再和 Gatekeeper 重复维护。
|
||
- `diagnosis_session.self_evaluation.verifier_evaluation.gatekeeper_result` 可回溯到本轮校验结果。
|
||
|
||
建议测试:
|
||
|
||
- `ExecutorGatekeeperServiceTest`
|
||
- `VerifierInputHookTest`
|
||
- `ChatServiceSequentialAgentTest`
|
||
|
||
建议验证命令:
|
||
|
||
```powershell
|
||
mvn "-Dtest=ExecutorGatekeeperServiceTest,VerifierInputHookTest,ChatServiceSequentialAgentTest" test
|
||
cmd /c openspec validate --specs
|
||
```
|
||
|
||
阶段退出条件:
|
||
|
||
- OpenSpec change 已 archive。
|
||
- devflow 已回填 brief、evidence、decisions、acceptance。
|
||
- 本阶段代码和文档已单独 commit。
|
||
|
||
### 阶段三:Verifier V2 可推导性校验
|
||
|
||
状态:实施中。
|
||
|
||
当前 OpenSpec change:
|
||
|
||
```text
|
||
openspec/changes/executor-verifier-claim-checks
|
||
```
|
||
|
||
接手说明:
|
||
|
||
- 如果该 active change 仍存在,继续完成阶段三,不要启动阶段四。
|
||
- 阶段三已有 OpenSpec proposal/design/spec/tasks,后续实现应先对照 `tasks.md` 逐项完成。
|
||
- 阶段三完成后必须 archive、回填 devflow、单独 commit,推荐提交信息:`feat(agent): add verifier claim checks`。
|
||
|
||
目标:Verifier 不再逐字扫描自然语言,而是校验 claim 是否能由证据合理推出。
|
||
|
||
改动范围:
|
||
|
||
- 更新 `chat-verifier-prompt.md`。
|
||
- Verifier 主输入改为 `executor_structured_output`。
|
||
- 输出新增 `claim_checks`。
|
||
- 兼容期继续输出或由代码生成 `facts_checked`。
|
||
- `parseVerifierDecision(...)` 能处理 `claim_checks` 和旧 `facts_checked`。
|
||
|
||
核心设计:
|
||
|
||
- Verifier 主校验对象是 `executor_structured_output.claims`。
|
||
- `executor_raw_output` / `executor_final_answer` 只作为 debug/fallback 字段,不作为新增事实来源。
|
||
- Verifier 不要求 claim 与证据逐字一致,而是判断是否可以合理推出。
|
||
- Gatekeeper fail 时,Verifier 不允许输出 `PASS`。
|
||
|
||
验收标准:
|
||
|
||
- `direct_observation`、`reasonable_inference`、`overstated`、`unsupported`、`external_unknown`、`contradicted` 均有测试覆盖。
|
||
- `gatekeeper_result.status=fail` 时 Verifier 不允许输出 `PASS`。
|
||
- malformed/missing Executor 输出不回退自然语言抽事实,直接进入 `LOW_CONFID`。
|
||
- `facts_checked` 兼容字段能继续支撑现有低置信模板、retry_context 和评测用例。
|
||
|
||
建议测试重点:
|
||
|
||
- `parseVerifierDecision(...)` 能解析 `claim_checks`。
|
||
- `facts_checked` 能从 `claim_checks` 兼容映射。
|
||
- `gatekeeper_result.status=fail` + Verifier 返回 PASS 时,代码侧应降级或测试 prompt 禁止该行为。
|
||
- `unsupported` / `overstated` 能进入低置信模板需要的 missing evidence 语义。
|
||
|
||
建议验证命令:
|
||
|
||
```powershell
|
||
mvn "-Dtest=ExecutorGatekeeperServiceTest,VerifierInputHookTest,ChatServiceSequentialAgentTest" test
|
||
cmd /c openspec validate executor-verifier-claim-checks
|
||
cmd /c openspec validate --specs
|
||
```
|
||
|
||
阶段退出条件:
|
||
|
||
- Verifier prompt 已不再要求逐字扫描最终自然语言答案。
|
||
- 审计中同时可见 `claim_checks` 和兼容 `facts_checked`。
|
||
- 现有低置信重试链路不回归。
|
||
- OpenSpec change 已 archive。
|
||
- devflow 已回填 brief、evidence、decisions、acceptance。
|
||
- 本阶段代码和文档已单独 commit。
|
||
|
||
### 阶段四:Composer 输出最终答案
|
||
|
||
状态:未开始。
|
||
|
||
目标:把最终用户表达从 Executor 中移出,由 Composer 基于 Verifier 允许的材料生成。
|
||
|
||
改动范围:
|
||
|
||
- 新增 `chat-composer-prompt.md` 或等价 Composer 调用。
|
||
- ChatService 在 Verifier 之后组装 Composer 输入。
|
||
- Composer 只接收 `allowed_claims`、`allowed_hypotheses`、`missing_info`、`recommended_actions`、`rationale`。
|
||
- PASS/LOW_CONFID/REJECT 均不再读取 Executor 的 `user_facing_answer`。
|
||
|
||
核心设计:
|
||
|
||
- Composer 是表达层,不是诊断层。
|
||
- Composer 不接收 raw tool output。
|
||
- Composer 不接收未经筛选的完整 Executor output。
|
||
- ChatService 负责根据 `claim_checks` 过滤出 `allowed_claims`。
|
||
- `overstated` claim 不得作为确认事实输出,可降级为 hypothesis 或 missing_info。
|
||
|
||
验收标准:
|
||
|
||
- Composer 输出严格 JSON,包含 `answer_summary`、`recommended_actions`、`user_facing_answer`。
|
||
- Composer 不接收 raw tool output。
|
||
- `REJECT` 时 `allowed_hypotheses=[]`,最终答案不出现根因结论。
|
||
- `LOW_CONFID` 时必须区分已确认信息和可能方向。
|
||
- `PASS` 时只有存在 root cause 类型 allowed claim,才允许表达“根因已确认”。
|
||
- `ChatService` 不再依赖临时 V2 renderer 生成 PASS 用户答案。
|
||
|
||
建议测试重点:
|
||
|
||
- Composer 输入不包含 raw tool output。
|
||
- 未通过 Verifier 的 claim 不进入最终答案。
|
||
- `LOW_CONFID` 能输出已确认信息、缺口、下一步建议。
|
||
- `REJECT` 不输出 root cause 结论。
|
||
- Composer malformed 输出时有降级策略,不向用户泄露 raw JSON。
|
||
|
||
阶段退出条件:
|
||
|
||
- 最终用户答案唯一来源是 Composer 或固定降级模板。
|
||
- Executor 的 `user_facing_answer` 不再参与任何最终答案路径。
|
||
- Stage 1 的临时 renderer 被移除或明确只作为关闭 Composer 时的安全降级,不恢复 Executor 表达。
|
||
|
||
### 阶段五:回归评测与审计闭环
|
||
|
||
状态:未开始。
|
||
|
||
目标:确认新链路真的降低证据归因幻觉,而不是只改变字段名。
|
||
|
||
改动范围:
|
||
|
||
- 扩展 `VerifierInputHookTest`。
|
||
- 扩展 `ChatServiceSequentialAgentTest`。
|
||
- 增加诊断 eval fixtures,覆盖伪造 ID、张冠李戴、过度推断、缺证据降级。
|
||
- 检查 `diagnosis_session.self_evaluation` 中的审计字段。
|
||
|
||
验收标准:
|
||
|
||
- 伪造 invocation id 必须进入 `REJECT` 或至少不可 `PASS`。
|
||
- excerpt 相似度不足但 invocation 合法时,默认进入 `LOW_CONFID`,不直接误杀为严重伪造。
|
||
- Executor 把 hypothesis 写进 claim 时,Verifier 至少判 `overstated` 或 `unsupported`。
|
||
- 最终答案中不再出现未通过 Verifier 的 claim。
|
||
- 审计链路能从 final answer 回溯到 Composer 输入、Verifier 判定、Gatekeeper 结果、tool invocation。
|
||
|
||
建议 fixture 场景:
|
||
|
||
| 场景 | 预期 |
|
||
|---|---|
|
||
| 伪造不存在的 invocation id | Gatekeeper fail,最终不可 PASS |
|
||
| 引用真实 id 但 tool_name 不匹配 | Gatekeeper fail,倾向 REJECT |
|
||
| excerpt 与真实工具输出明显不符 | Gatekeeper fail 或 warn,最终不可 PASS |
|
||
| hypothesis 写成 root_cause claim | Verifier 判 `overstated` 或 `unsupported` |
|
||
| Composer 输入不含某错误码但输出包含该错误码 | 测试失败 |
|
||
|
||
阶段退出条件:
|
||
|
||
- 关键回归测试已进入 CI 可运行测试集合。
|
||
- eval fixture 覆盖证据归因幻觉的主路径。
|
||
- 文档、OpenSpec、devflow 与实际代码行为一致。
|
||
|
||
---
|
||
|
||
## 22. 兼容与回滚策略
|
||
|
||
### 22.1 兼容字段
|
||
|
||
第一版保留以下兼容字段,降低一次性改动风险:
|
||
|
||
| 字段 | 保留原因 | 后续处理 |
|
||
|---|---|---|
|
||
| `executor_final_answer` | 当前 Verifier payload、fallback、日志中仍使用该名称 | 作为 deprecated raw 字段保留一版 |
|
||
| `facts_checked` | 当前低置信模板、retry_context、评测可能依赖 | 从 `claim_checks` 映射生成 |
|
||
| `traceability_version` | 当前审计结构已有字段 | V2 可改为 `v2`,但不作为功能判断依据 |
|
||
|
||
### 22.2 回滚开关
|
||
|
||
建议保留最小运行时开关:
|
||
|
||
```text
|
||
structuredOutputV2.enabled
|
||
gatekeeper.enabled
|
||
composer.enabled
|
||
```
|
||
|
||
回滚策略:
|
||
|
||
- 仅 Executor V2 出问题:关闭 `structuredOutputV2.enabled`,回到 V1 prompt。
|
||
- Gatekeeper 误杀:关闭 `gatekeeper.enabled`,Verifier 仍可按 V2 claim 校验运行。
|
||
- Composer 输出异常:关闭 `composer.enabled`,回到固定 LOW_CONFID/REJECT 模板;PASS 暂不直接使用 Executor 输出。
|
||
|
||
即使回滚 Composer,也不能恢复使用 Executor 的 `user_facing_answer`,否则会重新引入本 issue 要解决的问题。
|
||
|
||
---
|
||
|
||
## 23. 数据库与审计最小字段
|
||
|
||
不新增表,第一版继续写入 `diagnosis_session.self_evaluation`。
|
||
|
||
建议结构:
|
||
|
||
```json
|
||
{
|
||
"verifier_evaluation": {
|
||
"verdict": "LOW_CONFID",
|
||
"groundedness_score": 0.62,
|
||
"critical_fact_count": 1,
|
||
"claim_checks": [],
|
||
"facts_checked": [],
|
||
"rationale": "...",
|
||
"round": 1,
|
||
"traceability_version": "v2",
|
||
"executor_output_parse_status": {},
|
||
"executor_structured_output": {},
|
||
"tool_trace_summary": [],
|
||
"gatekeeper_result": {},
|
||
"composer_output": {}
|
||
}
|
||
}
|
||
```
|
||
|
||
字段控制原则:
|
||
|
||
- Gatekeeper 只落 `status`、`failed_rules`、`warnings`、`errors`。
|
||
- 不在数据库中保存规则元数据完整快照。
|
||
- 不新增 `checked_at`、`rule_set_version`、`details` 等重字段。
|
||
- 如果后续需要复盘规则版本,再单独设计审计版本字段。
|
||
|
||
---
|
||
|
||
## 24. 实施前待确认点
|
||
|
||
以下问题不阻塞第一阶段,但实施前需要明确默认答案:
|
||
|
||
| 问题 | 建议默认 |
|
||
|---|---|
|
||
| Composer 是第四个 Agent 还是普通服务调用? | 作为 `chat_composer` Agent,但由 ChatService 在 Verifier 后过滤输入再调用 |
|
||
| `schema.executor_v2` 失败是否 REJECT? | 第一版 `LOW_CONFID`,因为它可能是格式退化,不一定是伪造 |
|
||
| `evidence.excerpt_similarity` 失败是否 REJECT? | 默认 `LOW_CONFID`;只有明显张冠李戴才 REJECT |
|
||
| `claim.evidence_utilization` 是否启用? | 第一版禁用或 warn-only,避免中文分词误杀 |
|
||
| 是否改 Planner? | 不改 |
|
||
| 是否改现有重试机制? | 不改;Gatekeeper 失败第一版不触发自动回退重试 |
|
||
|
||
---
|
||
|
||
## 25. Definition of Done
|
||
|
||
本 issue 完成时,需要同时满足:
|
||
|
||
- Executor 不再输出 `diagnosis_summary` 和 `user_facing_answer`。
|
||
- Gatekeeper 已接入 `VerifierInputHook`,并进入 Verifier payload 与 `self_evaluation`。
|
||
- Verifier 以 `claim_checks` 为主输出,并保留 `facts_checked` 兼容。
|
||
- Composer 负责最终 `user_facing_answer`。
|
||
- PASS 答案不包含未通过 Verifier 的 claim。
|
||
- LOW_CONFID/REJECT 答案不泄露 Executor 原始结论。
|
||
- 关键回归测试覆盖伪造 ID、工具名不匹配、excerpt 张冠李戴、hypothesis 写成事实、schema 退化。
|