RAGFlow 分块策略全景与 Book 策略深度解析

一、RAGFlow 分块策略概览

RAGFlow 一共有 15 种分块策略 ,定义在 common/constants.pyParserType 枚举中,通过 rag/svr/task_executor.pyFACTORY 字典分发到各自的实现模块。

整体架构

复制代码
┌─────────────────────────────────────────────────────────────────┐
│                     用户上传文档                                  │
│                          │                                      │
│                    parser_id 选择                                │
│                          │                                      │
│              ┌───────────┼───────────┐                          │
│              ▼           ▼           ▼                           │
│         ┌────────┐  ┌────────┐  ┌────────┐                     │
│         │ naive  │  │ paper  │  │ book   │  ...共15种            │
│         │(通用)   │  │(论文)  │  │(书籍)  │                      │
│         └───┬────┘  └────────┘  └────────┘                     │
│             │                                                    │
│       ┌─────┼──────────┐  (naive 的二级分发)                    │
│       ▼     ▼          ▼                                        │
│   deepdoc  mineru    docling  ... (PDF解析引擎)                  │
└─────────────────────────────────────────────────────────────────┘

15 种策略一览

# 策略 实现文件 适用文件类型 核心思路
1 naive(通用) rag/app/naive.py docx, pdf, excel, txt, md, html, json 等 用分隔符切分文本,再按 token 数上限合并。支持 overlap、子分隔符、嵌入文件提取。默认策略
2 manual(手册) rag/app/manual.py pdf, docx 针对 FAQ 型手册,识别层级标题结构,构建问答树
3 qa(问答) rag/app/qa.py xlsx, csv, txt, pdf, docx 提取问答对。Excel 第一列=问题,第二列=答案;PDF/Docx 用编号模式匹配
4 table(表格) rag/app/table.py xlsx, xls, txt, csv 每行一个 chunk,支持列头的语义扩展(同义词、枚举值)
5 paper(论文) rag/app/paper.py pdf 检测双栏布局,识别摘要/关键词,确保摘要完整保留
6 book(书籍) rag/app/book.py docx, pdf, txt, html 层级合并,检测冒号标题,去除目录页,适合长文档
7 laws(法律) rag/app/laws.py docx, pdf, txt 识别条款编号的层级结构(条、款),树形合并保留法律层级
8 resume(简历) rag/app/resume.py pdf, docx YOLOv10 布局检测 + LLM 并行提取(基本信息、工作经历、教育)
9 picture(图片) rag/app/picture.py 图片, 视频 OCR 提取文本 + VLM 生成语义描述。每张图/视频一个 chunk
10 one(整篇) rag/app/one.py docx, pdf, xlsx, txt 等 整个文档作为一个 chunk,不切分
11 audio(音频) rag/app/audio.py wav, mp3, aac 等 语音转文字(ASR),转写结果作为一个 chunk
12 email(邮件) rag/app/email.py eml 解析邮件头和正文,递归处理附件
13 tag(标签) rag/app/tag.py xlsx, csv, txt 两列格式:内容 + 标签,用于构建标签知识库
14 presentation(演示) rag/app/presentation.py pdf, pptx 每张幻灯片一个 chunk,按位置排序文本/表格/图片
15 knowledge_graph 委托给 naive 同 naive 实际的图谱抽取是后处理阶段(graphrag 任务类型)

数据流全貌

复制代码
文档上传
  │
  ▼
Redis 队列 (te.0.common / te.1.common)
  │
  ▼
Worker 取任务 (collect)
  │
  ▼
FACTORY[parser_id].chunk()  ──→  得到 chunk 列表
  │
  ▼
后处理增强:
  ├─ 自动关键词 (LLM生成)
  ├─ 自动问题   (LLM生成)
  ├─ 元数据提取 (LLM生成)
  └─ 标签匹配   (tag知识库)
  │
  ▼
Embedding 向量化
  │
  ▼
写入 ES / Infinity (insert_chunks)
  │
  ▼
更新文档统计 (chunk数, token消耗)

几个有意思的设计点

  1. naive 是万能底座 --- knowledge_graph 直接复用 naive,email 内部也调用 naive_chunk() 处理附件
  2. 二级分发 --- naive 内部还有 PARSERS 字典,根据 layout_recognizer 配置选择 PDF 解析引擎(deepdoc / mineru / docling / paddleocr 等)
  3. 领域特化程度不同 --- lawspaper 是结构化很强的领域策略;naive 则是通用瑞士军刀;one 几乎不做什么

二、Book 策略完整机制解析

第一步:解析文档,获取 sections

Book 支持多种文件格式,每种格式的解析路径不同:

