LangChain 系列之Retriever:RAG 的核心不是生成,而是检索
12 / 14 章
01 前言 Retriever 是 RAG 的入口。它接收一个 query,返回一组 List[Document]。模型不是先出场,资料先出场。 前面几章我们讲了 Loader、Splitter、Embedding、VectorStore。
01 前言
Retriever 是 RAG 的入口。它接收一个 query,返回一组 List[Document]。模型不是先出场,资料先出场。
前面几章我们讲了 Loader、Splitter、Embedding、VectorStore。它们解决的是“资料怎么进库”。这一章讲 Retriever,解决的是“用户问的时候,资料怎么被找出来”。
很多人做 RAG,第一反应是换模型、调 Prompt、加长上下文。方向没错,但不是第一优先级。真正影响答案质量的,往往是 Retriever:它有没有把正确资料找出来。
检索错了,模型会一本正经地胡说。检索少了,答案缺关键信息。检索脏了,模型会被无关上下文带偏。
02 Retriever 到底是什么?
通俗说,Retriever 就是“检索器”。用户给它一个问题,它负责从知识库、向量库、关键词索引、数据库、外部 API 里找相关资料。
它不负责生成答案。它只负责找资料。
它的输入很简单:一个字符串 query。它的输出也很清晰:List[Document]。每个 Document 里有 page_content、metadata、id。
别混淆:VectorStore 是底层仓库,Retriever 是上层查询入口。VectorStore 可以作为 Retriever 的后端,但 Retriever 不一定只能查 VectorStore。
03 官方文档怎么定义它?
LangChain 的语义搜索教程把 Documents、Text Splitters、Embeddings、Vector Stores 和 Retrievers 放在同一条知识库链路里。官方说明这些抽象用于从向量数据库和其他来源取回数据,再放到 LLM 工作流里推理。
RAG 教程也把流程拆成两段:Indexing 负责离线入库,Retrieval and generation 负责运行时检索和生成。也就是说,Retriever 是在线链路的核心开关。
源码里,BaseRetriever 的定位更直接:它是一个文档检索系统的抽象基类,拿字符串 query,返回相关 Document 列表。
04 源码入口:BaseRetriever.invoke()
LangChain 新版组件基本都走 Runnable 接口。Retriever 也一样。业务代码不应该再到处调用老式的 get_relevant_documents,而是调用 retriever.invoke(query)。
invoke 是门面。它做三件事:整理 config,启动 callback trace,调用真正的检索方法。真正的检索逻辑在 _get_relevant_documents。
# 源码链路压缩版,不是完整源码
retriever.invoke(query)
-> ensure_config(config)
-> callback_manager.on_retriever_start(...)
-> self._get_relevant_documents(query, run_manager=run_manager)
-> run_manager.on_retriever_end(result)
-> return List[Document]
源码重点:_get_relevant_documents 是子类必须实现的方法。你写自己的检索器,本质就是重写这个方法。
05 自定义 Retriever 的本质:重写一个方法
BaseRetriever 是抽象类。它规定统一入口和统一返回值,但不规定你怎么找资料。
你可以用向量检索,也可以用 BM25,也可以查 MySQL,也可以请求一个内部搜索服务。只要最后返回 List[Document],就能接入 LangChain 的 RAG、Agent、Tool、LangSmith。
from langchain_core.retrievers import BaseRetriever
from langchain_core.documents import Document
class MyRetriever(BaseRetriever):
def _get_relevant_documents(self, query: str, *, run_manager):
# 这里可以查 ES、Milvus、MySQL、HTTP API
return [Document(page_content="相关内容", metadata={"source": "demo"})]
这段代码不要只看成 Demo。它背后的设计很关键:Retriever 只暴露检索能力,不暴露底层细节。业务层只认 invoke(query),不关心你底层是 ES、Milvus 还是混合检索。
这就是工程价值。换检索方案,不改上层业务。
06 VectorStoreRetriever:最常用的 Retriever
大多数 RAG 项目,Retriever 都是从 VectorStore 来的。代码一般是这样:vectorstore.as_retriever()。
源码里,as_retriever() 会返回一个 VectorStoreRetriever。它不是新建一份数据,而是把原来的 VectorStore 包起来,变成统一的 Retriever 接口。
VectorStoreRetriever 里面有三个关键属性:vectorstore、search_type、search_kwargs。
retriever = vectorstore.as_retriever(
search_type="mmr",
search_kwargs={"k": 5, "fetch_k": 30, "lambda_mult": 0.4}
)
docs = retriever.invoke("LangChain Retriever 是什么?")
07 search_type:三种模式决定检索风格
VectorStoreRetriever 的 search_type 不是装饰参数。它直接决定底层怎么找资料。
similarity 是默认模式。它只看相似度,简单直接。
mmr 会兼顾相似度和多样性,适合长文档、重复 Chunk 多的知识库。
similarity_score_threshold 会设置最低相关度门槛,相关度不够就不返回。它适合对准确性要求高的场景。
# 源码逻辑压缩版
if search_type == "similarity":
return vectorstore.similarity_search(query, **kwargs)
if search_type == "similarity_score_threshold":
docs_and_scores = vectorstore.similarity_search_with_relevance_scores(query, **kwargs)
return [doc for doc, score in docs_and_scores]
if search_type == "mmr":
return vectorstore.max_marginal_relevance_search(query, **kwargs)
建议:先用 similarity 跑通链路;如果结果重复,换 MMR;如果无关资料太多,加 score_threshold。
08 search_kwargs:真正调效果的是这些参数
很多人调 RAG,只调 Prompt。其实 Retriever 参数更重要。
k 控制最终返回几条文档。不是越多越好。返回太多,Prompt 会被噪声塞满。
fetch_k 是 MMR 的候选池大小。先多拿,再挑出既相关又分散的结果。
lambda_mult 控制相似度和多样性的平衡。越接近 1,越偏相似;越接近 0,越偏多样。
score_threshold 控制最低相关度。低于门槛就不要,宁缺毋滥。
filter 用元数据过滤结果。比如只查某个业务线、某个产品、某个时间范围、某个文档来源。
09 Retriever 变成工具:Agentic RAG 的关键
普通 RAG 是固定流程:用户一问,就先检索。
Agentic RAG 更灵活:Agent 先判断需不需要查资料,需要时才调用检索工具。
LangChain 提供 create_retriever_tool,可以把 Retriever 包成 StructuredTool。这样 Agent 就可以像调用天气、订单、行情工具一样调用知识库。
from langchain_core.tools import create_retriever_tool
tool = create_retriever_tool(
retriever,
name="search_docs",
description="检索知识库,返回和问题相关的资料",
response_format="content_and_artifact",
)
源码里,这个工具内部会调用 retriever.invoke(query),然后把每个 Document 格式化成文本。
如果 response_format 是 content_and_artifact,它还会把原始 docs 作为 artifact 返回。这样模型看到的是文本,系统还能保留 Document 元数据,后续做引用、溯源、评分。
10 生产级 Retriever:不要只看能不能跑
能返回文档,不代表 Retriever 合格。生产系统要看四个指标:命中率、相关度、覆盖率、延迟。
命中率:标准答案所在文档有没有被找出来。
相关度:TopK 里有多少是真正有用的。
覆盖率:多跳问题需要的多个信息点有没有都找齐。
延迟:一次检索耗时能不能撑住线上并发。
工程经验:Retriever 必须做日志。每次请求至少记录 query、改写 query、返回文档 id、score、metadata、耗时、最终是否被模型引用。
# 建议记录的检索日志
{
"query": "用户原始问题",
"rewritten_query": "问题改写后文本",
"retriever": "VectorStoreRetriever",
"search_type": "mmr",
"search_kwargs": {"k": 5, "fetch_k": 30},
"docs": [{"id": "doc-001", "score": 0.86, "source": "manual.pdf"}],
"latency_ms": 42
}
11 常见坑:为什么你的 RAG 答案不准?
坑一:只用向量检索:向量检索适合语义相似,但对编号、专有名词、精确字段不一定稳。企业知识库建议做混合检索。
坑二:Chunk 切得太碎:太碎会丢上下文,Retriever 找到的只是半句话。需要 Parent Document 或上下文窗口补全。
坑三:k 设置太大:返回太多文档,看似信息丰富,实际容易把模型带偏。
坑四:没有元数据过滤:不同业务线、不同版本、不同时间的资料混在一起,答案就会乱。
坑五:没有评测集:凭感觉调参数,最后只能得到“看起来还行”的系统。
12 总结
Retriever 是 RAG 的胜负手。
Loader、Splitter、Embedding、VectorStore 负责把资料放进库。Retriever 负责在用户提问时,把正确资料找出来。
源码上看,BaseRetriever 统一了 invoke 入口,真正检索逻辑由 _get_relevant_documents 实现。VectorStoreRetriever 是最常用实现,它把 VectorStore 包成统一检索入口,并通过 search_type 选择 similarity、MMR 或 threshold 检索。
做企业级 RAG,不要只盯着模型。先把 Retriever 做准,模型才有发挥空间。
下一章预告:下一章进入 Rerank 与上下文压缩:为什么召回 TopK 后,还要再精排一次?
相关推荐
LangChain 系列:从 0 搭一个企业知识库问答系统
这一章不再单讲一个组件,而是把前面所有 RAG 组件合起来:从文件上传,到向量入库,再到用户提问、检索证据、模型回答、日志追踪。 01 先搞清楚:企业知识库问答不是 Demo Demo 只要能回答。企业系统要能上传、能解析、能检索、能引用、
Tensor:PyTorch 世界里的一切都是张量
1. Tensor 是 PyTorch 的基本单位 PyTorch 里,模型吃进去的是 Tensor,参数保存的是 Tensor,梯度也是 Tensor,GPU 上跑的还是 Tensor。 你可以把 Tensor 理解成“加强版数组”。但这
Rerank 与上下文压缩:为什么召回 TopK 后还要重排?
RAG 最容易踩的坑,不是“没有召回”。 而是召回了一堆看起来相关、实际会干扰模型的资料。 所以,成熟的 RAG 链路不会把 TopK 直接塞进 Prompt。 它会先召回,再重排,再压缩。 1. 为什么召回 TopK 后还要重排? 向量召
环境安装与开发姿势:CPU、CUDA、ROCm、MPS 怎么选
学 PyTorch,第一个门槛不是模型。是环境。环境装错,代码还没开始跑,报错已经堆满屏。 这一章只讲一件事:把 PyTorch 环境一次性讲清楚。不是背安装命令,而是看懂安装背后的逻辑。 因为命令会随着版本变化。逻辑不会。你只要能判断 C
PyTorch 到底是什么?为什么它能成为深度学习主流框架
很多人学 PyTorch,一上来就背 API。结果代码能跑,报错不会查,性能不会调,模型为什么能训练也说不清。 这一章先解决一个问题:PyTorch 不是“一个深度学习库”,它是一整套把数据、模型、梯度、GPU 和工程训练串起来的系统。 一
Vector Store:FAISS、Chroma、Milvus、Qdrant、ES 怎么选?
1、Vector Store 不是普通数据库 普通数据库更擅长精确匹配。你查订单号,它能准。你查用户 ID,它也能准。 但 RAG 场景不是这么问的。用户不会说“我要第 32 页第 5 段”。用户只会问:“合同里违约责任怎么写?”、“三安光
阅读补充
一句话看懂
01 前言 Retriever 是 RAG 的入口。它接收一个 query,返回一组 List[Document]。模型
事件背景
这篇内容围绕“LangChain”展开,热闻岛基于公开信息整理事件背景、主要进展与可继续关注的方向。
事件时间线
发布
相关信息进入公开传播
更新
热闻岛对内容进行整理与补充。
看点
- · LangChain的最新进展是什么
- · 相关信息对用户或行业会带来哪些影响
- · 后续是否会有新的回应或处理结果
后续关注
- · 后续官方回应或权威通报
- · 相关主体的进一步说明
- · 事件对普通用户和平台传播的持续影响
免责声明:本文仅代表作者观点,不构成投资建议、法律建议、医疗建议。财经类内容尤其需要注意风险;爆料类信息请以权威通报为准。
评论 (0)
登录后即可发表评论
去登录