文档分割提高RAG的常用技巧

文档分割提高RAG的常用技巧

文档分割的核心作用

  1. 检索效率与精度
    • 效率:恰当的分割能大幅减少索引体积和计算开销,提升检索速度(经验上可优化30-50%)。
    • 精度:避免过大片段包含无关噪声,或过小片段丢失关键上下文,使检索结果更相关、更聚焦。
  2. 上下文完整性
    • 良好的分割确保每个片段尽可能包含一个完整的语义单元(如一个论点、一个事件描述、一个QA对),避免信息被生硬切断导致的语义碎片化,为后续生成提供坚实基础。
  3. 生成内容质量
    • 实验数据和实践经验表明,优化后的分割策略能显著提升LLM生成答案的准确性相关性连贯性(准确率提升40-60%的案例并不罕见),因为它确保了输入LLM的上下文片段本身是信息完整且目标明确的。

常用文档分割技巧

1. 固定长度分割法

适用场景:标准化文档(如普通文本文档、无明显强结构的网页内容)、追求简单快速实现的场景,或作为其他方法的预处理步骤。

原理:严格按照字符数或Token数进行切割。

示例(LangChain)

from langchain.text_splitter import CharacterTextSplitter

text_splitter = CharacterTextSplitter(
    chunk_size=1000,  # 每个片段最大字符数
    chunk_overlap=200,  # 相邻片段重叠字符数
    separator="\n",     # 优先在换行符处分割,其次按字符切
    length_function=len # 计算长度的方法(此处按字符数)
)
chunks = text_splitter.split_text(your_text)

参数优化建议与考量

  • chunk_size (核心)
    • 通用范围:500-1500字符(或对应Token数)是常见起点。
    • 文档类型
      • 技术文档/论文:800-1500字符(需保留公式、代码上下文)
      • 新闻/博客/社交媒体:400-800字符
      • 对话记录/邮件:300-600字符(保持对话轮次完整)
    • LLM上下文窗口限制:必须远小于LLM的最大上下文窗口(需预留空间给用户问题、系统指令和生成结果)。
  • chunk_overlap (关键)
    • 目的:防止关键信息(如恰好位于分割点的术语、结论)丢失,提高上下文连续性。
    • 建议:通常设置为chunk_size的10%-25%。信息密度高或结构松散文档可适当增加。
  • separator (提升语义)
    • 设置合理的分割符优先级(如 ["\n\n", "\n", " ", ""]),优先在段落、句子、单词边界处切割,避免在单词或数字中间切断。
  • length_function:根据需求选择按字符数 (len) 或 Token 数(如 tiktoken 库计算)分割。LLM处理基于Token,按Token分割通常更精确匹配其处理能力。

优点:实现简单,计算高效。

缺点:容易破坏自然语义结构(切断句子、段落),可能导致检索片段包含不完整信息。

2. 结构化文档分割法

适用场景:具有明显层级结构的文档(如 Markdown, HTML, LaTeX, PDF with Headings, Word with Styles)。

原理:利用文档固有的结构标记(标题、章节、列表等)作为分割点,保持逻辑单元的完整性。

示例(LangChain - Markdown)

from langchain.text_splitter import MarkdownHeaderTextSplitter

headers_to_split_on = [
    ("#", "Header 1"),  # 一级标题
    ("##", "Header 2"), # 二级标题
    ("###", "Header 3"),# 三级标题
]

markdown_splitter = MarkdownHeaderTextSplitter(
    headers_to_split_on=headers_to_split_on,
    strip_headers=False  # 可选:是否在输出中保留标题文本
)
chunks = markdown_splitter.split_text(your_markdown_text)

关键点

  • 结构识别:分割器需要能解析文档的特定结构(Markdown标题、HTML标签等)。
  • 保留元信息:分割时通常会将父级标题信息(如 Header 1 > Header 2 > Header 3)作为元数据添加到片段中,极大增强检索和生成时的上下文理解。
  • 粒度控制:通过选择分割的标题级别(如只按 ## 切)可以控制片段的粒度。

优点:最大程度保持语义和逻辑单元的完整性,片段自带结构信息,检索和生成质量通常很高。

缺点:依赖文档的结构化程度和质量,对非结构化或结构混乱的文档效果不佳。

3. 递归分割法(推荐常用)

适用场景:通用性强,尤其适用于混合内容或结构不清晰但仍有部分分隔符(如段落、句子)的文档。是固定长度分割的智能升级版。

原理:采用分层分割策略。优先使用较大的分隔符(如双换行\n\n)将文本分割成大块。如果大块仍然超过chunk_size,则使用下一级分隔符(如单换行\n)继续分割。依此类推,直到分割成满足chunk_size要求的片段。这尽可能地在较大语义边界处切割。

示例(LangChain)

from langchain.text_splitter import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000,
    chunk_overlap=200,
    separators=["\n\n", "\n", ". ", "? ", "! ", " ", ""]  # 分割符优先级列表
)
chunks = text_splitter.split_text(your_text)

参数说明

  • separators最关键参数。定义分割符的优先级列表。分割器会顺序尝试列表中的分隔符。例如,先尝试用"\n\n"分,分出来的块如果还太大,再用"\n"分这个块,还不够小再用". "分,以此类推,直到满足chunk_size或没有分隔符可用(最后按字符切)。
  • chunk_size, chunk_overlap:作用同固定长度分割法。

