本文定位RAG 评测 / 数据工程 / 可复现实验示例环境Python 3.11、PostgreSQL 16、pgvector 0.7.x、Embedding 模型以 1536 维为例。本文中的指标示例用于说明实验记录方式发布前应替换为自己的数据集结果。摘要很多 RAG 项目把 Chunk Size 当成一个经验参数切成 500 字、重叠 50 字然后开始调 Prompt。结果不稳定时团队往往继续更换模型却很少回头检查“证据是不是在正确的片段里”。实际上分块策略直接影响召回粒度、上下文噪声、引用完整性、Token 成本和回答延迟。本文用实验设计的方式比较三种常见策略固定长度切分、递归结构切分和语义切分。重点不是给出一个对所有项目都适用的数字而是说明如何构造数据集、如何避免评测泄漏、如何计算 RecallK、MRR、引用准确率和成本并根据文档类型选择方案。一、分块问题到底影响了什么一份文档从原始文件到模型上下文会经过解析、结构识别、切分、向量化、检索和重排。Chunk 过大单个片段包含多个主题向量表示会变得模糊Chunk 过小关键条件可能被拆开模型只能看到“必须先停止服务”而看不到“仅在 E102 连续出现三次时”。原始文档解析与结构识别切分策略Embedding向量库Top-K召回上下文拼装模型回答Recall/MRR引用准确率/拒答率分块策略至少影响五个指标检索召回正确证据能否出现在前 K 条。证据完整性一个 Chunk 是否包含回答所需的前置条件和限制条件。噪声比例无关文本占上下文的比例是否过高。生成成本每次请求注入的 Token 数以及模型输入价格。更新成本文档修改后是否需要重新计算大量向量。因此分块不是孤立的数据处理步骤而是检索系统的第一层质量控制。二、实验前先固定变量如果一次实验同时更换 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:int500,overlap:int80)-list[str]:ifsizeoverlap:raiseValueError(size 必须大于 overlap)chunks[]start0whilestartlen(text):endmin(len(text),startsize)valuetext[start:end].strip()ifvalue:chunks.append(value)ifendlen(text):breakstartend-overlapreturnchunks固定切分适合日志、连续转写文本和结构很弱的内容但在制度文档中要谨慎。一个条款的“例外情况”被切到下一个 Chunk 后模型很容易只看到主规则而忽略例外。2. 递归结构切分优先按标题、段落、列表、句子等分隔符切分只有当单元超过最大长度时才继续拆分。它通常是通用文档的首选基线。SEPARATORS[\n## ,\n### ,\n\n,\n,。,, ]defrecursive_split(text:str,max_chars:int800)-list[str]:texttext.strip()iflen(text)max_chars:return[text]iftextelse[]forseparatorinSEPARATORS:partstext.split(separator)iflen(parts)1:continueresult,current[],forpartinparts:candidatef{current}{separator}{part}ifcurrentelsepartiflen(candidate)max_chars:currentcandidateelse:result.extend(recursive_split(current,max_chars))currentpart result.extend(recursive_split(current,max_chars))returnresultreturn[text[:max_chars],*recursive_split(text[max_chars:],max_chars)]递归切分的关键不在递归本身而在分隔符顺序。把“句号”放在“标题”之前会让结构信息先丢失把空格作为最后分隔符可以避免英文代码块过度拆分。3. 语义切分先按句子切分再计算相邻句子的语义相似度。当相似度出现明显下降时把前面的句子聚合为一个语义单元。它适合长篇说明文、会议纪要和知识密度不均匀的文档但需要额外的 Embedding 计算离线入库时间更长。defsemantic_split(sentences,embeddings,threshold0.45):groups,current[],[]forindex,sentenceinenumerate(sentences):current.append(sentence)ifindexlen(sentences)-1:breaksimilaritycosine(embeddings[index],embeddings[index1])ifsimilaritythreshold:groups.append(.join(current).strip())current[]ifcurrent:groups.append(.join(current).strip())returngroups语义阈值不能脱离数据集凭感觉确定。阈值过低多个主题会被粘在一起阈值过高Chunk 数量暴涨导致检索噪声和索引成本上升。实际使用时还要叠加最大 Token 限制避免单个语义单元过长。四、评测指标怎么计算设某条问题的标准证据集合为G检索返回前 K 条结果为RK。RecallKRecallK |G ∩ RK| / |G|它回答“正确证据有没有被召回”。如果 Recall 很低继续调 Prompt 没有意义应先检查分块、Embedding 或检索条件。MRR如果第一个正确证据出现在排名第rank位则该题的 reciprocal rank 是1 / rank。所有问题取平均得到 MRR。它比 Recall 更重视正确证据是否靠前。引用准确率不是模型引用了 Chunk 就算正确还要判断 Chunk 是否真的支持结论。可以让人工评审给出支持、部分支持、不支持三个标签并同时记录引用覆盖率。成本与延迟对每种切分策略记录文档 Chunk 数、索引写入耗时、平均输入 Token、P95 检索延迟、单问题平均成本。不要只看准确率一个准确率提高 2% 但成本增加 5 倍的方案可能并不适合普通客服场景。五、示例实验记录与解读下面的数值是“记录格式示例”不是对任何真实数据集的承诺。发布自己的文章时应把它替换成实际执行结果并说明数据规模和评测脚本版本。策略Chunk数Recall5MRR平均输入TokenP95检索延迟固定 500/8012400.780.66312082ms递归 8009800.840.73268075ms语义 最大 90011300.860.76281096ms从这种结果中不能直接得出“语义切分永远最好”。它只说明在当前数据集上语义切分可能提高证据命中但索引和检索成本也更高。还要看知识库外问题的拒答率、引用准确率和不同文档类型的分组结果。如果制度文档的 Recall 很高但引用准确率低可能是 Chunk 包含了多个相互冲突的条款如果故障手册的 Recall 低可能是“现象—原因—处理步骤”被拆成了三个不相邻的片段。评测报告应该进一步定位到文档类型而不是只报一个总分。六、混合策略结构优先语义兜底实际工程中最稳妥的方案通常不是三选一而是分层处理先解析标题、表格、列表、代码块和页码。以章节或业务小节作为一级 Chunk。一级 Chunk 超过 Token 上限时使用递归切分。对长段落或无结构文本使用语义切分。为每个子 Chunk 保存父章节标题和来源位置。检索子 Chunk回答时可回填父章节摘要。这样做的好处是既保留文档结构又避免单个片段过长。父子 Chunk 也要注意权限继承子 Chunk 不能因为回填父章节而跨越原有权限边界。七、上线前的验证清单同一文档重复导入Chunk 数是否保持不变。文档修改一个段落是否只更新受影响的向量。标题、页码、章节和版本信息能否在回答中正确引用。知识库外问题是否会拒答而不是返回相似但错误的内容。同一问题在不同租户下是否得到不同的证据集合。Embedding 模型升级后旧向量是否会与新向量混用。Chunk 数增加十倍时P95 检索延迟是否仍在可接受范围。评测集是否和训练、调参数据隔离避免结果过于乐观。八、结论分块策略没有脱离业务的万能答案。固定切分适合建立基线递归切分适合大多数结构化文档语义切分适合主题变化明显且对证据完整性要求高的内容。真正高质量的 RAG 文章不应该只给出“建议用 500 字”而要告诉读者如何实验、如何判断、如何解释结果以及在什么边界内使用。如果你正在搭建企业知识库建议先用递归结构切分跑通全链路再建立小型评测集最后针对失败样本选择性引入语义切分。先测量再优化比盲目更换模型更节省时间和成本。读者讨论你的知识库主要是制度文档、故障手册、代码文档还是聊天记录文档类型不同分块策略往往也应该不同欢迎分享你的评测指标和失败样本。