news 2026/8/11 4:49:49

RAG 分块策略实测:固定长度、递归切分与语义切分如何选择

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
RAG 分块策略实测:固定长度、递归切分与语义切分如何选择

本文定位:RAG 评测 / 数据工程 / 可复现实验

示例环境:Python 3.11、PostgreSQL 16、pgvector 0.7.x、Embedding 模型以 1536 维为例。本文中的指标示例用于说明实验记录方式,发布前应替换为自己的数据集结果。

摘要

很多 RAG 项目把 Chunk Size 当成一个经验参数:切成 500 字、重叠 50 字,然后开始调 Prompt。结果不稳定时,团队往往继续更换模型,却很少回头检查“证据是不是在正确的片段里”。实际上,分块策略直接影响召回粒度、上下文噪声、引用完整性、Token 成本和回答延迟。

本文用实验设计的方式比较三种常见策略:固定长度切分、递归结构切分和语义切分。重点不是给出一个对所有项目都适用的数字,而是说明如何构造数据集、如何避免评测泄漏、如何计算 Recall@K、MRR、引用准确率和成本,并根据文档类型选择方案。

一、分块问题到底影响了什么

一份文档从原始文件到模型上下文,会经过解析、结构识别、切分、向量化、检索和重排。Chunk 过大,单个片段包含多个主题,向量表示会变得模糊;Chunk 过小,关键条件可能被拆开,模型只能看到“必须先停止服务”而看不到“仅在 E102 连续出现三次时”。

原始文档

解析与结构识别

切分策略

Embedding

向量库

Top-K召回

上下文拼装

模型回答

Recall/MRR

引用准确率/拒答率

分块策略至少影响五个指标:

  1. 检索召回:正确证据能否出现在前 K 条。
  2. 证据完整性:一个 Chunk 是否包含回答所需的前置条件和限制条件。
  3. 噪声比例:无关文本占上下文的比例是否过高。
  4. 生成成本:每次请求注入的 Token 数以及模型输入价格。
  5. 更新成本:文档修改后是否需要重新计算大量向量。

因此,分块不是孤立的数据处理步骤,而是检索系统的第一层质量控制。

二、实验前先固定变量

如果一次实验同时更换 Embedding 模型、向量距离、Rerank 模型和 Prompt,最后即使分数变化,也无法判断原因。建议为每轮实验固定以下条件:

变量固定方式
原始文档使用同一批 PDF、Markdown、HTML 和表格
问题集由人工编写并复核,包含答案外问题
Embedding固定模型、维度和归一化方式
检索算法先只测向量检索,再测试混合检索
Top-K例如 3、5、10 分别统计
生成模型评测召回和生成时分别记录
版本代码、模型、数据集都记录 Git Commit

问题集不应该只包含“文档标题里出现过的词”。建议覆盖四类问题:原文复述、跨段推理、带条件的故障处理、知识库外问题。每条问题至少标注一个正确证据 Chunk,复杂问题可以标注多个。

{"id":"q-017","question":"E102连续出现三次后,重启服务前还需要做什么?","gold_chunk_ids":["doc-8-p18-c03","doc-8-p18-c04"],"answer_points":["检查采集链路","确认影响范围","再重启采集服务"],"is_answerable":true}

三、三种切分策略

1. 固定长度切分

按字符数或 Token 数截断,并保留固定重叠。优点是简单、速度快、实现成本低,适合快速建立基线。缺点是容易切断标题、列表和步骤。

deffixed_split(text:str,size:int=500,overlap:int=80)->list[str]:ifsize<=overlap:raiseValueError("size 必须大于 overlap")chunks=[]start=0whilestart<len(text):end=min(len(text),start+size)value=text[start:end].strip()ifvalue:chunks.append(value)ifend==len(text):breakstart=end-overlapreturnchunks

固定切分适合日志、连续转写文本和结构很弱的内容,但在制度文档中要谨慎。一个条款的“例外情况”被切到下一个 Chunk 后,模型很容易只看到主规则而忽略例外。

2. 递归结构切分

优先按标题、段落、列表、句子等分隔符切分,只有当单元超过最大长度时才继续拆分。它通常是通用文档的首选基线。

