评测与迁移项目
项目目标
本项目用两套完全隔离的手工向量空间练习四件事:
- 空间契约与向量输入校验;
- ACL 过滤后的 exact similarity search;
- Recall@k、MRR、nDCG 与子组报告;
- 模型迁移的 inventory/canonical 对账与 top-k 变化审计。
它不下载或运行真实 embedding 模型,输出也明确声明 hand-authored vectors。这能验证代码和门禁,不代表任何模型的业务效果。
项目文件
[[Knowledge/AI Agent Engineer/docs-CN/Embedding/examples/evaluate_embedding_space.py|evaluate_embedding_space.py]]:严格加载、数学函数、检索、指标、inventory 和 migration audit;[[Knowledge/AI Agent Engineer/docs-CN/Embedding/examples/embedding-fixture.json|embedding-fixture.json]]:两个空间、20 个空间内 items 和 4 条分级相关 query cases;[[Knowledge/AI Agent Engineer/docs-CN/Embedding/examples/test_evaluate_embedding_space.py|test_evaluate_embedding_space.py]]:32 项标准库unittest。
脚本只输出 ID、分数、指标与契约摘要,不输出真实凭据;fixture 只有教学文本和手工向量。
两套空间刻意不兼容
| 字段 | toy-v1 | toy-v2 |
|---|---|---|
| provider/model | local-fixture / toy-bi-encoder | 相同教学名称 |
| revision | v1 | v2 |
| dimension | 4 | 5 |
| metric | cosine | dot |
| normalized | false | true |
| dtype | float32 | float32 |
| items | 6 documents + 4 queries | 相同 canonical items |
即使两套空间的文本与 item IDs 相同,revision、dimension、metric 和 normalization 已不同,向量不能直接交叉比较。migration_audit因此报告 vectors_directly_comparable=false。
每个空间中的 query/document role 都显式为 query / document。真实模型可能使用不同 method、task type 或 prompt,应把实际值写入契约。
严格输入门禁
Fixture 顶层只有 contracts/items/queries。加载器拒绝:
- 重复 JSON key、NaN/Infinity 和超过 5 MiB 的 fixture;
- 多余/缺失字段;
- 重复 space 或 item;
- 不存在的空间、错误 role;
- bool 冒充数字、错维、零向量、NaN/Inf,以及会在转换为 float 时溢出的超大整数;
normalized=true但 norm 不在容差内;- 空文本、重复 ACL 和坏 source revision;
- query/gold 缺项、grade 不在 1..3;
- gold document 对查询主体未授权。
文本先做 LF 与 Unicode NFC 规范化,再计算 content_sha256。迁移审计用 role、text、ACL、source revision 与 hash 比较 canonical 数据。
检索流程
search 对一个空间执行:
- 找到该空间的 query item;
- 验证它使用 contract.query_role;
- 主体 groups 为空时 fail-closed 返回空;
- 只保留同空间、document role、ACL 相交的 documents;
- 用该空间 contract.metric 算 exact score;
- 按 score 降序、item ID 升序稳定打破平分;
- 返回 top-k。
未授权文档即使与 query 向量完全相同,也不会进入评分候选。项目只演示 group OR 语义,不是完整租户/IAM 实现。
项目没有 score threshold,所以 authorized 集里即使低分/零分也可能补足 top-k。这正好提醒:最近邻不等于可回答;无答案阈值必须在真实开发集按空间校准,本 fixture 不评测端到端拒答。
三个检索指标
Recall@k
把 relevance grade 大于 0 的 documents 都视为相关:
它关注必要候选是否找全,不关心相关项内部排序。
MRR
对每个 query 找第一个相关项的排名 ,记 ;未命中为 0,再对查询平均。MRR 强调首个相关项出现多早,但不能保证多证据齐全。
nDCG@k
项目允许 grade 1..3。第 位的增益:
再除以理想排序的 IDCG@k。nDCG 会惩罚高等级证据排在低等级证据之后。一个 ranked list 中每个 item ID 必须唯一;重复同一高等级 ID 会重复计算 gain,甚至制造大于 1 的伪 nDCG。项目因此在 Recall、MRR 与 nDCG 的公共入口统一拒绝重复 ID,并验证 qrels grade 是 1~3 的非布尔整数。指标仍必须一起看:Recall 1.0 可能伴随较低 nDCG。
运行项目
从项目根目录(同时包含 docs/ 与 .website/)运行:
$env:PYTHONDONTWRITEBYTECODE = '1' # 禁止写入 Python 字节码缓存,保持项目目录可复现。
python -B -W error '.\docs\Embedding\examples\evaluate_embedding_space.py' # 用默认 k=3 执行离线 exact 检索和迁移审计。可显式改变 k 或 fixture:
python -B -W error '.\docs\Embedding\examples\evaluate_embedding_space.py' --k 2 # 覆盖默认值,只取每条查询的前两个授权候选。非正整数 k 会失败,不会静默改成默认值。
当前确定性结果
截至 2026-07-22,默认 k=3:
| 空间 | item 数 | 原始向量理论 bytes | mean Recall@3 | MRR | mean nDCG@3 |
|---|---|---|---|---|---|
| toy-v1 | 10 | 160 | 1.0 | 1.0 | 1.0 |
| toy-v2 | 10 | 200 | 1.0 | 1.0 | 1.0 |
estimated_raw_vector_bytes = item_count × dimension × 4,不含 ID、metadata、索引、对齐或副本。
迁移报告:
- inventory match:true;
- canonical match:true;
- mechanical gates pass:true;
- 三个质量均值 delta:0;
- mean top-k Jaccard:0.875;
- quality decision required:true。
为什么质量均值全相同但 Jaccard 不是 1?image-example的首个相关文档两边都正确,后两个零分候选因空间坐标和平分顺序不同而变化。业务指标没有退化,但候选集合发生改变。真实迁移要检查 per-query 变化和下游影响,不能只看三个均值。
“机械门禁通过”也不自动表示可以上线;许可、隐私、延迟、费用、关键子组和回滚仍需决策,所以报告固定保留 quality_decision_required=true。
运行测试
$env:PYTHONDONTWRITEBYTECODE = '1' # 不把测试运行产生的缓存文件留在课程目录中。
python -B -m unittest discover -s '.\docs\Embedding\examples' -p 'test_evaluate_embedding_space.py' -v # 正常模式运行全部单元测试并显示每个用例。
python -B -O -m unittest discover -s '.\docs\Embedding\examples' -p 'test_evaluate_embedding_space.py' -v # 优化模式复跑,检查没有依赖裸 assert。
python -B -W error -m unittest discover -s '.\docs\Embedding\examples' -p 'test_evaluate_embedding_space.py' -v # 将 warning 升级为错误,发现弃用或资源问题。
python -B -O -W error -m unittest discover -s '.\docs\Embedding\examples' -p 'test_evaluate_embedding_space.py' -v # 同时覆盖优化与严格 warning 的最严运行组合。测试覆盖:
- contract 签名、非法 dimension/metric/role/dtype;
- normalize、cosine、dot、Euclidean 与坏数值;
- 严格 JSON、精确字段、重复 ID、错空间;
- 向量维度、bool、零向量、normalized norm;
- query/gold existence、grade 与授权;
- 两空间预期排名、ACL 评分前过滤、空主体 fail-closed;
- Recall/MRR/nDCG 已知算例、重复排名与非法 grade 失败关闭;
- 子组、inventory bytes 与 norm;
- 迁移的 inventory/canonical drift;
- CLI 普通与
-O输出逐字节一致; - 非正
k失败且 stderr 为 UTF-8。
如何读迁移审计
Mechanical gates
inventory_match:旧新 item ID 集合一致;canonical_match:同 ID 的 role/text/ACL/source revision/hash 一致;mechanical_gates_pass:以上两项都通过。
这些门禁抓“漏编码、错正文、ACL 漂移”,不比较向量数值是否相同;新空间本来就应产生不同向量。
Quality delta
对同一 query/gold,计算新均值减旧均值。Delta 为 0 不代表两空间相同,也不代表没有关键 query 退化。查看 details、subgroups 和 per_query_agreement。
Top-k Jaccard
它测候选集合变化,不判断哪边更相关。必须与人工 relevance、rerank 和最终回答联合解释。
必做实践
A. 制造可解释退化
修改 toy-v2 的 q-retry 或 doc-retry 向量,让高等级相关文档掉到第二/第四位。预测 Recall、MRR、nDCG 哪些变化,再运行验证。不要修改 gold 迎合结果。
B. 测试 canonical drift
只改 toy-v2 doc-timeout 的正文,保持 item ID。观察 canonical_mismatches 和 mechanical gate;解释为什么“向量生成成功”仍不得切换。
C. 测试角色和归一化错误
分别:
- 把 document role 写成 query;
- 给
normalized=true的向量 norm=2; - 用 bool 代替一个数值。
记录加载器在哪一层失败,以及这类错误若未拦截会怎样污染索引。
D. 增加子组
新增专名、数字、英文或代码 query,不改变现有 query。为每条标 grade 1..3,并比较总体与子组指标。确保 gold documents 对主体可见。
E. 设计无答案阈值实验
不要直接给脚本写一个“0.8 通用阈值”。先准备有答案/无答案开发集,分别在 toy-v1/toy-v2 校准;保存 precision/recall 或误报/漏报;最后在未调参测试集评估。
接入真实模型的最小改造
保持 QueryCase、relevance 与指标层不变,新增 provider adapter:
- 固定 canonical texts 和 input hashes;
- 保存官方 model/revision/role/dimension/metric/normalize/SDK/获取日期;
- 文档和 query 分别按官方契约编码;
- 对响应逐项 ID/维度/finite/norm 对账;
- 写入新
space_id,不覆盖 toy 或旧生产空间; - 先用 exact float 比较候选模型;
- 再单独测降维、量化、ANN 和 rerank;
- 原始 per-query 结果保留为可审计产物,但不要提交敏感正文或大型向量数据。
无密钥或无网络时明确记录“未执行”,不能把 fixture 结果写成真实模型成绩。
发布验收清单
- Gold、开发集与保留测试集用途分开。
- Canonical item、ACL、revision 和删除水位对账。
- 每个空间有完整签名且物理/逻辑隔离。
- Query/document 角色按当前官方文档编码。
- 质量报告含 per-query、关键子组与多个指标。
- Score threshold 在每个空间开发集单独校准。
- 延迟、吞吐、存储、重建时间和费用在预算内。
- 越权、空主体与 parent 展开测试 fail-closed。
- 影子/灰度、原子 alias、监控和回滚演练通过。
- 许可、隐私、保留与删除策略获批。
- Fixture 成绩没有被表述为真实模型效果。
参考资料
来源获取日期:2026-07-22。完成后返回 [[Knowledge/AI Agent Engineer/docs-CN/Embedding/00-目录|Embedding 目录]],继续 [[Knowledge/AI Agent Engineer/docs-CN/向量数据库/00-目录|向量数据库]]。