在上一篇,我们费了九牛二虎之力,把公司各种 PDF、网页、代码,全部洗成了干净的纯文本(比如几十本十几万字的 Markdown 文档)。

你可能会想:“太好了!现在只要把这 10 万字的《员工手册》转成向量存进数据库,AI 就能回答问题了吧?”
绝对不行!

如果你把一整本 10 万字的书当成一个整体去算“语义相似度”,结果就是:它的语义变成了一锅大杂烩。当用户问“婚假怎么请?”时,系统根本无法从这“一锅粥”里精准捞出写着婚假的那一小段话。

为了让检索精准,我们必须把长文档**“切碎”**。这在 RAG(检索增强生成)工程中,被称为 Chunking(切分)
同时,为了切碎后还能找到出处,我们必须给每块碎肉贴上标签,这叫 Metadata(元数据)

本篇,我们就来攻克 RAG 系统里最考验内功的一环:如何切得刚刚好?


1. 切分(Chunking):为什么切,怎么切?

Chunk(数据块/片段)是 RAG 检索的最小单位。
你可以把一本书想象成一根长长的香肠,Chunk 就是你切下来的一片片香肠。当 AI 饿了(用户提问)时,检索系统只会挑最符合口味的 3~5 片香肠喂给它,而不是把整根香肠塞进去。

常见的切分策略(从初级到高级)

  1. 按固定字符数切分(Fixed-size Chunking)

    • 做法:简单粗暴,每 500 个字切一刀。
    • 坑点:极其危险!如果刚好在“因为张三没有 / 签字,所以报销被驳回”这句话中间切了一刀,前一个 Chunk 只剩“张三没有”,后一个 Chunk 只有“签字”,AI 看了直接懵逼。
  2. 按自然段落切分(Recursive Character Text Splitter)

    • 做法:优先按双换行符(\n\n,即段落)切;如果一个段落太长(超过 1000 字),再按单换行符(\n)切;如果还长,再按句号()切。
    • 优点:这是目前最常用的基线策略,能最大程度保证一句话或一个段落的完整性。
  3. 按文档结构切分(Markdown/HTML Header Splitter) —— (强烈推荐)

    • 做法:利用我们在上一步清洗出的 Markdown 标题结构来切。比如把 ## 婚假规定 下面的所有正文作为一个 Chunk,把 ## 产假规定 下面的作为另一个 Chunk。
    • 优点:极度精准!因为同一个标题下的内容,天然具有最强的语义连贯性。

关键技巧:重叠(Overlap)

就算按句号切分,有时也会遇到上下文断裂的问题。
比如:

  • Chunk 1: 张三是公司的财务总监。
  • Chunk 2: 他昨天审批了一笔 100 万的报销。
    如果系统只检索到了 Chunk 2,AI 根本不知道“他”是谁。

解决方案:在切分时设置 重叠区(Overlap)
假设每个 Chunk 长 500 字,我们让相邻的 Chunk 重叠 50 个字。
这样 Chunk 2 的开头就会带上 Chunk 1 的结尾,变成:...张三是公司的财务总监。他昨天审批了一...。有了重叠,上下文就接上了!


2. 元数据(Metadata):碎片的“寻亲记”

当你把一本《员工手册》切成了 500 个 Chunk,并且把它们扔进了由几十万个 Chunk 组成的向量数据库(Vector DB)后,这 500 个片段就像滴入大海的水滴,瞬间失去了身份。

如果这时候系统捞出了一个写着“批准报销上限为 500 元”的 Chunk,AI 会很绝望:

  • 这是哪本书里的规定?
  • 哪一年发布的?现在还生效吗?
  • 这是普通员工的规定,还是高管的规定?

为了回答这些问题,在切分 Chunk 的同时,我们必须给每一个 Chunk 贴上标签,这就是 元数据(Metadata)

RAG 必备的 5 类元数据标签

