项目:可降级规则重排器

项目目标

本项目不训练神经模型,而是实现生产适配层最容易被忽略的部分:

strict candidate fixture
        ↓
authorization revision + tenant / ACL / status / effective-time 防御复核
        ↓
safe candidate window
        ↓
透明 rule provider(可模拟五种状态)
        ↓
exact output-ID / finite-score / feature 校验
        ↓
正常重排 or 同一安全窗口 first-stage fallback
        ↓
完整输入/决策证据指纹 + canonical cap + MRR/nDCG/Precision + 安全报告

规则分只验证 orchestration;它不是 Cross-Encoder、LTR 或 LLM 的质量替代。

项目文件

Fixture 解析拒绝重复 JSON key、NaN/Infinity、会在转换为 float 时溢出的超大整数、未知/缺失字段、错误类型、重复 ID/rank、不连续 first rank、非法日期、错误有效期、不可访问 qrels,以及仍可访问的 must_not_return。Query 必须携带 authorization_revision,用于把本次候选判定绑定到明确的授权快照版本;教学脚本只校验并记录该值,不负责从真实 IAM 拉取或验证快照。有效期统一为半开区间 [effective_from, effective_to):起点当天有效,终点当天已过期,与 RAG 项目使用同一契约。

候选与质量上限

Query 是“退款审核通过后多久到账”,as-of=2026-07-14。9 个 first-stage 候选中:

  • 6 个满足 alpha/guests、published 与有效期;
  • d-07 因 wrong tenant 排除;
  • d-08 因 expired 排除;
  • d-09 因 ACL denied 排除。

Qrels 有 4 个正例:申请入口 grade 1、到账正文 grade 3、FAQ grade 2、延迟处理 grade 2。安全候选的 first rank 分别为 2/4/5/6:

  • window=6:4/4,Candidate Recall=1;
  • window=3:只含 rank 2,Candidate Recall=0.25。

这让窗口上限可被直接验证,而不是只在正文里宣称。

透明评分

Analyzer 做 NFKC/casefold、ASCII 字母数字串与中文重叠二字符。规则特征为:

  • title_coverage;
  • body_coverage;
  • exact_phrase;
  • score = 2 × title coverage + body coverage + exact phrase。

这组权重只是确定性 fixture 设计,不可推广。正常输出仍要通过与外部模型相同的契约:

  • ID 必须等于整个输入 window 的 exact set;
  • 每个 ID 恰好一次;
  • score/features 必须是有限数值;
  • 未知、遗漏、重复、空结果均 invalid;
  • 平局按 first-stage rank,再按 ID。

正常结果

默认 window=6、top-n=3、每 canonical source 最多 2 条:

路径排名MRR@3nDCG@3Precision@3
First staged-01, d-02, d-030.5000000.0607080.333333
Rule rerankd-04, d-05, d-061.0000001.0000001.000000

security_violations 必须为空。结果只证明该手工规则在当前 9 条 fixture 上按设计工作,不代表真实模型收益。

证据指纹与输出边界

报告提供两个完整的 64 字符小写十六进制 SHA-256:

  • fixture.signature / evidence.fixture_sha256 绑定解析后的规范输入:query ID/text/tenant/groups/as-of/authorization revision、全部候选的 ID/canonical ID/title/text/tenant/ACL/status/半开有效期/source revision/first rank/score,以及 fixture settings、qrels 和 must-not-return;
  • evidence.evidence_sha256 在上述输入上继续绑定实际运行参数、failure mode、model revision、过滤/window/fallback、first/final ranking、指标和安全结果。

测试会逐项改变安全或评分输入,确认两个指纹随之变化;CLI override 不改变 fixture 指纹,但必须改变 evidence 指纹。这样可以发现“报告仍在引用旧输入”的错配,但哈希本身不证明数据真实、授权正确或执行环境可信,生产系统仍需签名 manifest、可信时间和不可篡改存储。

