8.1 KiB
检索与可观测性架构
更新日期:2026-07-06
状态:当前可运行架构
参考历史文档:archive/2026-07-05-legacy/knowledge-retrieval-architecture.md
1. 定位
本文补充 rag-architecture.md 中的检索细节,重点回答:
- 查询如何进入
lookup_knowledge。 - L0 和 L1 当前分别承担什么职责。
- 检索结果如何归一化、去重、记录。
- 如何通过 trace 和 eval 判断检索质量。
当前架构与旧版最大的差异是:L0 不再因为唯一命中而默认跳过 L1,也不在 L1 失败时作为事实证据兜底。L0 是 hint 和解释信号,L1 语义检索是默认召回路径。
2. 检索总图
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,输出是解释性结构:
matches
matchedKeywords
domains
singleDomainOrNull
当前职责:
| 职责 | 说明 |
|---|---|
| domain hint | 判断 query 可能属于哪个知识域 |
| entity / keyword hint | 记录命中的关键词、错误码、服务名等 |
| category filter candidate | 当只有单一领域时,给 L1 一个 metadata filter 候选 |
| trace explanation | 写入 tool_invocation.retrieval_details,用于解释检索为什么这么走 |
不再承担:
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 路径
SearchRequest
-> query
-> topK
-> similarityThresholdAll
-> optional filterExpression
-> VectorStore.similaritySearch
Milvus SDK fallback
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引用验真源。 - 由 Gatekeeper 核验后,经
VerifiedInputNode给 Verifier 构造最小verified_evidence投影。 - 供 EvaluationService 计算 evidence score。
6. 文档切片和 metadata
当前保留 Markdown-aware chunking。
关键 metadata:
docId
chunkIndex
totalChunks
title
breadcrumb
category
source
embedding 输入已经增强为:
title + breadcrumb + content
这解决旧版检索中的一个主要问题:单个 chunk 被召回后,LLM 不知道它属于哪个文档、哪个章节。
7. 输出和记录
lookup_knowledge 的输出会进入两条路径:
flowchart LR
LookupResult["LookupResult: evidenceBlocks/contextPack/traces"] --> Agent["Agent context"]
LookupResult --> Recorder["ToolInvocationRecorder"]
Recorder --> Invocation["tool_invocation"]
Invocation --> Trace["DiagnosisTraceService"]
Invocation --> Gatekeeper["ExecutorGatekeeperService"]
Gatekeeper --> Projection["VerifiedInputNode"]
Projection --> Verifier["chat_verifier"]
Invocation --> Eval["EvaluationService / RAG eval"]
当前 StateGraph 不生成或读取 tool_trace_summary,也不会把完整工具调用摘要输入 Verifier。
tool_invocation 中与检索相关的字段:
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 证据链路的精确引用源:
{
"evidence_refs": [
{
"raw_path": "$.evidence_blocks[0]",
"text": "最小证据文本"
}
]
}
如果检索返回 no evidence,应使用 raw_path=$.no_evidence 记录负向证据。它只能说明“本次检索没有匹配证据”,不能作为“问题不存在”的证明。
8. 去重与行动记忆
当前 session 级去重由 RetrievedDocTracker 负责。
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 |
运行环境验收 |
评测层次:
offline baseline
-> 不依赖服务和外部组件
live acceptance
-> 调用 /api/search/similar
-> 验证重建索引后的真实检索
trace inspection
-> 检查 Agent 是否真的调用 lookup_knowledge
-> 检查 tool_invocation 证据是否完整
10. 后续增强
近期优先:
- 邻居 chunk / 同章节上下文扩展。
- metadata taxonomy 清理。
- Query Transformer / MultiQuery 可回退接入。
- 更完整的 Recall@K、MRR、nDCG 报告。
暂不优先:
- 重新引入 L0 直接返回。
- 重新引入 L0 文档作为 L1 失败时的事实证据兜底。
- 一次性迁移所有写入路径。
- 在没有评测收益前引入模型 rerank / RRF / BM25。