SEPARATORS=["\n## ","\n### ","\n\n","\n","。",";"," "]defrecursive_split(text:str,max_chars:int=800)->list[str]:text=text.strip()iflen(text)<=max_chars:return[text]iftextelse[]forseparatorinSEPARATORS:parts=text.split(separator)iflen(parts)==1:continueresult,current=[],""forpartinparts:candidate=f"{current}{separator}{part}"ifcurrentelsepartiflen(candidate)<=max_chars:current=candidateelse:result.extend(recursive_split(current,max_chars))current=part result.extend(recursive_split(current,max_chars))returnresultreturn[text[:max_chars],*recursive_split(text[max_chars:],max_chars)]

递归切分的关键不在递归本身,而在分隔符顺序。把“句号”放在“标题”之前,会让结构信息先丢失;把空格作为最后分隔符,可以避免英文代码块过度拆分。

3. 语义切分

先按句子切分,再计算相邻句子的语义相似度。当相似度出现明显下降时,把前面的句子聚合为一个语义单元。它适合长篇说明文、会议纪要和知识密度不均匀的文档,但需要额外的 Embedding 计算,离线入库时间更长。

defsemantic_split(sentences,embeddings,threshold=0.45):groups,current=[],[]forindex,sentenceinenumerate(sentences):current.append(sentence)ifindex==len(sentences)-1:breaksimilarity=cosine(embeddings[index],embeddings[index+1])ifsimilarity<threshold:groups.append("".join(current).strip())current=[]ifcurrent:groups.append("".join(current).strip())returngroups

语义阈值不能脱离数据集凭感觉确定。阈值过低,多个主题会被粘在一起;阈值过高,Chunk 数量暴涨,导致检索噪声和索引成本上升。实际使用时还要叠加最大 Token 限制,避免单个语义单元过长。

四、评测指标怎么计算

设某条问题的标准证据集合为G,检索返回前 K 条结果为R@K

Recall@K

Recall@K = |G ∩ R@K| / |G|

它回答“正确证据有没有被召回”。如果 Recall 很低,继续调 Prompt 没有意义,应先检查分块、Embedding 或检索条件。

MRR

如果第一个正确证据出现在排名第rank位,则该题的 reciprocal rank 是1 / rank。所有问题取平均得到 MRR。它比 Recall 更重视正确证据是否靠前。

引用准确率

不是模型引用了 Chunk 就算正确,还要判断 Chunk 是否真的支持结论。可以让人工评审给出支持、部分支持、不支持三个标签,并同时记录引用覆盖率。

成本与延迟

对每种切分策略记录:文档 Chunk 数、索引写入耗时、平均输入 Token、P95 检索延迟、单问题平均成本。不要只看准确率;一个准确率提高 2% 但成本增加 5 倍的方案,可能并不适合普通客服场景。

五、示例实验记录与解读

下面的数值是“记录格式示例”,不是对任何真实数据集的承诺。发布自己的文章时,应把它替换成实际执行结果,并说明数据规模和评测脚本版本。

策略Chunk数Recall@5MRR平均输入TokenP95检索延迟
固定 500/8012400.780.66312082ms
递归 8009800.840.73268075ms
语义 + 最大 90011300.860.76281096ms

从这种结果中不能直接得出“语义切分永远最好”。它只说明在当前数据集上,语义切分可能提高证据命中,但索引和检索成本也更高。还要看知识库外问题的拒答率、引用准确率和不同文档类型的分组结果。

如果制度文档的 Recall 很高但引用准确率低,可能是 Chunk 包含了多个相互冲突的条款;如果故障手册的 Recall 低,可能是“现象—原因—处理步骤”被拆成了三个不相邻的片段。评测报告应该进一步定位到文档类型,而不是只报一个总分。

六、混合策略:结构优先,语义兜底

实际工程中最稳妥的方案通常不是三选一,而是分层处理:

  1. 先解析标题、表格、列表、代码块和页码。
  2. 以章节或业务小节作为一级 Chunk。
  3. 一级 Chunk 超过 Token 上限时,使用递归切分。
  4. 对长段落或无结构文本使用语义切分。
  5. 为每个子 Chunk 保存父章节标题和来源位置。
  6. 检索子 Chunk,回答时可回填父章节摘要。

这样做的好处是既保留文档结构,又避免单个片段过长。父子 Chunk 也要注意权限继承:子 Chunk 不能因为回填父章节而跨越原有权限边界。

