RAG数据分块策略:高效实践与性能对比 2026-06-07阅读 0热度 0 Chunk 为什么不能把整份知识库直接交给大模型? 先说说最直接的想法。假设你是一个在线教育平台,正在做智能教务助手。平台里有一份 200 页的《课程服务知识库》,里面满满当当都是课程预约规则、调课政策、课时权益、发片流程这些东西。学员问一句“预约的课程开课前多久可以取消”,系统就得从这份知识库里找到最相关的内容,然后给出准确回答。 最直观的思路是什么?把整份知识库的文本,一次性塞到大模型的上下文里,让它自己看着办。 这个思路在小规模演示里似乎能走通,但放到真实系统中,会遇到两个硬伤。 1. 上下文窗口限制 大模型处理文本时,有一个上下文窗口的限制。可以把它想象成模型一次能“看到”的工作台大小:你塞进去的文本、检索出来的内容、系统提示词、用户的问题,还有模型输出的答案,全都要挤在这个工作台上。 现在主流的模型,上下文窗口从 128k token 到 1M token 不等。Token 可以粗略理解成模型处理文本的基本单位,中文场景下,一个汉字或者一个词片段,差不多就是一个 token。128k token 听起来挺多,但一份 200 页的课程服务知识库,纯文本量很容易就几十万字了。如果里面还夹杂着表格、FAQ、历史版本说明,直接一股脑塞进去,不仅可能超出长度限制,成本和延迟也会让人头疼。 所以,直接整篇输入的结果通常包括:文本被截断、单次请求成本飙升、响应时间变长、并发吞吐能力下降。即使未来模型的上下文窗口继续扩大,这个问题也不会彻底消失。上下文越长,模型要处理的海量信息就越多,实际业务系统还是得在成本、延迟、稳定性和回答质量之间做取舍。 2. 检索精度问题:信息越多,噪声也越多 假设模型拥有无限大的上下文窗口,直接提供整份知识库就行了吗?也不行,因为会带来检索精度问题。 比如学员问:“体验课结束后还能改约正式课吗?”如果系统把几十万字的课程规则、合同条款、教师排班、售后流程一股脑全丢给模型,模型就得在浩如烟海的信息里找出最相关的几段。上下文越长,噪声越多,模型越容易把不相关的条款混进回答里,或者干脆忽略了真正关键的限制条件。 这正是 RAG 架构依然重要的原因。RAG 的核心做法,不是把所有文本直接交给模型,而是先检索出与问题最相关的几个文本片段,再把它们作为上下文提供给模型。这样一来,模型拿到的是更干净、更聚焦的证据,回答质量和稳定性都会好很多。 但要做检索,首先得把文档拆成可以被检索的单元。把整份知识库作为一个检索单元,粒度太粗了,既不便于召回,也不利于排序。系统需要把长文本切成若干相对完整的小段,每个小段围绕一个知识点或业务规则展开。 这就是分块(Chunking)的作用。 分块到底在做什么? 1. 分块在 RAG 流程中的位置 在数据准备阶段,通常会先用 Apache Tika 这类工具,从 PDF、Word、HTML 文件里提取纯文本。但纯文本还不能直接进向量数据库,因为它太长了,结构也复杂,不同段落之间的语义边界不一定清晰。 一个典型的数据准备流程是:原始文档 → 文本抽取(如 Apache Tika)→ 文本清洗与结构修复 → 分块(Chunking)→ 向量化(Embedding)→ 写入向量数据库 → 检索增强生成(RAG)。分块之后,下一步通常是向量化。系统会把每个文本块转换成向量,后续根据用户问题与文本块向量之间的相似度进行检索。 所以说,分块的质量会直接影响检索质量。块切得合理,检索结果就更聚焦;块切得太大,会引入噪声;块切得太小,又会丢失上下文。一旦分块质量出了问题,后续再怎么调整向量模型、召回数量或者重排策略,都很难弥补回来。 2. 两个关键参数:chunk_size 与 overlap 在设计分块策略之前,得先理解两个核心参数: * `chunk_size`:每个文本块的长度上限。 * `overlap`:相邻两个文本块之间保留的重叠长度。 为了统一命名风格,下文代码示例将常见的 camelCase 写法改成了 snake_case,比如 `chunkSize` 改为 `chunk_size`。虽然 Ja va 社区通常用 camelCase,但这里调整一下命名,不影响逻辑理解。 2.1 如何理解 chunk_size? `chunk_size` 表示每个块的最大长度。比如设置 `chunk_size = 200`,意味着每个块最多包含 200 个字符,或者最多 200 个 token,具体要看系统用什么计量单位。 `chunk_size` 没有固定标准,但需要理解它背后的权衡: | 设置情况 | 影响 | | --- | --- | | 块太大,例如 2000 字 | 单个块包含更多信息,但检索结果容易混入不相关内容,精度下降。比如学员只问“开课前多久能取消预约”,结果召回了一整章“预约、调课、退款、发片”的说明,模型还得再从中筛选一遍。 | | 块太小,例如 50 字 | 单个块更精准,但容易把完整规则切断。比如“开课前 24 小时可免费取消”被拆成两段,单独看哪一段都不完整。 | 在许多中文知识库问答场景中,`chunk_size` 可以先从 200 到 1000 个字符之间试起。课程预约、退课规则、FAQ 这类问答型知识,适合偏小一点的块;而课程体系介绍、长篇说明、教学方案摘要等场景,可以适当增大块的大小。 2.2 overlap 是什么,为什么需要它? `overlap` 指的是相邻两个块共享的文本长度。它的作用是缓解边界切割带来的语义断裂。 举个例子,课程服务知识库里有这么一段规则: > 直播课程开课前 24 小时以上,学员可以在学习中心自助取消预约,系统会自动返还对应课时。开课前 24 小时以内取消,需联系班主任人工处理;如教师已完成课前准备,该课时可能不予返还,具体以课程协议为准。 如果 `chunk_size = 40` 且不设置 `overlap`,可能会切成这样: * 块 1:直播课程开课前 24 小时以上,学员可以在学习中心自助取消 * 块 2:预约,系统会自动返还对应课时。开课前 24 小时以内取消,需联系 * 块 3:班主任人工处理;如教师已完成课前准备,该课时可能不予返还 * 块 4:,具体以课程协议为准。 看,关键信息“取消预约”“返还课时”被切得支离破碎。用户搜索“开课前多久取消预约可以返还课时”时,任何一个块都可能信息不完整。 如果设置 `overlap = 15`,相邻块之间保留一部分重复内容,切分效果会好不少: * 块 1:直播课程开课前 24 小时以上,学员可以在学习中心自助取消 * 块 2:心自助取消预约,系统会自动返还对应课时。开课前 24 小时以内 * 块 3:前 24 小时以内取消,需联系班主任人工处理;如教师已完成 * 块 4:师已完成课前准备,该课时可能不予返还,具体以课程协议为准。 有了重叠,边界处的关键信息更容易在某个块里完整出现。 但 `overlap` 也不是越大越好。重叠越大,重复文本就越多,向量化成本、存储成本和检索计算成本都会跟着涨。常见的经验是,把 `overlap` 设为 `chunk_size` 的 10% 到 25%。 2.3 用字符还是 token 作为单位? `chunk_size` 可以按字符算,也可以按 token 算。这两者是不等价的。 字符是人眼看到的符号数量,比如“预约课程”是 4 个字符,而 `Hello World` 是 11 个字符(包含空格)。Token 则是模型处理文本时的基本单位。在英文里,一个常见单词通常接近 1 个 token,长一点的单词可能会拆成多个 token;在中文里,一个汉字可能对应 1 个或多个 token,具体要看模型的分词器。 | 文本 | 字符数 | 大约 token 数 | | --- | --- | --- | | 取消课程预约 | 6 | 6 ~ 10 | | Hello World | 11 | 2 | | 课时返还规则 | 6 | 6 ~ 10 | 大模型的上下文窗口是按 token 算的,所以基于字符的 `chunk_size` 只是一个近似控制。对于入门阶段或简单系统,按字符分块通常够用了;但当系统对成本、上下文长度和多语言兼容性要求更高时,更推荐使用基于 token 的分块方式。 主流分块策略详解 下面用同一段在线教育课程服务知识库示例,来说明几类常见分块策略。 > 一、课程预约规则 > 学员可在学习中心预约直播课程。普通课程最早可提前 14 天预约,最晚需在开课前 2 小时完成预约。热门班课名额有限,系统会按照预约时间顺序锁定席位。预约成功后,学员会收到信息、站内信和学习中心提醒。 > > 二、取消与改约规则 > 直播课程开课前 24 小时以上,学员可以自助取消预约,系统会自动返还对应课时。开课前 24 小时以内取消,需联系班主任人工处理;如教师已完成课前准备,该课时可能不予返还。因平台故障或教师原因导致课程无法正常进行的,课时将原路返还。 > > 三、学员权益 > 基础学员每月可预约 4 次答疑课。进阶学员每月可预约 8 次答疑课,并享受作业优先批改服务。尊享学员每月可预约 12 次答疑课,享受专属学习顾问、阶段测评报告和学习路径调整建议。 > > 四、上课通知与直播安排 > 课程默认使用平台直播教室。开课前 30 分钟,系统会自动生成直播入口,并通过站内信和信息提醒学员。若课程需要使用第三方会议工具,班主任会在开课前至少 2 小时发送会议链接和注意事项。 > > 五、教务支持与发片服务 > 学员可在学习中心提交教务工单,咨询课程安排、课时记录、发片开具和学习资料下载等问题。工作日 9:00 至 21:00 提交的工单通常会在 4 小时内处理;非工作时间提交的工单会在下一个工作日优先处理。 这段文本包含 5 个相对独立的章节,每个章节对应一个业务规则。下面就对不同分块策略分别说明。 1. 固定大小分块(Fixed Size Chunking) 1.1 原理 固定大小分块是最直接的方式:不理解文本结构,也不识别段落或句子边界,只按固定字符数切割。 假设 `chunk_size = 100`、`overlap = 0`,系统就会这样切: * 块 1:从第 1 个字符开始,取 100 个字符 * 块 2:从第 101 个字符开始,再取 100 个字符 * 块 3:从第 201 个字符开始,再取 100 个字符 * …… 这种方式实现简单、性能稳定,但很容易把一句话、一个条款甚至一个关键词从中间切断。对于课程预约知识库这种有明确章节和规则边界的文本,固定大小分块通常只能作为兜底方案。 1.2 Ja va 代码实现 ```ja va import ja va.util.ArrayList; import ja va.util.List; public class fixed_size_chunker { /** * 固定大小分块:按字符数量进行硬切分。 */ public static List split_text(String source_text, int chunk_size) { List chunk_list = new ArrayList<>(); int start_index = 0; while (start_index < source_text.length()) { int end_index = Math.min(start_index + chunk_size, source_text.length()); chunk_list.add(source_text.substring(start_index, end_index)); start_index = end_index; } return chunk_list; } public static void main(String[] arguments) { String source_text = "直播课程开课前 24 小时以上,学员可以在学习中心自助取消预约," + "系统会自动返还对应课时。开课前 24 小时以内取消,需联系班主任人工处理;" + "如教师已完成课前准备,该课时可能不予返还,具体以课程协议为准。"; List chunk_list = split_text(source_text, 40); for (int index = 0; index < chunk_list.size(); index++) { System.out.println("=== 块 " + (index + 1) + " ==="); System.out.println(chunk_list.get(index)); System.out.println(); } } } ``` 这段代码会严格按照 40 个字符来切分。切割位置不考虑句子边界,所以部分块的开头或结尾读起来可能不完整。 1.3 优缺点 | 维度 | 说明 | | --- | --- | | 优点 | 实现简单,性能好,不依赖 NLP 处理或额外模型。 | | 缺点 | 完全忽略文本结构,容易切断句子、段落和规则条件,导致语义不完整。 | | 适合 | 日志、纯数据文本、格式混乱但无需精细语义边界的内容;也可作为其他策略失败时的兜底方案。 | | 不适合 | 课程规则、协议条款、操作手册、FAQ 等结构清晰且语义边界重要的知识库。 | 2. 重叠分块(Overlapping Chunking) 2.1 原理 重叠分块是在固定大小分块基础上的改进。核心思想是:相邻块之间保留一段重复文本,降低边界切割造成的信息丢失。 假设 `chunk_size = 100`、`overlap = 25`: * 块 1:从第 1 个字符开始,取 100 个字符 * 块 2:从第 76 个字符开始,取 100 个字符 * 块 3:从第 151 个字符开始,取 100 个字符 * …… 每次向前移动的步长不是 100,而是 `chunk_size - overlap`,也就是 75。这样相邻块之间就会共享 25 个字符。 与固定大小分块相比,重叠分块虽然仍然不理解语义,但可以显著缓解边界断裂问题。 2.2 Ja va 代码实现 ```ja va import ja va.util.ArrayList; import ja va.util.List; public class overlapping_chunker { /** * 重叠分块:在固定大小分块基础上,为相邻块保留重叠区域。 */ public static List split_text(String source_text, int chunk_size, int overlap) { if (overlap >= chunk_size) { throw new IllegalArgumentException("overlap 必须小于 chunk_size"); } List chunk_list = new ArrayList<>(); int step_size = chunk_size - overlap; int start_index = 0; while (start_index < source_text.length()) { int end_index = Math.min(start_index + chunk_size, source_text.length()); chunk_list.add(source_text.substring(start_index, end_index)); start_index += step_size; } return chunk_list; } public static void main(String[] arguments) { String source_text = "直播课程开课前 24 小时以上,学员可以在学习中心自助取消预约," + "系统会自动返还对应课时。开课前 24 小时以内取消,需联系班主任人工处理;" + "如教师已完成课前准备,该课时可能不予返还,具体以课程协议为准。"; List chunk_list = split_text(source_text, 40, 10); for (int index = 0; index < chunk_list.size(); index++) { System.out.println("=== 块 " + (index + 1) + " ==="); System.out.println(chunk_list.get(index)); System.out.println(); } } } ``` 这段代码的关键是 `start_index += step_size`。当 `step_size = chunk_size - overlap` 时,下一块会从上一块结束位置之前开始,从而形成重叠。 2.3 优缺点 | 维度 | 说明 | | --- | --- | | 优点 | 实现简单,能够有效缓解边界处的语义断裂。 | | 缺点 | 仍然不识别文本结构;重叠区域会带来重复存储和额外向量化成本。 | | 适合 | 通用知识库的入门方案,尤其适合结构不稳定、段落边界不可靠的文本。 | | 不适合 | 对条款完整性要求极高的内容,例如课程合同、合规声明或收费协议。 | 3. 递归分块(Recursive Chunking) 3.1 原理 递归分块是实际 RAG 项目中非常常用的策略。它的核心思路是:先尝试用更大的语义边界切分,如果切出来的块仍然超过 `chunk_size`,再使用更细的边界继续切分。 常见的分隔符优先级如下: ``` ["\n\n", "\n", "。", ",", " ", ""] ``` 切分过程可以概括为: 先按空行切分章节 → 章节仍然过长,则按换行切 → 片段仍然过长,则按句号切 → 句子仍然过长,则按逗号或空格切 → 最后才按字符兜底切分。 这种方式的优势在于,它尽量尊重文档结构。能按章节切,就不按句子切;能按句子切,就不按字符硬切。 拿课程预约知识库来说,如果 `chunk_size = 200`,递归分块通常会先按章节之间的空行切分。“取消与改约规则”这一节如果没有超过 200 字,就会作为一个完整的块保留;如果超过了上限,再继续按句号拆分。 3.2 优缺点 | 维度 | 说明 | | --- | --- | | 优点 | 兼顾语义完整性和块大小控制,是多数文本型 RAG 系统的稳妥默认选择。 | | 缺点 | 依赖文本中存在合理分隔符;分隔符列表需要根据语言和文档格式调整。 | | 适合 | 课程知识库、操作手册、帮助中心、教学说明、政策类文档等结构清晰的文本。 | | 不适合 | 代码文件、复杂表格、扫描 OCR 结果、条款跨页严重的 PDF 等需要专门解析的内容。 | 4. 语义分块(Semantic Chunking) 4.1 原理 固定大小分块、重叠分块和递归分块都属于规则驱动方法。它们主要依赖长度、标点和段落结构,并不真正理解文本内容。 语义分块则使用 Embedding 模型判断文本含义,在语义发生明显变化的位置切分。典型流程如下: 1. 将长文本切成句子或短段 2. 为每个句子生成 Embedding 向量 3. 计算相邻句子之间的向量相似度 4. 当相似度低于阈值时,判断为主题切换点 5. 在主题切换点进行分块 举个例子,在课程服务知识库中,“取消与改约规则”内部的几句话都围绕课程取消、课时返还和人工处理展开,相邻句子的向量相似度通常比较高。当文本从“取消与改约规则”切换到“学员权益”时,话题发生了明显变化,相似度会下降,这时候就适合作为分块边界。 语义分块的好处是文本块内部主题更加内聚,不容易把多个不相关主题混在一个块里。但它需要额外调用 Embedding 模型,或者使用大模型辅助判断切分位置,因此成本和延迟都更高。 4.2 Embedding 语义分块与 LLM 辅助分块对比 | 维度 | Embedding 语义分块 | LLM 辅助分块 | | --- | --- | --- | | 原理 | 计算相邻句子或段落的向量相似度。 | 让大模型直接理解文本并判断切分位置。 | | 分块质量 | 依赖 Embedding 模型质量和阈值设置。 | 通常更能处理复杂语境、隐含主题和跨段关系。 | | 速度 | 较快,适合批量处理。 | 较慢,通常以秒级计。 | | 成本 | 较低。 | 较高。 | | 适用场景 | 大批量文档处理、通用知识库优化。 | 高价值文档、复杂条款、需要人工级别边界判断的内容。 | 4.3 优缺点 | 维度 | 说明 | | --- | --- | | 优点 | 切分依据更接近真实语义,文本块主题内聚性更强,检索精度通常更高。 | | 缺点 | 需要额外模型调用;阈值需要调参;对模型质量和文本预处理质量有依赖。 | | 适合 | 合同条款、收费协议、课程服务协议、合规文档、高价值知识库。 | | 不适合 | 文档量特别大且对处理延迟敏感的场景;或者文本结构已经非常清晰、递归分块足够有效的场景。 | 5. 混合分块(Hybrid Chunking) 5.1 原理 真实项目中,单一策略往往无法覆盖所有文档。在线教育平台的知识库可能同时包含课程说明、FAQ、直播操作指南、合同条款、发片规则、教师排班说明和客服工单模板。不同内容适合不同的切分方式。 混合分块的思路,就是根据文档类型、文本结构和业务用途选择不同策略,必要时进行后处理。 常见组合方式包括: #### 方式一:递归分块 + 语义分块 先用递归分块按章节和段落粗切,再对过长或主题复杂的块使用语义分块细切。这样可以兼顾速度和分块质量。 ``` 长文档 ↓ 按章节/段落递归粗切 ↓ 识别过长或主题复杂的片段 ↓ 使用 Embedding 或 LLM 进行语义细切 ↓ 输出最终 chunk ``` #### 方式二:按文档类型路由 根据文档类型选择分块器: | 文档类型 | 推荐策略 | | --- | --- | | 课程帮助中心 | 递归分块 | | FAQ 问答对 | 按问答对切分 | | 课程服务协议 | 语义分块 | | 直播故障日志 | 按行切分或固定大小分块 | | HTML 帮助页面 | 先清洗标签,再递归分块 | | OCR 扫描文本 | 清洗修复后使用重叠分块或语义分块 | #### 方式三:分块后处理 先用基础策略生成初始块,再补一轮质量修正: * 合并过短块 * 拆分过长块 * 为每个块补充元数据,比如课程类型、章节标题、文档来源、版本号、生效日期 * 对相邻块做语义检查,避免一个规则被拆散 * 对 FAQ 保持问答成对,不让问题和答案分离 以在线教育平台为例,一个可落地的混合方案可以是: | 内容类型 | 策略 | | --- | --- | | 课程预约、取消、改约等规则正文 | 递归分块,优先按章节和条款切分。 | | FAQ 问答 | 每个 Q&A 作为一个块,必要时补充所属课程和适用范围。 | | 课程详情页 | 提取结构化字段(价格、课时、适用年级),描述文本再用重叠分块。 | | 课程协议和退款条款 | 语义分块或人工复核后的混合分块。 | | 教务工单模板 | 按模板类型或业务流程节点切分。 | 5.2 优缺点 | 维度 | 说明 | | --- | --- | | 优点 | 灵活度高,可针对不同内容选择最合适的策略,整体检索效果通常最好。 | | 缺点 | 实现复杂度较高,需要维护多套分块逻辑、路由规则和质量评估机制。 | | 适合 | 企业级 RAG 系统、多类型知识库、对回答准确率和可解释性要求较高的场景。 | | 不适合 | 简单 demo、POC 或文档量很小的试验项目。 | 使用可视化工具对比分块效果 光靠文字描述还不够直观。实践中可以使用 ChunkViz 这类可视化工具,观察不同分块策略对同一段文本的切割效果。 推荐做几组对比实验: | 实验 | 策略 | chunk_size | overlap | 观察重点 | | --- | --- | --- | --- | --- | | 实验 1 | CharacterTextSplitter | 100 | 0 | 观察边界是否频繁切在句子中间。 | | 实验 2 | CharacterTextSplitter | 100 | 20 | 观察重叠区域是否缓解边界断裂。 | | 实验 3 | RecursiveCharacterTextSplitter | 100 | 0 | 对比固定大小分块,观察递归策略是否更倾向于在段落或句子边界切分。 | | 实验 4 | RecursiveCharacterTextSplitter | 200 | 0 | 增大块大小,观察块数量和语义完整性的变化。 | 在课程规则类文本中,通常可以观察到: * 固定大小分块容易切断规则条件 * 加入 overlap 后,边界信息更容易被保留 * 递归分块更倾向于保留章节或句子完整性 * 当 `chunk_size` 过大时,召回内容可能包含多个主题 * 当 `chunk_size` 过小时,规则上下文可能不完整 CharacterTextSplitter 示例观察 配置如下: ``` Chunk Size: 100 Chunk Overlap: 20 ``` 这种配置可以缓解部分边界问题,但仍然不理解课程规则本身。如果一个取消政策包含多个条件,比如“开课前 24 小时以上”“开课前 24 小时以内”“因平台原因取消”,固定字符分割仍然可能把条件和处理结果拆开。 RecursiveCharacterTextSplitter 示例观察 配置如下: ``` Chunk Size: 200 Chunk Overlap: 0 ``` 对于标题清晰、段落稳定的课程知识库,递归字符分割通常能得到比较好的结果。它会尽量按章节、段落或句子边界切分,减少语义断裂。 不过,当一个章节内部包含多个互相依赖的业务条件时,递归分块仍可能不够精细。比如“取消与改约规则”中同时包含自助取消、人工处理、平台责任和课时返还,简单按句子切分可能会把条件和例外说明分开。这时可以考虑增加 overlap,或者对关键章节使用语义分块与人工复核。 分块策略怎么选? 1. 不同文档类型的推荐策略 | 文档类型 | 推荐策略 | 理由 | | --- | --- | --- | | 课程帮助中心 / 教务知识库 | 递归分块 | 通常有清晰标题、段落和条款结构,递归策略能利用这些边界。 | | FAQ / 问答对 | 按问答对切分,必要时结合递归分块 | 每个 Q&A 是天然知识单元,不应该把问题和答案拆开。 | | 课程协议 / 退款条款 | 语义分块 | 条款边界和例外条件需要精确识别,规则分块容易切错。 | | 直播故障日志 | 固定大小分块或按行切分 | 日志通常按记录组织,结构简单。 | | 代码文件 | 专用代码分块器,按类、函数或方法切分 | 通用文本分块不理解代码结构。 | | HTML 帮助页面 | 先清洗 HTML 标签,再递归分块 | 标签、导航栏和脚注会干扰分块,需要先处理。 | | OCR 扫描文本 | 清洗修复后使用重叠分块或语义分块 | OCR 文本分隔符不可靠,先做去噪和结构修复更稳妥。 | | 多类型企业知识库 | 混合分块 | 不同内容采用不同策略,整体效果更稳定。 | 2. chunk_size 与 overlap 的经验参考 下面这些参数不是标准答案,而是常见的起点: | 参数 | 推荐范围 | 说明 | | --- | --- | --- | | `chunk_size` | 200 ~ 1000 字符 | 问答型知识偏小,例如 200 ~ 500;摘要或长说明偏大,例如 500 ~ 1000。 | | `overlap` | `chunk_size` 的 10% ~ 25% | 例如 `chunk_size = 500` 时,`overlap` 可从 50 ~ 125 之间试起。 | 一个实用的调参路径可以这样走: 初始配置:`chunk_size = 500`,`overlap = 50` → 准备 20 ~ 50 个真实用户问题 → 观察 Top-K 检索结果 → 如果召回内容噪声太多,就减小 `chunk_size`;如果答案上下文经常断裂,就增大 `overlap` 或改用递归/语义分块;如果不同文档表现差异大,就引入混合分块和文档类型路由。 企业级实践中的注意事项 在真实项目里,分块效果不好往往不只是 `chunk_size` 没调对,也可能是上游文本抽取已经产生了噪声。尤其是 PDF、扫描件和复杂网页,常见的问题包括: * 页眉、页脚、目录、页码混入正文 * 表格被拆成错位字段 * 换行错误导致一句话被切成多段 * 课程名称、价格、适用人群等结构化字段丢失 * 历史版本规则与当前规则混在一起 * 免责声明、版权信息、导航菜单干扰正文 所以说,稳定的流程不应该是“抽取完成后立刻分块”,而应该是: 文本抽取 → 去噪与结构修复 → 版本识别与元数据补充 → 分块 → 向量化 → 质量评估 → 必要时人工二次编排 对于中小规模但高价值的课程知识库,还可以在基础分块之后加入人工二次编排。做法是先用递归分块或语义分块生成初稿,再由运营、教务或知识库维护人员检查关键块,对不完整、不准确或过长的块进行合并、拆分和补充。 这种方式能解决单纯调参难以解决的问题。比如,某条退款规则需要和例外说明合并;某个 FAQ 的答案依赖前置适用范围,需要补充元数据;某段课程协议跨页了,需要人工恢复完整条款;某个知识块需要标记生效日期,避免召回过期规则。 对于 Dify、RAGFlow 等标准 RAG 平台,基础分块能力通常能覆盖大多数入门场景。但在企业级系统中,单一分块策略容易出现“顾此失彼”: * `chunk_size` 变大,完整上下文更容易召回,但噪声可能增加 * `chunk_size` 变小,检索更精确,但规则更容易被切断 * `overlap` 增大,边界信息保留更好,但存储与向量化成本增加 * 只依赖递归分块,结构清晰的文档效果好,但复杂条款仍可能切错 * 只依赖语义分块,质量可能更高,但批量处理成本和延迟更高 因此,更稳妥的工程方案通常是:文本清洗打底,递归分块作为默认策略,关键文档引入语义分块,重要知识块允许人工校正,并通过真实查询集持续评估效果。 文末总结 分块是 RAG 数据准备阶段的关键步骤。文本抽取之后,系统不能直接把长文档整体用于检索,而需要将其拆成大小合适、语义相对完整的文本块,再进行向量化和相似度检索。 常见分块策略各有定位: | 策略 | 核心特点 | | --- | --- | | 固定大小分块 | 最简单,性能好,但容易切断语义。 | | 重叠分块 | 在固定分块基础上保留重叠区域,缓解边界断裂。 | | 递归分块 | 逐层使用段落、句子、标点等边界,是多数文本知识库的默认选择。 | | 语义分块 | 基于 Embedding 或 LLM 判断主题变化,质量高但成本更高。 | | 混合分块 | 针对不同文档类型组合多种策略,适合复杂企业知识库。 | 参数上,可以从 `chunk_size = 500`、`overlap = 50` 开始,用真实用户问题来评估检索效果。如果结果噪声多,就适当减小 `chunk_size`;如果上下文断裂,就适当增大 `overlap`,或者切换到更能保留结构的策略。 分块之后,每个文本块仍然只是人类可读的文本。要让计算机进行相似度检索,还需要将文本块转换为向量表示,这一步就是 Embedding。分块质量越高,后续向量检索、重排和生成回答的上限也就越高。