202 lines
6.0 KiB
Markdown
202 lines
6.0 KiB
Markdown
# 模块化 RAG Pipeline 架构
|
||
|
||
**更新日期**:2026-07-06
|
||
**状态**:当前已实现架构
|
||
**关联 OpenSpec**:`openspec/changes/archive/2026-07-06-modular-rag-pipeline`
|
||
|
||
## 1. 定位
|
||
|
||
本文记录 `lookup_knowledge` 的当前模块化 RAG 实现。它是 [rag-architecture.md](rag-architecture.md) 的落地版,重点说明代码模块、数据契约、降级策略和可观测性边界。
|
||
|
||
核心目标:
|
||
|
||
- 保留显式 Agent Tool:`lookup_knowledge(query)`。
|
||
- L0 只作为 query understanding / filter / rerank / trace hint。
|
||
- L1 向量检索作为事实证据来源。
|
||
- filtered L1 低质量时,降级为 raw query unfiltered L1 retry。
|
||
- 输出 evidence-first contract,替代旧 `primary/supplement`。
|
||
|
||
## 2. 当前链路
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
Agent["Executor Agent"] --> Tool["LookupKnowledgeTool.lookupKnowledge(query)"]
|
||
|
||
Tool --> Transform["KnowledgeQueryTransformer"]
|
||
Transform --> KQ["KnowledgeQuery"]
|
||
|
||
KQ --> Retriever["KnowledgeDocumentRetriever"]
|
||
Retriever --> Attempt1["FILTERED_VECTOR or UNFILTERED_VECTOR"]
|
||
Attempt1 --> Post1["KnowledgeEvidencePostProcessor"]
|
||
Post1 --> Quality{"usable evidence?"}
|
||
Quality -->|yes| Pack
|
||
Quality -->|no and categoryFilter exists| Retry["UNFILTERED_VECTOR_RETRY"]
|
||
Retry --> Post2["KnowledgeEvidencePostProcessor"]
|
||
Post2 --> Pack["KnowledgeContextPacker"]
|
||
|
||
Pack --> Assemble["LookupResultAssembler"]
|
||
Assemble --> Result["LookupResult"]
|
||
Result --> Dedup["RetrievedDocTracker session dedup"]
|
||
Dedup --> Recorder["ToolInvocationRecorder"]
|
||
Recorder --> Trace["tool_invocation.retrieval_details"]
|
||
Result --> Agent
|
||
```
|
||
|
||
对应代码:
|
||
|
||
| 阶段 | 类 | 职责 |
|
||
|---|---|---|
|
||
| Tool Boundary | `LookupKnowledgeTool` | 接收 Agent 工具调用,编排 pipeline,处理 session dedup 和 recorder |
|
||
| Query Transformation | `KnowledgeQueryTransformer` | 复用 L0,输出 query hints 和可选 category filter |
|
||
| Retrieval | `KnowledgeDocumentRetriever` | 调用 `VectorSearchService`,统一 filtered / unfiltered attempt |
|
||
| Post-Retrieval | `KnowledgeEvidencePostProcessor` | L2 归一化、证据块构建、source dedup、规则 rerank |
|
||
| Context Packing | `KnowledgeContextPacker` | 按字符预算打包 Agent 可消费 context |
|
||
| Result Assembly | `LookupResultAssembler` | 统一 evidence result、no-evidence result、dedup result |
|
||
| Observability | `ToolInvocationRecorder` | 写入 query transform、retrieval trace、rerank trace、context pack summary |
|
||
|
||
## 3. L0 与 L1 边界
|
||
|
||
L0 来源于 `KnowledgeIndexService.analyzeQuery`,输出进入 `KnowledgeQuery`:
|
||
|
||
```text
|
||
originalQuery
|
||
rewrittenQuery
|
||
domainHints
|
||
matchedKeywords
|
||
entities
|
||
categoryFilter
|
||
l0Titles
|
||
l0MatchCount
|
||
```
|
||
|
||
L0 可以做:
|
||
|
||
- 给 L1 提供单一 category filter。
|
||
- 给 rerank 提供 domain / keyword / entity boost 信号。
|
||
- 给 trace 提供解释信息。
|
||
|
||
L0 不再做:
|
||
|
||
- 不因唯一命中直接返回文档正文。
|
||
- 不在 L1 无结果时作为事实证据兜底。
|
||
- 不进入 `evidenceBlocks`,除非未来明确引入新的 evidence source 规则。
|
||
|
||
L1 通过 `VectorSearchService.searchSimilarDocuments(query, topK, category)` 执行,内部仍保留 Spring AI VectorStore 优先和 Milvus SDK fallback。
|
||
|
||
## 4. 降级策略
|
||
|
||
MVP 降级策略保持简单:
|
||
|
||
```text
|
||
if categoryFilter exists:
|
||
run FILTERED_VECTOR
|
||
if empty / no evidence / top similarity < referenceThreshold:
|
||
run UNFILTERED_VECTOR_RETRY with original query
|
||
else:
|
||
run UNFILTERED_VECTOR
|
||
```
|
||
|
||
fallback reason:
|
||
|
||
| reason | 含义 |
|
||
|---|---|
|
||
| `filtered_vector_no_evidence` | filtered L1 无候选或 post-processing 后无 evidence |
|
||
| `filtered_vector_low_quality` | filtered L1 有候选,但 top normalized similarity 低于 `retrieval.normalization.reference-threshold` |
|
||
|
||
当 retry 后仍无证据:
|
||
|
||
- `found=false`
|
||
- `evidenceStatus=no_evidence`
|
||
- 保留 `retrievalTrace`
|
||
- 不返回 L0 文档作为事实证据
|
||
|
||
## 5. Evidence-First Contract
|
||
|
||
`LookupResult` 当前核心字段:
|
||
|
||
```text
|
||
found
|
||
evidenceBlocks
|
||
contextPack
|
||
retrievalTrace
|
||
rerankTrace
|
||
relevanceLevel
|
||
completenessHint
|
||
retrievedDomainsThisSession
|
||
message
|
||
```
|
||
|
||
旧字段已删除:
|
||
|
||
```text
|
||
primary
|
||
supplement
|
||
```
|
||
|
||
这是一项 L4 breaking interface change。项目内已同步迁移:
|
||
|
||
- `LookupKnowledgeTool`
|
||
- `ToolInvocationRecorder`
|
||
- executor prompts
|
||
- lookup / recorder tests
|
||
- RAG architecture docs
|
||
- OpenSpec 主 spec
|
||
|
||
## 6. Trace 结构
|
||
|
||
`retrieval_details` 保持 JSON 扩展,不改表结构。关键内容:
|
||
|
||
```json
|
||
{
|
||
"query_transform": {},
|
||
"retrieval_trace": {
|
||
"selected_attempt": "UNFILTERED_VECTOR_RETRY",
|
||
"fallback_reason": "filtered_vector_no_evidence",
|
||
"attempts": []
|
||
},
|
||
"context_pack_summary": {},
|
||
"rerank_trace": {},
|
||
"evidence_blocks": []
|
||
}
|
||
```
|
||
|
||
这样 Trace API、Verifier、Eval 可以继续从 `tool_invocation` 读取证据链。
|
||
|
||
## 7. Review 修正
|
||
|
||
归档前 review 发现:session dedup 命中时返回 `found=false`,但仍携带 `evidenceBlocks/contextPack`,可能导致 Agent 重复消费证据。
|
||
|
||
当前行为已修正:
|
||
|
||
- dedup result 不再返回可消费 evidence/context。
|
||
- 保留 message、retrieval trace、relevance hint 和 retrieved domains。
|
||
- 测试覆盖:`LookupKnowledgeToolTest.sessionDedupDoesNotReturnConsumableEvidenceAgain`。
|
||
|
||
## 8. 验证
|
||
|
||
已执行:
|
||
|
||
```powershell
|
||
mvn -q -DskipTests compile
|
||
mvn -q "-Dtest=LookupKnowledgeToolTest,ToolInvocationRecorderTest" test
|
||
$env:MILVUS_TOKEN = <application.yml 中的 milvus.token>; mvn -q test
|
||
openspec validate --all --strict
|
||
git diff --check
|
||
```
|
||
|
||
结果:全部通过。
|
||
|
||
注意:
|
||
|
||
- `MilvusConnectionTest` 直接读 `MILVUS_TOKEN` 环境变量,不读 Spring 配置。
|
||
- 完整测试需要在 Maven 进程里注入该环境变量。
|
||
|
||
## 9. 后续演进
|
||
|
||
建议后续按评测结果推进,而不是先堆复杂能力:
|
||
|
||
- 增加 RAG eval cases:固定 query、期望 source、期望 fallback path。
|
||
- 引入更严格的 evidence grounding 检查。
|
||
- 当规则 rerank 不足时,再考虑 model-based rerank。
|
||
- 当召回覆盖率不足时,再考虑 BM25/RRF/hybrid retrieval。
|