检索评测与切分项目

项目目标

你将比较两个切分策略,但不会用“看起来更自然”作为结论:

  • structured:不跨 source、revision、ACL、section 与内容 family;完整短元素优先合并,超长单元素使用 overlap 窗口;表格行可获得检索专用表头上下文。
  • fixed_window:只按 source、revision 与 ACL 分组,再用固定 lexical-unit 窗口切,可跨 section,作为简单基线。

两者使用同一 corpus、query、词法检索器、ACL 过滤、k=3 与来源锚点。这样差异才能主要归因于切分方式。

外推边界

项目不调用 tokenizer、Embedding、向量数据库、reranker 或 LLM。词项重叠检索只用于验证数据流与评测逻辑,不能证明真实 RAG 的生产质量。

项目文件

  • chunking_lab.py:输入校验、两种切分、检索、评测与成本报告;
  • corpus.json:9 个元素,覆盖段落、代码块、表头、表格行、两个 ACL 与一个超长段落;
  • queries.json:7 个有答案、2 个无答案/未授权查询;
  • test_chunking_lab.py:32 项标准库 unittest。

所有 JSON 都使用 UTF-8,不含凭据;脚本使用严格字段集合、拒绝重复 key 与非有限数值。

Gold 为什么锚定来源

如果 gold 直接写 chunk_id,改变尺寸或策略后 ID 会变化,评测集随之失效。本项目把每条证据写成:

{
  "element_id": "api-e3",
  "quote": "认证失败不得重试"
}

JSON 不能合法地在每行末尾写注释:element_id 指向不随切分策略改变的来源元素;quote 是该元素中唯一出现、且会在加载时转换为单位区间的证据文本。保留这段纯 JSON,才能直接复制到 queries.json。

加载时会确认:

  1. element 确实存在;
  2. quote 在该 element 中恰好出现一次;
  3. quote 起止与 lexical-unit 边界对齐;
  4. 将 quote 转换为半开单位区间。

评测时,若某个 retrieved chunk 的 element span 完整覆盖该区间,就认为命中。这样重建 chunks 后,gold 仍锚在稳定来源上。

生产数据还可使用字符 offset、页/行/JSON Pointer 和来源哈希。Exact quote 便于人工审查,但重复句子需要额外 disambiguation。

运行实验

在项目根目录(同时包含 docs/ 与 .website/)执行:

$env:PYTHONDONTWRITEBYTECODE = '1'  # 禁止 Python 写入 __pycache__,避免教学目录产生无关生成文件。
python -B -W error '.\docs\Chunking策略\examples\chunking_lab.py'  # 执行离线实验,并把任何 warning 当作错误暴露。

输出是 JSON,包含:

  • unit_definition:明确不是模型 tokenizer;
  • config:hard max、overlap 与策略版本;
  • index_revision:本次词法索引实现版本;
  • cost:chunk 数、来源/正文/检索单位、重复率和长度分布;
  • evaluation:Recall@k、MRR、完整证据率、无答案准确率、上下文单位,以及逐查询的 retrieved_index_entry_ids。

截至 2026-07-22,本地 fixture 的确定性结果摘要为:

策略chunks正文重复率平均检索上下文单位anchor Recall@3MRR完整证据率无答案准确率
structured70.031996.22221.01.01.01.0
fixed_window60.0956126.77781.01.01.01.0

正确读法是:这 9 个查询里,两者都命中了所有锚点;结构策略在当前检索与 k 下送入的平均上下文更少。不能据此断言“结构切分召回更高”或推广到其他语料。

指标逐个解释

Anchor Recall@k

若一条 query 有 个来源锚点,top-k 完整覆盖其中 个:

它能发现“找到了一个段落,却漏了另一个必要条件”。

Complete evidence case rate

只有一条 query 的所有锚点都在 top-k 中,才记为 1。多证据回答中,它比“至少命中一个”更严格。

MRR

本项目取第一个覆盖任一相关锚点的排名 ,记 ,再对查询平均。它衡量首个证据出现多早,但不保证后续必要证据齐全,所以必须与 complete evidence 一起看。

No-answer accuracy

本项目的无答案样本要求检索器返回空。一个样本使用不存在的词项,另一个测试 ACL 隔离。真实 RAG 中“有词法命中但证据不足”也应拒答,因此这里的 1.0 只证明本地检索行为,不等于端到端拒答能力。

Context cost

把每个 query 返回块的 retrieval_unit_count 相加。它比只看 chunk 数更接近送入下游的相对成本,但依然不是计费 token;接入模型后必须换成真实 tokenizer 与实际打包器。

权限与来源不变量

检索函数先检查主体 group 与 chunk ACL 是否相交,再计算词项分数。未授权表格即使与 query 完全同词,也不会进入 scored candidates。

