# 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。 - 最终答案不使用“排除”“确认没有”“不存在该问题”等过度表达。