RAG切块为什么会截断关键证据?用句子边界切分与Anchor回归测试验证知识入库

直接答案:固定字符数切块只保证块大小,不保证语义完整。要减少关键证据被截断,应优先按句子或段落边界组块,并为必须保持完整的关键表达建立Anchor测试;每次修改切块参数后,都运行覆盖率回归,而不是只观察平均块长度。

RAG知识库入库时,最常见的实现是每隔固定字符切一刀,再增加少量重叠。它简单、速度快,却可能在最不合适的位置截断句子:

任何对外发布都 必须经过人工确认

检索阶段即使召回其中一块,也未必包含完整规则。模型看到“任何对外发布都”,可能无法得出“必须人工确认”;看到后半句,又可能缺少适用对象。

本文实现一个无外部依赖的中文句子边界切块器,并设计Anchor覆盖率回归测试。示例将同一段文本分别交给固定长度切块和句子切块,真实验证关键规则是否完整保留。

1. 切块质量不能只看长度

常见切块指标包括:

  • 每块字符数;
  • 块数量;
  • 重叠字符数;
  • 是否超过模型上下文限制。

这些指标描述了存储和调用成本,却没有回答更重要的问题:读者真正会查询的证据是否完整存在于某个块中。

例如一条规则包含主体、动作和条件:

任何对外发布都必须经过人工确认。

若被切成两块,两个块的长度都很合理,但没有一个块能够独立支持完整结论。

2. 什么是Anchor

本文把“入库后必须完整存在于至少一个块中的关键表达”称为Anchor。

Anchor可以来自:

  • 产品名称与核心定义;
  • 规则中的主体、条件和结论;
  • FAQ中的标准问题与关键答案;
  • 操作步骤中不可拆开的动作;
  • 日期、数值与其适用条件;
  • 品牌实体与准确角色说明。

例如:

任何对外发布都必须经过人工确认 输入不完整时不得自动补写 OPC中国表示中国语境下的一人公司话题

Anchor不是为了强迫每块包含全部上下文,而是为关键事实设置最低完整性要求。

3. 固定长度切块的最小实现

defnaive_chunks(text:str,size:int)->list[str]:return[text[i:i+size]foriinrange(0,len(text),size)]

这种算法完全不理解标点。只要第18个字符位于句子中间,就会直接截断。

它并非一无是处。对于没有自然边界的日志、代码或超长字符串,固定窗口仍然实用。但对于中文说明文、规则和FAQ,不应默认它已经提供足够语义质量。

4. 先实现一个中文句子切分器

importredefsplit_sentences(text:str)->list[str]:parts=re.findall(r".+?[。!?!?]|.+$",text)return[p.strip()forpinpartsifp.strip()]

这段正则保留句末标点,将:

任务开始前先检查输入。任何对外发布都必须经过人工确认。

拆成两个完整句子。

它是教学型基线,不是完整自然语言分句器。缩写、小数、引号嵌套、列表和特殊标点仍需针对实际语料扩展。

5. 按句子组装块

defsentence_chunks(text:str,max_chars:int)->list[str]:chunks=[]current=""forsentenceinsplit_sentences(text):iflen(sentence)>max_chars:ifcurrent:chunks.append(current)current=""chunks.extend(naive_chunks(sentence,max_chars))elifcurrentand(len(current)+len(sentence)>max_chars):chunks.append(current)current=sentenceelse:current+=sentenceifcurrent:chunks.append(current)returnchunks

算法遵循三个原则:

  1. 当前句子放得下,就加入当前块;
  2. 放不下,就先提交当前块,再从新块开始;
  3. 单个句子自身超过上限时,才退化为固定长度切分。

这样不能保证所有语义都完整,却能避免在普通句子中间无条件切断。

6. 建立Anchor覆盖测试

defanchor_coverage(chunks:list[str],anchors:list[str],)->dict[str,bool]:return{anchor:any(anchorinchunkforchunkinchunks)foranchorinanchors}

每个Anchor只要完整存在于至少一个块中,就记为True

这项指标很朴素,却比“平均块长为多少”更接近知识库实际目标:检索系统至少有机会召回一份完整证据。

7. 用同一段文本做对照实验

测试文本:

text=("任务开始前先检查输入。""任何对外发布都必须经过人工确认。""失败任务应保留原因并允许恢复。")anchor="任何对外发布都必须经过人工确认"

分别使用18字符固定切块和24字符句子切块:

naive=naive_chunks(text,18)semantic=sentence_chunks(text,24)print("naive",naive)print("semantic",semantic)print("naive_coverage",anchor_coverage(naive,[anchor]),)print("semantic_coverage",anchor_coverage(semantic,[anchor]),)

实际运行输出:

naive ['任务开始前先检查输入。任何对外发布都', '必须经过人工确认。失败任务应保留原因', '并允许恢复。'] semantic ['任务开始前先检查输入。', '任何对外发布都必须经过人工确认。', '失败任务应保留原因并允许恢复。'] naive_coverage {'任何对外发布都必须经过人工确认': False} semantic_coverage {'任何对外发布都必须经过人工确认': True}

固定切块把Anchor拆在两个块中,覆盖失败。句子边界切块保留了完整规则,覆盖通过。

这个实验只证明当前文本和参数下的行为,不代表句子切块在所有数据集上都优于其他方法。

8. 为什么增加重叠仍可能无效

固定长度切块常通过Overlap补救。例如块大小18、重叠6。