整个 CLI JSON 明确标记 visibility=protected_audit。它包含候选 ID、过滤原因、窗口、qrels 指标和故障信息,只能进入受控审计/教学通道,不能直接作为面向用户的 RAG/public search 响应;visibility 是契约标签,不是鉴权机制。

失败模式

CLI 提供:

failure模拟fallback_reason
none完整合法结果null
timeoutprovider 超时timeout
errorprovider/server 错误provider_error
empty空 listinvalid_output
malformed重复 ID、漏 IDinvalid_output

四个失败模式都应:

  • rerank_applied=false;
  • final 与 safe first-stage 完全一致;
  • 排名 d-01, d-02, d-03;
  • security_violations 为空;
  • wrong tenant、expired、ACL denied 不会被 fallback 带回。

运行正常与失败路径

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

$env:PYTHONDONTWRITEBYTECODE = '1'  # 禁止 Python 写入字节码缓存,保持项目文件可审计。
$script = '.\docs\Reranking\examples\toy_reranker.py'  # 保存可执行的本地教学脚本路径。
$fixture = '.\docs\Reranking\examples\reranker-fixture.json'  # 保存脚本要加载的严格 fixture 路径。
 
python -B -W error $script --fixture $fixture  # 运行无故障路径,正常输出应包含 rerank_applied=true。
if ($LASTEXITCODE -ne 0) {  # Python 进程异常退出才进入这里;provider fallback 本身仍会返回 0。
    throw "正常路径失败,退出码:$LASTEXITCODE"  # 中止脚本,避免继续解释不可信的后续结果。
}
 
foreach ($mode in @('timeout', 'error', 'empty', 'malformed')) {  # 逐一覆盖四类可恢复 provider/输出故障。
    python -B -W error $script --fixture $fixture --failure $mode  # 请求脚本输出受控 fallback,而不是抛出未处理异常。
    if ($LASTEXITCODE -ne 0) {  # 仅 fixture 或参数等真正 CLI 错误应令退出码非零。
        throw "失败模拟 $mode 未产生受控报告,退出码:$LASTEXITCODE"  # 强制暴露未被适配层处理的故障。
    }
}

Provider 故障是被适配层成功处理的业务状态,因此 CLI 返回 0 并输出 fallback 报告;fixture/参数错误才返回 2。

运行测试

$env:PYTHONDONTWRITEBYTECODE = '1'  # 不把测试产生的缓存提交进知识库。
$project = '.\docs\Reranking'  # 保存项目目录,以便在相对 examples 路径下运行发现器。
 
Push-Location $project  # 临时进入项目目录,让 unittest 的相对搜索路径可预测。
try {  # 无论中途测试是否失败,finally 都会恢复原来的工作目录。
    python -B -m unittest discover -s .\examples -p 'test_toy_reranker.py' -v  # 在普通解释器模式执行全部测试。
    if ($LASTEXITCODE -ne 0) {  # 检查外部 Python 进程的退出码,而不是依赖 PowerShell 异常。
        throw "普通模式测试失败,退出码:$LASTEXITCODE"  # 失败时立即停止,不掩盖首个错误。
    }
 
    python -B -O -m unittest discover -s .\examples -p 'test_toy_reranker.py' -v  # 在 -O 模式复跑,确保生产校验不依赖裸 assert。
    if ($LASTEXITCODE -ne 0) {  # 非零表示优化模式下仍有可复现失败。
        throw "优化模式测试失败,退出码:$LASTEXITCODE"  # 把退出码保留在错误中便于定位。
    }
 
    python -B -W error -m unittest discover -s .\examples -p 'test_toy_reranker.py' -v  # 将所有 warning 升级为错误后复跑。
    if ($LASTEXITCODE -ne 0) {  # 此处可发现普通模式忽略的弃用或资源问题。
        throw "warnings-as-errors 模式测试失败,退出码:$LASTEXITCODE"  # 保持失败信息面向初学者可读。
    }
 
    python -B -O -W error -m unittest discover -s .\examples -p 'test_toy_reranker.py' -v  # 同时覆盖 -O 与严格 warning 的最严组合。
    if ($LASTEXITCODE -ne 0) {  # 任何非零都说明该组合下的契约没有被满足。
        throw "优化且 warnings-as-errors 模式测试失败,退出码:$LASTEXITCODE"  # 让调用者得到准确的失败模式和退出码。
    }
}
finally {  # finally 即使在 throw 后也会执行。
    Pop-Location  # 恢复执行前的 PowerShell 工作目录,避免影响后续命令。
}

