# 检索与可观测性架构 **更新日期**:2026-07-06 **状态**:当前可运行架构 **参考历史文档**:`archive/2026-07-05-legacy/knowledge-retrieval-architecture.md` ## 1. 定位 本文补充 [rag-architecture.md](rag-architecture.md) 中的检索细节,重点回答: - 查询如何进入 `lookup_knowledge`。 - L0 和 L1 当前分别承担什么职责。 - 检索结果如何归一化、去重、记录。 - 如何通过 trace 和 eval 判断检索质量。 当前架构与旧版最大的差异是:L0 不再因为唯一命中而默认跳过 L1,也不在 L1 失败时作为事实证据兜底。L0 是 hint 和解释信号,L1 语义检索是默认召回路径。 ## 2. 检索总图 ```mermaid flowchart TD Query["Agent query / AIOps recommended query"] --> Tool["LookupKnowledgeTool"] Tool --> L0["KnowledgeIndexService.analyzeQuery"] L0 --> L0Result["L0 hint: matches / domains / keywords"] L0Result --> Filter["singleDomainOrNull -> category filter"] Tool --> L1["VectorSearchService.searchSimilarDocuments"] Filter --> L1 L1 --> Mode{"retrieval.vector-store.mode"} Mode -->|auto| Spring["Spring AI VectorStore"] Spring -->|failure| SDK["Milvus SDK fallback"] Mode -->|spring / spring-ai| Spring Mode -->|sdk| SDK Spring --> Candidates["L1 candidates"] SDK --> Candidates Candidates --> Quality{"filtered L1 usable?"} Quality -->|no| Retry["raw query unfiltered L1 retry"] Quality -->|yes| Post["post-retrieval processing"] Retry --> Post L0Result --> Post Post --> Pack["context packing"] Pack --> Result["LookupResult evidenceBlocks/contextPack/traces"] Result --> Dedup["RetrievedDocTracker session dedup"] Dedup --> Final["final tool output"] Final --> Invocation["tool_invocation"] Final --> Agent["Agent Executor"] ``` ## 3. L0 Hint 层 L0 的输入是原始 query,输出是解释性结构: ```text matches matchedKeywords domains singleDomainOrNull ``` 当前职责: | 职责 | 说明 | |---|---| | domain hint | 判断 query 可能属于哪个知识域 | | entity / keyword hint | 记录命中的关键词、错误码、服务名等 | | category filter candidate | 当只有单一领域时,给 L1 一个 metadata filter 候选 | | trace explanation | 写入 `tool_invocation.retrieval_details`,用于解释检索为什么这么走 | 不再承担: ```text matches=1 -> skip L1 -> 直接返回 L0 文档正文 L1 无可用证据 -> 返回 L0 文档正文 ``` 原因: - 子串命中不等价于最终相关性。 - L0 没有稳定排序和语义相似度。 - AIOps query 往往包含多个字段,单点关键词命中容易误导。 ## 4. L1 语义检索层 L1 通过 `VectorSearchService` 调度,支持三种模式: | 模式 | 行为 | 用途 | |---|---|---| | `auto` | 优先 Spring AI VectorStore,失败 fallback 到 SDK | 默认运行模式 | | `spring` / `spring-ai` | 只走 Spring AI VectorStore | 验证框架路径 | | `sdk` | 只走 Milvus SDK | 对比旧链路或临时回退 | ### Spring AI VectorStore 路径 ```text SearchRequest -> query -> topK -> similarityThresholdAll -> optional filterExpression -> VectorStore.similaritySearch ``` ### Milvus SDK fallback ```text query -> VectorEmbeddingService.generateQueryVector -> Milvus search(vector, topK, L2) -> id / content / metadata ``` SDK fallback 保留的价值: - VectorStore bean 缺失时不让 MVP 主链路中断。 - Spring AI collection/schema 配置异常时可回退。 - 便于 SDK 与 VectorStore 的结果对比。 ## 5. 分数与相关性归一化 检索结果输出三类分数字段: | 字段 | 说明 | |---|---| | `score` | 兼容旧逻辑的距离型分数 | | `rawScore` | 底层检索实现原始分数 | | `scoreLabel` | 原始分数语义,例如 `similarity` 或 `l2_distance` | post-retrieval 层再把检索候选归一为: | relevanceLevel | 含义 | |---|---| | `PRECISE` | L1 相似度高且 query hint 与候选证据互相支撑 | | `HIGHLY_RELEVANT` | L1 相似度高 | | `REFERENCE` | 可作为参考,但不足以声明强证据 | | `DEDUPED` | 同 session 中已检索过,不重复注入上下文 | 归一化结果用于: - 给 Agent 输出 completeness hint。 - 写入 `tool_invocation.relevance_level`。 - 给 Gatekeeper 提供 `evidence_refs` 引用验真源。 - 给 Verifier 构造 `tool_trace_summary` 审计导航。 - 供 EvaluationService 计算 evidence score。 ## 6. 文档切片和 metadata 当前保留 Markdown-aware chunking。 关键 metadata: ```text docId chunkIndex totalChunks title breadcrumb category source ``` embedding 输入已经增强为: ```text title + breadcrumb + content ``` 这解决旧版检索中的一个主要问题:单个 chunk 被召回后,LLM 不知道它属于哪个文档、哪个章节。 ## 7. 输出和记录 `lookup_knowledge` 的输出会进入两条路径: ```mermaid flowchart LR LookupResult["LookupResult: evidenceBlocks/contextPack/traces"] --> Agent["Agent context"] LookupResult --> Recorder["ToolInvocationRecorder"] Recorder --> Invocation["tool_invocation"] Invocation --> Trace["DiagnosisTraceService"] Invocation --> Gatekeeper["ExecutorGatekeeperService"] Invocation --> Summary["ToolTraceSummaryService"] Summary --> Verifier["chat_verifier"] Invocation --> Eval["EvaluationService / RAG eval"] ``` `tool_invocation` 中与检索相关的字段: ```text retrieval_layer l0_match_count l1_match_count retrieval_details relevance_level dedup_reason output_preview duration_ms success ``` `retrieval_details` 承载更细信息,例如: - L0 命中文档标题和路径。 - L1 attempts、fallback reason、分数和 similarity。 - retrieved domains。 - evidence status。 - dedup reason。 - evidence block summaries。 - evidence refs:`raw_path + text`,用于核对 Executor 的 `evidence_excerpt`。 - context pack summary。 - rerank trace。 其中 `evidence_refs` 是当前 Chat 证据链路的精确引用源: ```json { "evidence_refs": [ { "raw_path": "$.evidence_blocks[0]", "text": "最小证据文本" } ] } ``` 如果检索返回 no evidence,应使用 `raw_path=$.no_evidence` 记录负向证据。它只能说明“本次检索没有匹配证据”,不能作为“问题不存在”的证明。 ## 8. 去重与行动记忆 当前 session 级去重由 `RetrievedDocTracker` 负责。 ```text sessionId + docKey -> already retrieved? -> yes: return dedup message and record dedup_reason -> no: mark retrieved and return evidence ``` 去重目的: - 避免同一文档反复进入上下文。 - 降低 token 浪费。 - 给 Executor 一个“这个方向已经查过”的行动记忆。 注意:去重不是全局缓存,只在当前诊断 session 内生效。 ## 9. 检索质量评测 检索质量不能只看一次接口返回,需要用固定 query 回归。 当前评测资产: | 资产 | 用途 | |---|---| | `eval/rag-retrieval/cases/golden-cases.json` | 固定 query 和期望证据 | | `eval/rag-retrieval/fixtures/` | 离线候选结果 | | `eval/rag-retrieval/reports/baseline.md` | 人类可读基线 | | `scripts/eval_rag_retrieval.py` | 离线回归 | | `scripts/eval_rag_live_acceptance.py` | 运行环境验收 | 评测层次: ```text offline baseline -> 不依赖服务和外部组件 live acceptance -> 调用 /api/search/similar -> 验证重建索引后的真实检索 trace inspection -> 检查 Agent 是否真的调用 lookup_knowledge -> 检查 tool_invocation 证据是否完整 ``` ## 10. 后续增强 近期优先: 1. 邻居 chunk / 同章节上下文扩展。 2. metadata taxonomy 清理。 3. Query Transformer / MultiQuery 可回退接入。 4. 更完整的 Recall@K、MRR、nDCG 报告。 暂不优先: - 重新引入 L0 直接返回。 - 重新引入 L0 文档作为 L1 失败时的事实证据兜底。 - 一次性迁移所有写入路径。 - 在没有评测收益前引入模型 rerank / RRF / BM25。