复制代码
┌─────────────────────────────────────────────────────────────────────┐
│                        文档输入                                      │
│                                                                      │
│   .docx ──→ naive.Docx() ──→ [(text, image, html), ...]            │
│                                                                      │
│   .pdf  ──→ PARSERS[layout_recognizer] ──→ (sections, tables)       │
│             支持: deepdoc / mineru / docling / paddleocr 等          │
│                                                                      │
│   .txt  ──→ 按行切分 ──→ [(line, ""), ...]                          │
│                                                                      │
│   .html ──→ HtmlParser() ──→ [(line, ""), ...]                      │
│                                                                      │
│   .doc  ──→ tika 解析 ──→ [(line, ""), ...]                         │
└─────────────────────────────────────────────────────────────────────┘

每个 section 是一个 (text, layout_tag) 元组。对于 PDF,layout_tag 可能是 "title""text" 等(由布局分析引擎输出)。

第二步:预处理 --- 冒号标题提升

python 复制代码
make_colon_as_title(sections)   # book.py:163

遍历所有 section,如果一行文本以 : 结尾,且冒号后面有足够多的内容(≥32 字符),就把冒号前面的部分插入为一个 "title" 类型的 section。

例子:

复制代码
之前: [("绪论:本文主要研究...", "text")]
之后: [("绪论", "title"), ("本文主要研究...", "text")]

第三步:检测编号模式

python 复制代码
bull = bullets_category([t for t, _ in sections])  # book.py:164

bullets_category 把所有 section 的文本拿去匹配 5 种编号模式组:

复制代码
模式组 0 (中式法律):  第一编/第一章/第一节/第一条/(一)
模式组 1 (阿拉伯):   第1章/第1节/1./1.1/1.1.1/1.1.1.1
模式组 2 (混合):     第一章/第一节/一、/(一)/(1)
模式组 3 (英文):     PART ONE/Chapter I/Section 1/Article 1
模式组 4 (Markdown): #/##/###/####/##### /######

返回命中数最多的 模式组索引(0~4),如果都没有命中返回 -1

第四步:分块 --- 两条路径

复制代码
                    sections
                       │
                make_colon_as_title()  ← 冒号行提升为标题
                       │
                bullets_category()     ← 检测编号模式
                       │
               ┌───────┴───────┐
               ▼               ▼
         bull >= 0        bull < 0
         (有编号模式)      (无编号模式)
               │               │
     hierarchical_merge()    naive_merge()
     (层级树形合并)          (按 token 数顺序合并)
路径 A:有编号模式 → hierarchical_merge

depth=5 硬编码,核心算法分为两步:层级构建二次合并

层级构建:从叶节点向上找爹

算法首先把每个 section 按匹配到的编号正则分组:

复制代码
假设文档内容,检测到模式组 1(阿拉伯编号):
  BULLET_PATTERN[1] = ["第N章", "第N节", "N.", "N.N", "N.N.N", "N.N.N.N"]

输入 sections:
  ┌───┬──────────────────────┬───────┐
  │ i │ text                 │ level │  ← 匹配到的 BULLET_PATTERN 索引
  ├───┼──────────────────────┼───────┤
  │ 0 │ "第1章 绪论"          │   0   │  ← "第N章" = pattern[0]
  │ 1 │ "1.1 研究背景"        │   2   │  ← "N."     = pattern[2]
  │ 2 │ "背景详细内容..."      │   7   │  ← 正文(无匹配)
  │ 3 │ "1.2 研究目标"        │   2   │
  │ 4 │ "目标详细内容..."      │   7   │
  │ 5 │ "第2章 方法"          │   0   │  ← 新章
  │ 6 │ "2.1 实验设计"        │   2   │
  │ 7 │ "实验内容..."         │   7   │
  └───┴──────────────────────┴───────┘

levels 数组(按 level 分组索引):
  Level 0 ("第N章"):    [0, 5]
  Level 1 ("第N节"):    []
  Level 2 ("N."):       [1, 3, 6]
  Level 3~5:            []
  Level 6 (title布局):  []
  Level 7 (正文):       [2, 4, 7]

然后 levels = levels[::-1] 反转整个数组,从最底层(正文)开始遍历:

复制代码
反转后:
  [0] = Level 7 (正文):       [2, 4, 7]     ← 最底层
  [1] = Level 6 (title布局):  []
  [2] = Level 5 (N.N.N.N):    []
  [3] = Level 4 (N.N.N):      []
  [4] = Level 3 (N.N):        []
  [5] = Level 2 (N.):         [1, 3, 6]
  [6] = Level 1 (第N节):      []
  [7] = Level 0 (第N章):      [0, 5]         ← 最顶层

