267 lines
6.8 KiB
Markdown
267 lines
6.8 KiB
Markdown
# 检索与可观测性架构
|
||
|
||
**更新日期**:2026-07-05
|
||
**状态**:当前可运行架构
|
||
**参考历史文档**:`archive/2026-07-05-legacy/knowledge-retrieval-architecture.md`
|
||
|
||
## 1. 定位
|
||
|
||
本文补充 [rag-architecture.md](rag-architecture.md) 中的检索细节,重点回答:
|
||
|
||
- 查询如何进入 `lookup_knowledge`。
|
||
- L0 和 L1 当前分别承担什么职责。
|
||
- 检索结果如何归一化、去重、记录。
|
||
- 如何通过 trace 和 eval 判断检索质量。
|
||
|
||
当前架构与旧版最大的差异是:L0 不再因为唯一命中而默认跳过 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-ai| Spring
|
||
Mode -->|sdk| SDK
|
||
|
||
Spring --> Candidates["L1 candidates"]
|
||
SDK --> Candidates
|
||
Candidates --> Normalize["relevance normalization"]
|
||
L0Result --> Normalize
|
||
Normalize --> Result["LookupResult"]
|
||
|
||
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 文档正文
|
||
```
|
||
|
||
原因:
|
||
|
||
- 子串命中不等价于最终相关性。
|
||
- L0 没有稳定排序和语义相似度。
|
||
- AIOps query 往往包含多个字段,单点关键词命中容易误导。
|
||
|
||
## 4. L1 语义检索层
|
||
|
||
L1 通过 `VectorSearchService` 调度,支持三种模式:
|
||
|
||
| 模式 | 行为 | 用途 |
|
||
|---|---|---|
|
||
| `auto` | 优先 Spring AI VectorStore,失败 fallback 到 SDK | 默认运行模式 |
|
||
| `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` |
|
||
|
||
工具层再把 L0/L1 情况归一为:
|
||
|
||
| relevanceLevel | 含义 |
|
||
|---|---|
|
||
| `PRECISE` | L0 单命中且 L1 相似度高 |
|
||
| `HIGHLY_RELEVANT` | L1 相似度高,或 L0 多命中且 L1 支撑强 |
|
||
| `REFERENCE` | 可作为参考,但不足以声明强证据 |
|
||
| `DEDUPED` | 同 session 中已检索过,不重复注入上下文 |
|
||
|
||
归一化结果用于:
|
||
|
||
- 给 Agent 输出 completeness hint。
|
||
- 写入 `tool_invocation.relevance_level`。
|
||
- 给 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"] --> Agent["Agent context"]
|
||
LookupResult --> Recorder["ToolInvocationRecorder"]
|
||
Recorder --> Invocation["tool_invocation"]
|
||
Invocation --> Trace["DiagnosisTraceService"]
|
||
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 分数。
|
||
- retrieved domains。
|
||
- evidence status。
|
||
- dedup reason。
|
||
|
||
## 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. 完整 evidence block 输出。
|
||
2. 邻居 chunk / 同章节上下文扩展。
|
||
3. metadata taxonomy 清理。
|
||
4. Query Transformer / MultiQuery 可回退接入。
|
||
5. 更完整的 Recall@K、MRR、nDCG 报告。
|
||
|
||
暂不优先:
|
||
|
||
- 重新引入 L0 直接返回。
|
||
- 一次性迁移所有写入路径。
|
||
- 在没有评测收益前引入 rerank / RRF / BM25。
|
||
|