优点

  • 在无法利用强结构时,比纯固定长度分割更能尊重自然语义边界(段落、句子)。
  • 通用性好,适用于多种文档类型。
  • 是 LangChain 等库中最常用且推荐的默认文本分割器

缺点:对于完全没有明显分隔符(如长段落无换行)的文本,最终还是会退化成按字符/Token切分。

分割前需要思考的地方

  1. 理解文档特性是第一要务
    • 分析文档类型(技术手册、新闻、对话、代码)、结构强度(Markdown/HTML vs 纯文本)、信息密度、典型长度。
    • 结构化文档优先尝试结构化分割法。
    • 通用文本或混合内容优先使用递归分割法。
    • 固定长度法作为兜底或预处理。
  • 案例:处理医学研究论文(PDF)时,发现其有清晰的章节结构(摘要/方法/结果)。
  • 操作
    • 使用PDF解析器提取标题层级
    • 选择结构化分割法,按## 方法## 结果等二级标题切分
    • 每个片段自动附加"章节标题"元数据
  • 错误示范:若用固定长度分割,可能将"实验组疗效数据"表格拦腰切断
  1. 保持语义单元完整
    • 这是最高原则。避免在句子中间、重要论点中途、关键实体(人名、地名、术语)中间、表格行间、代码块中间切断。利用分隔符和重叠尽可能规避。
  • 案例:分割用户客服对话日志(文本格式)
  • 操作
    • 设置递归分割器参数:separators=["\nUser:", "\nAgent:"]
    • 确保每个片段包含完整对话轮次(如用户提问+客服回复)
  • 错误示范:若用chunk_size=300固定分割,可能使回复"您的订单号是AB-X"被拆成[“您的订单”,“号是AB-X”]
  1. 重叠(Overlap)不是万能的,但不可或缺
    • 合理设置chunk_overlap能有效缓解分割点信息丢失问题,提高上下文连贯性。
    • 但过大的重叠会显著增加索引大小和检索冗余,需平衡。
  • 案例:法律合同分割(条款间存在引用关系)
  • 操作
    • 设置chunk_size=1200, chunk_overlap=300(25%)
    • 当分割点出现在"见第3.2条"时,overlap确保下个片段包含3.2条开头
  • 平衡实践:对新闻摘要等低密度文本,overlap降为10%避免冗余
  1. 考虑LLM的上下文窗口(Context Window)
    • 分割后的片段长度(加上用户问题、指令等)必须严格适配你使用的LLM的上下文窗口限制。预留足够空间给生成。
  • 案例:使用GPT-4-128K处理财报分析(平均每份PDF 50页)
  • 操作
    • 计算:用户问题(200token) + 系统指令(100token) + 生成空间(500token)
    • 确定chunk_size=800token(预留足够buffer)
  • 错误示范:若设置chunk_size=1500token,当需要同时插入2个片段时,总长度超模型限制
  1. 考虑检索效率与索引大小
    • 片段过小会导致索引条目过多,增加检索计算量和存储成本。
    • 片段过大会降低检索精度(包含无关信息)并消耗更多LLM Token。
    • 在语义完整性和检索效率间寻求平衡点。
  • 案例:电商百万级商品描述库
  • 操作
    • 测试发现:chunk_size=600时召回率85%,索引大小120GB
    • 优化为chunk_size=800后召回率82%,索引降至80GB(接受3%召回下降换取40%存储节省)
  • 平衡点:在召回率下降≤5%范围内最大化chunk_size
  1. 实验、评估与迭代
    • 没有放之四海而皆准的最优参数! 必须针对你的具体数据你的RAG Pipeline进行实验。
    • 评估指标:检索召回率(Recall)@K、检索结果相关性、生成答案的准确性(Factual Accuracy)、流畅性、人工评估。
    • 调整chunk_size, overlap, separators,观察效果变化。A/B测试不同策略非常有效。
  • 案例:科技博客RAG系统优化
  • 操作
    • A组:chunk_size=500, overlap=0
    • B组:chunk_size=800, overlap=100
    • 评估:B组在"解释技术原理"类问题准确率提升37%
    • 迭代:针对"代码示例"问题,额外增加按代码块分割策略
  1. 元数据是黄金
    • 在分割时尽可能保留和附加元数据(来源文件名、章节标题、页码、时间戳等)。这些信息对检索排序和LLM理解片段来源至关重要。结构化分割法通常天然支持此功能。
  • 案例:企业制度文档库版本管理
  • 操作
    • 分割时附加元数据:{"doc_id": "HR-2024", "section": "休假制度", "effective_date": "2024-01-01"}
    • 当用户问"最新病假政策"时,检索器优先返回带effective_date≥2024片段
  • 效果:避免返回已过期的2023年政策片段
  1. 处理长上下文依赖
    • 对于需要跨越多个片段才能理解的复杂问题(如文档摘要、多步骤推理),分割策略本身可能不够。需要结合其他技术(如父文档检索、句子窗口检索、摘要链等)。
  • 案例:研报中的跨页数据对比(如"图3"在P10,"分析结论"在P12)
  • 操作
    • 第一层:用结构化分割法按章节切分
    • 第二层:对"财务分析"章节采用父文档检索
# 子片段(用于检索)
chunks = split_text(chunk_size=400)  
# 父片段(用于生成)
parent_chunks = split_text(chunk_size=2000)  
  • 当检索到子片段"见图3趋势"时,自动关联所属的父片段(完整分析章节)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

极客李华

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值