02 - 高级检索器组合
02 - 高级检索器组合
学习目标
本章将带你探索 LlamaIndex 的多种检索器策略及其组合方式。你将学到:
- 基础向量检索器的检索流程
- FusionRetriever 的多查询 + 倒数排名融合(RRF)算法
- RouterRetriever 的 LLM 路由决策
- AutoMergingRetriever 的多粒度检索
- IndexNode 递归检索机制
- Postprocessor 的选择策略
前置知识
- RAG 全链路可视化管线 — 检索阶段
- 模板化文本切分策略 — 分块策略
项目实践
检索器架构概览
LlamaIndex 的所有检索器继承自 BaseRetriever,核心抽象方法是 _retrieve(query_bundle) -> List[NodeWithScore]。
VectorIndexRetriever:基础向量检索
检索流程分为四步:
- Embedding:如果查询还没有 embedding,调用 embed_model 转换
- 构建查询:创建
VectorStoreQuery对象(包含 query_embedding、similarity_top_k、filters) - 执行搜索:调用
vector_store.query()获取结果 - 文本回退:如果 vector store 不存文本(
stores_text=False),从 docstore 获取完整 node
retriever = index.as_retriever( similarity_top_k=5, vector_store_query_mode="default", # 或 "hybrid", "sparse", "mmr" filters=MetadataFilters(filters=[...]))nodes = retriever.retrieve("你的查询")FusionRetriever:多查询 + RRF 融合
FusionRetriever 解决单个查询的语义盲区问题。它先生成多个变体查询,然后对每个变体执行检索,最后用 倒数排名融合(RRF, Reciprocal Rank Fusion) 合并结果。
from llama_index.core.retrievers import QueryFusionRetriever
retriever = QueryFusionRetriever( retrievers=[retriever1, retriever2], similarity_top_k=5, num_queries=4, # 生成 4 个变体查询 use_async=True, # 并行执行所有检索 query_gen_prompt="..." # 可选:自定义查询生成 prompt)RRF 算法:每个节点在所有检索结果中的排名被转换为倒数分数 1 / (k + rank),然后累加。排名靠前的节点获得更高分数。
RouterRetriever:LLM 路由决策
RouterRetriever 使用 LLM 判断哪个检索器最适合当前查询。它特别适合混合数据源场景(如同时有结构化数据和向量数据)。
from llama_index.core.retrievers import RouterRetrieverfrom llama_index.core.selectors import LLMSingleSelector
retriever = RouterRetriever( selector=LLMSingleSelector.from_defaults(), retrievers=[ retriever1, # 描述:技术文档 retriever2, # 描述:客户反馈 retriever3, # 描述:产品规格表 ])路由 Prompt 设计:每个 retriever 必须提供 description 字段,LLM 根据查询与描述的相关性选择最佳检索器。描述越具体,路由越准确。
AutoMergingRetriever:多粒度检索
当使用 SentenceWindowNodeParser 等分块策略时,AutoMergingRetriever 可以实现”小粒度检索 + 大粒度返回”:
- 用小粒度 nodes 索引(如单句)
- 检索时匹配最相似的小 nodes
- 如果同一父节点下的小 nodes 有多数被命中,自动”合并”返回父节点(完整段落)
from llama_index.core.retrievers import AutoMergingRetriever
retriever = AutoMergingRetriever( vector_retriever, storage_context, auto_merging_threshold=0.5, # 50% 子节点命中则合并)设计原因:单句级别的 embedding 检索精度高,但单独返回一个句子给 LLM 时上下文不足。AutoMergingRetriever 在检索精度和上下文完整性之间取得平衡。
IndexNode 递归检索
IndexNode 是一种特殊的节点,它不存储文本,而是指向另一个 retriever、query engine 或 node。当检索到 IndexNode 时,会自动递归进入其指向的对象。
from llama_index.core.schema import IndexNode
# 创建一个指向另一个 retriever 的 IndexNodeindex_node = IndexNode( text="指向技术文档库", index_id="tech_docs",)
# 注册 object_mapretriever = index.as_retriever( object_map={"tech_docs": another_retriever})适用场景:构建”索引中的索引”——按主题分类的文档库,每个主题有自己的独立 retriever,顶层 IndexNode 路由到对应主题。
性能代价:每次递归检索至少增加一次额外的检索操作。深层嵌套可能导致检索延迟指数增长。
Postprocessor 选择策略
LlamaIndex 提供多种节点后处理器,可以在检索后进一步优化结果:
| 后处理器 | 策略 | 延迟影响 |
|---|---|---|
SimilarityPostprocessor | 按相似度阈值过滤 | 无(纯数值比较) |
KeywordNodePostprocessor | 关键词匹配过滤 | 无(spacy NLP) |
LongContextReorder | 高相关节点放首尾 | 无(排序操作) |
SentenceTransformerRerank | Cross-Encoder 重排序 | 中(本地模型推理) |
LLMRerank | LLM 批量选择最相关节点 | 高(LLM 调用) |
LongContextReorder 的设计原因:LLM 对上下文首尾位置的信息记忆更好(“lost in the middle”现象)。将最相关的节点放在首尾,可以提高回答质量。
问题与规避
| 陷阱 | 对策 |
|---|---|
| FusionRetriever 的查询生成消耗额外 LLM 调用 | 使用 num_queries=2-4 平衡准确性和成本;缓存常见查询的结果 |
| RouterRetriever 路由不准确 | 确保每个 retriever 的 description 具体且互斥;提供 few-shot 示例 |
| IndexNode 递归过深导致延迟 | 限制递归深度(通常不超过 2 层);在 retriever 中设置 recursion_depth 参数 |
| LLMRerank 成本过高 | 先使用 SimilarityPostprocessor 过滤掉低相似度节点,再对候选集 rerank |
| AutoMergingRetriever 合并不准确 | 调整 auto_merging_threshold;确保父子节点层级关系正确 |
设计取舍
单一检索器 vs 组合检索器
| 维度 | 单一 VectorIndexRetriever | 组合 (Fusion + Router + Postprocessor) |
|---|---|---|
| 延迟 | 低(一次向量搜索) | 高(多次检索 + LLM 路由 + 重排序) |
| 准确率 | 中 | 高 |
| 成本 | 低 | 高(额外 LLM 调用) |
| 适用场景 | 单一数据源、简单查询 | 混合数据源、复杂查询 |
为什么需要多种检索器:没有一种检索策略适合所有场景。向量检索擅长语义匹配但对关键词不敏感;关键词检索正好相反。LlamaIndex 通过组合模式让用户根据查询类型动态选择最优策略。
参考来源
- LlamaIndex 官方文档:https://developers.llamaindex.ai/python/framework/
- RRF (Reciprocal Rank Fusion) 算法:Cormack et al., “Reciprocal Rank Fusion outperforms Condorcet and individual Rank Learning”