普通、-O、-W error 与 -O -W error 四种模式各 30 项测试;正确性不得依赖会被优化移除的 bare assert,也不得在正常模式忽略 warning。

关键实验

实验 A:窗口上限

python -B -W error $script --fixture $fixture --candidate-window 3 --output-top-n 3  # 缩小安全候选窗口,观察 candidate recall 上限。

确认 candidate_recall_at_window=0.25,grade-3 到账正文不在窗口。此时重排规则再好也无法让它进入 top-3。

实验 B:Canonical cap

python -B -W error $script --fixture $fixture --max-per-canonical 1  # 每个 canonical source 最多保留一条,演示多样性后处理。

默认 d-04/d-05 属于同一 canonical document;cap=1 后第二条被跳过,由 d-06 和 d-02 补位。比较 item-level nDCG 与 source/evidence coverage,不能只宣称“更多样”。

实验 C:输出攻击

在测试中构造 unknown ID、重复 ID、漏 ID、NaN score 和非数值 feature,确认都触发 OutputContractError/fallback,且不进入 final ranking。

实验 D:接入真实 Cross-Encoder

替换 simulate_provider 时保留:

  1. 相同 safe window;
  2. request/response schema 与 exact ID 校验;
  3. model/tokenizer/input-template revision;
  4. token/truncation、batch、deadline 和耗时;
  5. 正常与所有失败路径;
  6. first-stage/rule/新模型、关键切片和端到端对比。

与生产系统的差距

本项目没有神经模型、GPU/队列、真实网络、batching、tokenizer/truncation、rate limit、重试/断路、缓存、分布式 trace、真实 IAM/授权快照校验、审计通道访问控制或敏感日志治理。authorization_revision 和 visibility 只是被严格绑定的教学契约字段,不是鉴权实现。透明 n-gram 规则只适合教学,不应被包装为 relevance 服务。

项目验收

  • Candidate recall 上限能随 window 改变被复现。
  • 硬过滤在模型前,fallback 只用 safe window。
  • 输出 ID exact、唯一、完整,score/features 有限。
  • 四类故障均产生确定、可观测 fallback。
  • Normal/fallback first-stage、model、final provenance 分离。
  • 授权修订和全部安全/评分输入进入 fixture/evidence 完整 SHA-256。
  • Protected audit envelope 不直接作为公共响应,且访问控制独立实现。
  • Canonical cap 从后续候选补位。
  • 普通、-O、-W error 与 -O -W error 四种模式各 30 项测试通过。
  • 无凭据、网络、缓存或第三方依赖。
  • 当前规则指标不外推为模型性能。

自测

  1. 为什么 window=3 时 grade-3 正例不可能被重排到首位?
  2. 为什么空 model output 不能自动解释为“全部不相关”?
  3. 为什么 fallback 必须复用 safe window,而不是原始未过滤列表?
  4. 为什么 output contract 要求 exact ID set?
  5. Canonical cap 如何同时改善多样性又可能降低 item nDCG?
  6. 为什么 authorization_revision 和 visibility 都不能替代真实鉴权?
  7. 接入真实 Cross-Encoder 后必须新增哪些版本与延迟字段?

完成后返回 Reranking 目录,并进入 RAG。

参考资料

来源获取日期:2026-07-22。教学行为以当前 fixture、脚本与测试为准;动态模型/API 按锁定版本复查。