元数据字段 英文名 示例值 为什么必须要有?
文档来源 source_url https://wiki/hr/leave 引用的底气:AI 回答时必须附带来源链接,让用户自己去核实,这是解决幻觉的终极武器。
文档标题 doc_title 2024年员工休假制度 防止语义丢失:如果 Chunk 只有“休 3 天”,加上标题后,AI 就知道这是“2024年休假制度里的休 3 天”。
层级标题 headers ['第二章 福利', '2.1 婚假'] 精确定位:这是按 Markdown 结构切分的好处,能完整保留这块碎肉在原书中的目录位置。
更新时间 updated_at 2024-03-01 时效过滤:如果库里有 2023 和 2024 两版制度,检索时可以用时间标签直接把旧版过滤掉。
权限标签 access_level ['public', 'hr_only'] 安全红线:普通员工提问时,底层检索器会直接带上条件 WHERE access_level CONTAINS 'public',让越权数据根本无法被召回。

3. 切分与元数据组合的威力:前置过滤(Pre-filtering)

很多人以为 RAG 里的向量数据库只能做“语义模糊搜索”(查出长得像的句子)。
其实,“元数据过滤 + 向量检索” 才是工业级 RAG 的真正形态。

场景:2024年3月,普通员工王五提问:“我结婚能休几天假?”

糟糕的 RAG(只有向量,没有元数据)

  1. 向量库算出“结婚、休假”的向量。
  2. 捞出了 3 个 Chunk:
    • 2018年废弃的休假制度(3天)
    • 高管的特殊休假制度(10天)
    • HR 内部的处理 SOP
  3. AI 看了这三个矛盾的片段,开始胡言乱语。

优秀的 RAG(向量 + 元数据)

  1. 系统先根据提问者身份和时间,拼装出检索条件:
    WHERE access_level = 'public' AND updated_at >= '2024-01-01' (这叫 前置过滤 Pre-filtering)。
  2. 在过滤后剩下的安全、最新的数据池里,再去算“结婚、休假”的向量相似度。
  3. 完美捞出唯一正确的 Chunk,AI 准确回答:“根据《2024年员工休假制度》(附链接),您可以休 3 天。”

4. 本篇产出:切分策略指南(可复用)

搭建知识库时,不要盲目调参。请把这份策略指南保存下来,作为你们团队切分数据的默认基线配置:

# 知识库 Chunk 切分与元数据基线配置 v1.0

## 一、 文本切分参数 (Text Splitter)
- **切分算法**:优先使用 `MarkdownHeaderTextSplitter`(按 H1/H2/H3 切分)。
- **降级算法**:如果 Markdown 某一段过长,降级使用 `RecursiveCharacterTextSplitter`。
- **Chunk 长度 (Chunk Size)**:500 - 800 Tokens(太短缺上下文,太长塞不进 Prompt,800 是兼顾命中率和消耗的甜点位)。
- **重叠长度 (Overlap Size)**:100 - 150 Tokens(通常为 Chunk 长度的 15% - 20%,保证句子不被硬切断)。

## 二、 必须注入的 Metadata 结构
每个送入向量库的 Chunk 必须且仅包含以下 JSON 结构:
{
  "chunk_id": "uuid-xxx",
  "text": "具体的正文内容...",
  "metadata": {
    "doc_id": "doc-001",              // 溯源:属于哪份原始文档
    "source": "https://wiki/...",     // 引用:给用户看的点击链接
    "title": "2024版报销制度",        // 补充语义:大标题
    "headers": ["第二章", "交通费"],  // 补充语义:所在章节
    "updated_at": "2024-03-01",       // 过滤条件:时效性
    "permission": ["all_staff"]       // 过滤条件:可见权限(极其重要!)
  }
}

总结与复盘

  • 切分(Chunking) 解决了“AI 一口吃不下个胖子”且“容易找错重点”的问题。合理利用 Markdown 结构切分并保留重叠区(Overlap),是保证语义完整的关键。
  • 元数据(Metadata) 是数据碎片的“身份证”。没有权限标签、时间标签和来源链接的数据,在企业应用中就是一颗定时炸弹。
  • 记住工业级 RAG 的黄金公式:元数据精准过滤 + 向量模糊召回

下一步路线提示
现在,带有漂亮标签的碎肉(Chunk)已经整整齐齐地躺在向量数据库里了。当用户抛出一个问题时,系统到底是怎么把最匹配的 Chunk “捞”出来的?仅仅靠相似度就够了吗?下一篇,我们将揭开 RAG 的心脏:《检索:召回、重排、过滤与多路检索》。

Logo

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

更多推荐