LangChain:RAG 与检索管线——从 Loader 到 Retriever 的拼装思路
很多用 LangChain 的项目,最终都落在各种形式的 RAG(Retrieval-Augmented Generation)上:给定问题,从知识库中检索相关内容,再让模型在上下文里作答。
这一篇尝试从工程视角拆解 RAG 管线在 LangChain 里的几个关键模块:Loader、Splitter、VectorStore、Retriever,以及它们在不同规模项目中的拼装方式与常见坑。
1. RAG 的最小闭环:从「一堆文档」到「一段回答」
先用一个最小可用 RAG 的流程回顾一下需求:
- 拿到一堆原始文档(本地文件、网页、数据库记录等);
- 预处理成向量索引:拆分、向量化、存储;
- 请求时,根据问题检索 top-k 相关片段;
- 把检索到的片段拼进 prompt,交给模型生成答案。
这个流程本身并不复杂,难点在于:
- 不同项目对「加载/切分/存储/检索」的实现差异很大;
- 需求变化时(换向量库、换切分策略、换召回/重排逻辑)容易牵一发而动全身。
LangChain 在这里提供的是一组可插拔的模块接口,把整条管线拆开。
2. Loader:把各种数据源抽象成「文档流」
2.1 Loader 的角色
Loader 负责的,是把「各种各样的数据源」统一变成一系列 Document:
- 本地文件系统:Markdown、PDF、HTML 等;
- 远程存储:S3、数据库、搜索引擎结果;
- 运行时生成的数据:日志、监控结果等。
抽象目标是:
- 不让上层关心「数据是从哪里来的、通过什么协议来的」;
- 只关心「拿到了一批内容 + 元数据(source、时间戳、标签等)」。
2.2 工程实践中的要点
- 对重要业务字段(来源、版本、权限标签等)在 Document 的 metadata 里做好标记;
- Loader 尽量做到「只负责取数据,不负责复杂业务逻辑」,便于复用。
3. Splitter:决定「一个文档切成多大一块」
3.1 为什么需要切分?
大模型 context 长度有限,直接把长文扔进去既浪费 token,又容易让模型迷失重点。
切分的目标是:
- 把文档拆成相对独立、语义上完整的小块;
- 既不过于碎片化(影响语义),又不过于庞大(超出上下文预算)。
3.2 不同切分策略的影响
常见的切分方式包括:
- 按固定长度字符/词切分;
- 按段落/标题结构切分;
- 按语义块(结合 Embedding / 标点 / DOM 结构等)。
在 LangChain 中,Splitter 作为独立模块存在,工程上的好处是:
- 可以针对不同数据源选择不同 Splitter;
- 在不动 Loader 和 VectorStore 的情况下,调整切分策略,做离线重建索引实验。
4. VectorStore:向量存储的抽象层
4.1 向量库差异不应该渗透到业务里
不同向量库(Faiss、Pinecone、Weaviate、自建 ES/PG 向量索引等)在:
- 索引结构;
- 相似度度量;
- 过滤条件与元数据支持;
上都有差异。如果直接在业务代码里调用这些 SDK,很难在后期替换或做 A/B 实验。
LangChain 的 VectorStore 抽象提供的是:
- 一个统一的「插入 Document + 向量化 + 检索」接口;
- 具体的实现由不同 VectorStore 后端适配器完成。
4.2 工程实践中的常见模式
- 开发期可以用简单本地向量库(如 in-memory/Faiss);
- 上生产后切换成托管服务或高可用部署;
- 对业务代码来说,变化仅在构造 VectorStore 的那一层。
5. Retriever:把「怎么取文档」独立出来
5.1 Retriever 与 VectorStore 的关系
VectorStore 更偏「存储与基础相似度检索」;
Retriever 的抽象则更贴近业务需求:
- 给定查询,如何组合多种检索手段(向量检索 + 关键字检索 + 规则过滤等);
- 如何在检索前后做 query 重写、重排、去重;
- 如何根据业务场景调整 top-k、置信度阈值等。
在 LangChain 中,Retriever 是一个高层接口,可以:
- 直接基于一个 VectorStore 构造;
- 或者是一个「复合 Retriever」,内部再调多个子 Retriever。
5.2 抽象的意义
把 Retriever 抽成一层,带来的直接好处是:
- 上层 RAG 链只关心「给我一个 Retriever 就行」,不关心底层怎么查;
- 方便在不同产品/页面中组合或替换检索策略。
6. 把 Loader / Splitter / VectorStore / Retriever 串起来
一条典型的 LangChain RAG 管线,大致可以想成两个阶段:
- 离线 / 预处理阶段
- 用 Loader 读取原始文档;
- 用 Splitter 切分成块;
- 通过 VectorStore 完成向量化并存储。
- 在线 / 查询阶段
- 收到用户问题;
- 用 Retriever 基于问题查询相关文档块;
- 用 PromptTemplate 组成带上下文的 prompt;
- 调用模型生成回答,必要时用 OutputParser 解析结构化结果。
LangChain 的价值在于:
- 每一块都是一个独立模块,可以单独替换、调参、复用;
- 整条链路可以用 Runnable/LCEL 组合成「可读、可插桩」的表达式。
7. 常见坑与工程建议
实践中,RAG 管线常会遇到几类问题:
- 切分策略不合适
- 块太小:语义被切碎,模型拿到的上下文缺乏必要背景;
- 块太大:上下文拥挤,token 成本高,还容易干扰模型聚焦关键信息。
- 检索信号不足或噪声太多
- 仅靠向量相似度,有时会召回主题相关但细节不准的片段;
- 没有基于 metadata 的过滤(时间、权限、标签等),会混入不相关信息。
- 上下文拼装方式粗糙
- 简单把多个块按顺序拼在一起,缺少结构化提示(标题、小节、来源说明);
- 没有告诉模型哪些信息更可信、哪些只是参考。
一些工程上的简单建议:
- 在构建索引前,先用小规模样本对不同 Splitter 策略做评估;
- 在 Retriever 层引入过滤与重排逻辑,而不是只用默认向量检索;
- 在 PromptTemplate 里清晰标注每个片段的来源、时间、置信度,帮助模型做更合理的判断。
8. 小结:RAG 在 LangChain 里的定位
从这一篇可以抽出几个关键信息:
- LangChain 并没有发明新的 RAG 算法,而是把 RAG 管线里「加载、切分、存储、检索、拼装」这些步骤模块化;
- Loader / Splitter / VectorStore / Retriever 这几层,让工程在调优与迁移时,不必反复重写业务逻辑;
- 真正决定效果的,仍然是:数据质量、检索策略、上下文构造与 prompt 设计。
后续如果继续展开 LangChain 与 RAG,可以进一步细化到:
- 针对代码仓库、长文档、日志等不同数据类型的切分与检索策略;
- 如何在 Retriever 层融合多种信号(向量 + 关键字 + 规则);
- 如何结合评估与监控体系,对 RAG 管线进行持续迭代。