LlamaIndex - 05 Retriever(检索器)
一、Retriever 是什么?
在 LlamaIndex 的架构中,Retriever(检索器) 负责根据用户查询(或聊天消息)获取最相关的上下文内容。它是连接索引(Index)和查询引擎(Query Engine)的核心桥梁。
整个查询流程包含三个核心组件:
| 组件 | 职责 | 类比 |
|---|---|---|
| Retriever | 从索引中检索相关节点 | 图书管理员在书架上找书 |
| Response Synthesizer | 将检索到的节点合成答案 | 阅读并总结找到的资料 |
| Query Engine | 整合上述两者,提供统一接口 | 前台的查询服务窗口 |
用户查询 → Retriever(检索相关节点)→ Response Synthesizer(合成答案)→ 返回响应二、快速上手:基础用法
2.1 最简单的使用方式
from llama_index.core import VectorStoreIndex
from llama_index.core import SimpleDirectoryReader
# 加载文档
documents = SimpleDirectoryReader("data").load_data()
# 构建索引
index = VectorStoreIndex.from_documents(documents)
# 获取检索器(最简形式)
retriever = index.as_retriever()
# 执行检索
nodes = retriever.retrieve("Who is Paul Graham?")💡
as_retriever()是从索引获取检索器的快捷方式。每个索引类型都有自己默认的检索器实现。
2.2 配置检索参数
# 配置返回节点数量(Top-K)
retriever = index.as_retriever(similarity_top_k=5)
# VectorStoreIndex 还支持向量搜索相关参数
retriever = vector_index.as_retriever(
similarity_top_k=3, # 返回最相似的3个节点
vector_store_query_mode="default",
)三、核心概念深入
3.1 Index(索引)与 Retriever 的关系
Index(索引) 是存储在文档对象之上的数据结构,旨在支持 LLM 进行查询。不同类型的索引对应不同的检索策略:
| 索引类型 | 检索器模式 | 适用场景 |
|---|---|---|
VectorStoreIndex | 向量相似度检索 | 语义搜索、事实查询 |
SummaryIndex | 列表式检索/LLM检索 | 摘要生成、全文遍历 |
KeywordTableIndex | 关键词匹配检索 | 精确关键词查询 |
from llama_index.core import SummaryIndex, VectorStoreIndex
# 不同索引的不同检索器配置
summary_index = SummaryIndex(nodes)
vector_index = VectorStoreIndex(nodes)
# SummaryIndex 支持多种检索模式
list_retriever = summary_index.as_retriever(
retriever_mode="llm", # 使用 LLM 选择
choice_batch_size=5, # 批处理大小
)
# 低阶 API:直接实例化检索器类
from llama_index.core.indices.list import SummaryIndexLLMRetriever
retriever = SummaryIndexLLMRetriever(
index=summary_index,
choice_batch_size=5,
)3.2 检索器在 RAG 流程中的位置
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Load │ -> │ Index │ -> │ Retriever │ -> │ Synthesize │
│ 加载文档 │ │ 构建索引 │ │ 检索节点 │ │ 生成答案 │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
↑
用户查询进入四、高阶检索技术
4.1 递归检索器(Recursive Retriever)
递归检索解决了两个核心痛点:
- 解耦检索与合成:有时通过摘要获取文档比直接搜索原始块更有效
- 文档内动态任务:在单个文档内可能需要超越事实问答的复杂操作
from llama_index.core import VectorStoreIndex, SummaryIndex
from llama_index.core.schema import IndexNode
from llama_index.core.tools import QueryEngineTool
from llama_index.agent.openai import OpenAIAgent
# 为每个文档构建文档代理(Document Agent)
agents = {}
for wiki_title in wiki_titles:
# 向量索引:用于语义搜索
vector_index = VectorStoreIndex.from_documents(docs)
# 摘要索引:用于总结
summary_index = SummaryIndex.from_documents(docs)
# 创建两个查询引擎工具
tools = [
QueryEngineTool(
query_engine=vector_index.as_query_engine(),
metadata=ToolMetadata(
name="vector_tool",
description=f"搜索 {wiki_title} 中的具体事实",
),
),
QueryEngineTool(
query_engine=summary_index.as_query_engine(),
metadata=ToolMetadata(
name="summary_tool",
description=f"总结 {wiki_title} 的内容",
),
),
]
# 文档代理:可动态选择使用向量搜索还是摘要
agent = OpenAIAgent.from_tools(tools)
agents[wiki_title] = agent
# 构建顶层检索器
objects = []
for title, agent in agents.items():
node = IndexNode(
text=f"关于 {title} 的维基百科内容",
index_id=title,
obj=agent,
)
objects.append(node)
top_index = VectorStoreIndex(objects=objects)
query_engine = top_index.as_query_engine(similarity_top_k=1)
# 查询会自动路由到正确的文档代理
response = query_engine.query("Tell me about sports teams in Boston")4.2 混合检索:自定义检索器
混合检索结合关键词匹配(BM25)和语义向量搜索,提升检索精度:
from typing import List
from llama_index.core import QueryBundle
from llama_index.core.retrievers import BaseRetriever, VectorIndexRetriever
from llama_index.core.retrievers import KeywordTableSimpleRetriever
from llama_index.core.schema import NodeWithScore
class HybridRetriever(BaseRetriever):
"""实现 AND/OR 逻辑的混合检索器"""
def __init__(
self,
vector_retriever: VectorIndexRetriever,
keyword_retriever: KeywordTableSimpleRetriever,
mode: str = "AND", # "AND" 取交集,"OR" 取并集
):
self.vector_retriever = vector_retriever
self.keyword_retriever = keyword_retriever
self.mode = mode
super().__init__()
def _retrieve(self, query_bundle: QueryBundle) -> List[NodeWithScore]:
# 分别执行两种检索
vector_nodes = self.vector_retriever.retrieve(query_bundle)
keyword_nodes = self.keyword_retriever.retrieve(query_bundle)
vector_ids = {n.node.node_id for n in vector_nodes}
keyword_ids = {n.node.node_id for n in keyword_nodes}
# 合并结果字典
combined = {n.node.node_id: n for n in vector_nodes}
combined.update({n.node.node_id: n for n in keyword_nodes})
# 根据模式取交集或并集
if self.mode == "AND":
result_ids = vector_ids.intersection(keyword_ids)
else:
result_ids = vector_ids.union(keyword_ids)
return [combined[rid] for rid in result_ids]
# 使用自定义检索器
vector_retriever = VectorIndexRetriever(index=vector_index, similarity_top_k=3)
keyword_retriever = KeywordTableSimpleRetriever(index=keyword_index)
hybrid_retriever = HybridRetriever(
vector_retriever,
keyword_retriever,
mode="AND"
)
# 接入查询引擎
from llama_index.core.query_engine import RetrieverQueryEngine
query_engine = RetrieverQueryEngine(
retriever=hybrid_retriever,
response_synthesizer=get_response_synthesizer(),
)4.3 检索器路由(Retriever Router)
当工具/索引数量较多时,传统路由器会受限于 Prompt 长度。检索器路由通过向量检索动态选择最相关的查询引擎:
from llama_index.core.objects import ObjectIndex, SimpleToolNodeMapping
from llama_index.core.query_engine import ToolRetrieverRouterQueryEngine
# 创建多个查询引擎工具
list_tool = QueryEngineTool.from_defaults(
query_engine=list_query_engine,
description="用于作者传记类问题",
)
vector_tool = QueryEngineTool.from_defaults(
query_engine=vector_query_engine,
description="用于检索作者生平的具体片段,如大学经历、YC经历等",
)
# 构建对象索引
tool_mapping = SimpleToolNodeMapping.from_objects([list_tool, vector_tool])
obj_index = ObjectIndex.from_objects(
[list_tool, vector_tool],
tool_mapping,
VectorStoreIndex,
)
# 路由检索器:动态选择工具
router_query_engine = ToolRetrieverRouterQueryEngine(obj_index.as_retriever())
# 路由会自动选择 vector_tool
response = router_query_engine.query("What did the author do at Y Combinator?")五、RetrieverQueryEngine 详解
RetrieverQueryEngine 是将检索器与响应合成器组合的标准方式:
from llama_index.core.query_engine import RetrieverQueryEngine
from llama_index.core import get_response_synthesizer
# 方式一:从检索器构建
query_engine = RetrieverQueryEngine.from_args(
retriever=my_retriever,
response_mode="compact", # 响应合成模式
text_qa_template=my_qa_template, # 自定义 Prompt
node_postprocessors=[...], # 节点后处理器
)
# 方式二:直接构造
response_synthesizer = get_response_synthesizer(response_mode="tree_summarize")
query_engine = RetrieverQueryEngine(
retriever=my_retriever,
response_synthesizer=response_synthesizer,
)
# 执行查询
response = query_engine.query("What is the main topic?")
print(response)支持的后处理组件
| 后处理器 | 功能 |
|---|---|
SimilarityPostprocessor | 按相似度分数过滤 |
KeywordNodePostprocessor | 按关键词过滤 |
PrevNextNodePostprocessor | 获取相邻节点 |
CohereRerank | 使用 Cohere 重排序 |
六、性能优化与最佳实践
6.1 检索参数调优
| 参数 | 推荐值 | 说明 |
|---|---|---|
similarity_top_k | 5-10 | 检索返回的节点数量 |
chunk_size | 512-1024 | 文档分块大小 |
response_mode | compact / tree_summarize | 合成策略 |
6.2 索引优化策略
# 1. 设置合适的 chunk_size
Settings.chunk_size = 512
Settings.chunk_overlap = 20
# 2. 使用高性能 embedding
from llama_index.embeddings.openai import OpenAIEmbedding
Settings.embed_model = OpenAIEmbedding(model="text-embedding-3-small")
# 3. 启用节点后处理(重排序提升准确率)
from llama_index.postprocessor.cohere import CohereRerank
rerank = CohereRerank(top_k=3)
query_engine = RetrieverQueryEngine(
retriever=retriever,
node_postprocessors=[rerank],
)6.3 场景选型建议
| 场景 | 推荐方案 |
|---|---|
| 中小型知识库(<10万文档) | LlamaIndex 开箱即用 |
| 高精度检索需求(法律/医疗) | 混合检索 + 重排序 |
| 多文档复杂推理 | 递归检索 + 文档代理 |
| 快速原型验证 | index.as_query_engine() 一键搭建 |
七、总结:Retriever 设计哲学
LlamaIndex 遵循 渐进式复杂度揭示(Progressive Disclosure of Complexity)的设计理念:
简单场景:index.as_retriever() → 一行代码搞定
↓
进阶场景:配置 similarity_top_k、retriever_mode
↓
高级场景:自定义 Retriever 类、实现混合检索
↓
专业场景:递归检索、检索器路由、多 Agent 协作💡 核心要点:从最简单的需求出发,当默认行为无法满足时,逐层深入使用更底层的 API。这种设计让初学者能够快速上手,同时为专家提供足够的灵活性。