跳到主内容

RAG 文档切分怎么做:块大小、重叠率与 4 种切分策略的选型结论

100%
RAG 文档切分怎么做:块大小、重叠率与 4 种切分策略的选型结论

RAG 的召回质量上限由切分决定,不是由向量库决定。可直接套用的结论:事实型问答用 64–128 token 的小块,需要跨段理解的场景用 512–1024 token 的大块,重叠率取块大小的 10%–20%,并且优先按标题等结构边界切而不是按字符数硬切。切错了,后面换再好的 embedding 模型也救不回来。

为什么切分是 RAG 的第一性问题

检索是「块级」的:你召回什么块,模型就只能基于什么块回答。切分带来两类失败,方向相反:

  • 块太大:一个块里塞进多个主题,向量被稀释成「平均语义」,相似度算不准;同时浪费上下文窗口,把噪声一起喂给模型。
  • 块太小:句子被拦腰截断,语义不完整,命中了也没法回答;需要跨块推理的问题直接失效。

多篇针对长文档检索的对比实验给出了一致的规律:最优块大小取决于你的查询类型,而且不同 embedding 模型对块大小的敏感度不同——有的模型(如 Stella 一类)在大块上更能利用全局上下文,有的(如 Snowflake 一类)在小块上做细粒度实体匹配更准。这意味着「抄一份通用参数」本身就是错的。

四种切分策略怎么选

策略做法适用代价
固定大小切分按 token/字符数切,带 10%–20% 重叠基线方案、格式杂乱的语料会切断段落与标题,语义不完整
结构(标题)切分按 Markdown/HTML 标题层级拆分,块内保完整小节文档、手册、 Wiki依赖源格式规整;小节过长需二次切分
语义切分按相邻句 embedding 相似度骤降处切分高精度场景(法律、医疗)需额外推理,长文档易漏掉跨块信息
父子块(Parent-Child)小块用于检索,命中后回填整篇父文档作上下文多跳推理、长文档、答案分散上下文变大、噪声增加、处理更慢
最实用的组合:结构切分打底 + 父子块兜底。先按标题切开保证语义完整,再对超长小节做二次固定切分;检索用小粒度子块保证精度,生成时用父块保证上下文完整。这套在中文技术文档上效果最稳。

落地步骤与可套用参数


  1. 先解析再切分:PDF/HTML 用专业解析库把文档拆成结构化元素(标题、正文、列表、表格),表格保留 HTML 表示。别用裸文本硬切,否则表格会碎成一堆无意义数字。

  2. 按结构边界切:使用层级分隔符(# 标题 → 代码块 → 分隔线 → 段落 → 句子 → 字符),让切分点尽量落在天然边界上。

  3. 用 tokenizer 计数,不用字符数:字符数与 token 数在中文里差距极大,用字符设 1000 可能实际超 1500 token,超过模型 max_seq_length 后会被静默截断,直接丢信息。

  4. 块大小对齐 embedding 模型上限:查模型的 max_seq_length(常见 512),块大小取其 70%–90%,重叠取块大小的 1/10 。短问答场景可降到 128–256 。

  5. 带上元数据一起存:每个块记录文档 ID 、所属章节、页码、源 URL 。元数据既能在检索时做过滤,也是生成答案时给出引用的唯一凭据。

用 tokenizer 计数的正确写法

from langchain.text_splitter import RecursiveCharacterTextSplitter
from transformers import AutoTokenizer

MARKDOWN_SEPARATORS = [
    "\n#{1,6} ", "```\n", "\n\\*\\*\\*+\n", "\n---+\n", "\n___+\n",
    "\n\n", "\n", " ", "",
]

EMBED_MODEL = "thenlper/gte-small"   # max_seq_length = 512

def split_documents(chunk_size, docs, tokenizer_name=EMBED_MODEL):
    splitter = RecursiveCharacterTextSplitter.from_huggingface_tokenizer(
        AutoTokenizer.from_pretrained(tokenizer_name),
        chunk_size=chunk_size,
        chunk_overlap=int(chunk_size / 10),   # 10% 重叠
        add_start_index=True,
        strip_whitespace=True,
        separators=MARKDOWN_SEPARATORS,
    )
    out, seen = [], set()
    for d in docs:
        for c in splitter.split_documents([d]):
            if c.page_content not in seen:    # 去重,避免重叠造成重复索引
                seen.add(c.page_content)
                out.append(c)
    return out

chunks = split_documents(448, docs)   # 512 的 ~87%,留截断余量
为什么必须检查块长度分布?
切完之后画一张 token 长度直方图,是唯一能验证参数是否合理的手段。如果右侧有明显长尾超过模型 max_seq_length,说明这些块在 embedding 时会被截断,尾部内容在检索阶段完全不可见——这是「明明文档里有,就是搜不到」最常见的根因。调小 chunk_size 直到长尾消失。

上线前必做的三件事

  1. 重排(Rerank):向量召回 top-K 后用 cross-encoder 重排,是性价比最高的一步提升,通常比调块大小见效更快。
  2. 建评测集:准备 20–50 条真实问题,标注预期命中文档。用 Faithfulness(是否只依据检索到的证据)、 Context Recall(所需信息是否被召回)等指标量化,别凭感觉调参。
  3. 引用与拒答:提示词里明确要求模型给出来源引用,并在证据不足时直接说「不知道」。没有引用的答案无法人工核验,在生产环境里等于不可用。
中文场景别直接套英文参数:同样 512 token,中文承载的信息量约为英文的 1.5–2 倍。中文技术文档建议从 256–384 token 起步做网格搜索,比照搬 512 更贴近实际。

相关阅读

延伸阅读:RAG 与向量数据库

块大小到底设多少?
没有通用值。经验起点:事实型短问答 64–128 token;需要上下文理解的长问答 512–1024 token;中文技术文档可从 256–384 token 起步做网格搜索。用你的评测集跑一遍,看 Context Recall 和 Faithfulness 哪个更高。

重叠率设多少合适?
取块大小的 10%–20%。低于 10% 易在边界处切断关键句,高于 20% 会显著增加索引体积和重复召回。父子块策略下可以更低,因为上下文由父块补齐。

能用字符数代替 token 数吗?
不能,中文尤其不行。中文一个字往往对应 1 个以上 token,按字符设 1000 实际可能远超模型上限被静默截断。必须用对应 embedding 模型的 tokenizer 计数。

表格和代码块怎么处理?
解析阶段就把表格保留为结构化元素(含 HTML 表示),代码块作为独立分隔符层级优先保护,避免被从中间切开。切碎的表格和代码在检索中几乎不可用,还会污染向量空间。

参考来源:Hugging Face 官方 Cookbook 《 Advanced RAG 》、 HF Papers 《 Rethinking Chunk Size For Long-Document Retrieval: A Multi-Dataset Analysis 》及 HF 社区 RAG 切分策略配置集。本文为基于上述公开材料的原创改写与工程化整理。

这篇有帮助吗?
云上的幻象
云上的幻象查看主页

七彩云博客,分享 WordPress 建站实战与 AI 工具测评,覆盖服务器运维、站长工具、软件资源与电商运营干货,专注原创实用的主题插件、网站加速与安全优化教程。

779文章4评论

相关文章

评论 (0)

欢迎你,新朋友,感谢参与互动!文明发言,理性交流 · 首次评论将在审核后展示