RAG 文本分块:七种 Chunking 策略与选型方法

为什么要把文档切成 Chunk

文档解析完成后,通常不能直接把整份内容做成一个向量。用户问的往往只是局部问题,整篇文档作为检索单元会混入大量无关内容;同时,长文档也可能超过 Embedding 模型或生成模型的输入限制。

于是需要把文档切成较小的 Chunk。Chunk 是检索系统返回的基本单位,它决定模型最终能看到多少上下文。

切得太大,向量会混合多个主题,检索不够精确;切得太小,完整答案会散落到多个片段中,甚至连主语和条件都被切开。分块的本质,是在"语义完整"和"定位精确"之间找平衡。

1. 固定长度分块

固定长度是最直接的方案:每 N 个字符或 Token 切一块。为了避免边界处丢失上下文,通常设置一段重叠区。

python 复制代码
def fixed_chunks(text, size=500, overlap=80):
    chunks = []
    start = 0
    while start < len(text):
        chunks.append(text[start:start + size])
        start += size - overlap
    return chunks

它速度快、结果稳定,适合结构很弱的纯文本。问题也很明显:现象1可能留在上一块,解释却落到下一块;代码、表格和句子都可能被从中间切断。重叠能缓解边界问题,却会增加存储量,并使多个召回结果包含重复内容。

2. 按句子分块

这种方法先识别句子边界,再把若干句组合为一个 Chunk。它至少不会从句子中间切开,适合新闻、FAQ 和普通说明文。

python 复制代码
sentences = sentence_splitter.split(text)
chunks = pack_by_token_limit(sentences, max_tokens=350)

难点在于句号不一定代表句子结束,例如版本号、缩写、小数和代码都可能包含点号。只按标点写正则,面对中英文混排时尤其容易误切。此外,一个句子保持完整,并不代表它脱离上一段后仍然有意义。

3. 递归分块

递归分块会按一组从强到弱的分隔符尝试切分,例如先按章节和空行,再按句子、空格,最后才按字符硬切。

python 复制代码
separators = ["\n\n", "\n", "。", "!", "?", ";", " ", ""]

def recursive_split(text, limit, level=0):
    if token_count(text) <= limit:
        return [text]
    sep = separators[level]
    parts = text.split(sep) if sep else list(text)
    packed = pack_with_separator(parts, sep, limit)
    return sum(
        (recursive_split(x, limit, level + 1) for x in packed),
        [],
    )

它比固定长度更尊重自然边界,而且不依赖额外模型,因此常被用作通用基线。它仍然不理解主题,只是尽量在更自然的位置断开。

4. 结构感知分块

若输入已经保留 Markdown 标题、HTML 标签或文档层级,应优先利用这些结构。例如把一个二级标题及其正文视为候选块,过长时再在内部递归切分。

text 复制代码
文档标题
└── 一级章节
    ├── 二级章节 A  → 一个或多个 Chunk
    └── 二级章节 B  → 一个或多个 Chunk

结构分块的优势是每个片段都能携带标题路径。即使正文写着"执行上述命令",检索结果也能同时带上"安装 > Linux 环境 > 启动服务"这样的语境。

它的局限是章节长度可能极不均匀,而且依赖上游解析质量。标题若识别错误,分块边界也会跟着错。因此常见做法是"结构定位 + 递归控制大小"。

5. 语义分块

语义分块先把相邻句子或段落转成向量,再比较相似度。当相邻内容的语义相似度明显下降时,说明话题可能发生了变化,可以在这里切开。

python 复制代码
vectors = embed(sentences)
similarities = [
    cosine(vectors[i], vectors[i + 1])
    for i in range(len(vectors) - 1)
]
boundaries = detect_drops(similarities, threshold=0.68)

这种方式对访谈、会议记录和话题频繁切换的长文本很有用。不过它需要额外计算 Embedding,阈值也会影响结果:过高会产生大量碎片,过低又无法分开不同主题。阈值不能凭经验一次定死,应在真实问题集上调试。

