🔗教程链接all-in-rag

第一节、数据加载

  • 垃圾进,垃圾出(Garbage in,Garbage out):高质量输入是高质量输出的前提

  • 数据加载是RAG流水线的第一步,也是最基础最关键的环节

一、文档加载器

主要功能:文档格式解析、元数据提取、统一数据格式

当前主流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(重点工具)

  • 格式支持广泛:PDF、Word、Excel、HTML、Markdown等
  • 智能内容解析:自动识别文档结构元素,保留文档元数据信息
  • 统一接口:无需为不同格式编写不同代码

重要文档元素类型

元素类型

元素类型 说明 重要性
Title 文档标题 ★★★★★
NarrativeText 正文文本 ★★★★★
Table 表格 ★★★★☆
ListItem 列表项 ★★★★☆
CompositeElement 分块产生的复合元素 ★★★★☆

特别注意CompositeElement是通过分块处理产生的特殊类型,理解它对于后续的文本分块很重要。

练习

window需要安装poppler,代码自动下载yolox_l0.05.onnx

from unstructured.partition.pdf import partition_pdf

# PDF文件路径
pdf_path = "../../data/C2/pdf/rag.pdf"

# 使用Unstructured加载并解析PDF文档
elements = partition_pdf(
    filename=pdf_path,
    content_type="application/pdf",

   languages=["eng", "chi_sim"],
    # hi_res_model_name="hi_res",
    # hi_res_model_name="ocr_only"
    # strategy="hi_res",  # 会下载yolox_l0.05.onnx
    strategy="ocr_only",  #

)
  • strategy: 处理策略
    • “auto”:自动选择策略(默认)
    • “fast”:快速策略,使用光学字符识别(OCR)以外的其他方法
    • “ocr_only”:仅使用OCR来提取文本
    • “hi_res”:高分辨率策略,使用OCR并且注重保持布局

pdf2image.exceptions.PDFInfoNotInstalledError: Unable to get page count. Is poppler installed and in PATH?

未成功

第二节、文本分块

一、 文本分块的本质

  • 定义:将长篇文档切分成更小、更易处理的单元
  • 地位:是后续向量检索和模型处理的基本单位
  • 目标:在保持语义完整性与控制块大小之间找到平衡

二、为什么需要文本分块(关键原理)

2.1 适应模型限制(上下文限制)

模型类型 限制影响 解决方案
嵌入模型 严格输入长度上限(如512token) 块大小必须≤模型窗口
大语言模型 上下文窗口限制 所有检索块+问题+提示词必须能放入窗口

2.2 为什么"块不是越大越好"(重要坑点)

信息损失问题
  • 嵌入过程:token向量→池化→单一向量
  • 向量稀释:块越长,语义信息越稀释,关键细节模糊化
"大海捞针"效应
  • LLM倾向于记住开头和结尾信息
  • 过大块会导致关键信息被淹没在噪音中
主题稀释导致检索失败

示例对比

  • 糟糕分块:"技能介绍+推荐出装+背景故事"在一个块
  • 优秀分块:三个主题分别独立分块
  • 结果:查询"出装"时,优秀分块能精准召回相关块

三、基础分块策略详解

3.1 固定大小分块(CharacterTextSplitter)

实际实现:并非严格固定大小,而是"段落感知的自适应分块"

工作流程

  1. 按段落分割(默认"\n\n"
  2. 智能合并,优先保持段落完整性
  3. 处理超长段落(警告但保留)

适用场景:日志分析、数据预处理等简单场景

优势:实现简单、处理速度快且计算开销小。

3.2 递归字符分块(RecursiveCharacterTextSplitter)

核心算法:分层递归处理

优先级:段落→句子→单词→字符

关键特性

  • 从分隔符列表找第一个存在的分隔符;
  • 切分后,短片段暂存,超长片段用剩余分隔符递归分割;
  • 合并暂存片段为块

优势:相比固定大小分块,更能处理超长文本,减少语义割裂

代码语言特化

splitter = RecursiveCharacterTextSplitter.from_language(
    language=Language.PYTHON,  # 自动使用代码结构分隔符
    chunk_size=500,
    chunk_overlap=50
)

3.3 语义分块(SemanticChunker)- 高级策略

核心思想:在语义主题显著变化处切分

工作流程

  1. 句子分割 → 2. 上下文感知嵌入 → 3. 计算语义距离 → 4. 识别断点 → 5. 合并成块

断点识别方法

  • percentile(默认):第95百分位作为阈值
  • standard_deviation:平均值 + 3倍标准差
  • interquartile:Q3 + 1.5倍IQR
  • gradient:对梯度应用百分位法(适合法律、医疗文档)

优势:无需依赖固定分隔符,更贴合语义逻辑。

3.4 基于文档结构的分块

Markdown分块优势

  • 保留标题层级元数据
  • 提供精确的"地址"信息
  • 两阶段处理:先按标题分组,再按大小细分

四、其他框架分块策略

4.1 Unstructured:基于文档元素

核心优势:“先理解、后分割”

  • 分区:解析为结构化元素(Title、NarrativeText等)
  • 分块方法
    • basic:连续组合元素直到达到大小限制
    • by_title:在Title元素处强制分块,保持章节完整性

4.2 LlamaIndex:面向节点的体系

核心概念:文档→节点→转换

  • 结构感知型:MarkdownNodeParser、JSONNodeParser
  • 语义感知型
    • SemanticSplitterNodeParser:类似语义分块
    • SentenceWindowNodeParser:单个句子+上下文窗口(检索精度高)
  • 良好互操作性:可封装LangChain的TextSplitter

4.3 可视化工具

  • ChunkViz:快速理解分块逻辑和重叠效果

总结

  1. 块大小并非越大越好,需平衡嵌入精度和 LLM 处理能力;
  2. 避免在语义边界(如句子、段落中间)切分,优先使用自然分隔符;
  3. 处理结构化文档时,忽略文档结构(如标题层级)会导致上下文丢失;
  4. 语义分块依赖嵌入模型质量,需选择适合的模型和断点识别方法。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