# 数据构建、向量入库与系统评测设计 | 项目 | 内容 | |---|---| | 文档版本 | v1.0-design | | 更新日期 | 2026-07-11 | | 默认切片 | 目标 512 Token,最大 800,重叠 64 | | 默认向量 | `text-embedding-v4`,1024 维,Cosine | | 正式验证集 | 建议 300 题(构建/开发/盲测各 100),500 题为扩展目标 | ## 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": "", "license_id": "INTERNAL-DEMO", "license_url": null, "rights_holder": "示例机构", "permission_evidence": "", "redistribution_allowed": false, "confidentiality": "internal", "security_review": "passed", "review_state": "CLOUD_APPROVED", "cloud_policy_id": "cloud-policy-v1", "cloud_text_sha256": "", "outbound_manifest_sha256": "", "parser_profile_hash": "", "ocr_profile_hash": "", "normalization_profile_hash": "", "chunk_profile_hash": "", "cloud_approved_at": "2026-07-11T00:00:00Z", "cloud_approved_by": "", "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.4–127.2 m,铜品位为 0.86%。 表格按 10–20 行切片,每个切片重复表名、列名、单位和页码。跨页表、续表、合并单元格必须抽检。目标为行列对齐 ≥ 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 | | 章节边界 | 不跨一级/二级标题 | | 表格 | 10–20 行,重复表头和单位 | 优先级为“章节 > 自然段 > 句子 > 长度兜底”。定义、枚举、完整成矿阶段和结论尽量保持在同一切片。若没有准确的百炼 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 规模与题型 | 阶段 | 文档 | 切片 | 问题 | |---|---:|---:|---:| | 管线冒烟 | 5–10 | 100–500 | 20–30 | | 试运行 | 50 | 2,000–10,000 | 100 | | 正式毕设 | 200–500 | 10,000–50,000 | 300(500 为扩展) | 推荐正式最少 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. 人工给池中候选 0–3 级相关性;新系统若在计分深度内返回未判定候选,必须补标,不能默认当作 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": "" } ], "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` 支持 64–2048 维、单次最多 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 | | 生成 | 专家答案通过率(0–3 级中 ≥ 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`:使用完整 0–3 等级衡量排序; - 忠实度:可被证据支持的原子事实声明 / 全部事实声明; - 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 记 0,Citation 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// config.yaml qrels.tsv anchor_resolution.jsonl predictions.jsonl metrics.json errors.jsonl ``` 真实受限数据位于 Git 之外;上面是运行时或受控数据区的逻辑结构,不代表可以提交原文。 `anchored_judgments.jsonl` 是全部 0–3 级 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: anchored_judgments_schema_version: 1 judgment_set_version: geo-rag-judgments-v1.0 judgment_set_sha256: corpus_manifest_sha256: git_commit: docker_image_digest: database_schema_version: parser_version: parser_config_hash: anchor_resolver_version: anchor_resolution_sha256: qrels_sha256: ocr_model: chunk_size: 512 chunk_overlap: 64 chunk_profile_hash: embedding_model: text-embedding-v4 embedding_dimension: 1024 embedding_index_namespace: embedding_1024_v1 embedding_api_mode: openai-compatible embedding_profile_hash: embedding_endpoint_region: cn-beijing embedding_endpoint_identity_hash: embedding_requested_revision: embedding_resolved_revision: embedding_cache_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: 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: rerank_resolved_revision: rerank_cache_epoch: rerank_top_n: 10 context_top_n: 8 max_context_tokens: 10000 rerank_instruct_hash: generator_model: deepseek-v4-flash generator_api_mode: openai-compatible generator_endpoint_identity_hash: generator_resolved_revision: generator_cache_epoch: provider_capability_fingerprint: prompt_hash: 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,禁止继续复用旧缓存。只有配置、代码、数据集/判断集哈希和镜像均冻结,论文中“同一实验”才具有可复现含义。