6. 针对特殊内容分块

代码、表格和问答对不适合套用普通段落规则。

  • 代码应尽量按类、函数或方法切分,并保留文件路径与符号名。
  • 表格应保留表头,并让每个数据块都能理解列含义。
  • FAQ 最自然的单元通常是一组问题与答案。
  • 对话记录需要保留说话人和时间,必要时按话题而非轮次切分。

例如,表格按行分块时可以把表头展开到每条记录:

text 复制代码
产品=A;版本=2.1;状态=可用
产品=B;版本=1.8;状态=停止维护

这样做牺牲了一点紧凑性,却让每个 Chunk 单独出现时仍然可读。

7. 使用大模型分块

大模型可以识别主题、论点、前置条件和引用关系,并按"能够独立回答一个问题"的标准切分。它适合结构混乱但价值很高的复杂资料。

text 复制代码
请把下列内容切成若干语义完整的知识片段。
每个片段必须:
1. 能脱离上下文独立理解;
2. 保留条件、例外和结论;
3. 返回标题、摘要和原文起止位置。

代价是调用成本、延迟和结果不完全稳定。大模型也受自身上下文窗口限制,因此更常作为高价值数据的精加工手段,而不是默认处理所有文件。

Chunk 大小怎样确定

不存在适用于所有项目的固定数字。合理大小取决于文档结构、问题粒度、Embedding 模型上限和生成阶段的上下文预算。

可以从一个中等大小开始,再围绕三类指标调整:

  • 检索命中率:标准答案所在片段能否进入 Top-K。
  • 片段纯度:召回块中无关内容有多少。
  • 答案完整度:模型是否拿到了条件、步骤和例外。

若经常命中主题却答不全,可增大 Chunk、增加重叠,或在命中后补充相邻块;若结果总是宽泛而不精确,应缩小 Chunk,或改用结构和语义边界。

实用的选型顺序

面对结构清楚的 Markdown、HTML 或技术手册,先按标题切,再对超长章节递归切分。普通纯文本可从递归分块开始。会议记录和话题跳跃明显的内容,可以尝试语义分块。代码、表格、FAQ 则应使用专门规则。

不要只观察切出来的文本是否整齐。最终目标是让真实问题稳定命中正确证据。把分块参数纳入检索评估,远比争论"500 还是 800 个字符"更有意义。

小结

分块不是文档预处理中的机械步骤,而是在设计知识库的最小语义单元。好的 Chunk 既能独立理解,又不会包含太多无关话题;既能被准确召回,也能为答案保留足够上下文。

实际项目中,组合策略往往比单一策略更可靠:利用结构确定大边界,用递归规则控制长度,对少量复杂内容再用语义或大模型精细处理。

相关推荐
youdexiang1 小时前
AI 生成会议纪要好用吗?多款 APP 功能分析
java·人工智能·音视频
阡陌数智1 小时前
大模型领域自适应微调:小样本场景下过拟合抑制与数据构建方法论
人工智能·深度学习·机器学习
AI创界者1 小时前
【开源实战】MiniMax-H3 本地离线高自由度 ComfyUI 工作流搭建指南(含规则松绑与提示词映射)
人工智能·aigc·音视频
程序猿老A1 小时前
GPU云服务器怎么安装AI
运维·服务器·人工智能
库拉大叔1 小时前
从 0 到 1 做一部 AI 漫剧,知漫剧完整制作流程
人工智能·aigc
Omics Pro1 小时前
之江实验室NAR|虚拟细胞3阶段训练范式
数据库·人工智能·算法·机器学习·自然语言处理
AIGC小尼1 小时前
8G 显存零基础本地部署 AI 漫剧全流程|ComfyUI+Wan2.2+FFmpeg 离线成片完整方案(含代码 / 指令 / 排坑)
人工智能·ffmpeg·php·comfyui·ai漫剧
悟天特斯1 小时前
AI驱动的楼宇节能:从“经验省电“到“算法能效“的进化之路
人工智能·物联网