240 lines
5.7 KiB
Markdown
240 lines
5.7 KiB
Markdown
# ISS-009 negative_observation 精确引用 no-evidence 结果
|
||
|
||
**严重程度**:中
|
||
**状态**:已修复
|
||
**发现时间**:2026-07-08
|
||
**关联**:
|
||
- `ISS-007-verifier-evidence-summary-fidelity`
|
||
- `ISS-008-executor-narrow-scope-overreach`
|
||
- `executor-structured-output-v2`
|
||
|
||
---
|
||
|
||
## 背景
|
||
|
||
ISS-007 已经把正向证据引用收敛为:
|
||
|
||
```text
|
||
source_invocation_id + raw_path + evidence_excerpt
|
||
```
|
||
|
||
Gatekeeper 通过 `tool_invocation.retrieval_details.evidence_refs` 校验 Executor 引用是否真实存在。
|
||
|
||
但负向观察存在一个缺口:当工具明确返回“没查到”时,结果通常是空数组:
|
||
|
||
```json
|
||
{
|
||
"logs": [],
|
||
"total": 0,
|
||
"message": "未找到匹配的日志"
|
||
}
|
||
```
|
||
|
||
这时没有 `$.logs[0]`、`$.alerts[0]` 或 `$.evidence_blocks[0]` 可以引用。Executor 如果输出 `negative_observation`,Gatekeeper 无法稳定验证它引用的“无证据结果”,容易降级为 `LOW_CONFID` 或 `REJECT`。
|
||
|
||
---
|
||
|
||
## 问题
|
||
|
||
用户问:
|
||
|
||
```text
|
||
只确认 inventory-service 是否存在 HikariCP 连接池耗尽日志。
|
||
```
|
||
|
||
工具返回:
|
||
|
||
```json
|
||
{
|
||
"success": false,
|
||
"logs": [],
|
||
"total": 0,
|
||
"message": "未找到匹配的日志"
|
||
}
|
||
```
|
||
|
||
合理 claim 是:
|
||
|
||
```json
|
||
{
|
||
"claim_type": "negative_observation",
|
||
"claim_text": "未检索到 inventory-service 的 HikariCP 连接池耗尽日志。"
|
||
}
|
||
```
|
||
|
||
但旧设计只支持正向数组项:
|
||
|
||
```text
|
||
$.alerts[i]
|
||
$.logs[i]
|
||
$.evidence_blocks[i]
|
||
```
|
||
|
||
因此负向观察缺少可回溯的精确引用点。
|
||
|
||
---
|
||
|
||
## 修复决策
|
||
|
||
给 no-hit / no-evidence 结果增加一等证据引用:
|
||
|
||
```json
|
||
{
|
||
"raw_path": "$.no_evidence",
|
||
"text": "query_logs returned no evidence; evidence_status=no_evidence; query=inventory-service HikariCP; topic=application-logs; total=0; message=未找到匹配的日志"
|
||
}
|
||
```
|
||
|
||
Executor 可以引用:
|
||
|
||
```json
|
||
{
|
||
"tool_name": "query_logs",
|
||
"source_invocation_id": 123,
|
||
"raw_path": "$.no_evidence",
|
||
"evidence_excerpt": "query_logs returned no evidence; query=inventory-service HikariCP; total=0; evidence_status=no_evidence"
|
||
}
|
||
```
|
||
|
||
### 语义边界
|
||
|
||
`$.no_evidence` 只表示:
|
||
|
||
```text
|
||
该工具对当前查询返回无匹配证据。
|
||
```
|
||
|
||
它不表示:
|
||
|
||
- 问题绝对不存在。
|
||
- 根因被排除。
|
||
- 系统已经健康。
|
||
- 没有必要继续排查。
|
||
|
||
---
|
||
|
||
## 实施范围
|
||
|
||
### 已修改
|
||
|
||
- `ToolInvocationRecorder`
|
||
- `query_logs` no-hit 时生成 `evidence_refs[0].raw_path="$.no_evidence"`。
|
||
- `query_metrics` no-hit 时生成 `evidence_refs[0].raw_path="$.no_evidence"`。
|
||
- `lookup_knowledge` no-hit 且无 evidence blocks 时生成 `evidence_refs[0].raw_path="$.no_evidence"`。
|
||
- `ExecutorGatekeeperService`
|
||
- 复用既有 `evidence_refs` 校验逻辑,无需新增特殊分支。
|
||
- `$.no_evidence` 和普通 raw_path 一样必须存在于 `retrieval_details.evidence_refs`。
|
||
- `chat-executor-prompt.md`
|
||
- 明确 `negative_observation` 必须引用 `$.no_evidence`。
|
||
- 明确没有实际工具调用时禁止使用 `$.no_evidence`。
|
||
|
||
### 未修改
|
||
|
||
- 不新增数据库表。
|
||
- 不新增复杂 metadata。
|
||
- 不改变 Planner。
|
||
- 不改变 Agent 编排。
|
||
|
||
---
|
||
|
||
## 验收标准
|
||
|
||
1. `query_logs` 返回 `logs=[] / total=0 / evidence_status=no_evidence` 时,`tool_invocation.retrieval_details.evidence_refs` 包含:
|
||
|
||
```json
|
||
{
|
||
"raw_path": "$.no_evidence"
|
||
}
|
||
```
|
||
|
||
2. Executor 输出 `negative_observation` 并引用 `$.no_evidence` 时,Gatekeeper 可以校验通过。
|
||
|
||
3. Executor 如果用 `$.no_evidence` 搭配正向证据文本,例如 `HikariCP active=50/50`,Gatekeeper 必须拒绝。
|
||
|
||
4. `$.no_evidence` 不得被解释为“问题绝对不存在”,只能表达“当前查询未检索到匹配证据”。
|
||
|
||
5. HikariCP negative E2E:
|
||
|
||
```text
|
||
只确认 inventory-service 是否存在 HikariCP 连接池耗尽日志。
|
||
```
|
||
|
||
期望:
|
||
|
||
- 工具返回 no-hit。
|
||
- 不返回 `generic-service`。
|
||
- Executor 输出 `negative_observation`。
|
||
- `raw_path="$.no_evidence"`。
|
||
- Gatekeeper `pass/none`。
|
||
- Verifier 不误判为正向 HikariCP 证据。
|
||
|
||
---
|
||
|
||
## 验证记录
|
||
|
||
### 2026-07-08 单元测试
|
||
|
||
命令:
|
||
|
||
```text
|
||
mvn '-Dtest=ToolInvocationRecorderTest,ExecutorGatekeeperServiceTest,QueryLogsToolsTest' test
|
||
```
|
||
|
||
结果:
|
||
|
||
```text
|
||
Tests run: 23, Failures: 0, Errors: 0, Skipped: 0
|
||
BUILD SUCCESS
|
||
```
|
||
|
||
覆盖点:
|
||
|
||
- `query_logs` no-hit 生成 `$.no_evidence`。
|
||
- `query_metrics` no-hit 生成 `$.no_evidence`。
|
||
- `lookup_knowledge` no-hit 生成 `$.no_evidence`。
|
||
- Gatekeeper 可以校验 `$.no_evidence`。
|
||
- 多个 no-evidence 调用存在时,Gatekeeper 可按 `tool_name + raw_path + evidence_excerpt` 唯一回填 `source_invocation_id`。
|
||
- `negative_observation` 混绑正向 `$.logs[i]` 会被拒绝。
|
||
- HikariCP negative mock 不返回 `generic-service`。
|
||
|
||
### 2026-07-08 E2E 验证
|
||
|
||
输入:
|
||
|
||
```text
|
||
只确认 inventory-service 是否存在 HikariCP 连接池耗尽日志。
|
||
```
|
||
|
||
最终通过 session:
|
||
|
||
```text
|
||
sessionId: iss009-hikari-negative-latest-20260708-232428
|
||
verdict: PASS
|
||
groundedness_score: 1.0
|
||
gatekeeper_result.status: pass
|
||
gatekeeper_result.severity: none
|
||
claim_count: 1
|
||
claim_type: negative_observation
|
||
raw_path: $.no_evidence
|
||
generic_service_hit: false
|
||
overstate_hit: false
|
||
```
|
||
|
||
Executor claim:
|
||
|
||
```text
|
||
当前查询未检索到 inventory-service 的 HikariCP 连接池耗尽日志。
|
||
```
|
||
|
||
最终答案:
|
||
|
||
```text
|
||
本次查询在 inventory-service 中未发现 HikariCP 连接池耗尽的日志记录,检索结果未匹配到相关证据。
|
||
```
|
||
|
||
说明:
|
||
|
||
- Executor 仍可能输出多个 `$.no_evidence` binding。
|
||
- 如果 `source_invocation_id` 缺失,Gatekeeper 会按 `tool_name + raw_path + evidence_excerpt` 唯一匹配真实 invocation 并写入 warning。
|
||
- 最终答案不使用“排除”“确认没有”“不存在该问题”等过度表达。
|