七、上线前的验证清单

  • 同一文档重复导入,Chunk 数是否保持不变。
  • 文档修改一个段落,是否只更新受影响的向量。
  • 标题、页码、章节和版本信息能否在回答中正确引用。
  • 知识库外问题是否会拒答,而不是返回相似但错误的内容。
  • 同一问题在不同租户下是否得到不同的证据集合。
  • Embedding 模型升级后,旧向量是否会与新向量混用。
  • Chunk 数增加十倍时,P95 检索延迟是否仍在可接受范围。
  • 评测集是否和训练、调参数据隔离,避免结果过于乐观。

八、结论

分块策略没有脱离业务的万能答案。固定切分适合建立基线,递归切分适合大多数结构化文档,语义切分适合主题变化明显且对证据完整性要求高的内容。真正高质量的 RAG 文章,不应该只给出“建议用 500 字”,而要告诉读者:如何实验、如何判断、如何解释结果以及在什么边界内使用。

如果你正在搭建企业知识库,建议先用递归结构切分跑通全链路,再建立小型评测集,最后针对失败样本选择性引入语义切分。先测量,再优化,比盲目更换模型更节省时间和成本。

读者讨论

你的知识库主要是制度文档、故障手册、代码文档,还是聊天记录?文档类型不同,分块策略往往也应该不同,欢迎分享你的评测指标和失败样本。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/11 4:48:17

无源晶振起振电路原理、负载电容计算与 PCB 布线规范

文章导读时钟是 MCU、ARM 处理器的运行 “心跳”&#xff0c;无源晶振是嵌入式项目最常用的时钟方案。很多硬件新手只知道在晶振两边接两颗电容&#xff0c;却不清楚无源晶振无法独立起振、负载电容如何匹配、PCB 布局哪些坑会导致不起振、时钟漂移。本文完整讲解无源起振电路定…

作者头像 李华
网站建设 2026/8/11 4:47:30

IT6115技术解析:一款灵活的双模MIPI桥接芯片

引言在移动设备、VR/AR头显、车载多屏及嵌入式显示系统日益复杂的背景下&#xff0c;MIPI接口的带宽管理与协议转换成为系统设计的关键环节。ITE Tech Inc.&#xff08;联阳半导体&#xff09;推出的IT6115 MIPI Video Bridge&#xff0c;正是面向这一需求的高度集成接口控制器…

作者头像 李华
网站建设 2026/8/11 4:45:43

Python GUI自动化实战:基于PyAutoGUI的微信消息自动发送方案

1. 项目概述&#xff1a;从手动到自动的沟通效率革命 在数字化办公和社交沟通中&#xff0c;微信已经成为一个无法绕开的中心。无论是日常的工作汇报、客户维护&#xff0c;还是社群运营、活动通知&#xff0c;手动一条条发送消息不仅耗时费力&#xff0c;还容易出错。作为一名…

作者头像 李华
网站建设 2026/8/11 4:44:29

Unity舞台灯光插件SLM:基于DMX协议与Timeline的可视化灯光编排方案

1. 项目概述&#xff1a;Unity_StageLightManeuver是什么&#xff1f;如果你在Unity里做过灯光、做过演出效果&#xff0c;尤其是那种需要几十上百盏灯跟着音乐节奏、角色动作同步变化的项目&#xff0c;那你一定体会过那种“痛”。要么是写一堆脚本&#xff0c;用代码去控制每…

作者头像 李华
网站建设 2026/8/11 4:43:01

从晶体管开关到进制转换:一文彻底搞懂计算机底层二进制逻辑

1. 从“开关”到“数字”&#xff1a;为什么我们需要二进制&#xff1f;如果你拆开过任何一台现代电子设备&#xff0c;无论是手机、电脑&#xff0c;还是智能手表&#xff0c;你都会发现它的核心是一块布满微小元件的电路板。这些元件&#xff0c;特别是晶体管&#xff0c;本质…

作者头像 李华
网站建设 2026/8/11 4:42:58

C语言文件操作全解析:从流与缓冲区到安全编程实践

1. 项目概述&#xff1a;为什么C语言文件操作是程序员的必修课在编程世界里&#xff0c;数据是程序的血液&#xff0c;而文件系统则是存储和交换这些血液的血管。无论你是在开发一个简单的日志记录工具&#xff0c;还是一个复杂的数据库系统&#xff0c;最终都需要和硬盘上的文…

作者头像 李华