# LangChain RAG 应用开发组件深入:整体介绍

在 LangChain 中构建 RAG 应用时,核心链路不只是“用户提问 -> 大模型回答”。一个可维护的知识库问答系统,还需要把外部数据稳定地加载、解析、转换、分割、向量化并写入向量库,然后在用户提问时通过检索器召回相关文档,再把证据交给 Prompt 与 LLM。

这一部分主要围绕 LangChain 在 RAG 应用开发中常用的组件展开,包括文档加载器、文档分割器、文档转换器、VectorStore 组件以及检索器。

# 01. LangChain 文档组件与文档加载器

Document 组件是 RAG 应用中的基础数据结构。它负责承载文档正文和元数据,是文档加载器、文档分割器、向量数据库、检索器之间传递数据的统一对象。

文档加载器负责从不同数据源中读取内容,并转换成 LangChain 可处理的 Document。常见数据源包括 CSV 文件、HTML 网页、PDF 文件、文件夹、Markdown 文件、Office 文档,以及通用文件。

对于企业内部系统、数据库记录、API 接口、工单、知识库等高度定制化数据源,通用加载器往往只能解决“读出来”的问题,不能保证格式符合业务要求。这时就需要自定义文档加载器,把业务数据统一封装成 Document。

文档加载器这一层的重点有三个:

  • 屏蔽不同数据源的读取差异。
  • 保留 source、page、doc_id、line_number 等定位信息。
  • 输出标准 Document,交给后续切分、转换、向量化和检索链路。

# 02. LangChain 文档转换器与分割器

文档被加载出来以后,通常不能直接写入向量库。原始文档可能过长、格式混乱、包含噪声,或者缺少后续检索需要的元数据。因此,在向量化之前,经常需要先经过文档转换器和文档分割器。

文档转换器用于把一组 Document 转换成另一组 Document。转换动作可以包括拆分、合并、过滤、翻译、内容压缩、HTML 转文本、元数据增强等。

文档分割器则专门解决 chunk 粒度问题。不同的分割方式会直接影响召回质量和最终答案质量:

  • chunk 太大,可能超过上下文窗口,也会把无关内容带入 Prompt。
  • chunk 太小,可能丢失完整语义,模型拿到的证据不足。
  • chunk 没有重叠,跨段信息容易被切断。
  • chunk 没有 metadata,后续难以追踪来源。

LangChain 提供了多种文本分割器,例如字符分割器、递归字符文本分割器、HTML 标题分割器、JSON 分割器、基于 token 的分割器,以及语义分割器。实际使用时,需要根据文档类型、语言、内容结构和检索目标选择合适的分割策略。

# 03. VectorStore 组件与检索器

VectorStore 组件用于存储文本向量,并提供相似性搜索能力。在 RAG 应用中,向量库不是简单的“存一下向量”,它还承担了相似度检索、分数过滤、metadata 过滤、MMR 多样性召回等工作。

常见检索方式包括:

  • 普通相似性检索:根据 query 向量找最相似的文档片段。
  • 带得分的相似性检索:返回文档的同时返回相关性得分。
  • 阈值过滤检索:只保留高于指定分数的结果。
  • MMR 检索:在相关性和多样性之间做平衡,减少重复片段。

检索器是 RAG 应用面向上层链路的统一检索接口。它可以封装 VectorStore,也可以封装第三方搜索服务、数据库查询、关键词检索或自定义检索逻辑。

一个检索器是否好用,关键不只在于能不能返回 Document,还在于能否控制检索参数、记录检索过程、保留分数和来源,并在结果不好时快速定位问题。

# 整体链路

RAG 深入部分可以按两条链路理解。

离线或异步数据处理链路:

外部数据源 -> Loader -> Document -> Transformer -> TextSplitter -> Embedding -> VectorStore
1

在线问答链路:

User Query -> Retriever -> Documents -> Prompt -> LLM -> Answer
1

这两条链路之间的连接点就是向量库与检索器。前者决定知识如何进入系统,后者决定用户提问时能否找到正确证据。

# 组件边界

组件 职责
Document 承载正文和元数据
Loader 读取外部数据并转换成 Document
Blob 表示原始二进制或文件数据
BlobParser 把 Blob 解析成 Document
DocumentTransformer 对 Document 列表做转换
TextSplitter 把长文档切分成 chunk
VectorStore 存储向量并提供相似性搜索
Retriever 对外提供统一检索接口

把这些组件拆清楚后,RAG 应用就不再是一段混在一起的脚本,而是一条可以逐层调试的工程链路。

# 最新版 LangChain 用法提示

LangChain 1.x 之后,RAG 相关组件的整体思想仍然成立,但部分调用方式更推荐使用新的 Runnable 风格和拆分式写法。

