
🔥草莓熊Lotso: 个人主页
❄️个人专栏: 《C++知识分享》 《Linux 入门到实践:零基础也能懂》
✨生活是默默的坚持,毅力是永久的享受!
🎬 博主简介:

文章目录
- 前言
- [一. RAG 流程与文档加载的作用](#一. RAG 流程与文档加载的作用)
-
- [1.1 RAG 的完整工作流程](#1.1 RAG 的完整工作流程)
- [1.2 文档加载的核心任务](#1.2 文档加载的核心任务)
- [二. LangChain 文档的统一表示:Document 类](#二. LangChain 文档的统一表示:Document 类)
-
- [2.1 Document 类的核心属性](#2.1 Document 类的核心属性)
- [2.2 手动创建 Document 对象](#2.2 手动创建 Document 对象)
- [2.3 metadata 的重要性](#2.3 metadata 的重要性)
- [三. 实战:加载常见格式的文档](#三. 实战:加载常见格式的文档)
-
- [3.1 加载 PDF 文档](#3.1 加载 PDF 文档)
-
- [3.1.1 安装依赖](#3.1.1 安装依赖)
- [3.1.2 完整代码示例](#3.1.2 完整代码示例)
- [3.1.3 结果解析](#3.1.3 结果解析)
- [3.1.4 PyPDFLoader 的优缺点](#3.1.4 PyPDFLoader 的优缺点)
- [3.2 加载 Markdown 文档](#3.2 加载 Markdown 文档)
-
- [3.2.1 安装依赖](#3.2.1 安装依赖)
- [3.2.2 single 模式:整体加载](#3.2.2 single 模式:整体加载)
- [3.2.3 elements 模式:按元素拆分](#3.2.3 elements 模式:按元素拆分)
- [3.2.4 元素类型详解](#3.2.4 元素类型详解)
- [3.2.5 层级关系:parent_id 与 element_id](#3.2.5 层级关系:parent_id 与 element_id)
- [3.3 其他常见文档加载器简介](#3.3 其他常见文档加载器简介)
- [四. 文档加载的最佳实践与常见问题](#四. 文档加载的最佳实践与常见问题)
-
- [4.1 大文件加载的优化策略](#4.1 大文件加载的优化策略)
- [4.2 元数据的合理使用](#4.2 元数据的合理使用)
- [4.3 不同加载器的选择建议](#4.3 不同加载器的选择建议)
- [4.4 乱码与格式问题的解决方法](#4.4 乱码与格式问题的解决方法)
- 结尾:
前言
大型语言模型(LLM)虽然拥有强大的语义理解和文本生成能力,但存在两个致命的局限性:训练数据有截止日期 ,无法获取实时信息;无法访问私有数据 ,如企业内部文档、个人笔记等。为了解决这些问题,检索增强生成(Retrieval-Augmented Generation, RAG) 技术应运而生,成为当前大模型应用的核心模式。RAG 的核心思想是:当用户提问时,系统首先在私有知识库中进行语义搜索,找到最相关的内容,然后将这些内容和问题一起交给 LLM 生成答案。而构建 RAG 系统的第一步,就是将各种格式的数据源(PDF、Markdown、Word、网页等)加载并转换为 LLM 能够理解的统一格式。LangChain 作为最流行的大模型应用开发框架,提供了100 多种文档加载器,几乎覆盖了所有常见的数据格式和来源。本文将深入讲解 LangChain 文档加载器的核心原理,并通过实战演示如何加载 PDF 和 Markdown 这两种最常用的文档格式。

一. RAG 流程与文档加载的作用
1.1 RAG 的完整工作流程
RAG 系统的运行分为离线数据处理 和在线检索生成两个阶段:
Plain
离线数据处理:文档加载 → 文本分割 → 向量嵌入 → 向量存储
在线检索生成:用户查询 → 向量检索 → 提示词构建 → LLM 生成答案
- 文档加载 :从各种来源读取数据,转换为 LangChain 统一的
Document对象列表 - 文本分割:将长文档切分为适合模型上下文窗口的小块
- 向量嵌入:将文本块转换为高维向量,保留语义信息
- 向量存储:将向量存入向量数据库,支持高效的相似性搜索
- 向量检索:根据用户查询的向量,找到最相似的文本块
- 提示词构建:将查询和检索到的文本块组合成提示词
- LLM 生成:大模型根据提示词生成最终答案





1.2 文档加载的核心任务
文档加载器的核心任务是:将不同格式、不同来源的非结构化数据,转换为 LangChain 标准的 Document 对象列表。
每个 Document 对象代表文档的一个片段(通常是一页或一个段落),包含文本内容和相关元数据。这种统一的表示方式,使得后续的文本分割、向量嵌入等步骤可以标准化处理,无需关心原始数据的格式。
二. LangChain 文档的统一表示:Document 类
在 LangChain 中,所有文档都被抽象为 langchain_core.documents.base.Document 类。无论你加载的是 PDF、Markdown 还是网页,最终都会生成这个类的实例。
2.1 Document 类的核心属性
Document 类有三个核心属性:
page_content:字符串类型,存储文档的文本内容,这是最核心的属性metadata:字典类型,存储与内容关联的任意元数据,如文档来源、页码、作者、创建时间等id:可选的文档标识符,理想情况下在整个文档集合中唯一
2.2 手动创建 Document 对象
你可以直接手动创建 Document 对象,这在测试或处理简单数据时非常有用:
python
from langchain_core.documents import Document
# 创建单个Document对象
doc1 = Document(
page_content="狗是很好的伴侣,以忠诚和友好而闻名。",
metadata={"source": "mammal-pets-doc", "page": 1}
)
doc2 = Document(
page_content="猫是独立的宠物,经常享受自己的空间。",
metadata={"source": "mammal-pets-doc", "page": 2}
)
# 文档列表
documents = [doc1, doc2]
print(f"文档1内容:{doc1.page_content}")
print(f"文档1元数据:{doc1.metadata}")
2.3 metadata 的重要性
metadata 虽然是可选属性,但在实际应用中至关重要:
- 溯源:当 LLM 生成答案时,可以通过元数据告诉用户答案来自哪个文档的哪一页
- 过滤:在向量检索时,可以先根据元数据过滤文档(如只搜索特定来源或特定日期的文档)
- 排序:可以根据元数据对检索结果进行排序(如优先显示最新的文档)

三. 实战:加载常见格式的文档
3.1 加载 PDF 文档
PDF 是最常见的文档格式之一,LangChain 提供了多种 PDF 加载器,其中最常用的是 PyPDFLoader。
3.1.1 安装依赖
首先安装 pypdf 库,这是 PyPDFLoader 的依赖:
bash
pip install pypdf
3.1.2 完整代码示例
python
from langchain_community.document_loaders import PyPDFLoader
# 1. 初始化加载器,传入PDF文件路径
file_path = "./脚手架级微服务租房平台Q&A.pdf"
loader = PyPDFLoader(file_path)
# 2. 加载PDF文档,将每一页转换为一个独立的Document对象
docs = loader.load()
# 3. 查看加载结果
print(f"PDF 文件总页数:{len(docs)}")
print("-" * 50)
print(f"第一页文本内容前200个字符:\n{docs[0].page_content[:200]}")
print("-" * 50)
print(f"第一页元数据:\n{docs[0].metadata}")
3.1.3 结果解析
运行上述代码,你会看到类似以下的输出:
Plain
PDF 文件总页数:32
--------------------------------------------------
第一页文本内容前200个字符:
脚手架级微服务租房平台
通⽤问题
1. 为什么做这个项⽬?
• 回答1:(出于兴趣爱好开发)
大学期间,我和同学在外合租过一段时间,使⽤了一些租房平台,于是我有个想法,⾃己能不能开发
一个租房平台,可以让我将理论知识与实践相结合。我希望通过实际项⽬来加深对Java编程语⾔和相 关技术的理解。于是我便查找了一些资料,看了一些开源项⽬,进⾏了一些改进。
• 回答2:(开源项⽬的解释)
这个
--------------------------------------------------
第一页元数据:
{'producer': 'pdfcpu v0.8.1 dev', 'creator': 'Chromium', 'creationdate': '2025-08-28T17:52:34+08:00', 'moddate': '2025-08-28T17:52:34+08:00', 'source': './脚手架级微服务租房平台Q&A.pdf', 'total_pages': 32, 'page': 0, 'page_label': '1'}
从结果可以看出:
PyPDFLoader会将 PDF 的每一页转换为一个独立的Document对象- 元数据中包含了丰富的信息,如文档来源
source、总页数total_pages、当前页码page等


3.1.4 PyPDFLoader 的优缺点
优点:
- 简单易用,无需复杂配置
- 速度快,适合大多数标准 PDF 文档
- 自动提取页码等元数据
缺点:
- 对于包含复杂布局、图表或扫描件的 PDF 效果不佳
- 可能会丢失一些格式信息
3.2 加载 Markdown 文档
Markdown 是技术文档最常用的格式之一,LangChain 提供了 UnstructuredMarkdownLoader 来加载 Markdown 文档,它支持两种加载模式:single 和 elements。
3.2.1 安装依赖
首先安装所需的依赖库:
bash
pip install "unstructured[md]" nltk
3.2.2 single 模式:整体加载
single 模式是默认模式,会将整个 Markdown 文档作为一个单独的 Document 对象返回:
python
from langchain_community.document_loaders import UnstructuredMarkdownLoader
from langchain_core.documents import Document
# 1. 初始化加载器,使用single模式(默认)
markdown_path = "./脚手架级微服务租房平台Q&A.md"
loader = UnstructuredMarkdownLoader(markdown_path, mode="single")
# 2. 加载文档
data = loader.load()
# 3. 查看结果
print(f"文档数量:{len(data)}")
assert len(data) == 1
assert isinstance(data[0], Document)
print("-" * 50)
print(f"文档内容前200个字符:\n{data[0].page_content[:200]}")
print("-" * 50)
print(f"文档元数据:\n{data[0].metadata}")
运行结果:
Plain
文档数量:1
--------------------------------------------------
文档内容前200个字符:
通用问题
为什么做这个项目?
回答1
(出于兴趣爱好开发)
大学期间,我和同学在外合租过一段时间,使用了一些租房平台,于是我有个想法,自己能不能开发
一个租房平台,可以让我将理论知识与实践相结合。我希望通过实际项目来加深对 Java 编程语言和相
关技术的理解。于是我便查找了一些资料,看了一些开源项目,进行了一些改进。
--------------------------------------------------
文档元数据:
{'source': './脚手架级微服务租房平台Q&A.md'}



3.2.3 elements 模式:按元素拆分
elements 模式会将 Markdown 文档按语义元素拆分 ,生成多个 Document 对象,每个对象代表一个独立的元素(如标题、段落、列表、表格等):
python
from langchain_community.document_loaders import UnstructuredMarkdownLoader
# 1. 初始化加载器,使用elements模式
loader = UnstructuredMarkdownLoader(markdown_path, mode="elements")
# 2. 加载文档
data = loader.load()
# 3. 查看结果
print(f"文档数量:{len(data)}")
print("-" * 50)
print("前3个文档数据:")
for document in data[:3]:
print(f"{document}\n")
运行结果:
Plain
文档数量:441
--------------------------------------------------
前3个文档数据:
page_content='通用问题' metadata={'source': './脚手架级微服务租房平台Q&A.md', 'category_depth': 0, 'languages': ['zho'], 'file_directory': '.', 'filename': '脚手架级微服务租房平台Q&A.md', 'filetype': 'text/markdown', 'last_modified': '2025-08-29T10:56:36', 'category': 'Title', 'element_id': '3a0670f9bfd58576e430ef11def41593'}
page_content='为什么做这个项目?' metadata={'source': './脚手架级微服务租房平台Q&A.md', 'category_depth': 2, 'emphasized_text_contents': ['为什么做这个项目?'], 'emphasized_text_tags': ['b'], 'languages': ['zho'], 'file_directory': '.', 'filename': '脚手架级微服务租房平台Q&A.md', 'filetype': 'text/markdown', 'last_modified': '2025-08-29T10:56:36', 'parent_id': '3a0670f9bfd58576e430ef11def41593', 'category': 'Title', 'element_id': 'fcb08b2a85942455eecebb9467ffca4c'}
page_content='回答1:(出于兴趣爱好开发)' metadata={'source': './脚手架级微服务租房平台Q&A.md', 'emphasized_text_contents': ['回答1:(出于兴趣爱好开发)'], 'emphasized_text_tags': ['b'], 'languages': ['zho'], 'file_directory': '.', 'filename': '脚手架级微服务租房平台Q&A.md', 'filetype': 'text/markdown', 'last_modified': '2025-08-29T10:56:36', 'parent_id': 'fcb08b2a85942455eecebb9467ffca4c', 'category': 'UncategorizedText', 'element_id': 'a6fc0b5a457d21234bf1c4a6ae0a18db'}
3.2.4 元素类型详解
在 elements 模式下,每个 Document 对象的 metadata 中都有一个 category 字段,表示该元素的类型。常见的类型包括:
| 类型 | 描述 |
|---|---|
Title |
标题,包括一级、二级、三级等所有级别的标题 |
NarrativeText |
叙述性文本,一个或多个连续的段落 |
ListItem |
列表项,包括无序列表和有序列表 |
Table |
表格 |
Image |
图片 |
UncategorizedText |
未分类文本,如脚注、图片说明等 |
你可以通过以下代码查看文档中包含的所有元素类型:
python
print(set(document.metadata["category"] for document in data))
# 输出:{'Image', 'Title', 'ListItem', 'Table', 'NarrativeText', 'UncategorizedText'}
3.2.5 层级关系:parent_id 与 element_id
在 elements 模式下,每个元素都有一个唯一的 element_id,同时子元素会有一个 parent_id 指向其父元素的 element_id。通过这两个字段,我们可以还原出 Markdown 文档的完整层级结构。
例如,在上面的输出中:
- "通用问题" 是一级标题,
element_id为3a0670f9bfd58576e430ef11def41593 - "为什么做这个项目?" 是二级标题,
parent_id指向 "通用问题" 的element_id - "回答 1:(出于兴趣爱好开发)" 是二级标题下的内容,
parent_id指向 "为什么做这个项目?" 的element_id
3.3 其他常见文档加载器简介
除了 PDF 和 Markdown,LangChain 还支持加载多种其他格式的文档:
| 文档格式 | 加载器 | 安装依赖 |
|---|---|---|
| 网页 | WebBaseLoader |
pip install beautifulsoup4 |
| Word (.docx) | Docx2txtLoader |
pip install docx2txt |
| CSV | CSVLoader |
无需额外依赖 |
| Excel (.xlsx) | UnstructuredExcelLoader |
pip install "unstructured[xlsx]" |
| JSON | JSONLoader |
无需额外依赖 |
四. 文档加载的最佳实践与常见问题
4.1 大文件加载的优化策略
- 分块加载:对于非常大的文档(如几百页的 PDF),可以使用支持分块加载的加载器,避免一次性加载整个文件到内存
- 异步加载 :使用异步加载器(如
AsyncPyPDFLoader)提高加载速度 - 增量加载:如果文档会更新,可以只加载新增或修改的部分,而不是重新加载整个文档
4.2 元数据的合理使用
- 始终在
metadata中包含source字段,以便后续溯源 - 对于多页文档,包含
page字段,指明内容来自哪一页 - 根据业务需求添加自定义元数据,如
author、create_time、category等
4.3 不同加载器的选择建议
- 对于标准 PDF 文档,优先使用
PyPDFLoader - 对于包含复杂布局或扫描件的 PDF,可以使用
PyMuPDFLoader或PDFPlumberLoader,效果更好 - 对于 Markdown 文档,如果只需要整体内容,使用
single模式;如果需要保留文档结构,使用elements模式 - 对于网页,优先使用
WebBaseLoader,如果需要更强大的爬取能力,可以使用SeleniumLoader或PlaywrightLoader
4.4 乱码与格式问题的解决方法
- 确保文档本身没有损坏
- 尝试使用不同的加载器,不同的加载器对格式的支持程度不同
- 对于中文乱码问题,检查文档的编码格式,通常使用 UTF-8 编码
- 对于格式丢失问题,可以在加载后进行简单的文本清洗,如去除多余的空白字符
本文的核心要点:
- RAG 流程:离线处理(加载 - 分割 - 存储)和在线检索(检索 - 生成)
- Document 类 :LangChain 中文档的统一表示,包含
page_content和metadata两个核心属性 - PDF 加载 :使用
PyPDFLoader,每页生成一个Document对象 - Markdown 加载 :支持
single(整体加载)和elements(按元素拆分)两种模式 - 最佳实践:合理使用元数据,根据文档格式选择合适的加载器,优化大文件加载
结尾:
html
🍓 我是草莓熊 Lotso!若这篇技术干货帮你打通了学习中的卡点:
👀 【关注】跟我一起深耕技术领域,从基础到进阶,见证每一次成长
❤️ 【点赞】让优质内容被更多人看见,让知识传递更有力量
⭐ 【收藏】把核心知识点、实战技巧存好,需要时直接查、随时用
💬 【评论】分享你的经验或疑问(比如曾踩过的技术坑?),一起交流避坑
🗳️ 【投票】用你的选择助力社区内容方向,告诉大家哪个技术点最该重点拆解
技术之路难免有困惑,但同行的人会让前进更有方向~愿我们都能在自己专注的领域里,一步步靠近心中的技术目标!
结语:文档加载是构建 RAG 系统的第一步,也是至关重要的一步。LangChain 提供的文档加载器极大地简化了不同格式数据的处理过程,让开发者可以专注于业务逻辑,而无需关心底层的格式解析。完成文档加载后,下一步就是将长文档切分为适合模型上下文窗口的小块,这就是我们下一篇文章要讲解的文本分割器(Text Splitters)。
✨把这些内容吃透超牛的!放松下吧✨ ʕ˘ᴥ˘ʔ づきらど
