Files
SuperBizAgent-java/mvp/architecture/retrieval-observability.md
T

275 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 检索与可观测性架构
**更新日期**: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-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-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`。
- 给 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 --> 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。
- context pack summary。
- rerank trace。
## 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。