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)

递归检索解决了两个核心痛点:

  1. 解耦检索与合成:有时通过摘要获取文档比直接搜索原始块更有效
  2. 文档内动态任务:在单个文档内可能需要超越事实问答的复杂操作
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_k5-10检索返回的节点数量
chunk_size512-1024文档分块大小
response_modecompact / 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。这种设计让初学者能够快速上手,同时为专家提供足够的灵活性。