【一起来学AI大模型】RAG系统组件:检索器(LangChain)
RAG 系统中的另一个核心组件:检索器(Retriever),特别是在 LangChain 框架中的实现和应用。检索器是连接用户查询与知识库(通常存储在向量数据库中)的智能“桥梁”,负责根据查询找到最相关的上下文信息。
检索器(Retriever)在 RAG 系统中的作用
在 RAG 流程中:
用户提问 (Query): 用户输入一个问题或指令。
检索 (Retrieval): 检索器 接收用户查询,利用知识库(通常是向量数据库)查找与查询语义最相关的文档片段(Chunks)。
增强 (Augmentation): 将检索到的相关文档片段(Context)与原始用户查询组合,形成一个新的、信息更丰富的提示(Prompt)。
生成 (Generation): 将组合后的提示输入给大语言模型 (LLM),由 LLM 基于提供的上下文生成最终答案。
检索器的核心职责:
理解查询: 接收并可能预处理用户查询。
知识库交互: 根据查询,与底层的知识存储(如向量数据库、全文搜索引擎、图数据库等)进行交互。
执行检索: 调用相应的算法(主要是语义相似度搜索)从知识库中查找最相关的信息片段。
返回结果: 将检索到的、按相关性排序的文档片段列表返回给 RAG 系统的下一阶段(通常是提示构造器或直接给 LLM)。
关键目标: 高效、准确地找到对回答用户问题最有帮助的信息片段。检索质量直接决定了 LLM 最终生成答案的准确性和相关性。
LangChain 中的检索器 (Retriever)
LangChain 是一个强大的框架,用于构建基于大语言模型的应用程序。它将 RAG 流程中的各个环节(文档加载、文本分割、嵌入、向量存储、检索、提示构造、LLM 调用、输出解析)模块化和标准化。其中,Retriever
就是一个定义清晰接口的核心抽象概念。
LangChain 检索器的核心概念
抽象接口 (
BaseRetriever
):LangChain 定义了一个基础接口
BaseRetriever
。任何具体的检索器实现都需要遵循这个接口。核心方法:
get_relevant_documents(query: str) -> List[Document]
输入:一个字符串形式的用户查询 (
query
)。输出:一个
Document
对象的列表。Document
是 LangChain 中表示文本片段及其元数据的基本单位(通常包含page_content
和metadata
属性)。这个方法负责执行实际的检索逻辑。
可选方法:
aget_relevant_documents(query: str)
用于异步调用。
模块化与组合性:
LangChain 的强大之处在于其模块化设计。检索器可以独立于具体的向量数据库或检索算法实现。
你可以轻松地:
更换底层存储: 使用同一个检索器接口,后端可以是 ChromaDB、Pinecone、Elasticsearch、FAISS 等不同的向量数据库或搜索引擎。
组合不同检索技术: 使用
EnsembleRetriever
或MultiQueryRetriever
等组合多个不同底层检索器(如同时使用向量检索和关键词检索)的结果。增加后处理步骤: 在基础检索结果返回后,应用
ContextualCompressionRetriever
进行压缩、过滤或重排序。
与向量数据库的集成:
LangChain 为几乎所有流行的向量数据库(包括我们之前讨论的 ChromaDB)提供了开箱即用的集成。
这些集成通常表现为:
VectorStore
类 和VectorStoreRetriever
类。VectorStore
类: 封装了对特定向量数据库(如Chroma
,Pinecone
,FAISS
)的操作,包括添加文档、执行查询等。它更偏向于直接操作数据库。VectorStoreRetriever
类: 实现了BaseRetriever
接口。它内部持有一个VectorStore
实例,并在其get_relevant_documents
方法中调用该VectorStore
的相似性搜索方法。这才是你在构建 RAG 链时最常直接使用的检索器对象。 它提供了符合 LangChain 流程的标准检索接口。
LangChain 中常见的检索器类型(尤其与向量数据库结合)
基础向量存储检索器 (
VectorStoreRetriever
):这是最常用、最基础的类型。
它直接利用底层向量数据库(如 ChromaDB)的相似性搜索功能。
创建方式通常是通过向量存储对象(
VectorStore
)的.as_retriever()
方法。可配置参数 (在
.as_retriever()
方法中设置):search_type
: 默认是"similarity"
(基于向量余弦或 L2 距离的相似度搜索)。其他选项包括"mmr"
(最大边际相关性,兼顾相关性和多样性) 或特定数据库支持的其他算法(如"similarity_score_threshold"
)。search_kwargs
: 一个字典,用于传递搜索参数,最重要的包括:k
: 要返回的文档片段数量(默认为 4)。filter
/where
: 用于元数据过滤的条件(例如{"source": "wiki"}
)。score_threshold
: 当search_type="similarity_score_threshold"
时,设置相似度分数阈值。fetch_k
: 当search_type="mmr"
时,指定初始获取的候选文档数量(大于最终返回的k
)。lambda_mult
: 当search_type="mmr"
时,控制相关性与多样性的权重(0 偏向多样性,1 偏向相关性)。
示例 (结合 ChromaDB):
from langchain_community.vectorstores import Chroma from langchain_community.embeddings import OpenAIEmbeddings# 1. 创建或加载 Chroma VectorStore (假设已有持久化的 `persist_directory`) embedding_function = OpenAIEmbeddings() vectorstore = Chroma(persist_directory="./chroma_db",embedding_function=embedding_function )# 2. 创建 Retriever retriever = vectorstore.as_retriever(search_type="similarity", # 默认,可省略search_kwargs={"k": 5} # 返回最相关的 5 个文档片段 )# 3. 在 RAG 链中使用这个 retriever # ... (后续会结合 Chain 展示)
多路检索器 / 融合检索器 (
EnsembleRetriever
):用于组合多个不同检索器的结果,旨在利用不同检索技术的优势(例如:向量检索的语义理解能力 + 关键词检索的精确匹配能力)。
需要指定多个检索器以及它们的权重。
LangChain 提供
EnsembleRetriever
类来实现。示例:
from langchain.retrievers import EnsembleRetriever from langchain_community.retrievers import BM25Retriever from langchain_community.vectorstores import Chroma# 假设已有 vectorstore (Chroma) 和基于它的 retriever vector_retriever = vectorstore.as_retriever(search_kwargs={"k": 5})# 假设有一个基于文本列表的 BM25 关键词检索器 (需要先构建索引) # texts 是你的文档片段列表 bm25_retriever = BM25Retriever.from_texts(texts) bm25_retriever.k = 5 # 设置 BM25 也返回 5 个结果# 创建融合检索器,赋予向量检索更高权重 (0.7 vs 0.3) ensemble_retriever = EnsembleRetriever(retrievers=[vector_retriever, bm25_retriever],weights=[0.7, 0.3] ) # 现在可以使用 ensemble_retriever 进行检索,它会合并并重排两个子检索器的结果
自查询检索器 (
SelfQueryRetriever
):一个更智能的检索器。
它能自动解析用户查询中的过滤条件。
工作原理:
用户输入一个自然语言查询(如 “What did the CEO say about AI in the 2023 annual report?”)。
自查询检索器内部使用一个较小的 LLM (如
llm = ChatOpenAI(temperature=0, model="gpt-3.5-turbo")
)。这个小 LLM 被提示将用户查询分解成两部分:
query
(纯语义查询部分): “What did the CEO say about AI”filter
(结构化元数据过滤条件):"year == 2023 AND doctype == 'annual_report'"
检索器使用分解出的
query
部分进行向量相似度搜索。同时,使用分解出的
filter
条件对搜索结果进行元数据过滤。
这大大增强了检索的精确性,用户无需学习复杂的查询语法就能通过自然语言表达复杂的过滤需求。
关键依赖: 需要定义知识库文档的元数据结构 (
document_content_description
,metadata_field_info
) 供小 LLM 理解。示例 (伪代码,展示概念):
from langchain.retrievers.self_query.base import SelfQueryRetriever from langchain.chains.query_constructor.base import AttributeInfo# 1. 定义元数据字段信息 (告诉小LLM知识库里有什么元数据) metadata_field_info = [AttributeInfo(name="source",description="The source of the document, e.g. 'annual_report', 'news_article', 'internal_memo'",type="string",),AttributeInfo(name="year",description="The year the document was published",type="integer",),AttributeInfo(name="author",description="The author of the document",type="string",), ] document_content_description = "Content of business documents"# 2. 创建自查询检索器 retriever = SelfQueryRetriever.from_llm(llm=small_llm, # 用于解析查询的小LLMvectorstore=vectorstore, # 底层的向量存储 (如 Chroma)document_contents=document_content_description,metadata_field_info=metadata_field_info,verbose=True # 可选,打印解析过程 )# 3. 使用 - 用户查询包含自然语言过滤条件 relevant_docs = retriever.get_relevant_documents("What did the CEO say about AI in the 2023 annual report?" ) # 内部解析为: query="What did the CEO say about AI", filter=("year" == 2023 AND "source" == "annual_report")
上下文压缩检索器 (
ContextualCompressionRetriever
):用于优化基础检索器返回的结果。
常见场景:基础检索器可能返回很多片段(比如 k=10),但其中可能包含冗余或不太相关的信息。直接全部喂给 LLM 可能超出 token 限制或分散 LLM 注意力。
工作原理:
基础检索器(如
VectorStoreRetriever
)先返回一组候选文档片段(数量k
可以设大一点,比如 20)。一个 “文档压缩器” 对这些候选文档进行处理。常见的压缩器包括:
LLMChainExtractor
: 使用一个 LLM 来提取与原始查询最相关的句子或段落。EmbeddingsFilter
: 使用嵌入模型计算查询与每个候选文档的二次相似度,并过滤掉低于阈值的文档。DocumentCompressorPipeline
: 组合多个压缩步骤。
压缩器返回一个更精炼、更相关的文档片段列表(数量通常小于基础检索器的
k
)。
好处: 减少输入 LLM 的 token 数量,降低成本;提高输入上下文的质量;避免信息过载。
示例:
from langchain.retrievers import ContextualCompressionRetriever from langchain.retrievers.document_compressors import LLMChainExtractor from langchain_openai import ChatOpenAI# 0. 假设已有基础 retriever (比如 vectorstore.as_retriever(search_kwargs={"k": 20}))# 1. 创建一个文档压缩器 (这里用 LLM 提取关键信息) compressor = LLMChainExtractor.from_llm(ChatOpenAI(temperature=0, model="gpt-3.5-turbo"))# 2. 创建上下文压缩检索器 compression_retriever = ContextualCompressionRetriever(base_compressor=compressor,base_retriever=base_retriever # 第0步的基础检索器 )# 3. 使用 compression_retriever 进行检索 # 它会先通过 base_retriever 获取约 20 个候选文档,然后让 compressor 从中提取最相关的部分,最终返回一个更精炼的列表(数量由压缩器决定) compressed_docs = compression_retriever.get_relevant_documents(user_query)
在 LangChain RAG 链中使用检索器
检索器最终会集成到 LangChain 的 RetrievalQA
链或其变种中。这是最常见的构建 RAG 的方式:
from langchain.chains import RetrievalQA
from langchain_openai import ChatOpenAI# 1. 创建 LLM (负责最终生成答案)
llm = ChatOpenAI(model_name="gpt-4", temperature=0)# 2. 创建检索器 (Retriever) - 假设是之前创建的任意一种 (vector_retriever, ensemble_retriever, self_query_retriever, compression_retriever)
# retriever = ...# 3. 创建 RetrievalQA 链
qa_chain = RetrievalQA.from_chain_type(llm=llm,chain_type="stuff", # 处理检索到文档的方式:'stuff'(全塞进去), 'map_reduce', 'refine', 'map_rerank'retriever=retriever, # 这是我们精心构建的检索器!return_source_documents=True, # 可选,返回用于生成答案的源文档verbose=True # 可选,打印链的执行细节
)# 4. 运行 RAG 链
query = "What is the main advantage of using ChromaDB in RAG systems?"
result = qa_chain.invoke({"query": query})# 5. 查看结果
print(result["result"]) # LLM 生成的最终答案
if return_source_documents:for doc in result["source_documents"]:print(f"Source: {doc.metadata['source']}, Page: {doc.metadata.get('page', 'N/A')}")print(doc.page_content[:200] + "...") # 打印片段开头
选择与优化检索器的关键考虑因素
知识库特性:
大小: 小型库可能简单向量检索就够;大型库可能需要高效索引或融合检索。
内容类型: 纯文本?结构化/半结构化数据?(自查询检索器对后者很有效)。
元数据质量与丰富度: 是否有高质量、结构化的元数据支持过滤?(自查询、基础过滤依赖于此)。
查询复杂性:
简单事实性问题:基础向量检索器可能足够。
需要结合多个条件(时间、来源、类型等):自查询检索器是理想选择。
需要综合不同来源信息:考虑融合检索器。
性能要求:
延迟: 基础向量检索通常很快;融合检索、自查询(涉及额外LLM调用)、压缩检索会增加延迟。
召回率 vs 精确率: 调大
k
提高召回率但可能降低精确率(混入不相关结果);压缩检索、MMR 搜索、重排序可帮助在较高召回下提升精确率。
成本:
自查询检索器、上下文压缩检索器会调用额外的 LLM,增加成本。需要权衡效果提升与成本增加。
LLM 上下文窗口限制:
如果基础检索器返回的内容很容易超出 LLM 的上下文窗口,则必须使用压缩检索器或
chain_type
如"map_reduce"
/"refine"
来处理长文档。
总结
在 LangChain 构建的 RAG 系统中,检索器 (Retriever
) 是实现高效、精准信息获取的核心引擎。它标准化了与知识库(尤其是向量数据库如 ChromaDB)的交互接口。
基础
VectorStoreRetriever
: 最常用,直接对接向量数据库的语义搜索。SelfQueryRetriever
: 提供智能化,能自动从自然语言查询中解析元数据过滤条件,大幅提升复杂查询的精确性。EnsembleRetriever
: 融合多种检索技术(如向量+关键词),取长补短,提升召回率和鲁棒性。ContextualCompressionRetriever
: 优化基础检索结果,提取精华、过滤冗余,降低成本并提升输入 LLM 的上下文质量。
选择合适的检索器并对其进行配置(如 k
值、搜索算法、元数据过滤)是优化 RAG 系统检索阶段性能的关键步骤。 LangChain 提供的模块化和丰富的检索器实现,使得开发者能够灵活地根据应用需求构建最合适的检索方案,为 LLM 生成高质量答案奠定坚实的基础。理解这些检索器的原理和差异,是设计和调优高效 RAG 应用的核心能力。