组件/写法 现在是否建议 最新版建议
Document(page_content, metadata) 仍然建议 继续使用 langchain_core.documents.Document,metadata 要保留来源、页码、版本和权限字段
Loader.load() 仍然建议 小文件或一次性加载可以继续用
Loader.lazy_load() 仍然建议 大文件、目录、批量导入更推荐懒加载
load_and_split() 不优先建议 更推荐先 load() 或 lazy_load(),再显式调用 splitter
RecursiveCharacterTextSplitter 仍然建议 通用文本切分优先使用它,并设置 chunk_size、chunk_overlap、add_start_index=True
VectorStore.as_retriever() 仍然建议 用 search_type 和 search_kwargs 控制 similarity、mmr、score threshold
retriever.get_relevant_documents() 不建议 使用 retriever.invoke(query),批量场景使用 retriever.batch([...])
langchain.retrievers 旧导入 不建议 如果确实依赖旧检索器,使用 langchain-classic;新代码优先用 langchain_core、集成包和 Runnable 写法

最新版更推荐的整体写法是把加载、切分、入库、检索拆开:

from langchain_text_splitters import RecursiveCharacterTextSplitter

docs = loader.load()

splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000,
    chunk_overlap=200,
    add_start_index=True,
)
chunks = splitter.split_documents(docs)

vector_store.add_documents(chunks)

retriever = vector_store.as_retriever(
    search_type="similarity",
    search_kwargs={"k": 4},
)
documents = retriever.invoke("用户的问题")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

这样写比直接把所有步骤包进一个方法里更清晰,也更符合生产排查需要:每一步都能单独检查输入、输出和参数。

# 拓展

RAG 工程链路可以继续向三个方向扩展。

第一,数据入口可以从文件扩展到业务系统。除了 Markdown、PDF、Word、网页,还可以接入数据库、对象存储、客服工单、CRM、内部接口、日志系统。接入方式不同,但最终都应该转换成统一的 Document。

第二,检索可以从单一路径扩展到多路召回。除了向量相似度检索,还可以加入关键词检索、结构化 SQL 查询、标签过滤、时间过滤、权限过滤、重排序模型。多路召回后再合并、去重、排序,通常比单纯调大 top_k 更稳定。

第三,评估可以从人工查看扩展到固定测试集。每次修改 Loader、Splitter、Embedding、VectorStore 或 Retriever,都应该用固定问题集回归,观察目标证据是否被召回、答案是否引用正确、无答案问题是否拒答。

# 常见问题

# 为什么 RAG Demo 能跑,接真实数据后效果变差?

Demo 数据通常干净、短小、结构统一;真实数据会有格式混乱、重复内容、空白噪声、权限边界、版本过期、表格丢失、图片文字无法解析等问题。真实 RAG 的难点主要在数据处理链路,而不是简单调用 LLM。

# 为什么不能把加载、切分、入库、问答都写在一段代码里?

这样短期能跑,长期无法维护。线上结果变差时,无法判断问题来自数据读取、文本清洗、chunk 切分、向量检索、Prompt 组织还是模型输出。拆成组件后,每一层都能单独调试和替换。

# 为什么检索器要作为独立组件?

检索器把底层检索策略封装起来,上层只需要输入 query 并拿到 Document。底层可以是向量库、ES、数据库、自定义 API,甚至多路召回组合。这样 RAG 链路不依赖某一种具体数据库实现。

# 面试题

# RAG 深入阶段主要解决什么问题?

主要解决数据处理和检索工程化问题,包括文档加载、文档解析、文档转换、文本切分、向量存储、检索封装、metadata 追踪、线上问题回放等。

# RAG 中 Document、VectorStore、Retriever 的关系是什么?

Document 是数据载体,VectorStore 存储 Document 对应的向量并提供相似度搜索,Retriever 封装检索策略并向上层应用返回相关 Document。

# RAG 效果不好时如何排查?

先检查数据是否正确进入向量库,再检查 chunk 是否完整,然后检查 query、filter、top_k、score、召回文档和最终 Prompt。只有确认检索证据正确后,再去调整 Prompt 或模型。

# 生产问题排查

问题 常见原因 处理方式
召回不到正确文档 chunk 太大或太小、metadata filter 过严、Embedding 模型不适合 调整切分参数,放宽过滤条件,重建索引
返回很多重复片段 文档重复入库、chunk overlap 过大、top_k 过高 做文档去重,降低 overlap,使用 MMR
答案无法引用来源 metadata 缺少 source、page、doc_id 入库前补齐 metadata
用户看到无权限内容 metadata 没有权限字段,检索时没有 filter 增加 tenant_id、acl 等字段并强制过滤
更新文档后仍返回旧内容 chunk_id 不稳定,旧向量未删除 使用稳定 doc_id/chunk_id,更新前删除旧版本
Prompt 太长 top_k 过大,chunk 太大,召回内容重复 降低 top_k,压缩 chunk,增加重排序和去重