# RAG 检索可观测性、审计与 Trace(现行)
**更新日期**:2026-07-28
**状态**:当前可运行
**关联**:`lookup_knowledge`、Harness `ToolBoundary`、`tool_invocation`、`DiagnosisTraceService`、离线 eval
---
## 1. 三层边界
```mermaid
flowchart TB
subgraph A["A. 请求内 Trace"]
LR[LookupResult
retrievalTrace / rerankTrace / relevanceLevel]
end
subgraph B["B. 持久化审计 + Trace API"]
TI[tool_invocation 表]
DT[diagnosis_trace 事件摘要]
API["GET /api/diagnosis/{sessionId}/trace"]
end
subgraph C["C. 质量回归"]
EV[eval/rag-retrieval offline baseline]
end
subgraph agent["Agent 可见(非审计)"]
RT[RagToolResult
evidence + optional relevance_level]
end
LK[LookupKnowledgeTool] --> LR
LR --> PROJ[RagResultProjector]
PROJ --> RT
LR --> BOUND[ToolBoundary audit]
BOUND --> TI
BOUND --> DT
TI --> API
DT --> API
EV -.->|不替代运行时 Trace| LK
```
| 层 | 完善度 | 说明 |
|----|--------|------|
| A 请求内 | 高 | attempt / fallback / quality 齐全 |
| B 持久化 + Trace API | 中高 | RAG 富字段入 `tool_invocation`,经 Trace API 回放 |
| C 离线 eval | 高 | hybrid fixtures 回归 |
**Agent 看到的不是完整 Trace。** 完整检索轨迹在 A/B;Agent 只拿投影后的证据契约。
---
## 2. 端到端:从 lookup 到 Trace API
```mermaid
sequenceDiagram
participant Agent
participant Adapter as RagToolAdapter
participant Bound as ToolBoundary
participant Tool as LookupKnowledgeTool
participant Sink as JpaToolInvocationAuditSink
participant DB as tool_invocation
participant Trace as DiagnosisTraceService
participant API as GET .../trace
Agent->>Adapter: lookup_knowledge(query)
Adapter->>Bound: execute(legacy, projector)
Bound->>Tool: execute(query)
Tool-->>Bound: raw LookupResult JSON
Note over Tool: 内含 retrievalTrace / rerankTrace / evidenceBlocks
Bound->>Bound: project → RagToolResult
Bound->>Sink: AuditEvent + rawResultJson + agentResultJson
Sink->>Sink: RagLookupAuditEnricher
Sink->>DB: 富字段行
Bound-->>Agent: 投影后 agent_result(无完整 trace)
API->>Trace: sessionId + optional runId
Trace->>DB: find tool_invocation by run/session
Trace-->>API: DiagnosisTraceResponse.toolInvocations[]
```
---
## 3. A 层:请求内 Trace(`LookupResult`)
一次成功的 `lookup_knowledge` 内部出口是 **`LookupResult`**(比 Agent 契约更富)。
### 3.1 结构总览
```mermaid
flowchart TB
LR[LookupResult]
LR --> F[found]
LR --> EB[evidenceBlocks[]]
LR --> CP[contextPack]
LR --> RT[retrievalTrace]
LR --> RR[rerankTrace]
LR --> RL[relevanceLevel]
LR --> CH[completenessHint]
LR --> CNT[evidenceCandidateCount / evidenceBlockCount]
RT --> ATT[attempts[]]
RT --> SEL[selectedAttempt]
RT --> FB[fallbackReason]
RT --> HINT[queryHints L0]
```
| 字段 | 含义 |
|------|------|
| `found` | 是否有可用证据块 |
| `evidenceBlocks` | 后处理后的 chunk 级证据(含 evidenceKey、source、content…) |
| `contextPack` | 字符预算打包文本(内部/审计用) |
| `retrievalTrace` | **检索路径 Trace**(见下) |
| `rerankTrace` | 后处理排序/quality 痕迹(现多为保序后的 quality) |
| `relevanceLevel` | PRECISE / REFERENCE / null |
| `completenessHint` | 给模型的天花板提示文案 |
### 3.2 `retrievalTrace`(检索路径)
| 字段 | 含义 |
|------|------|
| `originalQuery` | 原始查询 |
| `rewrittenQuery` | L0/变换后用于检索的 query |
| `categoryFilter` | 首次过滤的 category(可 null) |
| `selectedAttempt` | 最终采用的 attempt 名 |
| `fallbackReason` | 如 `filtered_vector_low_quality`;未降级为 null |
| `evidenceStatus` | 内部:`supported` / `no_evidence` 等 |
| `queryHints` | L0:domains、keywords、entities、l0_match_count… |
| `attempts[]` | 每次检索尝试快照 |
**常见 `selectedAttempt`:**
| 值 | 含义 |
|----|------|
| `FILTERED_VECTOR` | 带 category 的首次检索即采用 |
| `UNFILTERED_VECTOR` | 无 category,直接全库检索 |
| `UNFILTERED_VECTOR_RETRY` | filtered 低质/无证据后去掉 category 重试 |
**单次 `attempts[]` 元素:**
| 字段 | 含义 |
|------|------|
| `name` | attempt 名 |
| `query` | 该次实际检索句 |
| `categoryFilter` | 该次 filter |
| `candidateCount` | 召回候选数 |
| `usable` | 后处理阈值后是否可用 |
| `topScore` / `topSimilarity` | 引擎分 / 归一化 quality(0~1) |
| `durationMs` | 耗时 |
| `errorMessage` | 失败时 |
### 3.3 一次典型路径(含 filter fallback)
```mermaid
flowchart TB
Q[query] --> L0[L0 hint → 可选 categoryFilter]
L0 --> A1[attempt FILTERED_VECTOR]
A1 --> PQ{isLowQuality?}
PQ -->|否| USE1[selectedAttempt = FILTERED_VECTOR]
PQ -->|是| A2[attempt UNFILTERED_VECTOR_RETRY]
A2 --> USE2[selectedAttempt = RETRY
fallbackReason = low_quality / no_evidence]
USE1 --> POST[PostProcess · evidenceBlocks · relevanceLevel]
USE2 --> POST
POST --> LR[LookupResult 完整 Trace]
```
### 3.4 与 Agent 投影的关系
```mermaid
flowchart LR
LR[LookupResult 全量 Trace] --> PROJ[RagResultProjector]
PROJ --> AG[RagToolResult]
AG --> F1[evidence_status]
AG --> F2[evidence excerpt]
AG --> F3[relevance_level 可选]
AG --> F4[truncated / returned_count]
LR -.->|不投影| X1[retrievalTrace]
LR -.->|不投影| X2[rerankTrace]
LR -.->|不投影| X3[raw scores / contextPack 全文]
```
人/系统要「为什么这样检索」→ 看 **A 全量** 或 **B 落库摘要**,不要只看 Agent 字段。
---
## 4. B 层:持久化 + Trace API
### 4.1 写入路径
| 组件 | 职责 |
|------|------|
| `ToolBoundary` | 执行后发 `ToolInvocationAuditEvent`(含 raw LookupResult JSON + agent JSON) |
| `RagLookupAuditEnricher` | 从 LookupResult 抽有界 RAG 字段 |
| `JpaToolInvocationAuditSink` | 写入 `tool_invocation` |
| `TraceAuditEvents.toolInvocation` | 另写一条 diagnosis_trace 摘要事件(不含全文 LookupResult) |
### 4.2 `tool_invocation` 列(RAG)
| 列 | lookup_knowledge | 其它工具 |
|----|------------------|----------|
| `tool_name` | `lookup_knowledge` | 各自工具名 |
| `retrieval_layer` | 通常 `L1` | `HARNESS` |
| `relevance_level` | **PRECISE / REFERENCE / …** | **null**(不再写 evidence_status) |
| `l0_match_count` | queryHints | null |
| `l1_match_count` | evidence 块数等 | null |
| `is_truncated` | 投影 truncated | false |
| `retrieval_details` | JSON `rag_lookup_v1` | 通用 status 元数据 |
| `output_preview` | level/attempt 摘要 | status=… |
| `duration_ms` / `success` | 有 | 有 |
### 4.3 `retrieval_details`(rag_lookup_v1)示例
```json
{
"audit_schema": "rag_lookup_v1",
"search_mode": "hybrid",
"selected_attempt": "UNFILTERED_VECTOR_RETRY",
"fallback_reason": "filtered_vector_low_quality",
"category_filter": "overfilter-decoy",
"evidence_keys": ["doc#chunk-0"],
"sources": ["doc"],
"evidence_candidate_count": 8,
"evidence_block_count": 2,
"l0_hints": { "domains": ["mysql"], "matched_keywords": ["pool"] },
"attempts": [
{
"name": "FILTERED_VECTOR",
"category_filter": "overfilter-decoy",
"candidate_count": 2,
"usable": false,
"top_similarity": 0.3,
"duration_ms": 12
},
{
"name": "UNFILTERED_VECTOR_RETRY",
"candidate_count": 5,
"usable": true,
"top_similarity": 0.9,
"duration_ms": 20
}
],
"truncated": false,
"returned_count": 2,
"evidence_status": "EVIDENCE_FOUND",
"invocation_status": "READY",
"tool_call_id": "call-…"
}
```
**默认不落库:** 原始 query 全文、chunk 正文 excerpt、完整 rerankTrace(体积与隐私)。
### 4.4 Trace API:人怎么读 RAG
**接口:**
```http
GET /api/diagnosis/{sessionId}/trace
GET /api/diagnosis/{sessionId}/trace?runId={runId}
```
**实现:** `DiagnosisTraceController` → `DiagnosisTraceService.getTrace`
按 `sessionId`(可选精确 `runId`)拉 run、steps、**toolInvocations**、摘要等。
**响应中与 RAG 相关的核心块:** `DiagnosisTraceResponse.toolInvocations[]`
| API 字段 | 来源列 | 读法 |
|----------|--------|------|
| `toolName` | `tool_name` | 是否为 `lookup_knowledge` |
| `retrievalLayer` | `retrieval_layer` | L1 / HARNESS |
| `relevanceLevel` | `relevance_level` | RAG 粗相关度(非 evidence_status) |
| `l0MatchCount` / `l1MatchCount` | 同名列 | L0/L1 规模提示 |
| `truncated` | `is_truncated` | 证据是否被投影截断 |
| `outputPreview` | `output_preview` | 一行摘要(level/attempt…) |
| `retrievalDetails` | 解析自 `retrieval_details` | **RAG Trace 主阵地** |
| `retrievalDetailsRaw` | 原始 JSON 字符串 | 调试 |
| `durationMs` / `success` / `errorMessage` | 同名列 | 耗时与成败 |
| `inputParams` | 通常仅 tool_call_id、request_bytes | **不含完整 query**(有意) |
```mermaid
flowchart TB
API["GET /api/diagnosis/{sessionId}/trace"] --> SVC[DiagnosisTraceService]
SVC --> ROW[tool_invocation 行]
ROW --> T1[列: relevanceLevel, L0/L1 count, layer…]
ROW --> T2[retrievalDetails Map]
T2 --> D1[search_mode]
T2 --> D2[selected_attempt / fallback_reason]
T2 --> D3[attempts[] / evidence_keys]
T2 --> D4[evidence_status 契约状态]
```
### 4.5 读 Trace 的推荐顺序(排查「这次知识库怎么检的」)
```mermaid
flowchart TB
S1[找到 toolName=lookup_knowledge 的 invocation] --> S2{success?}
S2 -->|否| E[看 errorMessage / evidence_status]
S2 -->|是| S3[看 retrievalDetails.search_mode]
S3 --> S4[看 selected_attempt + fallback_reason]
S4 --> S5[看 attempts[] 每次 candidate_count / top_similarity / usable]
S5 --> S6[看 evidence_keys / sources]
S6 --> S7[看 relevanceLevel 列]
S7 --> S8[需要原文?看 Agent 侧 evidence 或当时 canonical 存储 · 审计默认无 excerpt]
```
| 现象 | 优先看 |
|------|--------|
| 为何走了 retry | `fallback_reason` + 两次 `attempts` |
| 是否 hybrid | `search_mode` |
| 滤错域 | `category_filter` + L0 domains |
| 相关度档 | 列 `relevanceLevel`(PRECISE/REFERENCE) |
| 返回了哪些块 | `evidence_keys` / `sources`(无正文) |
| Agent 是否被截断 | `truncated` / `returned_count` |
### 4.6 diagnosis_trace 事件 vs tool_invocation 行
| 通道 | 内容 | 用途 |
|------|------|------|
| `tool_invocation` 行 | RAG 富字段完整摘要 | **主审计/回放** |
| `diagnosis_trace` 中 `TOOL_INVOCATION` | tool_call_id、status、字节数、`has_raw_result` 等薄摘要 | 时间线事件,**不含**完整 retrieval_details |
查 RAG 细节以 **`toolInvocations[].retrievalDetails`** 为准。
---
## 5. 与 Agent / Eval 的边界
```mermaid
flowchart LR
subgraph human["人 / 运维 / 评测"]
TRACE[Trace API]
EVAL[Offline eval]
end
subgraph model["模型"]
AGENT[RagToolResult only]
end
TI[(tool_invocation)] --> TRACE
FX[fixtures] --> EVAL
PROJ[Projector] --> AGENT
```
| 消费者 | 能看到 |
|--------|--------|
| Agent | evidence + 可选 relevance_level,无 attempt 细节 |
| Trace API | 落库摘要:mode/attempt/fallback/keys/level… |
| Offline eval | 冻结 fixture 全量 LookupResult(含 trace),与 golden 比对 |
---
## 6. 与旧文档差异
| 旧(archive `retrieval-observability`) | 现 |
|----------------------------------------|-----|
| `vector-store.mode` 多后端 | `search_mode` dense\|hybrid,单一 V2 store |
| sink 理想化未落地 | `RagLookupAuditEnricher` + 列回填 |
| `relevance_level` 混用 evidence_status | **列仅 RAG 等级**;契约状态在 details |
| 未写清 Trace API 读法 | 本文 §4.4–4.5 |
---
## 7. 代码锚点
| 职责 | 类 / 路径 |
|------|-----------|
| 内建 Trace | `LookupKnowledgeTool`、`RetrievalTrace`、`LookupResult` |
| 投影 | `RagResultProjector`、`RagToolResult` |
| 审计事件 | `ToolInvocationAuditEvent`、`ToolBoundary` |
| 富化 | `RagLookupAuditEnricher` |
| 落库 | `JpaToolInvocationAuditSink`、`ToolInvocation` |
| Trace API | `DiagnosisTraceController`、`DiagnosisTraceService`、`DiagnosisTraceResponse.ToolInvocationTrace` |
| 离线回归 | `eval/rag-retrieval/`、`scripts/eval_rag_retrieval.py` |
---
## 8. 已知限制
- 持久化 **不存** 完整 query/excerpt(有意);要正文需 Agent 侧证据或其它存储
- `dedup_reason` 列可能仍为空
- 非 `lookup_knowledge` 工具仍为薄审计
- **历史** `tool_invocation` 行可能仍把 evidence_status 写进 `relevance_level`(旧 sink)
- `diagnosis_trace` 时间线事件不替代 `retrieval_details`
---
## 9. 相关文档
| 文档 | 内容 |
|------|------|
| `mvp/architecture/RAG知识检索架构.md` | 检索主架构 |
| `docs/RAG-Agent如何读relevance_level.md` | Agent 如何读 level |
| `docs/RAG-Hybrid质量分与后处理.md` | quality / 排序闸门 |
| `docs/RAG离线评测-基线设计.md` | 离线评测(非运行时 Trace) |
| `mvp/architecture/session-trace-lifecycle.md` | 会话/run Trace 总览(若存在) |