1536 lines
52 KiB
Markdown
1536 lines
52 KiB
Markdown
# 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` 作为后续增强。 |
|