Retriever integrations - Docs by LangChain
一句话总结:检索器(Retriever)是 RAG(检索增强生成)系统中最核心的数据召回抽象接口,它接受非结构化的字符串查询并返回标准的 Document 对象列表,其范畴远超底层向量数据库,广泛涵盖了外部公网搜索引擎、企业级云知识库以及传统稀疏检索算法。
核心概念与常用 API 解析
在 LangChain 的架构设计中,Retriever 是连接大语言模型(LLM)与外部世界知识的标准化桥梁。
- BaseRetriever(检索器基类)
- 定义与作用:所有检索器的顶级抽象接口。它规定了统一的输入输出规范:输入为
query (字符串),输出为 List[Document]。
- 核心特性:继承自 Runnable 协议。这意味着 Retriever 天然支持
invoke、ainvoke、batch 和 stream 等标准化调用方式,可以无缝嵌入到 LCEL(LangChain 表达式语言)的数据处理链中。
- Retriever 与 VectorStore 的边界
- 差异解析:VectorStore 侧重于数据的存储与向量化计算(包含 add_documents 等写操作);而 Retriever 仅关注数据的读取与召回。
- 转化关系:通过
vector_store.as_retriever() 可以将向量库转化为检索器。但 Retriever 的外延更广,不依赖于本地存储的外部 API(如 Wikipedia 搜索)本身就是一个 Retriever。
|
Vector Store |
Retriever |
| 职责 |
存储 + 检索 |
只检索,不存储 |
| 数据源 |
自己管理的向量索引 |
向量库、搜索引擎、Wikipedia、互联网等任意来源 |
| 接口 |
add_documents / similarity_search / delete… |
只有一个:invoke(query) → List[Document] |
1
2
3
4
5
6
7
8
|
# 向量库转检索器,这是最常见的用法
retriever = vector_store.as_retriever(
search_type="similarity",
search_kwargs={"k": 3}
)
results = retriever.invoke("RAG 系统如何优化检索精度?")
# 返回 List[Document]
|
稠密检索 vs 稀疏检索
在理解具体的 Retriever 之前,必须先理清底层的两种核心检索流派。这是系统架构选型的前提,也是面试的高频考点。
稠密向量检索 (Dense Retrieval)
- 底层原理:由基于 Transformer 架构的 Embedding 模型(如 text-embedding-3、bge-m3)生成。它将文本压缩为一个固定维度(通常为 384 到 3072 维)的浮点数数组。称为“稠密”是因为数组中绝大多数维度都有非零的连续值,每一维代表某种抽象的语义特征。
- 解决的痛点(语义泛化):突破了“字面匹配”的限制。用户搜索“土豆”,系统能召回包含“马铃薯”的文档,因为它们在高维语义空间中的几何距离非常近。
- 致命弱点:对专有名词、特定编号(如订单号 X-990-PRO)极度不敏感,容易过度泛化产生“范围混淆”。
稀疏向量检索 (Sparse Retrieval)
- 底层原理:维度极高(通常与整个语言的词汇表大小一致,如几万到十万维)。称为“稀疏”是因为一句话只包含十几个词,向量中 99% 以上的值都是 0。传统的 TF-IDF、BM25 算法以及现代的神经稀疏模型 SPLADE 都属于此类。底层通常基于倒排索引(Inverted Index)实现。
- 解决的痛点(精准匹配):具备极强的词法精确度(Precision)。能瞬间定位包含极其罕见的专有名词或特定日志报错码的文档。
- 致命弱点:完全不具备语义理解能力。如果查询词使用了同义词或字面存在偏差,召回率将断崖式下跌。
核心对比与场景速记矩阵
| 对比维度 |
稠密检索 (Dense Retrieval) |
稀疏检索 (Sparse Retrieval) |
| 底层技术 |
Embedding 模型 (Transformer) |
BM25 / SPLADE / 倒排索引 |
| 向量特征 |
几百/几千维,绝大多数非零 |
几万/十万维,99% 为 0 |
| 核心优势 |
理解上下文,同义词/近义词泛化极强 |
专有名词、长串数字、产品型号精确匹配 |
| 核心劣势 |
容易“过度泛化”,丢掉精确的关键词 |
词汇不匹配时召回率极差(无语义能力) |
| 适用场景 |
知识库问答、长句搜索、口语化提问 |
电商商品搜索、日志/报错码查询、员工库检索 |
架构设计结论:生产环境中通常采用混合检索(Hybrid Search),即结合稠密检索的泛化能力与稀疏检索的精确能力,通过 RRF(倒数秩融合)等算法进行重排,以达到最佳的召回效果。
周边与扩展 API 梳理
在进行检索器(Retriever)的工程选型时,根据数据归属权与维护成本,业内通常将其划分为两大核心流派:
- 自有语料库检索:数据在自己手里,需“先建索引、后检索”(如各种 VectorStore 转化来的检索器、ElasticsearchRetriever)。数据安全可控,适合企业内部知识库。
- 外部索引联网检索:数据在第三方,直接调 API 拿结果,免去建库烦恼。用于给 Agent 提供实时信息,彻底解决大模型知识截止日期的问题。
基于上述选型理念,根据官方文档提供的集成矩阵,LangChain 生态中的高级检索组件可具体细分为以下四大工业级应用方向:
- 外部公网与学术搜索引擎 (External Index)
- TavilySearchAPIRetriever / PerplexitySearchRetriever:专为 LLM 优化的互联网实时检索引擎,直接返回结构化的答案和参考源,是构建联网 Agent 的首选。
- ArxivRetriever / WikipediaRetriever:针对特定高质量语料库(学术论文、维基百科)的定向检索工具,直接调用免建库。
- 传统词法/稀疏检索 (Sparse Retrieval)
- BM25Retriever / TFIDFRetriever:基于词频-逆文档频率的传统倒排索引检索算法。在 RAG 中,常与稠密向量检索(Dense Retrieval)结合形成混合检索(Hybrid Search),以弥补向量模型在专业术语、产品型号等精确匹配上的短板。
- 重排器与上下文压缩 (Rerankers & Compressors)
- 文档提取:官方“所有检索器”列表中提及了 Cohere reranker、FlashRank reranker 以及 LLMLingua Document Compressor。
- 应用价值:在初次召回大量文档后,使用更精确的小规模交叉编码器模型(Reranker)对召回内容进行重新打分和排序,剔除无关文档,这是目前提升 RAG 准确率的最关键步骤。
- 企业云原生检索引擎 (Cloud Offering)
- AmazonKnowledgeBasesRetriever / AzureAISearchRetriever / VertexAISearchRetriever:直接对接公有云厂商成熟的 RAG 服务。这类检索器通常在云端封闭完成了 OCR 解析、向量化与混合检索的全部工作。
工程化代码落地示例
在实际的工程落地中,Retriever 的核心价值体现在两点:一是屏蔽底层数据源差异(提供统一的接口),二是作为组件无缝嵌入到大模型的生成链路中。以下提供两个具有代表性的工程脚本:
示例 1:Retriever 的选型广度(外部联网 vs 本地稀疏)
此脚本演示了如何使用完全相同的标准化 API(invoke),去调度底层的外部公网接口(Wikipedia)和纯本地的稀疏算法(BM25)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Author : nanzet
# Description : 演示 LangChain Retriever 的统一接口特性,结合维基百科检索与 BM25 稀疏检索。
# requirements : pip install -U langchain langchain-community wikipedia rank_bm25
from langchain_community.retrievers import BM25Retriever, WikipediaRetriever
def demonstrate_external_retriever():
"""
流派二:外部索引联网检索
数据在第三方,直接调 API 拿结果,不需要自己建索引,解决大模型知识截止日期问题。
不需要本地存储,直接通过网络 I/O 返回标准的 Document 列表
"""
print("--- 1. 测试外部接口检索器 (Wikipedia) ---")
# 初始化维基百科检索器,限制返回前 2 条结果
wiki_retriever = WikipediaRetriever(top_k_results=2, lang="zh")
# 遵循 Runnable 接口规范,调用 invoke 方法
query = "量子计算"
docs = wiki_retriever.invoke(query)
print(f"针对 Query: '{query}' 的召回结果:")
for i, doc in enumerate(docs):
# 截取前 100 个字符展示
print(f" [结果 {i + 1}] 标题: {doc.metadata.get('title')}")
print(f" 摘要: {doc.page_content[:100]}...\n")
def demonstrate_sparse_retriever():
"""
流派一:自有语料库检索
数据在自己手里,需要先建索引再检索。适合企业内部知识库,数据安全可控。
这里以纯本地的 BM25 稀疏检索为例。
侧重于关键词精确匹配,常用于补足稠密向量(Dense Vector)检索的短板
"""
print("--- 2. 测试本地稀疏检索器 (BM25) ---")
# 构建本地企业语料库
corpus = [
"公司 2026 年第一季度的财报数据非常亮眼。",
"产品型号 X-990-PRO 的库存目前位于北京三号仓库。",
"员工差旅报销流程请参考内部文档 V2.4 版。",
"自然语言处理技术在 RAG 系统中应用广泛。",
]
# 实例化 BM25 检索器
bm25_retriever = BM25Retriever.from_texts(corpus)
bm25_retriever.k = 1 # 设置召回数量
# 测试精确关键词匹配(针对特定型号)
query = "X-990-PRO 库存在哪"
docs = bm25_retriever.invoke(query)
print(f"针对 Query: '{query}' 的召回结果:")
print(f" 命中内容: {docs[0].page_content}\n")
if __name__ == "__main__":
demonstrate_external_retriever()
demonstrate_sparse_retriever()
|
输出结果:
1
2
3
4
5
6
7
8
9
10
11
|
--- 1. 测试外部接口检索器 (Wikipedia) ---
针对 Query: '量子计算' 的召回结果:
[结果 1] 标题: 量子计算机
摘要: 量子计算机(英語:Quantum computer)是以量子力学为基础搭建、用于進行通用計算的設備。量子计算机使用量子比特来儲存數據,使用量子演算法操作數據。不同于电子计算机的比特只能处于0或1两种状...
[结果 2] 标题: 量子计算优越性
摘要: 量子计算优越性(英文:Quantum Advantage),或稱量子霸權(英語:quantum supremacy),是指用量子计算机解決古典電腦难以解决的問題,問題本身未必需要有實際應用。量子计算优...
--- 2. 测试本地稀疏检索器 (BM25) ---
针对 Query: 'X-990-PRO 库存在哪' 的召回结果:
命中内容: 产品型号 X-990-PRO 的库存目前位于北京三号仓库。
|
示例 2:Retriever 的深度集成(构建完整 RAG 链)
此脚本展示了在 RAG 系统的核心骨架中,如何利用 as_retriever() 将底层的 VectorStore 转化为合规的 Retriever,并通过 LCEL(RunnablePassthrough)将其隐式绑定到 Prompt 与 LLM 的数据流中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Author : nanzet
# Description : 完整可运行的 RAG 链示例,展示 VectorStore 转化为 Retriever 并在 LCEL 中的集成
# requirements : pip install -U langchain langchain-openai langchain-community langchain-huggingface faiss-cpu
import os
from langchain.chat_models import init_chat_model
from langchain_community.vectorstores import FAISS
from langchain_core.documents import Document
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_huggingface import HuggingFaceEmbeddings
def main():
# 注入 API KEY (请替换为真实 Key)
os.environ["DEEPSEEK_API_KEY"] = os.getenv("DEEPSEEK_API_KEY", "sk-your-deepseek-api-key")
# 第一步:准备知识库文档
docs = [
Document(page_content="Nike 2025财年总收入为 468 亿美元,同比下降 10%。"),
Document(page_content="Nike 2025财年毛利率为 41.5%,较上年下降 1.2 个百分点。"),
Document(page_content="Nike 2025财年净利润为 28 亿美元,同比下降 44%。"),
Document(page_content="Nike 主要业务分为北美、欧洲、大中华区和亚太区四个区域。"),
Document(page_content="Nike 大中华区 2025财年营收为 71 亿美元,占总营收约 15%。"),
]
# 第二步:初始化嵌入模型,将文档向量化并写入 FAISS
print("--- 正在加载嵌入模型并构建向量库 ---")
embeddings = HuggingFaceEmbeddings(
model_name="BAAI/bge-m3",
model_kwargs={"device": "mps"}, # Mac M 系列芯片用 mps,其他用 cpu
encode_kwargs={"normalize_embeddings": True},
)
vector_store = FAISS.from_documents(docs, embeddings)
print("向量库构建完毕")
# 第三步:向量库转检索器 (VectorStore 转化为 Retriever 接口)
retriever = vector_store.as_retriever(search_kwargs={"k": 2})
# 第四步:初始化大模型
llm = init_chat_model("deepseek-chat", model_provider="deepseek", temperature=0)
# 第五步:定义 Prompt 模板
prompt = ChatPromptTemplate.from_template(
"基于以下上下文回答问题,如果上下文中没有答案请明确说不知道。\n\n"
"上下文:{context}\n\n"
"问题:{question}"
)
# 第六步:组装 RAG 链 (利用 LCEL 将 Retriever 串入流中)
rag_chain = {"context": retriever, "question": RunnablePassthrough()} | prompt | llm
# 第七步:执行并输出结果
query = "Nike 2025年的总收入是多少?"
print(f"\n用户提问:{query}")
answer = rag_chain.invoke(query)
print(f"系统答案:{answer.content}")
if __name__ == "__main__":
main()
|
输出结果:
1
2
3
4
5
6
7
|
--- 正在加载嵌入模型并构建向量库 ---
Warning: You are sending unauthenticated requests to the HF Hub. Please set a HF_TOKEN to enable higher rate limits and faster downloads.
Loading weights: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 391/391 [00:00<00:00, 62190.86it/s]
向量库构建完毕
用户提问:Nike 2025年的总收入是多少?
系统答案:根据上下文,Nike 2025财年总收入为 468 亿美元。
|
常见踩坑与高频面试点
在高级 AI Agent 的架构设计与面试中,对于 Retriever 的认知深度直接反映了候选人对 RAG 系统性能瓶颈的把控能力。
高频面试点 1:Retriever 与 VectorStore 的本质区别是什么?
- 考察点:对系统架构边界的理解。
- 满分回答:VectorStore 是数据的物理存储层,它强依赖于 Embedding 模型,核心职责是执行高维空间的 ANN(近似最近邻)计算;而 Retriever 是更高层级的业务抽象,它是单向的“读接口”。一个 Retriever 底层可以包装一个 VectorStore(如 as_retriever),也可以包装一个外部搜索引擎(如 Tavily),或者是包装一个传统的 BM25 算法库。在面向接口编程时,业务逻辑层(Agent/Chain)只应该依赖 BaseRetriever,从而实现数据召回策略的无缝解耦与替换。
高频面试点 2:在生产环境中,如何解决稠密向量检索(Dense Retrieval)查不准专业词汇的问题?
- 考察点:对混合检索(Hybrid Search)架构的掌握程度。
- 满分回答:稠密向量检索擅长捕捉上下文泛化语义,但在面对明确的实体词、长串数字(如订单号、特定产品型号)时表现极差。生产环境的最佳实践是构建 混合检索(Hybrid Retrieval)。引入 BM25Retriever 或 Elasticsearch 的倒排索引来负责词法精确匹配。在工程实现上,使用 EnsembleRetriever 将两路召回的 Document 结果合并,并通过 RRF(倒数秩融合算法,Reciprocal Rank Fusion) 重新计算权重,最终结合 Reranker(重排模型)精筛,将高召回率与高准确率结合。
常见踩坑 1:忽视 Retriever 的网络 I/O 阻塞特性
- 现象与痛点:在构建高并发的多智能体服务时,直接使用 invoke 调用 TavilySearchAPIRetriever 或基于远端云的检索器,导致 Python 的主事件循环被长连接阻塞,接口吞吐量雪崩。
- 工程对策:由于 BaseRetriever 统一继承自 Runnable,在处理涉及网络 I/O 的检索组件时,必须强制在业务代码中使用 await retriever.ainvoke() 进行异步非阻塞调用;在需要对用户 Query 并发查询多个外部数据源时,应利用 asyncio.gather 或 retriever.batch() 提升并发效率。