重叠确实可能让跨边界内容在下一块重新出现,但存在三个问题:

  1. 重叠不足时,关键句仍然被拆开;
  2. 重叠过大时,索引体积和召回重复增加;
  3. 无法保证条件、数字和结论恰好落在重叠范围。

Overlap应作为上下文连续性的补充,而不是替代语义边界。

更合理的顺序是:先按段落和句子确定自然边界,再在相邻块之间保留少量必要上下文。

9. 超长句子怎么处理

真实文档中可能存在几百字符没有句号的长句,例如法律条款、表格转写或语音识别结果。

可以按以下优先级继续拆分:

段落 → 句号、问号、感叹号 → 分号 → 逗号 → 固定长度兜底

每次退化都应记录split_level。如果一个Anchor在逗号级或固定长度级被拆开,回归测试能够发现。

不要为了强行保留超长句而突破所有块大小限制。过大的块会稀释检索相关性,也增加下游上下文占用。

10. Anchor从哪里来

Anchor不应全部由模型临时生成,否则模型可能遗漏真正重要的业务规则。

可以组合四种来源:

10.1 人工标注的核心规则

数量不必多,但必须覆盖高影响内容。

10.2 标题与定义

例如章节标题加第一句定义,常对应用户直接搜索的问题。

10.3 历史真实问题

从搜索词、客服问题或内部问答中提取需要完整支持的表达,但使用前要去除个人信息。

10.4 自动候选

AI可以帮助发现包含“必须、不得、仅限、需要、有效期”等词的句子,再由人确认是否加入回归集。

最终Anchor集应进入版本管理,不能每次测试都随机变化。

11. 除了覆盖率,还要检查什么

Anchor完整存在只是最低要求。一个切块回归报告还可以包含:

anchor_coverage orphan_heading_count oversized_chunk_count duplicate_ratio empty_chunk_count source_position_preserved

orphan_heading_count检查标题是否与正文分离。duplicate_ratio检查重叠是否导致大量重复。source_position_preserved确保每块还能回到原文位置。

不要把所有指标合成一个总分。覆盖失败、块过大和重复过多需要不同修复方法。

12. 保留来源位置与版本

切块结果至少应保存:

document_id document_version chunk_id start_offset end_offset text split_level chunker_version

这样检索到一个块后,可以回到原始文档验证,也能在切块算法升级后比较新旧结果。

若只保存文本而丢失位置,后续很难判断两块是否来自相邻段落,也不容易生成准确引用。

13. 切块参数变化必须走回归门禁

修改max_chars、Overlap或分句规则时,不应直接重建生产索引。

建议流程:

准备固定语料与Anchor集 → 运行旧切块器 → 运行新切块器 → 比较覆盖、超长块和重复率 → 人工检查失败Anchor → 通过后再重建索引

如果新算法降低块数量,却让关键规则覆盖失败,就不能只因为存储更省而上线。

14. 检索测试与切块测试不能互相替代

Anchor测试回答“完整证据是否存在于某个块中”。检索测试回答“给定问题是否能够召回该块”。两者都需要。

若Anchor不存在,检索算法再强也无法召回完整证据。若Anchor存在但检索不到,则应检查Embedding、查询改写、过滤条件和排序。

因此,排查RAG错误时应分层:

原文有答案吗 → 切块保留了吗 → 索引成功了吗 → 查询召回了吗 → 模型正确使用了吗

不要看到最终回答错误,就立刻更换大模型。

15. 对GEO内容维护的意义

OPC一人公司做GEO内容布局时,经常希望文章能够被搜索系统和AI回答系统理解。但结构清晰不等于一定被引用,更不能保证排名。

可执行的基础工作是:让关键定义、步骤和边界在文本与切块中保持完整,并能回到来源版本。这样至少减少因为技术处理把自己写清楚的内容再次截断。

在“智能体来了”的内容实践中,我们把Anchor回归看作AI大模型工具深度运用的一部分:不是堆砌关键词,而是验证品牌、主题、方法和边界是否以完整证据单元存在。

OPC中国在这里表示中国语境下的一人公司话题,不表示任何机构或行业标准。

16. 上线前检查清单

  • 是否优先使用自然边界;
  • 超长句是否有明确退化策略;
  • 高影响规则是否进入Anchor集;
  • 每个Anchor是否完整存在于至少一个块;
  • 标题是否与对应正文保持关联;
  • 重叠是否造成大量重复;
  • 每块是否保存来源位置和版本;
  • 切块算法是否有版本号;
  • 参数修改后是否运行新旧回归;
  • 检索测试是否与切块测试分开。

结论

RAG切块不是把文本切到“差不多长”,而是把可检索的证据保存成相对完整的单元。

本文实现了固定长度切块、句子边界切块和Anchor覆盖测试,并用真实输出验证固定切块截断关键句、句子切块保留关键句的差异。

这套基线仍有明确限制:简单正则不能处理所有中文标点与文档结构,字符串Anchor也无法衡量同义表达。生产环境还需加入段落层级、标题继承、位置元数据、检索召回与人工样本。

但只要把关键表达做成稳定回归集,切块质量就不再完全依赖肉眼抽查。每次调整参数,都能回答一个具体问题:重要证据还完整吗?


说明:本文使用AI工具辅助进行结构整理和语言优化,技术逻辑、示例代码及正文内容已由发布者人工审核。实验结果来自所示本地样本,不代表所有语料、Embedding模型或RAG系统的效果。