Skip to content

Chunking

Chunking 是 RAG 的核心技术之一。Chunking 的作用是把长文档切成大小合适、语义完整、可以检索、可以组合的小知识单元。

注意:Chunking 的结果会直接影响:

  • 检索准确率
  • 回答质量
  • Token 消耗速度
  • Agent 的推理速度
  • Memory 的管理能力

1. 为什么 Agent 需要 Chunking?

  1. 突破上下文窗口限制 大模型的单词输入长度是有上限的。一个长文档、长对话历史如果不进行分块,就会直接超出窗口上限。

  2. 提升检索与推理精度 将信息切割成语义完整的小块,可以让 RAG 更精准定位到关键内容。

  3. 控制成本与延迟 分块之后需要把相关的片段送入大模型,可以大幅减少 Token 消耗,降低生成延迟,让 Agent 反应更快。

2. Chunking 的本质是什么?

Chunking 不只是:每 500 个字符切一刀做个分割,而是找到一个信息密度和语义完整性的平衡点。

Chunking 在这个过程中,将长文变成小段,块之间会有重叠的部分用来连接上下文,并且在切分的过程中尽量保证语义的完整。

3. Chunking 的过程

上一节说了 Chunking 追求的是信息密度与语义完整的平衡,下面通过一个具体例子来看这种平衡是如何在切分过程中体现的。

假如我们有以下原文

Agent 需要分块的原因主要有三点。第一,大模型的上下文窗口有限,长文档直接输入会超出处理上限。第二,分块能让检索更精准,只把相关的片段送给模型。第三,分块可以控制 token 消耗,降低延迟和成本。因此,在 RAG 系统中,chunking 是索引前的关键步骤。

Chunking 之后(假设按固定大小 50 字,重叠 10 字来切)

Chunk 1

Agent 需要分块的原因主要有三点。第一,大模型的上下文窗口有限,长文档直接输入会超出处理上限。第二,分块能让检索更精准

Chunk 2

会超出处理上限。第二,分块能让检索更精准,只把相关的片段送给模型。第三,分块可以控制 token 消耗,降低延迟和成本。因此,在 RAG

Chunk 3

降低延迟和成本。因此,在 RAG 系统中,chunking 是索引前的关键步骤。

上面的内容展示了 Chunking 的实际过程:

  1. 将长文变成小段,每块都能单独喂给 Embedding 模型。
  2. 块之间存在重叠(也叫 Chunk Overlap),比如:“会超出处理上限。”在 Chunk 1 结尾和 Chunk 2 开头都出现了。这样是防止关键信息卡在被分割点导致语义割裂,保证检索时任何一个 Chunk 都能包含完整的上下文。
  3. 尽量保证语义完整:切分边界通常选择在句号、换行等自然断点,而不是将句子拦腰截断(示例简单用固定字数演示,实际会用更智能的 RecursiveCharacterTextSplitter)。

当然,固定大小切分只是一种最朴素的策略。不同的参数配置和切分方法会直接决定检索质量的上限。

4. Chunking 的核心参数

4.1 Chunk size

一个 Chunk 多大? 其单位有:

  • character
  • token
  • sentence
  • paragraph

例如: Chunk Size = 500 tokens

场景推荐大小
代码文档200~500 tokens
技术文章500~1000 tokens
法律合同1000~2000 tokens
聊天Memory50~200 tokens
论文500~1500 tokens

为什么 Chunk 不是越大越好呢?

因为 Chunk 越大会导致信息更多,噪声更多,以至于 Retrieval 精度下降。

5. 主流 Chunking 方法

5.1 Fixed-Size Chunking(固定大小切片)

最简单的策略:按固定字符数或 token 数来切,每块大小一致。

原文: "Agent 需要分块的原因主要有三点。第一,大模型的上下文窗口有限..."
固定 50 字切分 →
  Chunk 1: "Agent 需要分块的原因主要有三点。第一,大模型的上下文窗口有限..."
  Chunk 2: "...第二,分块能让检索更精准,只把相关的片段送给模型..."
  Chunk 3: "...第三,分块可以控制 token 消耗,降低延迟和成本..."
维度评价
实现难度极低,一行代码
语义完整性差,可能在句子中间截断
检索精度低,信息碎片化
适合场景日志分析、快速原型、结构均匀的文本

Fixed-Size 通常只作为 baseline。生产环境几乎不会单独使用。


5.2 Recursive Chunking(递归切片)

Fixed-Size 的问题在于"在哪儿切"完全不管语义。Recursive Chunking 的改进思路是:按优先级从高到低尝试不同的分隔符,先找段落边界,找不到再找句子边界,再找不到按空格切,最后才按字符硬切

分隔符优先级(中文场景):

1. "\n\n"       → 段落之间(最优先)
2. "\n"         → 换行
3. "。"         → 中文句号
4. "!" "?"    → 感叹号、问号
5. ";"         → 分号
6. ","         → 逗号
7. " "          → 空格
8. ""           → 逐字符(兜底)

工作流程:

  1. 尝试用最高优先级的分隔符切分文本
  2. 如果某个片段超过 Chunk Size,对该片段递归使用下一级分隔符再切
  3. 直到所有片段 ≤ Chunk Size,或到达最低优先级