算法从每个叶节点(正文段落)出发,通过二分搜索在上级 levels 数组中找到最近的祖先标题,构建层级链:

复制代码
  j=2 (section "背景内容...")
    → 从 Level 2 找到 1.1 (index=1)
    → 从 Level 0 找到 第1章 (index=0)
    → 链: [第1章 绪论, 1.1 研究背景, 背景内容...]

  j=4 (section "目标内容...")
    → 从 Level 2 找到 1.2 (index=3)
    → 从 Level 0 找到 第1章 (index=0)
    → 链: [第1章 绪论, 1.2 研究目标, 目标内容...]

  j=7 (section "实验内容...")
    → 从 Level 2 找到 2.1 (index=6)
    → 从 Level 0 找到 第2章 (index=5)
    → 链: [第2章 方法, 2.1 实验设计, 实验内容...]
二次合并:token 上限 218

构建完层级链后,还有一个二次合并步骤。如果单条链(通常是纯正文段落)的 token 数 < 218,会被拼到前一个 chunk 里,可能跨越标题边界。

路径 B:无编号模式 → naive_merge

退化为顺序拼接,不考虑任何标题结构,纯粹按 chunk_token_num 配置的 token 数切分。


三、Book 策略中"标题"的真实角色

核心结论:标题是上下文,不是边界

Book 策略确实检测标题、使用标题 ,但标题在其中的角色是补充上下文 ,不是切分边界

复制代码
                    理想中的"按标题分块"

  ┌─ 第1章 绪论 ──────────┐   ← 在这里切开
  │  1.1 背景              │
  │  背景内容...            │
  │  1.2 目标              │
  │  目标内容...            │
  └────────────────────────┘   ← 在这里切开
  ┌─ 第2章 方法 ──────────┐
  │  2.1 实验设计           │
  │  实验内容...            │
  └────────────────────────┘


                    Book 实际的分块

  ┌─ [第1章 绪论 · 1.1 研究背景 · 背景内容...] ──┐  ← 以叶节点为单位
  └───────────────────────────────────────────────┘
  ┌─ [第1章 绪论 · 1.2 研究目标 · 目标内容...] ──┐  ← 同一章被拆成多个 chunk
  └───────────────────────────────────────────────┘
  ┌─ [第2章 方法 · 2.1 实验设计 · 实验内容...] ──┐
  └───────────────────────────────────────────────┘
维度 按标题分块 Book 实际行为
分块单位 一级标题下的所有内容 每个叶节点段落
标题的角色 切分锚点(边界) 附加上下文(前缀)
同一章的内容 在同一个 chunk 里 被拆成多个 chunk
chunk 粒度 粗(章级别) 细(段落级别)

Book 的 hierarchical_merge 做的是:给每个正文段落挂上它的祖先标题链 作为上下文,但切分点不在标题处,而在每个正文段落处。


四、Book 策略 vs 直接按段落切分的本质差异

具体对比

假设一份文档:

复制代码
第1章 绪论
  1.1 研究背景
    深度学习在自然语言处理领域取得了显著进展。
  1.2 研究目标
    本文旨在提出一种新的注意力机制。
第2章 相关工作
  2.1 Transformer架构
    Vaswani等人提出的Transformer架构改变了NLP的范式。
  2.2 预训练模型
    BERT通过双向编码器实现了突破性效果。
直接按段落切(naive 的做法)
Chunk # 内容
1 深度学习在自然语言处理领域取得了显著进展。
2 本文旨在提出一种新的注意力机制。
3 Vaswani等人提出的Transformer架构改变了NLP的范式。
4 BERT通过双向编码器实现了突破性效果。
Book 的做法(段落 + 祖先标题链)
Chunk # 内容
1 第1章 绪论 · 1.1 研究背景 · 深度学习在自然语言处理领域取得了显著进展。
2 第1章 绪论 · 1.2 研究目标 · 本文旨在提出一种新的注意力机制。
3 第2章 相关工作 · 2.1 Transformer架构 · Vaswani等人提出的Transformer架构改变了NLP的范式。
4 第2章 相关工作 · 2.2 预训练模型 · BERT通过双向编码器实现了突破性效果。

差异体现在 RAG 检索环节

RAG 的流程是:用户提问 → 检索相关 chunk → 喂给 LLM 生成回答。差异就在检索环节。

场景 1:用户问 "绪论部分的研究背景是什么?"

直接按段落切 --- 检索命中的 chunk:

复制代码
❌ "深度学习在自然语言处理领域取得了显著进展。"

