一、文档加载器
在RAG系统中,数据加载是整个流水线的第一步,也是至关重要的一步。
通过文档加载器,将 PDF、Word、Markdown、HTML 等非结构化文档,转换为程序可处理的结构化数据。
数据加载的质量会直接影响后续的索引构建、检索效果和最终的生成质量。
1.1 主要功能
1.2 当前主流RAG文档加载器
工具名称 | 特点 | 适用场景 | 性能表现 |
---|---|---|---|
PyMuPDF4LLM | PDF→Markdown转换,OCR+表格识别 | 科研文献、技术手册 | 开源免费,GPU加速 |
TextLoader | 基础文本文件加载 | 纯文本处理 | 轻量高效 |
DirectoryLoader | 批量目录文件处理 | 混合格式文档库 | 支持多格式扩展 |
Unstructured | 多格式文档解析 | PDF、Word、HTML等 | 统一接口,智能解析 |
FireCrawlLoader | 网页内容抓取 | 在线文档、新闻 | 实时内容获取 |
LlamaParse | 深度PDF结构解析 | 法律合同、学术论文 | 解析精度高,商业API |
Docling | 模块化企业级解析 | 企业合同、报告 | IBM生态兼容 |
Marker | PDF→Markdown,GPU加速 | 科研文献、书籍 | 专注PDF转换 |
MinerU | 多模态集成解析 | 学术文献、财务报表 | 集成LayoutLMv3+YOLOv8 |
二、Unstructured文档处理库
Unstructured 是一个专业的文档处理库,专门设计用于RAG和AI微调场景的非结构化数据预处理。提供了统一的接口来处理多种文档格式,是目前最受欢迎的文档加载解决方案之一。
三、从LangChain封装到原始Unstructured
直接使用Unstructured库
三、从LangChain封装到原始Unstructured
创建一个简单的示例,尝试使用Unstructured库加载并解析一个PDF文件:
from unstructured.partition.auto import partition
# PDFæä»¶è·¯å¾
pdf_path = "../../data/C2/pdf/rag.pdf"
# 使ç¨Unstructuredå 载并解æPDFææ¡£
elements = partition(
filename=pdf_path,
content_type="application/pdf"
)
# æå°è§£æç»æ
print(f"è§£æå®æ: {len(elements)} 个å
ç´ , {sum(len(str(e)) for e in elements)} å符")
# ç»è®¡å
ç´ ç±»å
from collections import Counter
types = Counter(e.category for e in elements)
print(f"å
ç´ ç±»å: {dict(types)}")
# æ¾ç¤ºææå
ç´
print("\nææå
ç´ :")
for i, element in enumerate(elements, 1):
print(f"Element {i} ({element.category}):")
print(element)
print("=" * 60)
练习
- 使用
partition_pdf
替换当前partition
函数并分别尝试用hi_res
和ocr_only
进行解析,观察输出结果有何变化。
使用hi_res模式解析(高精度模式,保留布局信息):
使用ocr_only模式解析(纯OCR模式,适合扫描件):
-
hi_res 模式(高精度模式)
- 工作原理:结合 PDF 的文本层和布局分析,保留原始排版结构
- 适用场景:包含可复制文本的 PDF(非扫描件)
- 预期结果:
- 元素类型更丰富(可能包含 Title、Header、Footer、Table 等)
- 文本提取更准确,保留原始格式和布局信息
- 解析速度相对较慢,但质量更高
- 对于有表格的 PDF,能更好地识别表格结构
-
ocr_only 模式(纯 OCR 模式)
- 工作原理:将 PDF 视为图片,通过 OCR 技术识别文本
- 适用场景:扫描件 PDF(没有文本层,只有图片内容)
- 预期结果:
- 元素类型相对简单(多为 Text、NarrativeText 等)
- 可能出现识别错误(尤其是特殊符号、复杂排版)
- 不保留原始布局信息,文本可能按阅读顺序排列
- 对于纯文本 PDF,识别效果可能不如 hi_res 模式
- 解析速度取决于 PDF 页数和图片复杂度
遇到问题:
Xet Storage is enabled for this repo, but the 'hf_xet' package is not installed. Falling back to regular HTTP download. For better performance, install the package with: `pip install huggingface_hub[hf_xet]` or `pip install hf_xet` Traceback (most recent call last): File "D:\Virtualenv\all-in-rag\lib\site-packages\pdf2image\pdf2image.py", line 581, in pdfinfo_from_path proc = Popen(command, env=env, stdout=PIPE, stderr=PIPE) File "D:\SoftInstall\StudySoft\python\lib\subprocess.py", line 969, in __init__ self._execute_child(args, executable, preexec_fn, close_fds, File "D:\SoftInstall\StudySoft\python\lib\subprocess.py", line 1438, in _execute_child hp, ht, pid, tid = _winapi.CreateProcess(executable, args, FileNotFoundError: [WinError 2] 系统找不到指定的文件。 During handling of the above exception, another exception occurred: Traceback (most recent call last): File "D:\Code\pyCharm\all-in-rag\code\C2\01_unstructured_example.py", line 12, in <module> elements = partition_pdf( File "D:\Virtualenv\all-in-rag\lib\site-packages\unstructured\partition\common\metadata.py", line 162, in wrapper elements = func(*args, **kwargs) File "D:\Virtualenv\all-in-rag\lib\site-packages\unstructured\chunking\dispatch.py", line 74, in wrapper elements = func(*args, **kwargs) File "D:\Virtualenv\all-in-rag\lib\site-packages\unstructured\partition\pdf.py", line 225, in partition_pdf return partition_pdf_or_image( File "D:\Virtualenv\all-in-rag\lib\site-packages\unstructured\partition\pdf.py", line 340, in partition_pdf_or_image elements = _partition_pdf_or_image_local( File "D:\Virtualenv\all-in-rag\lib\site-packages\unstructured\utils.py", line 216, in wrapper return func(*args, **kwargs) File "D:\Virtualenv\all-in-rag\lib\site-packages\unstructured\partition\pdf.py", line 647, in _partition_pdf_or_image_local inferred_document_layout = process_file_with_model( File "D:\Virtualenv\all-in-rag\lib\site-packages\unstructured_inference\inference\layout.py", line 388, in process_file_with_model else DocumentLayout.from_file( File "D:\Virtualenv\all-in-rag\lib\site-packages\unstructured_inference\inference\layout.py", line 62, in from_file _image_paths = convert_pdf_to_image( File "D:\Virtualenv\all-in-rag\lib\site-packages\unstructured_inference\inference\layout.py", line 414, in convert_pdf_to_image images = pdf2image.convert_from_path( File "D:\Virtualenv\all-in-rag\lib\site-packages\pdf2image\pdf2image.py", line 127, in convert_from_path page_count = pdfinfo_from_path( File "D:\Virtualenv\all-in-rag\lib\site-packages\pdf2image\pdf2image.py", line 607, in pdfinfo_from_path raise PDFInfoNotInstalledError( pdf2image.exceptions.PDFInfoNotInstalledError: Unable to get page count. Is poppler installed and in PATH?
原因:
缺少
poppler
工具,这是pdf2image
库(unstructured
解析 PDF 时会用到)依赖的 PDF 处理工具。以下是详细的解决步骤:1. 安装 poppler 工具
这是解决问题的核心,需要根据你的操作系统安装:
Windows 系统:
下载 poppler 安装包:
- 访问 poppler for Windows
- 选择最新版本的
poppler-xx.x.x.zip
下载(如poppler-23.11.0.zip
)解压并配置环境变量:
- 将下载的压缩包解压到一个路径(如
C:\Program Files\poppler-23.11.0
)- 找到解压目录下的
bin
文件夹(如C:\Program Files\poppler-23.11.0\Library\bin
)- 将这个
bin
路径添加到系统环境变量PATH
中验证安装:
- 重启命令提示符或 PyCharm
- 运行
pdfinfo -v
,如果能显示版本信息则安装成功
问题2:
Traceback (most recent call last): File "D:\Virtualenv\all-in-rag\lib\site-packages\unstructured_pytesseract\pytesseract.py", line 275, in run_tesseract proc = subprocess.Popen(cmd_args, **subprocess_args()) File "D:\SoftInstall\StudySoft\python\lib\subprocess.py", line 969, in __init__ self._execute_child(args, executable, preexec_fn, close_fds, File "D:\SoftInstall\StudySoft\python\lib\subprocess.py", line 1438, in _execute_child hp, ht, pid, tid = _winapi.CreateProcess(executable, args, FileNotFoundError: [WinError 2] 系统找不到指定的文件。 During handling of the above exception, another exception occurred: Traceback (most recent call last): File "D:\Code\pyCharm\all-in-rag\code\C2\01_unstructured_example.py", line 12, in <module> elements = partition_pdf( File "D:\Virtualenv\all-in-rag\lib\site-packages\unstructured\partition\common\metadata.py", line 162, in wrapper elements = func(*args, **kwargs) File "D:\Virtualenv\all-in-rag\lib\site-packages\unstructured\chunking\dispatch.py", line 74, in wrapper elements = func(*args, **kwargs) File "D:\Virtualenv\all-in-rag\lib\site-packages\unstructured\partition\pdf.py", line 225, in partition_pdf return partition_pdf_or_image( File "D:\Virtualenv\all-in-rag\lib\site-packages\unstructured\partition\pdf.py", line 340, in partition_pdf_or_image elements = _partition_pdf_or_image_local( File "D:\Virtualenv\all-in-rag\lib\site-packages\unstructured\utils.py", line 216, in wrapper return func(*args, **kwargs) File "D:\Virtualenv\all-in-rag\lib\site-packages\unstructured\partition\pdf.py", line 691, in _partition_pdf_or_image_local final_document_layout = process_file_with_ocr( File "D:\Virtualenv\all-in-rag\lib\site-packages\unstructured\utils.py", line 216, in wrapper return func(*args, **kwargs) File "D:\Virtualenv\all-in-rag\lib\site-packages\unstructured\partition\pdf_image\ocr.py", line 201, in process_file_with_ocr raise e File "D:\Virtualenv\all-in-rag\lib\site-packages\unstructured\partition\pdf_image\ocr.py", line 186, in process_file_with_ocr merged_page_layout = supplement_page_layout_with_ocr( File "D:\Virtualenv\all-in-rag\lib\site-packages\unstructured\utils.py", line 216, in wrapper return func(*args, **kwargs) File "D:\Virtualenv\all-in-rag\lib\site-packages\unstructured\partition\pdf_image\ocr.py", line 231, in supplement_page_layout_with_ocr ocr_layout = _ocr_agent.get_layout_from_image(image) File "D:\Virtualenv\all-in-rag\lib\site-packages\unstructured\partition\utils\ocr_models\tesseract_ocr.py", line 53, in get_layout_from_image ocr_df: pd.DataFrame = self.image_to_data_with_character_confidence_filter( File "D:\Virtualenv\all-in-rag\lib\site-packages\unstructured\partition\utils\ocr_models\tesseract_ocr.py", line 99, in image_to_data_with_character_confidence_filter hocr: str = unstructured_pytesseract.image_to_pdf_or_hocr( File "D:\Virtualenv\all-in-rag\lib\site-packages\unstructured_pytesseract\pytesseract.py", line 514, in image_to_pdf_or_hocr return run_and_get_output(*args) File "D:\Virtualenv\all-in-rag\lib\site-packages\unstructured_pytesseract\pytesseract.py", line 353, in run_and_get_output run_tesseract(**kwargs) File "D:\Virtualenv\all-in-rag\lib\site-packages\unstructured_pytesseract\pytesseract.py", line 280, in run_tesseract raise TesseractNotFoundError() unstructured_pytesseract.pytesseract.TesseractNotFoundError: tesseract is not installed or it's not in your PATH. See README file for more information.
原因:
1. 安装 Tesseract OCR 工具
Windows 系统:
下载安装包:
- 访问 UB-Mannheim/tesseract 下载最新的 Windows 安装程序(如
tesseract-ocr-w64-setup-5.3.3.20231005.exe
)- 运行安装程序,特别注意:在安装过程中勾选 "Add tesseract to system PATH" 选项(将 Tesseract 添加到系统环境变量)
验证安装:
- 重启命令提示符或 PyCharm
- 运行
tesseract --version
,如果显示版本信息则安装成功
第二节 文本分块
一、文本分块基础认知
- 定义:文本分块(Text Chunking)是 RAG 流程的关键步骤,核心是将加载后的长篇文档切分为更小、可处理的单元,这些单元是后续向量检索和模型处理的基本单位。
- 核心目标:平衡 “模型上下文限制” 与 “检索生成性能”,确保文本能被有效处理且语义完整。
二、文本分块的重要性(两大核心考量)
1. 满足模型上下文硬限制
文本分块首要目的是适配 RAG 中两个核心组件的长度上限:
- 嵌入模型(Embedding Model):如
bge-base-zh-v1.5
上下文窗口为 512 Token,超量文本会被截断,导致信息丢失、向量语义不完整,因此文本块需≤其 Token 上限。 - 大语言模型(LLM):虽上下文窗口更大(几千至百万 Token),但检索到的文本块 + 问题 + 提示词需整体适配窗口;若单块过大,会导致可容纳的相关块数量减少,限制信息广度。
2. 为何 “块不是越大越好”
即使未超模型上限,过大的块会严重损害 RAG 性能:
- 嵌入信息损失:嵌入模型通过 “分词→向量化→池化” 生成单向量,块越长语义点越多,向量承载的信息越稀释,关键细节模糊,检索精度下降。
- 生成 “大海捞针”(Lost in the Middle):LLM 对长上下文的开头 / 结尾记忆更清晰,中间信息易被忽略;大块含大量无关噪音时,模型难提取关键信息,易产生幻觉。
- 主题稀释导致检索失败:大块若包含多个无关主题(如 “鲁班技能 + 出装 + 背景” 混为一块),语义被稀释,查询 “出装” 时因相关性得分低无法召回;聚焦单主题的小块(如单独 “出装” 块)检索精度更高。
三、LangChain 核心文本分块策略
1. 固定大小分块(CharacterTextSplitter)
- 核心逻辑:“段落感知的自适应分块”,非严格按字符数切割:
- 先按默认分隔符
\n\n
(段落)分割文本; - 用
_merge_splits
合并段落,超chunk_size
时终止当前块,通过chunk_overlap
保持上下文连续; - 单个段落超限时发出警告但保留完整块,优先保语义完整性。
- 先按默认分隔符
- 优劣势:实现简单、速度快;但可能在语义边界切割,影响连贯性。
- 代码示例核心参数:
text_splitter = CharacterTextSplitter(chunk_size=200, chunk_overlap=10) # 目标200字符/块,重叠10字符
2. 递归字符分块(RecursiveCharacterTextSplitter)
- 核心逻辑:按分隔符层级递归切割,解决超长文本问题:
- 按优先级遍历分隔符(如
\n\n
→\n
→。
→,
),找到首个存在的分隔符; - 切割后,合格片段(≤
chunk_size
)暂存,超长片段用更细粒度分隔符递归切割; - 分隔符用尽时停止递归,保留超长块。
- 按优先级遍历分隔符(如
- 关键差异:相比固定大小分块,能对超长片段进一步细分(如段落→句子→单词)。
- 分隔符配置:
- 中文适配:
["\n\n", "\n", "。", ",", " ", ""]
; - 代码适配:用
from_language(Language.PYTHON)
调用编程语言专属分隔符。
- 中文适配:
3. 语义分块(SemanticChunker)
- 核心逻辑:基于语义主题切分,每个块语义高度一致,不依赖固定分隔符 / 字符数:
- 句子分割:按标点将文本拆为句子列表;
- 上下文感知嵌入:用
buffer_size
(默认 1)将句子与前后句组合后嵌入,向量含上下文语义; - 计算语义距离:算相邻句子向量的余弦距离(距离越大语义关联越弱);
- 识别断点:用统计方法(如百分位法)设动态阈值,超阈值处为语义断点;
- 合并成块:按断点合并句子为语义连贯的块。
- 断点识别方法:
percentile
(默认):取第 N 百分位(如 95)为阈值,超阈值为断点;standard_deviation
:超 “均值 + N 倍标准差”(如 3 倍)为断点;- 其他:
interquartile
(四分位距)、gradient
(梯度法,适配法律 / 医疗文档)。
4. 基于文档结构的分块(如 MarkdownHeaderTextSplitter)
- 核心逻辑:利用文档结构标记(如 Markdown 标题、HTML 标签)切分,保留宏观逻辑:
- 定义规则:映射标题层级(如
[("#", "Header 1"), ("##", "Header 2")]
); - 内容聚合:将标题下所有内容(至下一个同级 / 高级标题)聚合,赋予 “标题路径” 元数据(如
{"Header 1": "第三章", "Header 2": "3.2节"}
); - 组合使用:先按标题分大逻辑块,再用
RecursiveCharacterTextSplitter
切分至符合chunk_size
,最终小块继承标题元数据。
- 定义规则:映射标题层级(如
- 优势:元数据提供精确 “信息地址”,增强 LLM 对上下文来源的理解,是结构化文档(Markdown/HTML)的理想方案。
四、其他开源框架分块策略
框架 | 核心分块特点 |
---|---|
Unstructured | 1. 先 “分区”:将文档解析为带语义标签的元素(如 Title、ListItem); 2. 再 “分块”: - basic :组合元素至超max_characters ;- by_title :按 Title 元素分割,避免跨章节。 |
LlamaIndex | 1. 以 “节点(Node)” 为核心,文档先解析为节点,分块是节点转换的一环; 2. 节点解析器类型: - 结构感知型(MarkdownNodeParser); - 语义感知型(SemanticSplitterNodeParser); 3. 支持灵活流水线(如标题分块→句子分块)。 |
ChunkViz | 文本分块可视化工具,输入文档和分块配置,用颜色标记块边界与重叠,辅助理解分块逻辑。 |
【项目地址】https://github.com/datawhalechina/all-in-rags