Files
RAG/docs/01-data-and-evaluation.md
YoVinchen 75592af33a
Some checks failed
verify / verify (push) Has been cancelled
Isolate cloud model access before enabling product RAG workflows
The API and ingestion tools now use a fixed internal model gateway while
governed profiles, embedding cache assignments, traceable citations, and
stable API errors establish the boundaries required by later workflows.

Constraint: The current Alibaba Cloud workspace rejects all three live model calls with authentication failures
Rejected: Give the API or seed tools the Bailian key and direct egress | combines database access, cloud credentials, and public network access
Rejected: Mix offline and Bailian vectors in one demo namespace | makes profile activation and retrieval ambiguous
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep Bailian credentials and egress exclusive to model-gateway and create a new immutable profile hash for any embedding identity change
Tested: make verify; 121 backend tests; 14 frontend tests; fresh and populated Alembic upgrade-downgrade-upgrade; two idempotent offline seeds; Docker health and HTTP retrieval; isolated provider smoke
Not-tested: Successful live Bailian responses because the supplied workspace credential currently fails authentication
2026-07-13 04:09:06 +08:00

775 lines
35 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 数据构建、向量入库与系统评测设计
| 项目 | 内容 |
|---|---|
| 文档版本 | v1.0-design |
| 更新日期 | 2026-07-11 |
| 默认切片 | 目标 512 Token最大 800重叠 64 |
| 默认向量 | `text-embedding-v4`1024 维Cosine |
| 正式验证集 | 建议 300 题(构建/开发/盲测各 100500 题为扩展目标 |
## 1. 数据治理是入库前置条件
### 1.1 语料来源优先级
| 优先级 | 来源 | 适合内容 | 使用边界 |
|---|---|---|---|
| A | 学校、实验室、合作单位自有报告 | 勘查报告、钻孔描述、化验表 | 取得书面授权,确认不涉密且允许发送到云模型 |
| A | 明确 CC BY、CC0 等许可的数据 | 地层、地球化学、矿床描述 | 保存许可、DOI、版本和引用方式 |
| A | USGS 自制报告与数据 | 英文矿床模型、调查数据 | 多数自制内容属美国公共领域,但第三方图件例外,逐项检查[版权说明](https://www.usgs.gov/faqs/are-usgs-reportspublications-copyrighted) |
| B | 中国地质调查局“地质云”等公开服务 | 区域地质、资源环境产品 | 在线可看不等于允许批量下载、衍生入库或再分发,检查每项条款 |
| B | 国家科学数据中心 | 各类专题数据集 | 每个数据集许可独立,不用平台整体印象代替许可核验 |
| C | 开放获取论文 | 术语、矿床成因、方法综述 | 仅明确开放许可时保存全文,否则只保存合法题录/摘要 |
| 禁止默认纳入 | 商业数据库、付费标准、教材、来源不明扫描件 | — | 必须取得明确授权,不能因“下载得到”而入库 |
“公开访问”“研究可用”“允许批量下载”“允许建立衍生知识库”“允许公开发布”是五种不同授权。矿产地、钻孔、精确坐标和大比例尺图件还可能涉及受限地理信息或商业秘密。状态不明确时默认拒绝,而不是“先入库以后再处理”。
### 1.2 文档硬门禁
任何一个条件不满足,文档保持 `QUARANTINED_LOCAL_REVIEW`。授权人员必须在隔离、无外网、无模型调用的环境中完成完整解析/OCR、规范化、结构化切分和字段级脱敏生成待出域 bundle完成本地复核后进入 `LOCAL_PARSED_PENDING_CLOUD_REVIEW`。只有审核人批准这一个确切 bundle 后才能进入 `CLOUD_APPROVED`;在此之前严禁向量化、百炼调用、对普通用户展示或激活检索:
- 来源 URL/交付人、权利人、许可或授权证据可追踪;
- 保密/涉密等级已确认;
- 允许本项目处理和建立索引;
- 允许把选定字段发送到外部模型服务;
- 页码、报告编号或版本足以支持引用;
- 原始 SHA-256 与审核时一致;
- 是否允许随论文、演示或仓库发布衍生样例已单独标记。
原始 `raw_text` 永久保留在许可允许的受控边界内;另行生成每个待发送块的 `cloud_text`。精确坐标、单位名称或项目编号需要脱敏时,只修改 `cloud_text`,不得破坏原件。审批对象必须绑定 `raw_sha256 + parser_profile_hash + ocr_profile_hash + normalization_profile_hash + chunk_profile_hash + cloud_policy_id + outbound_manifest_sha256`manifest 对所有可能发送的 `cloud_text`、允许附带的标题/章节元数据、顺序和各自哈希做承诺。上述任一输入、处理器、规则、文本或元数据变化,数据库约束自动撤销原审批并回到 `LOCAL_PARSED_PENDING_CLOUD_REVIEW`。Embedding、Rerank 和 LLM 只能读取与当前已批准 manifest 完全一致的 `cloud_text`Worker 在发请求前再次核对哈希。
### 1.3 文档清单格式
`documents.jsonl` 示例仅使用虚构数据:
```json
{
"doc_id": "doc_demo_001",
"title": "某地区铜矿地质特征与找矿标志(演示)",
"authors": ["示例作者"],
"publisher": "示例机构",
"report_no": "DEMO-2026-001",
"doi": null,
"source_url": "https://example.invalid/document",
"retrieved_at": "2026-07-11",
"published_at": "2026-01-01",
"source_version": "v1",
"raw_sha256": "<sha256>",
"license_id": "INTERNAL-DEMO",
"license_url": null,
"rights_holder": "示例机构",
"permission_evidence": "<private-evidence-reference>",
"redistribution_allowed": false,
"confidentiality": "internal",
"security_review": "passed",
"review_state": "CLOUD_APPROVED",
"cloud_policy_id": "cloud-policy-v1",
"cloud_text_sha256": "<sha256>",
"outbound_manifest_sha256": "<sha256>",
"parser_profile_hash": "<sha256>",
"ocr_profile_hash": "<sha256-or-disabled>",
"normalization_profile_hash": "<sha256>",
"chunk_profile_hash": "<sha256>",
"cloud_approved_at": "2026-07-11T00:00:00Z",
"cloud_approved_by": "<reviewer-id>",
"regions": ["示例地区"],
"mineral_types": ["铜"],
"deposit_types": ["斑岩型"],
"geological_ages": ["侏罗纪"],
"language": "zh-CN"
}
```
真实授权文件放在受控文档系统,不提交到 Git仓库只保存字段定义和脱敏后的许可类型。
## 2. 原始资料到切片的数据流水线
```text
原始文件只读归档
-> MIME/加密/大小检查
-> 隔离环境完整解析/OCR/页码章节恢复/规范化/切分/脱敏
-> 人工复核低置信页与最终待出域内容
-> 生成 outbound manifest
-> LOCAL_PARSED_PENDING_CLOUD_REVIEW
-> CLOUD_APPROVED / REJECTED绑定 manifest
-> 向量化
-> 数据库校验
-> 文档版本激活
```
每一层都保存:输入哈希、处理器名和版本、配置哈希、输出哈希、开始/结束时间、错误码。原始文件不得被规范化结果覆盖。
### 2.1 数字 PDF
建议用 PyMuPDF 提取页级文本块和坐标,用 pdfplumber 辅助表格:
- 保存 `page_no``bbox`、阅读顺序、字体/字号等可用信息;
- 恢复多栏阅读顺序,不能直接按底层对象顺序拼接;
- 跨页重复出现于多数页面的页眉/页脚作为噪声候选,但删除前保留规则和样例;
- 不删除章节标题、图表编号、地层代号、钻孔号、元素符号、坐标、正负号和单位;
- 页码同时保存 PDF 物理页和报告印刷页,回答引用统一展示可理解的页码;
- 解析失败页单独标记,不能因整份文档大部分成功就静默跳过。
### 2.2 扫描 PDF 与 OCR
推荐流程:
1. 页面以 300 DPI 渲染;
2. 自动旋转、倾斜矫正、去噪和对比度增强;
3. PaddleOCR/PP-Structure 输出文本、坐标和置信度;
4. 地质词典用于提出纠错候选,不直接覆盖原始 OCR
5. 同时保存 `raw_text``normalized_text`、置信度和修订人;
6. 数值密集页、低置信页和表格页进入人工复核队列。
金标准页建议目标:
| 指标 | 目标 |
|---|---:|
| OCR 字符错误率 CER | ≤ 3% |
| 地质专业术语准确率 | ≥ 95% |
| 数字、符号和单位准确率 | ≥ 98% |
| 入库切片可追溯页码 | 100% |
平均置信度低于 0.85、关键数值置信度低、阅读顺序异常或单位缺失时,不允许自动激活。
### 2.3 表格
表格至少保存四种表示:
1. 原始页截图或坐标引用;
2. 结构化 JSON
3. 可展示 Markdown
4. 面向检索的逐行语义文本。
虚构示例:
```json
{
"table_id": "table_demo_p34_02",
"title": "ZK1203 钻孔样品分析结果(演示)",
"page_no": 34,
"columns": ["样品号", "起始深度_m", "终止深度_m", "Cu_pct"],
"rows": [
{
"样品号": "H12",
"起始深度_m": 125.4,
"终止深度_m": 127.2,
"Cu_pct": 0.86
}
],
"unit_notes": "Cu 品位单位为 %",
"source_bbox": [88, 201, 512, 690]
}
```
对应检索文本:
> 在演示表“ZK1203 钻孔样品分析结果”中,样品 H12 位于 125.4127.2 m铜品位为 0.86%。
表格按 1020 行切片,每个切片重复表名、列名、单位和页码。跨页表、续表、合并单元格必须抽检。目标为行列对齐 ≥ 97%,数值和单位 ≥ 99%。
### 2.4 图件与地质图
纯文本模型可处理图名、图注、图例 OCR、邻近解释文字和人工复核摘要但不能因此宣称理解断层、矿体或蚀变带在整幅图中的几何关系。
第一版把以下问题放入挑战集:
- 某断层位于矿体哪一侧;
- 某地层与侵入体在图上的接触关系;
- 剖面图中的空间延伸;
- 只存在于图形、没有文字描述的测量值。
后续如引入多模态向量和重排,需要新增数据格式、模型 ADR 和独立指标。
## 3. 规范化、切分和去重
### 3.1 规范化规则
允许:
- Unicode NFKC、全角/半角统一;
- 连续空白压缩、OCR 断行和错误连字符修复;
- 可逆的单位别名映射;
- 页眉页脚规则化删除;
- 在单独字段中保存术语同义词。
禁止:
- 全部英文转小写而破坏元素符号;
- 从受控原件删除数字、坐标、钻孔号、地层代号;云处理所需脱敏只作用于单独的 `cloud_text`
- 无原文依据扩写缩写;
- 把不同年代或不同报告的结论合并成“统一事实”;
- 用 LLM 改写后的正文替代原文。
### 3.2 基线切分
| 参数 | 初始值 |
|---|---:|
| 目标长度 | 约 512 Token |
| 最小长度 | 120 Token |
| 最大长度 | 800 Token |
| 相邻重叠 | 64 Token |
| 章节边界 | 不跨一级/二级标题 |
| 表格 | 1020 行,重复表头和单位 |
优先级为“章节 > 自然段 > 句子 > 长度兜底”。定义、枚举、完整成矿阶段和结论尽量保持在同一切片。若没有准确的百炼 tokenizer使用保守估算并让切片远小于 8192 Token而不是依赖服务端截断。
```text
display_text = 权限允许时贴近原文,用于展示和引用
cloud_text = 经字段级策略批准、可发送到云端的文本
embedding_text = [允许发送的标题/地区/矿种/章节路径] + cloud_text
```
是否添加元数据前缀由消融实验决定,不能把经验判断写成既定收益。
### 3.3 去重和版本
- 精确重复:规范化文本 SHA-256 相同;
- 文档近重复MinHash/Jaccard 参考阈值 0.92,进入人工确认;
- 切片近重复:参考阈值 0.95,保留主切片和来源别名;
- 修订版、扫描版、OCR 版归入同一 `duplicate_family_id`
- 有冲突的历史结论不去重,标记 `supersedes`/`conflicts_with`
- 同一重复族必须进入同一数据划分,避免评测泄漏。
## 4. Ground-truth 验证集生成
### 4.1 规模与题型
| 阶段 | 文档 | 切片 | 问题 |
|---|---:|---:|---:|
| 管线冒烟 | 510 | 100500 | 2030 |
| 试运行 | 50 | 2,00010,000 | 100 |
| 正式毕设 | 200500 | 10,00050,000 | 300500 为扩展) |
推荐正式最少 300 题,构建/开发/盲测各 100有足够标注人力时扩展至 500 题。题型比例:
| 类型 | 比例 |
|---|---:|
| 直接事实、术语定义 | 20% |
| 数值、钻孔、表格 | 20% |
| 地层—构造—矿化关系 | 15% |
| 多证据、多跳推理 | 15% |
| 区域或矿床比较 | 10% |
| 找矿方法与工作流程 | 10% |
| 无答案、歧义或冲突 | 10% |
每题标注模态、矿种、地区、难度、单跳/多跳、是否可回答和证据组,保证结果可以分层分析。
### 4.2 人工 + 半自动流程
1. 先按文档族完成数据划分。
2. 在每个 split 内按来源、矿种、题型和模态分层抽样。
3. LLM 只根据给定切片生成候选问题、短答案、证据原句和页码。
4. 自动检查证据是否真实存在、数字/单位是否一致、页码是否有效。
5. 标注员 A 修改或拒绝候选。
6. 标注员 B 独立验证答案、证据和可回答性。
7. 分歧交由地质专业教师或第三名标注员裁决。
8. 将所有参评的 Dense、词法、Hybrid 和 Rerank 系统候选取 union 建池,记录系统版本与 pooling depth初始建议每系统 Top 20。
9. 人工给池中候选 03 级相关性;新系统若在计分深度内返回未判定候选,必须补标,不能默认当作 0。
10. 冻结测试集、源证据锚点、corpus manifest 和整个数据集哈希。
大模型不能同时成为唯一出题者、唯一裁判和最终评分者。若团队资源有限,至少保证正式测试集双人审核,且 LLM 评分抽取 20% 以上由人复核。
### 4.3 评测问题格式
```json
{
"qid": "q_test_0042",
"query": "演示钻孔 ZK1203 在 125.4 至 127.2 米的铜品位是多少?",
"answerable": true,
"reference_answer": "0.86%",
"answer_aliases": ["0.86 %"],
"numeric_tolerance": {"absolute": 0.001, "unit": "%"},
"question_type": "table_numeric",
"difficulty": "medium",
"modality": "table",
"gold_evidence_groups": [
{
"required": true,
"doc_id": "doc_demo_001",
"document_version_id": "docver_demo_001",
"page_no": 34,
"char_start": 1280,
"char_end": 1348,
"bbox": [88, 201, 512, 238],
"evidence_sha256": "<sha256>"
}
],
"split": "test",
"security_classification": "internal",
"redistribution_allowed": false,
"created_by": "llm_assisted",
"review_status": "double_reviewed"
}
```
Gold 主锚点必须是跨切分配置稳定的源位置,不是 `chunk_id`。每次实验按 `anchor_resolver_version``doc/version/page/char offsets/bbox/evidence hash` 映射到本次切片并生成 run-specific qrels跨边界或 OCR 变化的歧义映射进入人工复核。这样 256/512/800 Token 等消融才使用同一事实真值。`evidence_text` 只可存在于有权限的内部 benchmark公开版本只保留哈希、允许公开的短引文或完全脱敏锚点。
Pool 中所有等级 1/2/3 的人工 judgment 也必须保存候选的源位置锚点、证据哈希、等级和判定理由,不能只有 Gold 使用稳定锚点。等级 0 保存候选来源锚点与“不相关/实体冲突”等理由。重新切片时解析全部 anchored judgments无法可靠映射的候选重新建池和补标否则该 run 不计算 nDCG。
### 4.4 无答案与困难负例
无答案题应覆盖:
- 语料完全没有的矿种或地区;
- 将真实问题的地区、矿床或年份替换形成的错误前提;
- 同矿种不同矿区、同地区不同矿种;
- 相邻但不能回答问题的切片;
- 新旧资料冲突且问题未限定时间;
- 缺少关键条件的歧义问题。
判定“无答案”前至少用关键词、向量和人工三种方式确认没有支持证据。随机无关句不够难,不能代表真实拒答能力。
### 4.5 构建、开发与测试隔离
RAG 不训练基础模型,这里的 train 更准确地说是“构建/校准集”:
- corpus 按文档族做 60/20/20而不是按切片随机划分问题集建议构建/开发/盲测各 100 题;
- 相同 DOI、报告号、扫描版、修订版和近重复文档必须在同一 split
- 构建集用于开发解析器、词典、Prompt 和初始参数;
- 开发集用于选择维度、切片、Top K、重排数量和拒答阈值
- 测试问题和答案在调参期间不可查看;
- Gold 问答、参考答案、qrels 绝不能写进 RAG 知识库;
- 主指标始终在冻结的全量 `train + dev + test` 文档索引上检索,并把 `corpus_manifest_sha256` 写入 run只检索 test 文档子库仅作诊断,不能与主指标混报;
- 盲测问题、答案和标签由独立保管人或受控凭据管理,开发者在参数冻结前不可访问;
- 同一次论文结论只运行一次冻结盲测。若盲测结果被用于修改参数,该测试集立即退役并转为开发数据,最终结论必须使用新的未见盲测集;更换 `run_id` 不能恢复盲测有效性。
标注一致性目标:相关/不相关二分类 Cohen's kappa ≥ 0.75,证据定位 F1 ≥ 0.85。数值、冲突和多跳问题全部双人审核。
### 4.6 评测数据的安全边界
问题、参考答案和证据锚点可能复制或推断受限原文,因此评测项继承源文档的许可、保密级别和销毁策略:
- 内部完整 benchmark 使用独立存储和独立访问凭据,不进入 Git
- 公开 benchmark 只包含获得再分发许可或充分脱敏的内容;
- 通用知识库导入器以 allowlist 指定语料目录,自动测试保证 `eval/``runs/` 和私有标注目录永不被扫描入库;
- 导出论文附件前再次执行版权、坐标和商业秘密检查;
- 评测备份、保留和删除与源资料策略一致。
## 5. 向量化 API 与写库测试
### 5.1 基线配置
```yaml
model: text-embedding-v4
dimension: 1024
encoding_format: float
distance: cosine
synchronous_batch_items: 8
chunk_target_tokens: 512
chunk_max_tokens: 800
```
百炼官方说明 `text-embedding-v4` 支持 642048 维、单次最多 10 条、单条最多 8192 Token北京单请求总计最多 33000 Token并推荐 1024 维作为通用平衡点,见[Embedding 规格](https://help.aliyun.com/zh/model-studio/embedding)。同步接口适合首版OpenAI Batch 适合数十万切片以上的离线任务,可降低调用价格,但需要提交、轮询、下载和失败行重试,因此不进入最小运行链路。
生产表固定 `vector(1024)`。512/1536 维消融每个维度使用独立 schema/table/HNSW index或一次性独立评测数据库通过相同不可变证据锚点生成各自 qrels不同维度绝不共用同一普通 HNSW 索引。
### 5.2 三层验证数据
第一层是 API 契约样例,验证字段、下标和维度;第二层是 100 组语义三元组;第三层是正式 qrels 验证集。
语义三元组格式:
```json
{
"id": "triplet_demo_001",
"query": "斑岩铜矿常见哪些蚀变找矿标志?",
"positive": "斑岩铜矿常见钾化、绢英岩化和青磐岩化分带。",
"hard_negative": "矽卡岩矿床常见于侵入岩与碳酸盐岩接触带。",
"tags": ["synonym", "deposit_type", "hard_negative"]
}
```
三元组至少覆盖:
- 地质术语同义表达;
- 同矿种不同地区;
- 同地区不同矿种;
- 数值和单位差异;
- 地层代号、钻孔号、图幅号;
- 中文简称与全称;
- OCR 轻微错误;
- 标题前缀有无。
建议门槛:`sim(query, positive) > sim(query, hard_negative)` ≥ 90%;原文式查询目标切片 Top 1合理改写 Top 3。不要使用未校准的固定余弦分数判断所有问题。
### 5.3 API 契约断言
用已轮换的新 Key、从 Secret 注入后执行:
1. 空字符串在客户端拒绝。
2. 批次数量超过 10 在客户端拒绝。
3. 单条超长文本由切分器拒绝或重新切分,不依赖服务静默截断。
4. 批次估算总 Token 超过 33000 时客户端重新分批。
5. HTTP 200 且返回模型可识别。
6. `len(data) == len(input)`
7.`data[].index` 恢复原输入顺序。
8. 每个向量长度严格为 1024。
9. 每个元素为有限浮点数,无 NaN/Inf。
10. L2 norm 大于 0。
11. 同一文本重复请求的余弦相似度接近 1。
12. 保存 request ID、Token 和耗时,不保存鉴权头。
13. 401/403/404 不自动重试429/5xx 有上限退避。
### 5.4 入库算法
外部请求和数据库事务分离:
```text
解析并持久化 chunks(status=PENDING)
-> 选取未向量化批次并建立幂等键
-> 调用 Embedding事务外
-> 验证返回数量、下标、维度和数值
-> 短事务批量 UPDATE/UPSERT embedding
-> 提交检查点
-> 全部完成后执行文档级一致性校验
-> 单事务更新 documents.active_version_id 和 searchable chunks 投影
```
Embedding profile 与缓存键分离:
```text
embedding_profile_hash = SHA256(
provider + endpoint_region + endpoint_identity_hash + api_mode + requested_model
+ resolved_model_revision_or_cache_epoch
+ dimension + output_type + text_type + instruct_hash
+ normalization_version
)
embedding_cache_key = SHA256(
embedding_text_sha256 + embedding_profile_hash
)
chunk_embedding_assignment = UNIQUE(chunk_id, embedding_profile_hash)
```
相同文本可以安全复用向量缓存,但每个 chunk 都必须建立独立 assignment不能用缓存键替代任务/行唯一键。模型别名无法解析到稳定 revision 时,每次冻结实验显式设置 `cache_epoch`,并把它纳入不可变的 `embedding_profile_hash`epoch 变化必须创建新 profile禁止原地修改 profile 后继续复用旧缓存。激活前再次核对当前 `embedding_text_sha256`、完整 profile 与 assignment任何一项变化都生成新索引版本。
`endpoint_identity_hash` 由规范化后的工作空间/部署端点身份计算,仅保存哈希而不在实验导出中暴露真实工作空间。这样同地域、同模型别名但不同 MaaS 工作空间的向量不会错误共用缓存。
### 5.5 数据库完整性查询(示意)
```sql
-- 1. 待激活且当前未删除版本中必须没有空向量
SELECT count(*) AS missing_vectors
FROM chunks
WHERE document_version_id = :version_id
AND approval_status = 'APPROVED'
AND deleted_at IS NULL
AND (
embedding IS NULL
OR index_status <> 'READY'
OR embedded_text_sha256 <> embedding_text_sha256
OR embedding_profile_hash <> :expected_profile_hash
);
-- 2. 所有向量维度必须一致
SELECT vector_dims(embedding) AS dims, count(*)
FROM chunks
WHERE document_version_id = :version_id
AND approval_status = 'APPROVED'
AND deleted_at IS NULL
GROUP BY vector_dims(embedding);
-- 3. 预期切片数、实际向量数应相等
SELECT
count(*) AS chunk_count,
count(embedding) AS vector_count
FROM chunks
WHERE document_version_id = :version_id
AND approval_status = 'APPROVED'
AND deleted_at IS NULL;
-- 4. Cosine 检索
SELECT id, document_version_id,
1 - (embedding <=> :query_vector) AS cosine_similarity
FROM chunks
WHERE searchable = true
AND deleted_at IS NULL
AND knowledge_base_id = ANY(:allowed_kb_ids)
AND access_scope_id = ANY(:allowed_scope_ids)
ORDER BY embedding <=> :query_vector
LIMIT 50;
```
`documents.active_version_id` 是唯一权威激活状态;`chunks.searchable` 只是为了 filtered HNSW 的事务性派生投影。激活事务先验证新版本全部 READY/哈希/profile 一致,再将同文档旧切片设为不可检索、新版本切片设为可检索,并更新 `documents.active_version_id` 后一次提交。约束触发器和 reconciliation 检查保证每份文档只有一个 active version且所有 searchable chunk 都属于该版本。权限过滤在候选生成时生效,而不是检索后才删除未授权结果。
### 5.6 写库验收清单
- `approved chunks == vector rows`
- `chunk_id` 唯一,重复任务行数不增长;
- 文本未变时不产生新模型费用;
- 文本改变时旧版本退出检索,新版本完整激活;
- `documents.active_version_id`、searchable 投影、当前文本哈希和 embedding profile 完全一致;
- 删除文档后立即无法检索,后台完成物理清理;
- 中断后从检查点恢复,不重复已提交批次;
- Docker 重启后数据库和文件仍存在;
- 备份恢复后行数、哈希和随机抽样向量一致;
- 每个向量可回溯模型、维度、文本哈希和请求 ID。
### 5.7 HNSW 质量测试
首个 schema 版本即建立 HNSW在专用评测事务中关闭 index/bitmap scan并使用完全相同的知识库和 access-scope 过滤得到 exact 真值。对至少 200 个查询比较 ANN Top 10 与 exact Top 10
```text
ANN Recall@10 = |ANN Top10 ∩ Exact Top10| / 10
```
Exact 基线在隔离连接中使用 `SET LOCAL enable_indexscan = off``SET LOCAL enable_bitmapscan = off`,查询仍保留相同的 `allowed_kb_ids/allowed_scope_ids`;不能拿无权限过滤的全库 exact 结果与过滤后的 ANN 比较。
建议目标 ≥ 0.98,同时记录索引构建时间、索引大小、查询 p50/p95、`ef_search`、iterative scan 和 over-fetch。数据很小时仍保留 exact 指标,不把 HNSW 的存在当作质量结论。
## 6. Rerank 与端到端验证
### 6.1 Rerank 契约
基线Dense Top 50 -> `qwen3-rerank` 保存 Top 10 供 MRR/nDCG@10 评测 -> 前 8 条进入生成上下文。
断言:
- 请求文档数和总 Token 未超限;
- 总 Token 按 `Query Tokens × Document 数量 + Document Tokens 总和` 计算,断言 Query 被按候选数重复计入;
- 候选在本地保留,响应 `index` 必须映射到正确候选;
- `results` 数量不超过 `top_n`
- 分数为有限数值且结果按降序;
- 记录是否发生客户端裁剪;
- 用同一 qrels 比较重排前后的 MRR/nDCG
- 初召回没有 Gold 时,错误归为 `RETRIEVAL_MISS`,不能归因给 Rerank。
### 6.2 生成验证
端到端结果保存:
```json
{
"qid": "q_test_0042",
"retrieved_chunk_ids": ["..."],
"vector_scores": [0.0],
"reranked_chunk_ids": ["..."],
"rerank_scores": [0.0],
"answer": "...",
"citations": [{"label": "S1", "chunk_id": "..."}],
"latency_ms": {"embedding": 0, "retrieve": 0, "rerank": 0, "generate": 0},
"usage": {"embedding_tokens": 0, "rerank_tokens": 0, "llm_input_tokens": 0, "llm_output_tokens": 0}
}
```
模型生成的引用编号必须由后端映射到真实切片。引用存在不等于支持该声明,还需人工或经过验证的 claim-evidence 评审。
## 7. 指标与建议目标
这些是项目目标而不是预先宣称的结果。试运行后、正式盲测前冻结。
| 层级 | 指标 | 建议目标 |
|---|---|---:|
| 数据 | searchable 切片可追溯当前文档版本/页码 | 100% |
| OCR 金标准页 | CER | ≤ 3% |
| 表格 | 数值及单位准确率 | ≥ 99% |
| 初召回 | Hit@20 | ≥ 0.85 |
| 初召回 | Hit@50 | ≥ 0.93 |
| 多跳初召回 | CompleteHit@20 | ≥ 0.70 |
| 多跳初召回 | EvidenceGroupRecall@20 | ≥ 0.85 |
| 初召回 | MRR@10 | ≥ 0.60 |
| 初召回 | nDCG@10 | ≥ 0.65 |
| 重排 | Hit@5 | ≥ 0.80 |
| 重排 | MRR@10 | ≥ 0.70 |
| 重排 | nDCG@10 | ≥ 0.75 |
| 重排收益 | nDCG@10 绝对提升 | ≥ 0.05 |
| 生成 | 专家答案通过率03 级中 ≥ 2 | ≥ 0.80 |
| 生成 | 事实忠实度 | ≥ 0.95 |
| 引用 | Citation Precision | ≥ 0.90 |
| 引用 | Citation Recall | ≥ 0.85 |
| 数值题 | 数值及单位正确率 | ≥ 0.95 |
| 无答案 | 拒答 F1 | ≥ 0.85 |
| 风险 | 无证据事实占比 | ≤ 5% |
| 端到端 | 完整成功率 | ≥ 0.75 |
| 多跳困难集 | 完整成功率 | ≥ 0.60 |
| 系统 | 稳态错误率 | < 1% |
| 系统 | 端到端 p95 | 建议 ≤ 10 秒 |
qrels 相关等级:
| 等级 | 定义 |
|---:|---|
| 3 | 直接回答问题或属于必要 Gold 证据组 |
| 2 | 明确支持关键子结论,但单独不足以完整回答 |
| 1 | 主题相关或提供背景,不能支持关键答案 |
| 0 | 无关、实体混淆或与问题条件不匹配 |
指标定义与分母:
- `Hit@K`Top K 至少包含一个等级 ≥ 2 的证据;
- `CompleteHit@K`:多跳问题的所有必要证据组都在 Top K
- `EvidenceGroupRecall@K`Top K 覆盖的必要证据组 / 全部必要证据组;
- `MRR@K`:第一个等级 ≥ 2 结果排名倒数的均值;
- `nDCG@K`:使用完整 03 等级衡量排序;
- 忠实度:可被证据支持的原子事实声明 / 全部事实声明;
- Citation Precision正确支持相关声明的引用 / 全部引用;
- Citation Recall带正确引用的必要事实 / 全部必要事实;
- 完整成功:答案正确、必要证据齐、引用正确且无关键无依据声明。
生成评分按以下冻结规约执行:
1. **专家答案等级**3 表示结论完整正确且无实质错误2 表示核心结论正确、仅有非关键遗漏1 表示只答对局部或有重大遗漏0 表示错误、矛盾、捏造,或应回答却拒答。`answerable=true` 的答案通过率以等级 ≥ 2 二值化;数值题还必须落在单位换算后的容差内。
2. **原子事实**:把答案拆为单一主语—谓语—宾语、单一数值+单位+对象、或单一时间/空间限定命题;“且/以及/分别”连接的可独立验证命题拆开,语义重复命题只计一次。纯格式、寒暄和引用标签不是事实。
3. **证据边界**:忠实度和 Citation Precision 的“证据”只指本次实际送入生成器的上下文切片;任意 Gold 原文不能事后补救未检索到的证据。专家正确性对照 reference answer 与稳定 Gold 锚点。一个声明的多个引用可按其并集判断是否充分蕴含。
4. **空分母**:正确拒答且没有事实声明时,忠实度和 Citation Precision 记为 N/A 并从宏平均分母排除;可回答题无引用时 Citation Recall 记 0Citation Precision 为 N/A有事实但无支持时这些事实全部计为无证据。无答案题交由拒答指标不用空答案抬高生成分数。
5. **一致关系**`无证据事实占比 = 1 - 事实忠实度`,两者使用同一原子事实分母,所以 ≥ 0.95 与 ≤ 5% 是同一验收约束,不重复计为两个独立成功条件。
6. **裁决**:冻结测试集的答案等级、原子事实边界和 claim-evidence 关系由两名评审独立标注;分歧由第三名地质专业评审裁决。报告原始一致率与 Cohen's kappa并保存脱敏后的裁决理由、rubric 版本和 reviewer blind 状态。
Hit/MRR/nDCG/CompleteHit 只在 `answerable=true` 上计算。无答案题单独计算拒答 Precision/Recall/F1正类定义为 `should_refuse=true`;不能把无答案题当作所有检索结果均错误后混进 MRR 分母。按问题类型、来源、模态、难度分别报告,并对查询做 1000 次 bootstrap 给出 95% 置信区间。RAGAS 可作为辅助自动评测,不能代替专家金标准;其方法将 RAG 拆为检索上下文、忠实使用和回答质量多个维度,见[RAGAs 论文](https://aclanthology.org/2024.eacl-demo.16/)。
Pooling 使用所有参评系统结果的 union。保存参与系统、模型/切片配置和 depth新系统若在指标计算深度内产生未判定候选先补标再计分。`unjudged` 不得默认等于 0否则指标会系统性偏向参与建池的系统。
## 8. 消融实验矩阵
为了避免组合爆炸,先在开发集逐项改变一个变量,再对最佳配置运行冻结测试集:
1. Closed-book LLM 与完整 RAG
2. 词法、Dense、Hybrid
3. 512、1024、1536 维;
4. 256、512、800 Token 切片;
5. 0、64、128 Token 重叠;
6. 原文与“标题/地区/矿种/章节”前缀;
7. OpenAI 兼容 Embedding 与原生 `query/document` 模式;
8. Top 20、50 候选重排;
9. 无 Rerank 与 `qwen3-rerank`
10. 上下文 Top 3、5、8
11. 表格 Markdown 与逐行语义化;
12. Query 改写开关;
13. `enable_thinking` 开关(只用于复杂题子集);
14. 数字 PDF 主集与 OCR/表格挑战集。
每个实验同时报告质量、p50/p95、Token、调用次数和每 1000 查询估算成本。不得只报告更高准确率而忽略费用和延迟。
## 9. 错误分类
| 代码 | 含义 |
|---|---|
| `SOURCE_LICENSE` | 许可或来源问题 |
| `CORPUS_MISSING` | 语料不存在答案 |
| `PARSE_LAYOUT` | 阅读顺序或版面错误 |
| `OCR_TERM` / `OCR_NUMBER` | 术语或数值 OCR 错误 |
| `TABLE_ALIGNMENT` | 表格行列错位 |
| `FIGURE_UNDERSTANDING` | 依赖图件空间关系 |
| `CHUNK_BOUNDARY` | 必要信息跨切片 |
| `DUPLICATE_CROWDING` | 重复切片占满候选 |
| `ENTITY_COLLISION` | 同名矿床/地区/地层混淆 |
| `RETRIEVAL_MISS` | 初召回未找到证据 |
| `RERANK_WRONG_ORDER` | 已召回但重排顺序错误 |
| `RERANK_TRUNCATION` | 候选被截断 |
| `GEN_HALLUCINATION` | 生成无依据内容 |
| `GEN_OMISSION` | 证据存在但答案遗漏 |
| `NUMERIC_UNIT` | 数值或单位错误 |
| `CONFLICT_VERSION` | 忽略资料版本冲突 |
| `CITATION_MISMATCH` | 引用不支持声明 |
| `REFUSAL_OVER/UNDER` | 过度拒答或应拒未拒 |
| `SYSTEM_TIMEOUT/RATE_LIMIT` | 外部超时或限流 |
错误记录包含 qid、运行配置、期望、实际、召回/重排列表、回答、引用、根因、严重性和修复建议。
## 10. 可复现实验目录与配置
```text
data/
raw_manifest.jsonl
documents.jsonl
pages.jsonl
chunks.jsonl
tables.jsonl
eval/
questions.jsonl
anchored_judgments.jsonl
judgment_set_manifest.json
evidence_groups.jsonl
runs/<run_id>/
config.yaml
qrels.tsv
anchor_resolution.jsonl
predictions.jsonl
metrics.json
errors.jsonl
```
真实受限数据位于 Git 之外;上面是运行时或受控数据区的逻辑结构,不代表可以提交原文。
`anchored_judgments.jsonl` 是全部 03 级 judgment 的权威快照;每行至少包含 schema 版本、qid、grade、理由、doc/version/page/char/bbox 锚点、evidence hash、标注人盲化 ID 和裁决状态。`judgment_set_manifest.json` 冻结 schema 版本、judgment set 版本、条目数、文件 SHA-256、问题集 SHA-256、创建时间和审批人。文件只追加新版本不原地覆盖。
每个 run 的 `qrels.tsv` 由同一版本的全部不可变、已判定源锚点(不仅是 Gold解析为切片级视图不是人工 judgment 的唯一存储:
```text
qid 0 chunk_id relevance_grade
q001 0 chk_a 3
q001 0 chk_b 1
```
补标或裁决变化会产生新的 judgment set 版本和哈希;同一比较表中的所有系统必须用该新版本重新生成 qrels 并重新计分。不同切片配置可以得到不同 chunk ID但必须来自同一 anchored judgment snapshot、resolver 版本和 corpus manifest禁止让不同系统偷偷使用不同真值集合。
`config.yaml` 至少冻结:
```yaml
dataset_version: geo-rag-eval-v1.0
dataset_sha256: <sha256>
anchored_judgments_schema_version: 1
judgment_set_version: geo-rag-judgments-v1.0
judgment_set_sha256: <sha256>
corpus_manifest_sha256: <sha256>
git_commit: <commit>
docker_image_digest: <digest>
database_schema_version: <revision>
parser_version: <version>
parser_config_hash: <sha256>
anchor_resolver_version: <version>
anchor_resolution_sha256: <sha256>
qrels_sha256: <sha256>
ocr_model: <version-or-disabled>
chunk_size: 512
chunk_overlap: 64
chunk_profile_hash: <sha256>
embedding_model: text-embedding-v4
embedding_dimension: 1024
embedding_index_namespace: embedding_1024_v1
embedding_api_mode: openai-compatible
embedding_profile_hash: <sha256>
embedding_endpoint_region: cn-beijing
embedding_endpoint_identity_hash: <sha256>
embedding_requested_revision: <provider-model-alias>
embedding_resolved_revision: <provider-revision-or-unknown>
embedding_cache_epoch: <controlled-epoch>
vector_distance: cosine
retrieval_mode: dense
retrieval_top_k: 50
hnsw_ef_search: 100
hnsw_iterative_scan: strict_order
ann_overfetch_k: 200
lexical_analyzer_version: <disabled-or-version>
hybrid_fusion: disabled
hybrid_dense_weight: null
hybrid_lexical_weight: null
rrf_k: null
rerank_model: qwen3-rerank
rerank_api_mode: bailian-compatible-rerank
rerank_endpoint_identity_hash: <sha256>
rerank_resolved_revision: <provider-revision-or-unknown>
rerank_cache_epoch: <controlled-epoch>
rerank_top_n: 10
context_top_n: 8
max_context_tokens: 10000
rerank_instruct_hash: <sha256>
generator_model: deepseek-v4-flash
generator_api_mode: openai-compatible
generator_endpoint_identity_hash: <sha256>
generator_resolved_revision: <provider-revision-or-unknown>
generator_cache_epoch: <controlled-epoch>
provider_capability_fingerprint: <sha256>
prompt_hash: <sha256>
enable_thinking: false
enable_search: false
temperature: 0.0
top_p: 1.0
max_output_tokens: 1200
random_seed: 42
```
若供应商响应不暴露 resolved revision字段必须写 `unknown`,同时保存脱敏 endpoint identity、受控 cache epoch、能力探测指纹和响应中的 model 字段;观察到别名行为漂移时提升 epoch禁止继续复用旧缓存。只有配置、代码、数据集/判断集哈希和镜像均冻结,论文中“同一实验”才具有可复现含义。