切分后的验证还检查:

  • chunk ID 唯一,ordinal 从 1 连续;
  • 正文非空且不超过 hard max;
  • ACL 非空;
  • 内容/检索哈希与实际文本一致;
  • index_entry_id 绑定 chunk_id、实际 retrieval_sha256、index_revision 与 ACL 快照;
  • span 不越界;
  • chunk 不跨 source、revision 或 ACL;
  • 每个 element 的所有单位至少覆盖一次。

这是教学安全基线,不是完整 IAM。租户隔离、deny 规则、用户/组继承与审计需在系统授权模型中另行设计。

运行测试

普通与优化模式都应通过;-O 会移除裸 assert,因此双模式能防止把生产校验误写成 assert。

$env:PYTHONDONTWRITEBYTECODE = '1'  # 仍禁止生成字节码缓存,保持仓库干净。
python -B -W error -m unittest discover -s '.\docs\Chunking策略\examples' -p 'test_chunking_lab.py' -v  # 在正常解释器模式发现并详细运行本项目测试。
python -B -O -W error -m unittest discover -s '.\docs\Chunking策略\examples' -p 'test_chunking_lab.py' -v  # 在 -O 模式复跑,确认校验不依赖会被移除的 assert。

测试覆盖:

  • lexical-unit 字符 offset 与非法配置;
  • JSON 重复 key、非有限值、精确字段和坏引用;
  • quote 唯一性与单位边界;
  • hard max、全覆盖和所有隔离边界;
  • 超长元素的实际 overlap 与终止;
  • 表头进入 retrieval text 而非行正文;
  • chunk ID 对无关前插稳定、对内容/版本/策略敏感;
  • 标题路径或表头上下文变化时,即使正文 chunk 身份可保持,retrieval_sha256 与 index_entry_id 也必须变化;
  • index_entry_id 对索引版本与 ACL 快照敏感,并出现在逐查询结果中;
  • ACL 在评分前过滤;
  • 指标与 CLI 在普通/-O 模式下确定一致。

如何扩展成真实实验

每次只改一层,并保存版本:

  1. 真实 tokenizer:替换计量器,记录包版本、encoding/model 标识与模型官方输入限制。
  2. 真实 embedding:保留同一 chunks/query/gold,记录模型、dimensions、归一化和批处理失败。
  3. 向量检索:记录距离度量、过滤语义、top-k、索引版本。
  4. reranker:固定初始候选集,另报 rerank 前后指标与延迟。
  5. 回答评测:增加引用支持、答案正确性、无答案拒答和安全测试。
  6. 影子重建:新策略写入新版本,完成对账后再切 published pointer。

网络实验若没有密钥或依赖,不要伪造运行结果;提交配置模板时仅使用占位符或 .env.example。

实战任务

必做任务 A:建立不相等的质量结果

扩展 corpus/query,使固定窗口恰好切断一个多单位证据锚点,而结构策略保留完整 element。不要修改评测代码来“制造胜利”;说明新增样本为何代表真实查询类型。

必做任务 B:做 overlap 消融

固定其他变量,对 overlap_units=0,4,8,16 分别运行,记录:

  • anchor Recall@3;
  • complete evidence rate;
  • body duplication ratio;
  • mean context units。

用表格给出选择与停止条件。若所有质量指标相同,应优先考虑更低重复策略,而不是声称更大 overlap 更好。

必做任务 C:错误分类

人工检查失败 query,将原因归为:缺标题、断条件、混主题、断表、代码边界、ACL、词汇不匹配、top-k 不足或 gold 错误。每类至少给一个可执行修正与可能副作用。

进阶任务:替换检索器

实现一个新 retrieve adapter,但保持 QueryCase、EvidenceAnchor 和 evaluate 不变。这样能独立比较检索后端,避免同时重写 gold 与指标。

掌握检查

  • 我能解释两个策略唯一的主要差异。
  • Gold 锚定 element/span,而不是固定 chunk 序号。
  • 我能区分 Recall、MRR、完整证据率与无答案准确率。
  • 我会把质量、重复、上下文和重建成本一起报告。
  • 我不会把离线词法实验外推为真实 RAG 结论。
  • 权限过滤发生在评分前,parent/合并阶段仍会复核。
  • 我能解释为何标题/表头变化可能不改正文 chunk,却必须使旧索引记录失效。
  • 普通与 -O 测试都通过,CLI 输出确定。
  • 任何动态模型/API 事实都有版本与获取日期。

参考资料

来源获取日期:2026-07-22;离线执行复核日期:2026-07-22。完成后返回 Chunking 策略目录,并进入 Embedding。完成下游检索课程后,可用 RAG 来源到引用证据链验证从 index entry 到 citation 的跨层身份。