flowchart TD
    A[输入文本] --> B{用 \n\n 切分}
    B -->|片段 ≤ chunk_size| C[保留为 Chunk]
    B -->|片段 > chunk_size| D{用 \n 切分}
    D -->|片段 ≤ chunk_size| C
    D -->|片段 > chunk_size| E{用 。切分}
    E -->|片段 ≤ chunk_size| C
    E -->|片段 > chunk_size| F[... 逐级下降 ...]
    F --> G[最终按字符数硬切]
    G --> C
维度评价
实现难度低,LangChain 等库内置
语义完整性较好,优先在自然边界切
检索精度中等偏上
适合场景最通用的默认选择,文章、文档、代码注释都适用

RecursiveCharacterTextSplitter(LangChain)是业界使用最广的实现。


5.3 Semantic Chunking(语义切片)

前两种方法只关心"边界字符",不关心"内容含义"。语义切片的核心思想是:用 Embedding 模型把句子变成向量,通过向量相似度判断哪些句子应该放在一起

工作流程:

  1. 将文本拆成句子
  2. 对每个句子做 Embedding,得到句向量
  3. 计算相邻句子的余弦相似度
  4. 在相似度骤降的位置切一刀 —— 那通常就是话题切换点
flowchart LR
    S1[句1: Camera2 架构介绍...] --> E1[向量1]
    S2[句2: CameraDevice 负责...] --> E2[向量2]
    S3[句3: OpenGL ES 是图形库...] --> E3[向量3]
    S4[句4: Shader 编程涉及...] --> E4[向量4]

    E1 ---|相似度 0.92| E2
    E2 ---|相似度 0.35 骤降| E3
    E3 ---|相似度 0.88| E4

示例对比:

txt
原文(两个不相关的话题混在一起):
  Android Camera2 介绍
  Camera2 API 架构...
  CameraDevice...
  ---------
  OpenGL ES 介绍
  Shader...
  Rendering Pipeline...
方法结果
固定 500 tokenCamera2 + OpenGL 混在同一个 Chunk
语义切片Chunk 1 全是 Camera2,Chunk 2 全是 OpenGL
维度评价
实现难度中,需 Embedding 模型
语义完整性最优
检索精度
成本较高(每句都要调 Embedding API)
适合场景高质量知识库、多主题混合文档

如果文档本身主题单一(比如一篇纯技术文章),语义切片的收益可能不明显,Recursive 就够了。


5.4 Document-Structure Chunking(文档结构切片 / Parent-Child)

利用文档自身的结构信息来切分——Markdown 的标题层级、代码文件的函数/类边界、HTML 的 DOM 树。

典型实现:Parent-Child(父子)模式

Parent Chunk(大块,保留完整上下文):
  ┌─────────────────────────────────────────┐
  │ ## Android 性能优化                       │
  │                                          │
  │ ### RecyclerView 优化                     │
  │ RecyclerView 是 Android 中最常用的列表组件... │
  │ 优化策略包括:ViewHolder 复用、              │
  │ setHasFixedSize、DiffUtil 差异计算...       │
  │                                          │
  │ ### Bitmap 优化                           │
  │ 图片加载是内存泄漏的重灾区...                 │
  │ 使用 RGB_565 替代 ARGB_8888 可节省一半内存... │
  └─────────────────────────────────────────┘

Child Chunks(小块,用于精确检索):
  ┌── RecyclerView 优化 ──┐  ┌── Bitmap 优化 ──┐
  │ RecyclerView 是...    │  │ 图片加载是...    │
  │ 优化策略包括...        │  │ 使用 RGB_565...  │
  └────────────────────────┘  └─────────────────┘

检索逻辑:

  1. 用户提问 → 检索到最匹配的 Child Chunk
  2. 返回该 Child 所属的 Parent Chunk(包含更完整的上下文)
  3. 将 Parent 喂给 LLM 生成回答

这就是 Small-to-Big Retrieval 的核心机制——用小粒度检索保证精度,用大粒度返回保证上下文完整。

维度评价
实现难度
语义完整性优秀(利用文档作者的结构意图)
检索精度高(小粒度检索 + 大粒度返回)
适合场景Markdown 文档、API 文档、代码库、合同

5.5 Agentic Chunking(智能 Agent 切片)

把切分决策交给 LLM 自己——它不是按规则切,而是"读一遍文档,然后决定哪里该切、为什么该切、每个 Chunk 应该包含什么上下文"。

工作流程:

  1. 将文档分段发给 LLM
  2. LLM 分析每段的主题、论点和结构
  3. LLM 输出切分方案:{ chunk_id: 1, content: "...", topic: "Camera2 架构" }
  4. 可以同时生成每个 Chunk 的摘要、关键词等元数据

与传统方法的本质区别:

传统 ChunkingAgentic Chunking
决策者规则/算法LLM
理解内容不理解理解语义和论点结构
切分依据字符数 / 分隔符 / 向量相似度主题、论点、逻辑边界
产出纯文本片段可附带摘要、关键词、标签
维度评价
实现难度
语义完整性最优,LLM 真正"理解"内容
成本高(每篇文档都需要 LLM 调用)
速度
适合场景高价值知识库、企业文档、需要极致检索质量的场景

选型建议: 大部分项目从 Recursive Chunking 起步就够了。只有当检索质量成为瓶颈时,再考虑升级到 SemanticAgentic


5.6 方法选型速查

方法实现难度语义质量成本适用场景
Fixed-Size极低免费Baseline / 日志
Recursive免费默认首选
Semantic很好多主题混合文档
Parent-Child很好免费结构化文档 / 代码
Agentic最优高价值知识库