第 5 / 21 篇

第4章 切分窗口、重叠与证据粒度

把整本手册作为一个向量,容易让短小规定淹没在大量背景里;把每句话拆开,又可能把“适用于正式员工”与后面的金额限制分离。切分不是寻找一个神奇长度,而是在召回粒度、上下文和引用稳定性之间做可检查的选择。

一个有边界的切分器

chunks() 接收规范文本、窗口大小和重叠长度。默认窗口二百二十个码点,重叠四十;随书小语料为了观察跨片段行为,使用八十与十六。窗口到达中间位置以后,如果后半段存在空行,就尽量在空行后结束;否则按窗口上限切断。

每个片段携带顺序号、起止位置、原文、最近的标题和摘要。最近标题不等于完整标题树,长文档中可能缺少更高层的语境。索引没有把标题额外拼接进片段正文,以免引用坐标与实际正文混淆;标题在规则重排中作为单独特征使用。

from lab.text import chunks
body = "# 制度\n\n" + "说明文字" * 80
pieces = list(chunks(body, size=80, overlap=16))
assert all(body[p["start"]:p["end"]] == p["text"] for p in pieces)

下一片起点取 max(start + 1, end - overlap),即使自然段很长或边界安排不理想,也会向前推进。参数要求窗口在一到四千零九十六之间,重叠不小于零且小于窗口。测试覆盖超长自然段的有限推进、正文覆盖和坐标一致性。

重叠不是多份独立证据

同一段制度可能出现在相邻两个片段里。它们分别命中检索,不能证明有两个来源支持金额。回答阶段会跳过同一文档同一版本中与已选证据区间相交的片段,避免直接重复摘录;不相交的片段即使都被保留,也仍来自同一篇文档。

评估排名同样先按文档编号去重,再取前三篇。否则一篇相关文档的三个重叠片段会占满榜单,令片段命中数看起来很好,实际上没有增加证据覆盖。报告保留 returned_chunks,使读者能区分返回片段量和评估文档量。

窗口变大可能减少截断,却也可能把多个制度条款混在一起。重叠变大可以补回边界语境,但增加存储、编码时间和重复候选。要调整它们,应固定文档与查询,逐项比较遗漏的答案位置、重复比例和引用长度,而不是只看一个总体召回分数。

切分配置属于索引版本

同一正文在八十与二百二十窗口下,会得到不同片段编号、边界和向量。索引把编码器标识、窗口和重叠写入投影契约。用不同配置打开已有数据库会得到 index_projection_contract_changed_rebuild_required,要求重新建立索引。

教学实现选择整体拒绝混用,而没有实现双版本在线迁移。生产环境可以在新索引完整构建和验证后切换读指针,但必须让查询向量、片段与引用坐标属于一致版本。让旧片段和新编码器短暂混用,并不能通过最终一次全量重建来解释期间的错误回答。

切分验收的终点应是证据能否恢复,而不是片段数量是否整齐。下一章把这些片段放入真正的全文索引,观察词面召回究竟做了什么。