feat(agent): support no-evidence references

This commit is contained in:
aruo
2026-07-08 23:30:00 +08:00
parent 7b8c75e571
commit 9a84b3de34
9 changed files with 1040 additions and 28 deletions
@@ -0,0 +1,239 @@
# 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。
- 最终答案不使用“排除”“确认没有”“不存在该问题”等过度表达。