Files
SuperBizAgent-java/mvp/issues/ISS-007-verifier-evidence-summary-fidelity.md
T

1536 lines
52 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.
# ISS-007 Verifier 证据摘要保真与工具命中质量问题
**状态**:设计已收口,待实施
**严重程度**:高
**发现时间**:2026-07-08
**来源**:Executor V2 / Gatekeeper / Verifier / Composer 端到端验证
**关联**:
- `executor-structured-output-v2`
- `executor-evidence-attribution-hallucination`
- `ISS-005-evidence-trace-hardening`
- `ISS-006-diagnosis-eval-harness`
---
## 背景
当前复杂诊断链路已经形成:
```text
chat_planner
-> chat_executor
-> VerifierInputHook 内 Gatekeeper
-> chat_verifier
-> chat_composer
-> final answer
```
最近端到端验证中,部分窄范围问题本应具备足够证据,但最终仍被 Verifier 判为 `LOW_CONFID`。进一步查看审计数据后发现,这几类失败并不完全是 Executor 幻觉,也不完全是 Verifier 代码 bug,而是暴露出一组更经典的工程问题:
1. 工具原始返回和 Executor `evidence_excerpt` 中存在证据,但 `tool_trace_summary.output_summary` 压缩后丢失关键证据。
2. Verifier 当前更依赖工具摘要,而不是直接消费 Executor 绑定的原文片段。
3. mock 工具命中规则存在质量问题,某些查询返回了 `generic-service` 占位日志,导致负向结论虽然被正确识别,但无法验证正向路径。
4. Executor 对窄范围问题仍可能输出过多扩展 claim,增加 Verifier 判定压力。
这个 issue 的目标不是立即确定实现方案,而是把这些问题归档为一组可讨论、可优化、可验收的工程问题。
---
## 复现样例
### 1. HighCPUUsage:正向 PASS
**Session**:`e2e-pass-highcpu-raw-20260708-1106`
**问题范围**:只确认 `payment-service` 当前是否存在 CPU 使用率过高告警。
**结果**:
- `verdict=PASS`
- `groundedness_score=1.0`
- `gatekeeper_result.status=pass`
- `composer_output.status=valid`
**结论**:
- 工具 mock、日志、知识库 runbook 三者可以匹配。
- 这是当前链路能够通过的健康样例,可作为后续回归基线。
---
### 2. HighMemoryUsage:证据存在但 LOW_CONFID
**Session**:`e2e-pass-memory-20260708-1117-a1`
**结果**:
- `verdict=LOW_CONFID`
- `groundedness_score=0.2`
- `gatekeeper_result.status=pass`
**审计发现**:
- Executor 的 `evidence_excerpt` 中包含完整证据:
- `HighMemoryUsage` firing
- 当前内存使用率 `91%`
- JVM 堆内存 `3.8GB/4GB`
- 持续 `15m`
- 多个采样点:`86.2 -> 87.4 -> 88.6 -> 89.8 -> 91.0`
- Full GC `15` 次,平均 `850ms`
- 但 `tool_trace_summary.output_summary` 只保留了一条内存日志:
- `[11:17][WARN][order-service] 内存使用率过高: 91.0%, JVM堆内存: 3.8GB/4GB, GC次数: 128`
**初步归类**:
- 主要问题:`ToolTraceSummaryService` 摘要压缩丢失趋势和 GC 证据。
- 次要问题:Verifier 未直接使用 Executor 的 `evidence_excerpt` 做可推导性判断。
- 不是主要幻觉问题:Executor 引用的片段里确实包含关键事实。
---
### 3. SlowResponse:证据存在但 LOW_CONFID
**Session**:`e2e-pass-slowresponse-20260708-1117-a1`
**结果**:
- `verdict=LOW_CONFID`
- `groundedness_score=0.2`
- `gatekeeper_result.status=pass`
**审计发现**:
- Executor 的 `evidence_excerpt` 中包含:
- `SlowResponse` alert:`user-service P99=4.2s`,`firing`,持续 `10m`
- 慢请求日志:
- `/api/v1/users/profile`: `3600 / 3900 / 4200ms`
- `/api/v1/users/orders`: `3750 / 4050ms`
- 但 `tool_trace_summary.output_summary` 只保留了一条 `/api/v1/users/profile 4200ms`。
- `query_metrics` summary 没有完整展示 `SlowResponse` firing alert。
**初步归类**:
- 主要问题:工具摘要没有保留足够多的关键事件和指标。
- 次要问题:Verifier 过度依赖 summary,导致它看不到 Executor 已经绑定的证据。
- 不是主要幻觉问题:证据片段本身存在。
---
### 4. HikariCP:负向 PASS,但正向路径缺失
**Session**:`e2e-pass-hikari-20260708-1117-a1`
**结果**:
- `verdict=PASS`
- `groundedness_score=1.0`
- `gatekeeper_result.status=pass`
**实际结论**:
```text
application-logs 中未检索到 order-service 的真实 HikariCP 连接池耗尽日志
```
**审计发现**:
- mock 工具返回的是 `generic-service` 占位日志。
- `evidence_level=none`
- `no_hit_invocation_count=6`
- Verifier 正确识别“没查到真实 HikariCP 日志”。
**初步归类**:
- Verifier 的负向 PASS 是合理的。
- 真正问题在 mock 工具命中质量:`HikariCP`、`active=50/50`、`order-service` 等查询没有命中正向 mock 分支。
---
## 问题归类
| 问题 | 类型 | 严重程度 | 当前判断 |
|---|---|---:|---|
| `tool_trace_summary.output_summary` 只保留少量日志,丢失趋势、采样点、关键 alert | 工具摘要质量 | 高 | 需要优化 |
| Verifier 主要依赖 summary,未充分利用 Executor `evidence_bindings[].evidence_excerpt` | 架构 / 输入设计 | 高 | 需要讨论 |
| `query_metrics` summary 没有完整展示所有 firing alert | 工具摘要质量 | 高 | 需要优化 |
| Executor 对窄范围问题输出多条扩展 claim | Agent 约束 | 中 | 需要讨论 |
| mock 日志返回 `generic-service` 占位日志 | 工具 / mock 质量 | 中 | 需要优化 |
| Gatekeeper pass 但 Verifier LOW_CONFID | 分层行为 | 低 | 不是问题,属于正常分工 |
---
## 当前结论
### Memory / SlowResponse
主要不是 LLM 编造事实,也不是 Gatekeeper 漏拦,而是证据在链路中被摘要层压缩丢失。
当前证据链路存在一个断点:
```text
tool raw output / executor evidence_excerpt 有证据
-> tool_trace_summary.output_summary 丢失部分证据
-> verifier 看不到足够材料
-> LOW_CONFID
```
### HikariCP
当前负向 PASS 是合理的,因为系统确实没有检索到真实 `order-service` HikariCP 连接池耗尽证据。
真正需要优化的是工具 mock 的命中规则和 fixture 数据质量,否则后续很难验证 HikariCP 的正向路径。
---
## 已确认设计方向:Executor 引用证据,Gatekeeper 核对引用
针对“摘要失真”问题,优先采用以下主线,而不是继续让 Executor 或 `ToolTraceSummaryService` 承担更重的自然语言压缩职责:
```text
tool raw output
-> Executor 产出 claim + 引用证据片段
-> Gatekeeper 用代码核对引用是否真实
-> Verifier 判断 claim 是否能由已核对证据推出
-> Composer 只表达 Verifier 允许输出的内容
```
### 分工边界
#### Executor:证据引用器,而不是可信摘要器
Executor 不再把工具结果压缩成一段“可信诊断摘要”后交给 Verifier 判断,而是负责:
- 产出结构化 claim。
- 为每个 claim / recommended action 绑定证据引用。
- 引用真实 `source_invocation_id + raw_path`。
- 给出与 claim 直接相关的 `evidence_excerpt`。
- 不编造 invocation id。
- 不把 runbook 通用经验升级成当前已确认事实。
目标是让 Executor “少总结,多引用”。
#### Gatekeeper:引用真实性校验
Gatekeeper 负责用代码检查 Executor 的引用是否真实,先拦截物理级幻觉:
- `source_invocation_id` 是否真实存在于本轮 session 的 tool invocation 历史中。
- `source_invocation_id` 是否属于允许被引用的 evidence tool。
- `evidence_excerpt` 是否能在对应 tool invocation 的原始输出中找到,或达到可接受的相似度。
- 是否存在“真实 invocation id + 编造 excerpt”的张冠李戴问题。
- 是否存在空证据、伪造证据、引用无关工具输出等问题。
Gatekeeper 只判断“引用是否真实”,不判断“claim 是否成立”。
#### Verifier:判断可推导性
Verifier 在 Gatekeeper 通过后,再判断:
- 这些已核对的证据是否足以支持 claim。
- claim 是否存在过度推断。
- claim 是否只是合理怀疑,而不是已确认事实。
- recommended action 是否能由当前证据和缺口合理推出。
Verifier 不再把主要精力放在逐字核对引用真伪上,而是做“证据能否推出结论”的判断。
#### ToolTraceSummary:全局导航摘要,不再作为唯一证据源
`tool_trace_summary.output_summary` 仍然保留,但定位调整为:
- 帮助 Verifier / 审计理解本轮工具调用全貌。
- 提供全局 evidence overview。
- 作为辅助证据视图,而不是唯一判定依据。
也就是说,summary 可以继续优化,但不应该再承担“唯一证据源”的职责。
### 这个方向解决的问题
该设计直接针对当前 Memory / SlowResponse 的失败模式:
```text
工具原始返回和 Executor evidence_excerpt 有证据
-> summary 压缩时丢失关键证据
-> Verifier 看不到足够材料
-> LOW_CONFID
```
调整后变为:
```text
工具原始返回有证据
-> Executor 引用关键片段
-> Gatekeeper 校验片段来自真实工具输出
-> Verifier 基于已核对片段判断 claim 是否可推出
```
这样即使 `output_summary` 较短,Verifier 仍能看到 claim-local evidence。
### 设计约束
- Verifier 不能无条件相信 Executor 的 `evidence_excerpt`。
- Gatekeeper 的 excerpt 原文回溯是 Verifier 消费 excerpt 的前置安全条件。
- Gatekeeper 校验通过不代表 claim 通过,只代表“引用是真的”。
- Verifier 判为 `PASS` 必须基于“真实引用 + 可推导结论”两个条件同时成立。
- 不能通过简单放宽 Verifier 阈值来掩盖证据传递问题。
---
## Verifier 输入数据结构定义(当前设计)
本节定义“Executor 引用证据,Gatekeeper 核对引用”方案下,进入 Verifier 的数据结构。核心原则:
```text
证据定位:source_invocation_id + raw_path
证据文本:evidence_excerpt
证据真实性:由 Gatekeeper 在 Verifier 前校验
Verifier 职责:只判断 claim_text 是否能由已核验 evidence_excerpt 推出
```
### 1. 工具调用侧最小证据引用
当前工具尚未统一返回 `evidence_items`。第一版不引入复杂 metadata,也不要求新增数据库表,建议在 `tool_invocation.retrieval_details` 中补充最小 `evidence_refs`:
```json
{
"evidence_status": "supported",
"evidence_refs": [
{
"raw_path": "$.alerts[1]",
"text": "HighMemoryUsage firing, service=order-service, current=91%, duration=15m; JVM heap usage 3.8GB/4GB"
}
]
}
```
字段定义:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---:|---|
| `raw_path` | string | 是 | 证据在该工具返回中的稳定定位符。第一版只支持 `$.alerts[i]`、`$.logs[i]`、`$.evidence_blocks[i]`。 |
| `text` | string | 是 | 系统从该位置提取出的最小证据文本。Gatekeeper 用它比对 Executor 的 `evidence_excerpt`,Verifier 用 `evidence_excerpt` 做推导判断。 |
设计约束:
- 第一版不需要 `metadata`。
- 第一版不需要 `evidence_id`。
- `raw_path` 只在单次 `tool_invocation` 内有意义,必须和 `source_invocation_id` 搭配使用。
- 如果工具返回会被 `output_preview` 截断,`evidence_refs[].text` 必须保存在 `retrieval_details` 中,不能只依赖 `output_preview`。
### 2. Executor 结构化输出
Executor 输出不再把证据压缩成可信摘要,而是输出 claim 与证据引用:
```json
{
"claims": [
{
"claim_id": "claim-1",
"claim_type": "observation",
"claim_text": "order-service 当前存在 HighMemoryUsage 告警,内存使用率达到 91%。",
"evidence_bindings": [
{
"tool_name": "query_metrics",
"source_invocation_id": 12345,
"raw_path": "$.alerts[1]",
"evidence_excerpt": "HighMemoryUsage firing, service=order-service, current=91%, duration=15m"
}
]
}
],
"hypotheses": [
{
"hypothesis_id": "hyp-1",
"hypothesis_text": "order-service 可能存在内存泄漏风险。",
"basis_claim_ids": ["claim-1"],
"missing_info": "缺少堆 dump、对象分配统计或更长时间窗口的内存曲线,不能确认内存泄漏。"
}
],
"missing_info": [
{
"info_id": "missing-1",
"description": "缺少堆 dump 或对象分配统计,无法确认内存泄漏根因。"
}
],
"recommended_actions": [
{
"action_id": "action-1",
"action_text": "继续查看 order-service 的 GC 日志、堆 dump 或对象分配统计。",
"reason": "当前证据可以确认内存使用率过高,但不足以确认是否存在内存泄漏。",
"evidence_bindings": [
{
"tool_name": "query_metrics",
"source_invocation_id": 12345,
"raw_path": "$.alerts[1]",
"evidence_excerpt": "HighMemoryUsage firing, service=order-service, current=91%, duration=15m"
}
]
}
]
}
```
字段定义:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---:|---|
| `claims` | array | 是 | Executor 提出的待验证事实断言。 |
| `claims[].claim_id` | string | 是 | claim 唯一标识,供 Verifier / Composer 引用。 |
| `claims[].claim_type` | string | 是 | claim 类型,例如 `observation`、`root_cause`、`risk`、`negative_observation`。 |
| `claims[].claim_text` | string | 是 | Executor 提出的事实断言,不是证据本体,也不是已验证结论。 |
| `claims[].evidence_bindings` | array | 是 | 支撑该 claim 的证据引用列表。 |
| `evidence_bindings[].tool_name` | string | 是 | 证据来源工具,例如 `query_metrics`、`query_logs`、`lookup_knowledge`。 |
| `evidence_bindings[].source_invocation_id` | number | 是 | 单次真实工具调用 ID。第一版使用单数;如果一个 claim 需要多个工具调用,拆成多条 binding。 |
| `evidence_bindings[].raw_path` | string | 是 | 该证据在工具返回中的路径,和 `source_invocation_id` 共同定位唯一证据位置。 |
| `evidence_bindings[].evidence_excerpt` | string | 是 | Executor 引用出来给 Verifier 阅读的证据片段。Gatekeeper 必须先核对其真实性。 |
| `hypotheses` | array | 否 | 假设,不是 confirmed fact。不能因为写在这里就进入最终确认结论。 |
| `missing_info` | array | 否 | 证据缺口。 |
| `recommended_actions` | array | 否 | 建议动作。本期只允许证据收集动作,不允许修复动作;动作可以绑定证据,但不能把缺证据假设写成已确认事实。 |
### 3. Gatekeeper 输出
Gatekeeper 在 Verifier 前校验所有 `evidence_bindings`:
```json
{
"status": "pass",
"severity": "none",
"checked_bindings": [
{
"claim_id": "claim-1",
"tool_name": "query_metrics",
"source_invocation_id": 12345,
"raw_path": "$.alerts[1]",
"status": "pass",
"matched_text": "HighMemoryUsage firing, service=order-service, current=91%, duration=15m"
}
],
"failed_rules": [],
"warnings": [],
"errors": []
}
```
字段定义:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---:|---|
| `status` | string | 是 | `pass` 或 `fail`。失败时 Verifier 不得输出 `PASS`。 |
| `severity` | string | 是 | `none`、`low_confid` 或 `reject`。`status=pass` 时必须为 `none`。 |
| `checked_bindings` | array | 是 | 已校验的证据绑定明细。 |
| `checked_bindings[].claim_id` | string | 否 | 对应 claim。recommended action 的 binding 可使用 `action_id`。 |
| `checked_bindings[].tool_name` | string | 是 | Executor 声称的工具名,必须和真实 invocation 对齐。 |
| `checked_bindings[].source_invocation_id` | number | 是 | 被核验的真实工具调用 ID。 |
| `checked_bindings[].raw_path` | string | 是 | 被核验的工具返回路径。 |
| `checked_bindings[].status` | string | 是 | 单条 binding 的校验结果:`pass` 或 `fail`。 |
| `checked_bindings[].matched_text` | string | 否 | Gatekeeper 从 `retrieval_details.evidence_refs` 或工具返回中找到的系统侧证据文本。 |
| `failed_rules` | array | 是 | 失败规则列表,例如 `evidence.invocation_ref`、`evidence.raw_path`、`evidence.excerpt_mismatch`。 |
| `warnings` | array | 是 | 非阻断风险。 |
| `errors` | array | 是 | 阻断错误。 |
Gatekeeper 校验规则:
1. `source_invocation_id` 必须存在于当前 session。
2. `tool_name` 必须与该 invocation 的真实工具名一致。
3. `raw_path` 必须能定位到该 invocation 的真实证据引用。
4. `evidence_excerpt` 必须与系统侧 `text` 一致或高度相似。
5. Gatekeeper 只判断“引用是否真实”,不判断 claim 是否成立。
### 4. Verifier 输入完整结构
Verifier 最终接收的输入结构如下:
```json
{
"original_query": "请排查 order-service 当前是否存在 HighMemoryUsage 告警,只确认内存使用率过高这一件事。",
"executor_structured_output": {
"claims": [
{
"claim_id": "claim-1",
"claim_type": "observation",
"claim_text": "order-service 当前存在 HighMemoryUsage 告警,内存使用率达到 91%。",
"evidence_bindings": [
{
"tool_name": "query_metrics",
"source_invocation_id": 12345,
"raw_path": "$.alerts[1]",
"evidence_excerpt": "HighMemoryUsage firing, service=order-service, current=91%, duration=15m"
}
]
},
{
"claim_id": "claim-2",
"claim_type": "observation",
"claim_text": "order-service JVM 堆内存使用接近上限,当前为 3.8GB/4GB。",
"evidence_bindings": [
{
"tool_name": "query_metrics",
"source_invocation_id": 12345,
"raw_path": "$.alerts[1]",
"evidence_excerpt": "JVM heap usage 3.8GB/4GB"
}
]
},
{
"claim_id": "claim-3",
"claim_type": "observation",
"claim_text": "order-service 日志中也出现了内存使用率过高记录。",
"evidence_bindings": [
{
"tool_name": "query_logs",
"source_invocation_id": 12346,
"raw_path": "$.logs[0]",
"evidence_excerpt": "[11:17][WARN][order-service] 内存使用率过高: 91.0%, JVM堆内存: 3.8GB/4GB, GC次数: 128"
}
]
}
],
"hypotheses": [
{
"hypothesis_id": "hyp-1",
"hypothesis_text": "order-service 可能存在内存泄漏风险。",
"basis_claim_ids": ["claim-1", "claim-2"],
"missing_info": "缺少堆 dump、对象分配统计或更长时间窗口的内存曲线,不能确认内存泄漏。"
}
],
"missing_info": [
{
"info_id": "missing-1",
"description": "缺少堆 dump 或对象分配统计,无法确认内存泄漏根因。"
}
],
"recommended_actions": [
{
"action_id": "action-1",
"action_text": "继续查看 order-service 的 GC 日志、堆 dump 或对象分配统计。",
"reason": "当前证据可以确认内存使用率过高,但不足以确认是否存在内存泄漏。",
"evidence_bindings": [
{
"tool_name": "query_metrics",
"source_invocation_id": 12345,
"raw_path": "$.alerts[1]",
"evidence_excerpt": "HighMemoryUsage firing, service=order-service, current=91%, duration=15m"
}
]
}
]
},
"gatekeeper_result": {
"status": "pass",
"severity": "none",
"checked_bindings": [
{
"claim_id": "claim-1",
"tool_name": "query_metrics",
"source_invocation_id": 12345,
"raw_path": "$.alerts[1]",
"status": "pass",
"matched_text": "HighMemoryUsage firing, service=order-service, current=91%, duration=15m"
},
{
"claim_id": "claim-2",
"tool_name": "query_metrics",
"source_invocation_id": 12345,
"raw_path": "$.alerts[1]",
"status": "pass",
"matched_text": "JVM heap usage 3.8GB/4GB"
},
{
"claim_id": "claim-3",
"tool_name": "query_logs",
"source_invocation_id": 12346,
"raw_path": "$.logs[0]",
"status": "pass",
"matched_text": "[11:17][WARN][order-service] 内存使用率过高: 91.0%, JVM堆内存: 3.8GB/4GB, GC次数: 128"
}
],
"failed_rules": [],
"warnings": [],
"errors": []
},
"tool_trace_summary": [
{
"trace_ref": "trace-1",
"tool_name": "query_metrics",
"source_invocation_ids": [12345],
"evidence_level": "direct",
"output_summary": "metric_evidence: HighMemoryUsage service=order-service current=91% duration=15m",
"invocation_count": 1,
"no_hit_invocation_count": 0
},
{
"trace_ref": "trace-2",
"tool_name": "query_logs",
"source_invocation_ids": [12346],
"evidence_level": "direct",
"output_summary": "log_evidence: [11:17][WARN][order-service] 内存使用率过高: 91.0%",
"invocation_count": 1,
"no_hit_invocation_count": 0
}
],
"executor_output_parse_status": {
"status": "valid",
"detail": "parsed executor evidence contract"
},
"retry_context": null
}
```
Verifier 处理规则:
- 先看 `gatekeeper_result.status`。如果为 `fail`,不得输出 `PASS`。
- 正常校验时,只基于已通过 Gatekeeper 的 `evidence_bindings[].evidence_excerpt` 判断 `claim_text` 是否可推导。
- `tool_trace_summary.output_summary` 只作为全局导航摘要,不作为主证据源。
- `raw_path` 是 Gatekeeper 回查字段,不是 Verifier 推理字段。
- Verifier 不读取 raw tool output,不重新检索,不替 Executor 补证据。
### 5. `tool_trace_summary` 精简字段
在该设计下,`tool_trace_summary` 只保留全局导航和审计索引能力。建议进入 Verifier 的最小字段为:
```json
{
"trace_ref": "trace-1",
"tool_name": "query_logs",
"source_invocation_ids": [12346],
"evidence_level": "direct",
"output_summary": "log_evidence: ...",
"invocation_count": 1,
"no_hit_invocation_count": 0
}
```
字段定义:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---:|---|
| `trace_ref` | string | 是 | Verifier / 审计引用证据组的编号。 |
| `tool_name` | string | 是 | 证据组对应工具名。 |
| `source_invocation_ids` | array | 是 | 该证据组聚合的真实工具调用 ID。 |
| `evidence_level` | string | 是 | `direct`、`indirect` 或 `none`。只代表证据组强度,不代表 claim 通过。 |
| `output_summary` | string | 是 | 全局导航摘要。不得作为 Verifier 唯一证据源。 |
| `invocation_count` | number | 是 | 该证据组聚合的调用次数。 |
| `no_hit_invocation_count` | number | 是 | 无有效证据调用次数,用于负向场景和审计。 |
以下字段不建议进入 Verifier 输入,只保留在审计或 trace UI:
- `input_summary`
- `failed_invocation_count`
- `query_samples`
- `retrieval_layers`
- `relevance_levels`
- `source_documents`
---
## 已确认优化方向:mock 工具命中质量先做小阶段
HikariCP 类问题归属工具 / mock 命中质量,不归属 Verifier。当前负向 PASS 是合理的,因为系统没有检索到真实 `order-service` HikariCP 连接池耗尽证据;不能通过放宽 Verifier 或让 runbook 代替事实证据来解决。
该问题可以作为独立小阶段优先落地,风险低、见效快。
### 阶段目标
让 HikariCP 相关问题能够稳定区分:
```text
有真实 mock 日志
-> Executor 可以引用真实 evidence
-> Gatekeeper / Verifier 可以验证
-> 正向场景允许 PASS
没有真实 mock 日志
-> 明确 logs=[]
-> evidence_status=no_evidence
-> 不被 generic-service 占位日志污染
```
### 改造点
1. `query_logs` no-hit 时不得 fallback 到 `generic-service` 占位日志。
2. `query_logs` 应补齐 HikariCP / connection pool / active=50/50 / order-service 的正向 mock 日志。
3. HikariCP 相关查询应支持同义表达命中:
- `HikariCP`
- `HikariPool`
- `connection pool`
- `数据库连接池`
- `连接池耗尽`
- `active=50/50`
- `waiting`
- `request timed out after 30000ms`
- `order-service`
4. no-hit 应表达为“工具调用成功但没有证据”,而不是工具失败:
```json
{
"success": true,
"logs": [],
"total": 0,
"message": "未找到匹配的日志"
}
```
并在 `tool_invocation.retrieval_details.evidence_status` 中记录:
```json
{
"evidence_status": "no_evidence"
}
```
### HikariCP 正向 mock 日志示例
```text
[ERROR][order-service] HikariPool-1 - Connection is not available, request timed out after 30000ms
[WARN][order-service] HikariCP pool stats: active=50/50, idle=0, waiting=32
```
### 验收标准
1. 查询 `HikariCP` / `connection pool` / `active=50/50` / `order-service` 能命中真实 `order-service` 连接池日志。
2. 查询不存在的服务或不相关关键词时,返回 `logs=[]`,并记录 `evidence_status=no_evidence`。
3. 不再返回 `generic-service` 占位日志作为假证据。
4. HikariCP positive case 能稳定走到 `PASS`。
5. HikariCP negative case 仍然表达“未检索到真实证据”,不能误判 `PASS`。
### 边界
- 不通过 Verifier 放宽解决工具未命中问题。
- 不允许 runbook 通用排查建议升级成“当前已经发生 HikariCP 连接池耗尽”的事实结论。
- mock 数据应尽量和 `knowledge_base` 中的连接池排查 runbook 对齐,便于 eval 和 demo 形成闭环。
---
## 已确认优化方向:本期先用 Executor Prompt 控制过度展开
Planner 当前不会产出 `scope_contract`。本期先不改 Planner 输出契约,也不新增 scope contract 解析逻辑;先通过 Executor prompt 控制窄范围问题的过度展开。
### 本期边界
```text
不改 Planner
不新增 scope_contract
不新增 Controller
不做复杂 scope Gatekeeper
只调整 Executor Prompt 的单一职责和输出边界
```
### Executor 单一职责
Executor 在本期应被约束为:
```text
证据收集 + 微观事实提炼
```
也就是:
- 调用工具收集当前任务范围内的证据。
- 输出工具证据直接支持的 observation / negative_observation claim。
- 为 claim 绑定证据引用。
- 输出 missing_info 表达证据缺口。
Executor 不应负责:
- 生成最终用户答案。
- 输出修复方案。
- 扩展到用户未要求的服务、订单、告警、数据库、连接池或下游依赖。
- 将 Runbook、Skill 或知识库中的通用经验写成当前环境已发生的事实。
- 在证据不足时用“可能是、一般来说、根据经验”等话术补 claim。
### 窄范围问题识别
如果用户问题包含以下表达,Executor 应视为窄范围确认任务:
- `只确认`
- `只排查`
- `只看`
- `不要分析`
- `不要扩展`
- `只回答`
- `是否真实存在`
- `是否存在某告警 / 某日志 / 某错误`
窄范围确认任务下,Executor 必须遵守:
1. 只输出 `observation` / `negative_observation` 类型 claim。
2. claim 数量应为最少必要数量,通常 1 条,最多 2 条。
3. 只能围绕用户明确要求的目标对象和主题输出 claim。
4. 用户明确排除的对象、告警、服务、订单、数据库、连接池等,禁止出现在 claim 中。
5. Runbook / Skill / 知识库只能用于指导要查什么,不能作为当前事实 claim。
6. 如果证据不足,只输出 `missing_info`,不要补充合理化解释。
7. `recommended_actions` 如果保留,只能是继续收集证据的动作,不允许是修复动作。
### 限制 claim 数量不是限制证据数量
这里限制的是:
```text
claim 数量
```
不是限制:
```text
工具调用数量
证据数量
evidence_bindings 数量
missing_info 数量
```
窄范围任务的理想输出是:
```text
1 条核心 claim
多条直接相关 evidence_bindings
必要的 missing_info
```
示例:
```json
{
"claim_id": "claim-1",
"claim_type": "observation",
"claim_text": "order-service 当前存在 HighMemoryUsage 告警,内存使用率达到 91%。",
"evidence_bindings": [
{
"tool_name": "query_metrics",
"source_invocation_id": 12345,
"raw_path": "$.alerts[1]",
"evidence_excerpt": "HighMemoryUsage firing, service=order-service, current=91%, duration=15m"
},
{
"tool_name": "query_metrics",
"source_invocation_id": 12345,
"raw_path": "$.alerts[1]",
"evidence_excerpt": "JVM heap usage 3.8GB/4GB"
},
{
"tool_name": "query_logs",
"source_invocation_id": 12346,
"raw_path": "$.logs[0]",
"evidence_excerpt": "[11:17][WARN][order-service] 内存使用率过高: 91.0%, JVM堆内存: 3.8GB/4GB, GC次数: 128"
}
]
}
```
不建议拆成:
```text
claim-1: 存在 HighMemoryUsage
claim-2: 当前 91%
claim-3: JVM 3.8GB/4GB
claim-4: Full GC 增多
claim-5: 可能内存泄漏
```
原因是这些大多属于同一观察事实或证据细节,应合并到一条 claim 的 `evidence_bindings` 中。证据不足时应进入 `missing_info`,而不是生成更多弱 claim。
### Prompt 草案
可加入 `chat-executor-prompt.md` 的约束草案:
```text
## 单一职责与输出边界
你是证据收集与微观事实提炼专家。
你只负责调用工具收集证据,并输出工具证据直接支持的微观事实断言。
你不得输出最终用户答案。
你不得生成修复方案。
你不得扩展到用户未要求的服务、订单、告警、数据库、连接池或下游依赖。
你不得将 Runbook、Skill 或知识库中的通用经验写成当前环境已发生的事实。
## 窄范围问题 HARD-GATE
如果用户问题包含“只确认、只排查、只看、不要分析、不要扩展、只回答、是否真实存在、是否存在某告警/日志/错误”等表达,视为窄范围确认任务。
窄范围确认任务必须遵守:
1. 只输出 observation / negative_observation 类型 claim。
2. claim 数量使用最少必要数量,通常 1 条,最多 2 条。
3. 限制 claim 数量不限制 evidence_bindings 数量;每条 claim 应绑定所有直接相关证据。
4. 不得把同一观察事实拆成多条 claim。
5. 只能围绕用户明确要求的目标对象和主题输出 claim。
6. 用户明确排除的对象、告警、服务、订单、数据库、连接池等,禁止出现在 claim 中。
7. Runbook / Skill / 知识库只能用于指导要查什么,不能作为当前事实 claim。
8. 如果证据不足,只输出 missing_info,不要补充合理化解释。
9. recommended_actions 只能是继续收集证据的动作,不能是修复动作。
```
### 验收标准
1. 用户说“只确认 HighCPUUsage”时,Executor 不输出订单、OOM、DB、HikariCP。
2. 用户说“只确认 HighMemoryUsage”时,Executor 不输出“内存泄漏已确认”。
3. 用户说“不要分析连接池”时,claim 中不出现 `HikariCP` / `connection pool`。
4. runbook 内容只能进入 `missing_info` 或证据收集动作,不能变成 confirmed claim。
5. 窄范围正向场景仍能绑定多条证据,不因 claim 数量限制导致证据不足。
### 后续可选增强
如果仅靠 Executor prompt 不稳定,再考虑后续引入:
- Planner `scope_contract`
- Gatekeeper scope 校验
- eval fixture 中的 forbidden claim 断言
---
## 当前剩余实施问题
当前真正剩余的设计问题主要是实施细节,而不是方向选择:
1. 已决:`evidence_refs` 第一版由 `ToolInvocationRecorder` 根据工具返回统一抽取生成。
2. 已决:`raw_path` 第一版只支持 `$.alerts[i]` / `$.logs[i]` / `$.evidence_blocks[i]` 三类稳定定位符。
3. 已决:Gatekeeper 校验失败分级处理;伪造 / 张冠李戴类 `REJECT`,证据缺失 / 过渡期兼容类 `LOW_CONFID`。
4. 已决:`VerifierInputHook` 自动回填必须收紧;本期只作兼容,不作为 `PASS` 依据,后续废弃。
5. 已决:本期采用 Prompt-first 策略,只实现 Executor Prompt 约束;`scope_contract` 作为后续增强。
6. 已决:补最小 eval / E2E 覆盖矩阵,覆盖 Memory、SlowResponse、HikariCP positive、HikariCP negative、Gatekeeper reject、窄范围 forbidden claim。
---
## 已确认实施决策:`evidence_refs` 第一版由 Recorder 统一抽取
第一版 `evidence_refs` 由 `ToolInvocationRecorder` 在工具调用入库时,根据 `toolName` 和工具返回 JSON 统一抽取生成,并写入 `tool_invocation.retrieval_details.evidence_refs`。
采用该方案的原因:
- 当前 `query_logs` / `query_metrics` 返回已经是结构化 JSON。
- 第一版只需要 `raw_path + text`,不需要复杂 metadata。
- 抽取逻辑集中在 recorder 附近,更容易保证 `evidence_refs` 格式一致。
- 不需要一次性改造所有工具返回协议。
第一版抽取规则:
```text
query_metrics:
$.alerts[i] -> text = alert_name + state + description/duration
query_logs:
$.logs[i] -> text = timestamp + level + service + message
lookup_knowledge:
retrieval_details.evidence_blocks[i] -> text = content/title/source
```
实现边界:
- Recorder 只做结构化抽取,不做诊断推理。
- Recorder 不得把 `HighMemoryUsage`、`JVM 3.8GB/4GB` 等证据推理成“内存泄漏已确认”。
- 如果 output JSON 解析失败或工具无可抽取结构,`evidence_refs` 可以为空,但必须保留原有 `evidence_status`。
- 后续如果某个工具返回特别复杂,可以允许工具显式传入 `evidence_refs` 覆盖默认抽取,但第一版不做。
---
## 已确认实施决策:`raw_path` 第一版使用稳定定位符
第一版 `raw_path` 不实现完整 JSONPath,也不支持任意深层字段路径。它只是 `retrieval_details.evidence_refs` 内的稳定定位符,用来和 `source_invocation_id` 共同定位一条证据。
支持的路径格式仅包括:
```text
$.alerts[i]
$.logs[i]
$.evidence_blocks[i]
```
对应工具:
| 工具 | raw_path 格式 | 说明 |
|---|---|---|
| `query_metrics` | `$.alerts[i]` | 第 i 条告警证据 |
| `query_logs` | `$.logs[i]` | 第 i 条日志证据 |
| `lookup_knowledge` | `$.evidence_blocks[i]` | 第 i 个知识库证据块 |
第一版不支持:
```text
$.alerts[1].description
$.logs[0].message
$.data.alerts[0]
$.retrieval_details.evidence_blocks[0]
过滤表达式或复杂 JSONPath
```
原因:
- Gatekeeper 不需要实现通用 JSONPath 引擎。
- Executor 不需要理解工具返回内部字段细节。
- `evidence_refs[].text` 已经保存该 raw path 对应的最小证据文本。
- Gatekeeper 只需要在 `retrieval_details.evidence_refs` 中按 `raw_path` 精确匹配,然后比对 `evidence_excerpt` 与系统侧 `text`。
示例:
```json
{
"evidence_refs": [
{
"raw_path": "$.alerts[1]",
"text": "HighMemoryUsage firing, service=order-service, current=91%, duration=15m; JVM heap usage 3.8GB/4GB"
}
]
}
```
Executor 引用:
```json
{
"tool_name": "query_metrics",
"source_invocation_id": 12345,
"raw_path": "$.alerts[1]",
"evidence_excerpt": "HighMemoryUsage firing, service=order-service, current=91%, duration=15m"
}
```
Gatekeeper 校验:
1. 查 `source_invocation_id=12345` 是否属于当前 session。
2. 查该 invocation 的 `retrieval_details.evidence_refs` 是否存在 `raw_path="$.alerts[1]"`。
3. 比对 `evidence_excerpt` 是否被该 `text` 支撑。
---
## 已确认实施决策:Gatekeeper 失败分级并入库审计
Gatekeeper 校验失败不一律 `REJECT`。第一版按失败性质分级:
```text
物理级幻觉 / 证据污染 -> REJECT
证据缺失 / 格式不完整 / 过渡期兼容问题 -> LOW_CONFID
```
### Gatekeeper 输出结构
建议 Gatekeeper 输出增加 `severity` 字段:
```json
{
"status": "fail",
"severity": "reject",
"checked_bindings": [
{
"claim_id": "claim-1",
"tool_name": "query_metrics",
"source_invocation_id": 12345,
"raw_path": "$.alerts[9]",
"status": "fail",
"rule": "evidence.raw_path",
"message": "raw_path not found in retrieval_details.evidence_refs"
}
],
"failed_rules": ["evidence.raw_path"],
"warnings": [],
"errors": [
{
"rule": "evidence.raw_path",
"message": "raw_path not found in retrieval_details.evidence_refs"
}
]
}
```
字段定义:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---:|---|
| `status` | string | 是 | `pass` 或 `fail`。 |
| `severity` | string | 是 | `none`、`low_confid`、`reject`。`status=pass` 时为 `none`。 |
| `checked_bindings` | array | 是 | 每条 evidence binding 的校验结果。 |
| `failed_rules` | array | 是 | 命中的失败规则名。 |
| `warnings` | array | 是 | 非阻断风险。 |
| `errors` | array | 是 | 阻断错误,必须可审计。 |
### `REJECT` 级失败
以下属于物理级幻觉或证据污染,应标记:
```json
{
"status": "fail",
"severity": "reject"
}
```
规则:
1. `source_invocation_id` 不存在。
2. `source_invocation_id` 不属于当前 session。
3. `source_invocation_id` 存在,但 `tool_name` 与真实 invocation 不一致。
4. `raw_path` 不存在于该 invocation 的 `retrieval_details.evidence_refs`。
5. `evidence_excerpt` 与 `evidence_refs[].text` 明显不匹配。
这些情况表示 Executor 声称引用了某条证据,但系统无法核实,或核实结果与声称内容冲突。该类结果不得进入正常 Verifier 推导链路。
### `LOW_CONFID` 级失败
以下属于证据不足、格式不完整或过渡期兼容问题,应标记:
```json
{
"status": "fail",
"severity": "low_confid"
}
```
规则:
1. `evidence_bindings` 为空。
2. `raw_path` 缺失,但 `source_invocation_id` 存在。
3. 旧工具调用未生成 `retrieval_details.evidence_refs`。
4. `evidence_excerpt` 太短或太泛,无法稳定比对。
5. Executor 输出格式不完整,但没有伪造具体 invocation / raw_path / excerpt。
这些情况不得 `PASS`,但不一定构成模型造假。
### 下游处理
- `severity=reject`:Verifier 不得输出 `PASS`,最终倾向 `REJECT`。
- `severity=low_confid`:Verifier 不得输出 `PASS`,最终输出 `LOW_CONFID`。
- `severity=none`:Verifier 正常判断 claim 是否可由已核验证据推出。
Gatekeeper 不直接生成最终用户答案,也不替代 Verifier。它只输出确定性校验结果和处理严重程度。
### 审计入库
Gatekeeper 的完整结果必须记录到数据库审计数据中。第一版不新增复杂表,复用当前 `DiagnosisSession.selfEvaluation` 中的 `verifier_evaluation.gatekeeper_result`。
需要入库的最小字段:
```json
{
"gatekeeper_result": {
"status": "fail",
"severity": "reject",
"checked_bindings": [],
"failed_rules": [],
"warnings": [],
"errors": []
}
}
```
入库要求:
- 每次 Verifier 前置 Gatekeeper 执行结果都必须保存。
- `checked_bindings` 至少记录失败 binding;通过 binding 可按体积控制保留摘要。
- `failed_rules` / `errors` 必须保留,便于离线定位是伪造 ID、raw_path 不存在,还是 excerpt 不匹配。
- 审计数据必须能回答:Executor 引用了什么、Gatekeeper 查到了什么、为什么拦截或降级。
---
## 已确认实施决策:收紧 `VerifierInputHook` 自动回填
当前 `VerifierInputHook` 会在 Executor 未填写 `source_invocation_id` 时,根据 `tool_name` 从 `tool_trace_summary` 自动回填 invocation id。旧版实现里该字段可能表现为 `source_invocation_ids`。该逻辑是旧链路中的兼容补救,但与新设计的“精确证据引用”存在冲突。
新设计下,证据引用必须由:
```text
source_invocation_id + raw_path + evidence_excerpt
```
共同构成。只按 `tool_name` 自动回填 invocation id,会把宽泛工具调用误包装成精确证据引用。
### 本期策略:兼容但收紧
本期暂不完全删除自动回填,但必须收紧:
1. 不再根据 `tool_name` 批量回填多个 invocation id。
2. 只有同一 `tool_name` 下存在唯一候选 invocation 时,才允许回填 `source_invocation_id`。
3. 不允许自动回填 `raw_path`。
4. 自动回填必须写入 `gatekeeper_result.warnings`。
5. 自动回填后的 binding 如果缺少 `raw_path`,Gatekeeper 必须标记为 `severity=low_confid`,不得作为 `PASS` 依据。
6. 伪造 `source_invocation_id`、`raw_path` 或 `evidence_excerpt` 仍然是 `severity=reject`。
warning 示例:
```json
{
"rule": "evidence.invocation_auto_backfill",
"message": "source_invocation_id was auto-filled from the unique tool invocation candidate; raw_path remains missing"
}
```
### 后续策略:废弃自动回填
等 Executor prompt 和输出结构稳定后,废弃自动回填:
```text
Executor 必须自己输出 source_invocation_id + raw_path + evidence_excerpt。
缺少精确引用时,不能 PASS。
```
最终规则:
```text
缺失引用 -> LOW_CONFID
伪造引用 -> REJECT
真实引用 + 可推导 claim -> PASS 候选
```
---
## 已确认实施决策:Prompt-first,Contract-later
本期不实现 Planner `scope_contract`,只通过 Executor Prompt 控制窄范围问题的过度展开。
原因:
```text
scope_contract 不只是加一个字段。
它会牵动 Planner 输出契约、PlannerSkillMetadataHook、Executor 对 planner_plan 的解析、可能的 Gatekeeper scope 校验和 eval fixture。
```
本期真正要先验证的是:
```text
Executor 是否能在 prompt 约束下减少无关 claim、根因推断和修复建议。
```
### 本期做
1. 调整 `chat-executor-prompt.md`。
2. 强化 Executor 单一职责:证据收集 + 微观事实提炼。
3. 窄范围问题只输出 `observation` / `negative_observation`。
4. 输出最少必要 claim,通常 1 条,最多 2 条。
5. 不限制 `evidence_bindings` 数量。
6. Runbook / Skill / 知识库不能作为当前事实。
7. `recommended_actions` 如果保留,只能是证据收集动作,不能是修复动作。
8. 增加 eval / E2E 验证 forbidden claim。
### 本期不做
1. 不改 Planner。
2. 不新增 `scope_contract`。
3. 不解析 `planner_plan.scope_contract`。
4. 不做 Gatekeeper scope 校验。
5. 不改多 Agent 编排。
### 何时再引入 `scope_contract`
如果出现以下情况,再进入下一阶段:
1. Prompt 调整后,Executor 仍频繁输出用户明确排除的服务或主题。
2. 窄范围问题仍然生成 `root_cause` / `risk` / 修复建议。
3. eval 中 forbidden claim 仍不稳定。
4. Planner 已经能稳定识别用户 scope,但 Executor 不遵守。
### 本期验收
1. HighCPUUsage 窄范围 case:不出现 HighMemoryUsage、SlowResponse、order-123、HikariCP、DB。
2. HighMemoryUsage 窄范围 case:不出现“内存泄漏已确认”。
3. SlowResponse 窄范围 case:不出现数据库连接池耗尽根因。
4. 用户明确排除 HikariCP 时,`claim_text` 和最终答案不出现 HikariCP 确认结论。
---
## 已确认实施决策:最小 eval / E2E 覆盖矩阵
本期 eval / E2E 不铺太大,围绕已确认的改造点做最小闭环:
```text
evidence_refs 抽取
raw_path / excerpt Gatekeeper 校验
Verifier 基于已核验 evidence_excerpt 推导
mock 工具命中质量
Executor 窄范围 prompt 约束
```
### 1. HighMemoryUsage positive
目标:验证 `evidence_refs -> Executor evidence_binding -> Gatekeeper -> Verifier` 能让 HighMemoryUsage 从证据存在但 `LOW_CONFID` 变成可通过。
场景:
```text
只确认 order-service 是否存在 HighMemoryUsage 告警。
```
期望:
- Gatekeeper `pass`。
- Verifier `PASS`。
- claim 只包含 HighMemoryUsage 观察事实。
- 不确认内存泄漏。
覆盖点:
- `query_metrics` 生成 `evidence_refs`。
- `raw_path=$.alerts[i]`。
- `evidence_excerpt` 可核验。
- Verifier 基于已核验 excerpt 做推导。
### 2. SlowResponse positive
目标:验证一个 claim 可以绑定多条证据,不靠拆出多条 claim 凑信息。
场景:
```text
只确认 user-service 是否存在 SlowResponse 告警和慢请求日志。
```
期望:
- Verifier `PASS`。
- claim 数量 1-2 条。
- `evidence_bindings` 同时包含 alert 和 logs。
- 不推断数据库连接池耗尽。
覆盖点:
- `query_metrics` + `query_logs`。
- `raw_path=$.alerts[i]`。
- `raw_path=$.logs[i]`。
- 多 `evidence_bindings`。
- 窄范围不扩展根因。
### 3. HikariCP positive
目标:验证 HikariCP 相关查询能命中真实 mock 日志。
场景:
```text
确认 order-service 是否存在 HikariCP 连接池耗尽。
```
期望:
- `query_logs` 命中 `order-service` HikariCP 日志。
- 不返回 `generic-service`。
- Gatekeeper `pass`。
- Verifier `PASS`。
- 最终答案可以确认连接池耗尽日志存在。
覆盖点:
- `HikariCP` / `HikariPool` / `connection pool` / `active=50/50` 同义匹配。
- `raw_path=$.logs[i]`。
- HikariCP 正向 mock 数据。
### 4. HikariCP negative
目标:验证 no-hit 不污染证据链。
场景:
```text
确认 inventory-service 是否存在 HikariCP 连接池耗尽。
```
期望:
- `logs=[]`。
- `evidence_status=no_evidence`。
- 不返回 `generic-service`。
- Verifier 不输出正向 `PASS` 结论。
- 最终表达“未检索到真实证据”。
覆盖点:
- no-hit 语义。
- `negative_observation`。
- `generic-service` 占位日志清理。
### 5. Gatekeeper reject
目标:验证伪造 `raw_path` 或 “真实 invocation + 编造 excerpt” 会被拦截。
该场景优先做单元测试,不一定需要 E2E。
输入示例:
```json
{
"source_invocation_id": 12345,
"raw_path": "$.alerts[99]",
"evidence_excerpt": "HikariCP active=50/50"
}
```
期望:
- `gatekeeper_result.status=fail`。
- `gatekeeper_result.severity=reject`。
- `failed_rules` 包含 `evidence.raw_path` 或 `evidence.excerpt_mismatch`。
- 结果不得 `PASS`。
- `gatekeeper_result` 入库审计。
覆盖点:
- `raw_path` 不存在。
- `excerpt` 不匹配。
- `REJECT` 分级。
- 审计入库。
### 6. Executor narrow forbidden claim
目标:验证 Executor Prompt 对窄范围问题的约束有效。
场景:
```text
只确认 HighCPUUsage,不要分析订单123、OOM、数据库慢查询、连接池或 user-service。
```
期望:
- claims 只围绕 HighCPUUsage / payment-service。
- 不出现 `order-123`。
- 不出现 `OOM`。
- 不出现 `DB` / `database`。
- 不出现 `HikariCP` / `connection pool`。
- 不出现 `user-service`。
覆盖点:
- Executor Prompt。
- forbidden claim keywords。
- Composer 不泄漏 blocked 内容。
### 推荐测试层级
单元测试:
- `evidence_refs` 抽取。
- `raw_path` 校验。
- Gatekeeper `severity` 分级。
- `VerifierInputHook` 自动回填收紧。
离线 eval fixture:
- Gatekeeper reject。
- unsupported / forbidden claim。
- Memory / SlowResponse 的结构化输入。
E2E:
- HighMemoryUsage positive。
- SlowResponse positive。
- HikariCP positive。
- HikariCP negative。
- HighCPUUsage narrow forbidden。
### 本期最小可验收
如果时间紧,至少完成:
1. Gatekeeper raw_path reject 单测。
2. `evidence_refs` 抽取单测。
3. HikariCP positive E2E。
4. HikariCP negative E2E。
5. HighMemoryUsage positive E2E。
6. narrow forbidden E2E。
---
## 历史候选优化方向
说明:本节保留早期候选方向,便于理解设计演进。当前已确认的决策以上文“已确认设计方向 / 已确认优化方向”为准:
- Verifier 主证据源改为 Gatekeeper 已核验的 `evidence_excerpt`。
- Gatekeeper 需要核对 `source_invocation_id + raw_path + evidence_excerpt`。
- `tool_trace_summary` 降级为全局导航摘要,不再作为唯一证据源。
- HikariCP / generic-service 问题先作为 mock 工具命中质量小阶段处理。
- 本期暂不做 Planner `scope_contract`,先通过 Executor Prompt 控制窄范围过度展开。
### 方向 A:增强 `ToolTraceSummaryService` 的证据摘要保真度
让 summary 不再只是人类可读摘要,而是保留 Verifier 可用的最小机器证据。
候选策略:
- 每个 tool invocation 至少保留 top 3-5 条关键 evidence excerpt。
- 每个 firing alert 保留结构化摘要:告警名、服务、状态、当前值、阈值、持续时间。
- 日志类工具按错误码、服务名、时间、endpoint、耗时等字段保留关键片段。
- 对趋势型证据保留首尾值和采样数量。
需要讨论:
- `output_summary` 继续做人类摘要,还是升级为机器证据索引?
- 是否需要新增字段,而不是继续塞进 `output_summary`?
---
### 方向 B:Verifier 直接消费 Executor 的 `evidence_excerpt`
Verifier 判断 claim 是否可推导时,不只看 `tool_trace_summary.output_summary`,也看 Executor 已经绑定到 claim / action 的证据片段。
优点:
- 证据离 claim 更近。
- 对 Memory / SlowResponse 这类 case 更容易 PASS。
- 不要求 summary 承担全部证据承载职责。
风险:
- 如果 Executor 编造 excerpt,Verifier 会被污染。
- 需要 Gatekeeper 先保证 excerpt 能回溯到真实 tool output。
需要讨论:
- Verifier 消费 excerpt 前,Gatekeeper 是否必须加入原文回溯 / 相似度校验?
- Verifier 应该把 excerpt 当作主证据,还是辅助证据?
---
### 方向 C:Gatekeeper 增加 excerpt 原文回溯校验
在 Verifier 前,用代码验证 Executor 声称的 `evidence_excerpt` 是否能在对应 tool invocation 的原始输出中找到或高度相似。
候选规则:
- 证据引用必须能回溯到真实 tool invocation。
- `evidence_excerpt` 与系统侧证据文本明显不匹配时拒绝。
- 找不到引用片段时,不进入 Verifier,避免张冠李戴。
优点:
- 为 Verifier 直接消费 excerpt 建立前置安全条件。
- 能拦住“真实 ID + 编造文本”的物理级幻觉。
风险:
- 原始输出和 excerpt 经过格式化后可能不完全一致,需要容忍截断、空白、标点差异。
- 相似度阈值需要通过 fixture 校准。
---
### 方向 D:窄范围问题限制 Executor claim 数量
对于用户明确要求“只确认某一件事”的问题,Executor 输出应更克制。
候选策略:
- Planner 或 Executor prompt 生成 scope contract。
- Executor 对窄范围问题最多输出 1-2 个核心 claim。
- 与用户明确排除的服务、告警、错误类型无关的 claim 不输出。
需要讨论:
- 这个约束放在 Planner,还是 Executor 自己根据用户问题判断?
- 是否需要 Gatekeeper 增加 scope 校验?
---
### 方向 E:修复 mock 工具命中质量
针对 HikariCP 这类 case,补齐正向 mock 分支或改进查询匹配。
候选策略:
- `query_logs` 支持 `HikariCP`、`connection pool`、`active=50/50`、`order-service` 等同义匹配。
- 避免正向场景返回 `generic-service` 占位日志。
- no-hit 时明确标记为无证据,减少污染。
需要讨论:
- mock 工具是只服务 eval,还是也作为 demo 数据源长期维护?
- mock 数据是否应该和 `knowledge_base` fixture 建立对应关系?
---
## 初步验收标准
优化后至少需要覆盖以下回归:
1. `HighCPUUsage` 正向场景稳定 `PASS`。
2. `HighMemoryUsage` 在证据存在时不再因为 summary 丢证据而 `LOW_CONFID`。
3. `SlowResponse` 在 alert 与慢请求日志都存在时不再因为 summary 丢证据而 `LOW_CONFID`。
4. `HikariCP` 负向场景仍能正确表达“未检索到真实证据”。
5. `HikariCP` 正向场景能够命中真实 mock 数据并被 Verifier 正确识别。
6. Gatekeeper 审计字段完整记录校验结果。
7. 最终 `user_facing_answer` 不泄漏 raw JSON、tool raw output 或 Executor 未通过 Verifier 的 claim。
---
## 历史问题记录
以下问题是早期讨论时提出的候选方向,当前均已被上文“已确认设计方向 / 已确认实施决策”覆盖:
| 历史问题 | 当前决策 |
|---|---|
| Verifier 后续应该以 `tool_trace_summary` 为唯一证据源,还是同时消费 Executor `evidence_excerpt`? | Verifier 主证据源改为 Gatekeeper 已核验的 `evidence_excerpt`,`tool_trace_summary` 降级为全局导航摘要。 |
| 如果 Verifier 消费 `evidence_excerpt`,Gatekeeper 是否必须先做 excerpt 原文回溯? | 必须。Gatekeeper 校验 `source_invocation_id + raw_path + evidence_excerpt` 后,Verifier 才能使用 excerpt。 |
| `ToolTraceSummaryService` 应该增强现有 `output_summary`,还是新增更结构化的 evidence excerpt 字段? | 本期不把 `output_summary` 作为主证据源;结构化证据引用进入 `tool_invocation.retrieval_details.evidence_refs`。 |
| summary 保留多少条 excerpt 才够,不会重新变成超长上下文? | 不靠 summary 承载主证据;claim-local evidence 由 Executor 引用并经 Gatekeeper 校验。 |
| `generic-service` 占位日志是否应该彻底视为 `success=false` 或 `evidence_level=none`? | no-hit 应返回 `logs=[]` 且 `evidence_status=no_evidence`,不再返回 `generic-service` 占位日志作为证据。 |
| 窄范围问题是否需要显式 scope contract,避免 Executor 过度展开? | 本期采用 Prompt-first,只调 Executor Prompt;`scope_contract` 作为后续增强。 |