# RAG 检索可观测性、审计与 Trace(现行) **更新日期**:2026-09-29 **状态**:当前可运行 **关联**:`lookup_knowledge`、Harness `ToolBoundary`、`tool_invocation`、`DiagnosisTraceService`、离线 eval > **2026-09-29 RAG 抽离影响**:检索后端切换为 py-rag 服务(见 [RAG知识检索架构.md](./RAG知识检索架构.md))。 > Trace / 审计的三层边界与读写接口**不变**;变化仅在内容语义: > L0 已下沉(`queryHints` 恒为空结构、`categoryFilter` 恒为 null、attempt 只剩 `UNFILTERED_VECTOR`), > 质量分统一为 py-rag rerank 绝对分(scoreLabel=RERANK)。 > 文中涉及 FILTERED/RETRY attempt 的示例为历史数据读法,保留供回放旧 Run。 --- ## 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` | 用于检索的 query(L0 下沉后恒等于 originalQuery) | | `categoryFilter` | 首次过滤的 category(L0 下沉后恒为 null) | | `selectedAttempt` | 最终采用的 attempt 名 | | `fallbackReason` | 如 `filtered_vector_low_quality`;未降级为 null | | `evidenceStatus` | 内部:`supported` / `no_evidence` 等 | | `queryHints` | L0 提示(下沉后恒为空 domains/keywords/entities 与 l0_match_count=0) | | `attempts[]` | 每次检索尝试快照 | **`selectedAttempt` 取值:** | 值 | 含义 | |----|------| | `UNFILTERED_VECTOR` | **当前唯一会出现**:无 category,直传 py-rag 检索 | | `FILTERED_VECTOR` | (历史)带 category 的首次检索即采用;L0 下沉后不再产生 | | `UNFILTERED_VECTOR_RETRY` | (历史)filtered 低质/无证据后去掉 category 重试;分支保留但不可达,仅见于旧 Run 回放 | **单次 `attempts[]` 元素:** | 字段 | 含义 | |------|------| | `name` | attempt 名 | | `query` | 该次实际检索句 | | `categoryFilter` | 该次 filter | | `candidateCount` | 召回候选数 | | `usable` | 后处理阈值后是否可用 | | `topScore` / `topSimilarity` | 引擎分 / 归一化 quality(0~1) | | `durationMs` | 耗时 | | `errorMessage` | 失败时 | ### 3.3 一次典型路径(当前:单 attempt 直传) ```mermaid flowchart TB Q[query 原始句直传] --> A1[attempt UNFILTERED_VECTOR
PyRagKnowledgeSearchAdapter → py-rag] A1 --> POST[PostProcess · evidenceBlocks · relevanceLevel] POST --> LR[LookupResult 完整 Trace] ``` > 历史 filter fallback 路径(L0 → FILTERED_VECTOR → 低质 → UNFILTERED_VECTOR_RETRY)的流程图已随 L0 下沉移除; > 旧 Run 的 Trace 回放仍可见该结构,字段含义见 §3.2。 ### 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", "fallback_reason": null, "category_filter": null, "evidence_keys": ["e2e-gateway-b9c1fa12-md-34223174#chunk-1"], "sources": ["e2e-gateway-b9c1fa12-md"], "evidence_candidate_count": 5, "evidence_block_count": 2, "l0_hints": { "domains": [], "matched_keywords": [] }, "attempts": [ { "name": "UNFILTERED_VECTOR", "candidate_count": 5, "usable": true, "top_similarity": 0.91, "duration_ms": 640 } ], "truncated": false, "returned_count": 2, "evidence_status": "EVIDENCE_FOUND", "invocation_status": "READY", "tool_call_id": "call-…" } ``` **默认不落库:** 原始 query 全文、chunk 正文 excerpt、完整 rerankTrace(体积与隐私)。 历史 Run 中 `selected_attempt=FILTERED_VECTOR` / `UNFILTERED_VECTOR_RETRY` 与非空 `l0_hints` 为 L0 下沉前的旧数据形态。 ### 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] ``` | 现象 | 优先看 | |------|--------| | 是否 hybrid | `search_mode`(hybrid/semantic 对应 py-rag 融合/纯向量) | | 滤错域 | (历史)`category_filter` + L0 domains;L0 下沉后恒为 null | | 相关度档 | 列 `relevanceLevel`(PRECISE/REFERENCE) | | 返回了哪些块 | `evidence_keys` / `sources`(无正文) | | Agent 是否被截断 | `truncated` / `returned_count` | | 为何走了 retry | (历史)`fallback_reason` + 两次 `attempts`;L0 下沉后单 attempt,不再产生 | ### 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(现映射 py-rag semantic\|hybrid) | | sink 理想化未落地 | `RagLookupAuditEnricher` + 列回填 | | `relevance_level` 混用 evidence_status | **列仅 RAG 等级**;契约状态在 details | | 未写清 Trace API 读法 | 本文 §4.4–4.5 | | L0 hint / FILTERED-RETRY attempt(2026-07-28 形态) | 2026-09-29 L0 下沉 py-rag:单 attempt、queryHints 恒空、质量分 RERANK 直传 | --- ## 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` | 检索主架构 | | `../engineering/rag/RAG-Agent如何读relevance_level.md` | Agent 如何读 level | | `../engineering/rag/RAG-Hybrid质量分与后处理.md` | quality / 排序闸门 | | `../engineering/rag/RAG离线评测-基线设计.md` | 离线评测(非运行时 Trace) | | `mvp/architecture/session-trace-lifecycle.md` | 会话/run Trace 总览(若存在) |