这个 chunk 里**没有"绪论"、没有"研究背景"**这些关键词。向量检索和全文检索都会困难------因为"深度学习"、"自然语言处理"跟用户问题的语义距离很远。

Book 的做法 --- 检索命中的 chunk:

复制代码
✅ "第1章 绪论 · 1.1 研究背景 · 深度学习在自然语言处理领域取得了显著进展。"

这个 chunk 里包含"绪论"、"研究背景",跟用户问题高度匹配。检索命中率大幅提升。

场景 2:用户问 "Transformer 相关的工作在哪一章?"

直接按段落切

复制代码
❌ "Vaswani等人提出的Transformer架构改变了NLP的范式。"

没有章节信息,LLM 拿到这个 chunk 后不知道它属于哪一章,无法回答"在哪一章"。

Book 的做法

复制代码
✅ "第2章 相关工作 · 2.1 Transformer架构 · Vaswani等人提出的..."

LLM 能看到 "第2章 相关工作",能直接回答

总结

复制代码
┌─────────────────────────────────────────────────────┐
│                                                      │
│   直接按段落切:每个 chunk 只有 "WHAT"(内容)        │
│                                                      │
│   Book 策略:   每个 chunk 有 "WHERE" + "WHAT"       │
│                 (位置上下文 + 内容)                  │
│                                                      │
│   标题链的作用:给每个段落 chunk 带上它所在的          │
│   "章节路径",解决检索时 "这段话属于哪、               │
│   在什么上下文中" 的问题                              │
│                                                      │
└─────────────────────────────────────────────────────┘

Book 策略的核心价值不是"怎么切",而是"切完之后每个 chunk 携带什么上下文"。 它把层级标题作为前缀注入每个 chunk,提升检索命中率。


五、Paper 策略与 Book 策略的对比

Paper 策略同样利用标题信息,但机制不同:

维度 Paper Book
标题检测 依赖 PDF 布局分析(layoutno 含 "title")+ BULLET_PATTERN 同左 + 额外的 make_colon_as_title(冒号行提升为标题)
分块依据 "出现频率最高的标题级别"作为切分锚点 完整的层级树构建,自底向上合并
层级深度 扁平------只看 most_level 这一层 depth=5,最多支持 5 级嵌套
无标题退化 不退化,强制用 title_frequency 退化到 naive_merge(纯按 token 数)
特殊处理 摘要独占 chunk;双栏布局检测 目录页移除(remove_contents_table

六、关键源码索引

模块 文件路径
Book 策略入口 rag/app/book.py
Paper 策略入口 rag/app/paper.py
Laws 策略入口 rag/app/laws.py
编号模式定义 BULLET_PATTERN rag/nlp/__init__.py:169-201
编号模式检测 bullets_category rag/nlp/__init__.py:216-233
层级合并 hierarchical_merge rag/nlp/__init__.py:980-1067
树形合并 tree_merge rag/nlp/__init__.py:931-978
标题频率 title_frequency rag/nlp/__init__.py:901-920
冒号标题提升 make_colon_as_title rag/nlp/__init__.py:879-898
顺序合并 naive_merge rag/nlp/__init__.py:1070-1126
策略枚举 ParserType common/constants.py:106-122
工厂分发 FACTORY rag/svr/task_executor.py:114-131
相关推荐
满怀冰雪2 小时前
第05篇-滑动窗口算法-一套模板解决子串与子数组问题
java·算法
码云骑士2 小时前
【3.1Java基础】Java运算符常见错误排查:10个高频编译运行错误一网打尽
java·开发语言
阿里嘎多学长2 小时前
2026-06-09 GitHub 热点项目精选
开发语言·程序员·github·代码托管
枫叶丹42 小时前
【HarmonyOS 6.0】MDM Kit 新增限制策略深度解析:短信、蜂窝数据、飞行模式、通知消息与 NFC 管控
开发语言·华为·harmonyos
吴声子夜歌2 小时前
JVM——线程池实现原理
java·jvm·线程池
雾沉川2 小时前
IntelliJ IDEA 2025.2 安装与基础配置技术教程
java·ide·intellij-idea
AC赳赳老秦2 小时前
技术文章素材收集自动化:用 OpenClaw 自动爬取行业资讯、技术热点、优质文章
运维·开发语言·python·自动化·wpf·deepseek·openclaw
辰海Coding2 小时前
MiniSpring框架学习笔记-JDBC 访问框架:如何抽取 JDBC 模板并隔离数据库?
java·数据库·笔记·学习·spring
CodeStats2 小时前
从 CPU 指令执行到权限管控:对比三大操作系统,梳理编程语言演进,解读 HTML/CSS/JS 浏览器解析的共通底层逻辑
java·linux·windows