一段答案后面挂满了方括号,看起来总比空口断言可靠。点开引用,资料也确实存在。检查似乎已经走完,直到有人追问:这条材料究竟支持了句子里的哪一件事?
引用可以装得很整齐,分母却可能悄悄换了。按题平均和按句平均不是同一个问题;正文写了五个引用,也不保证评测器真的检查了五个。要把生成后的答案交给上一篇的发布门禁,先得知道那一个支持率,究竟怎么算出来。
本篇不再编写合成回答充当模型输出。实际运行的是一套原创 Node.js 审计程序,输入来自 ALCE 项目公开存档的真实历史模型输出及既有人工、自动评审记录。本文没有调用生成模型,没有重新运行 NLI 模型,也没有新增真人标注。 重跑的是数据核验和统计,不是重现当年的模型生成过程。资料核对日期:2026 年 10 月 5 日。
1. 先把可复现的范围圈出来#
ALCE 论文把带引用生成的评价拆成多个维度,其中包括答案正确性与引用质量。本文只研究其公开引用评审档案中的一个固定切片,不用这一小块材料替整个 RAG 系统打总分。这是上游选定模型与问题的人类评审样本,不是完整 ASQA 数据集的随机无偏评测。
输入锁定在官方仓库提交 246c476a4edfc564266b7346b6e29ef4861ae937 的 human_eval_citations_completed.json,选择 asqa 下的 asqa-gpt-35-turbo-gtr-shot2-ndoc5-42-azure.json。这是一个有名称、有原始字节、有问题 ID 的历史配置,而不是今天还能原样调用的模型接口。档案未提供可唯一恢复的服务端模型快照;配置名中的模型别名,不能被写成精确权重版本。
复现分两层:下载器检查提交地址和 SHA-256,保证拿到指定字节;审计器检查字段、配对、标签取值和分母约定,保证这些字节按同一规则被计数。前一层不能证明标注正确,后一层也不能证明旧知识今天仍然正确。二者合起来能回答的是:这份存档里的结果,在明确规则下怎样汇总,哪里有分歧。
网站仅提供原创代码、统计结果和可定位的问题 ID,不打包复制整份模型回答与检索段落。源仓库有 MIT 许可,但检索材料涉及 Wikipedia 等底层来源,不能据此假定所有来源文字都变成 MIT。下载器把原始 JSON 留在读者本地;若要再分发原文,应另行检查相应材料的许可与署名要求。原始 JSON 始终作为数据读取,不执行其中任何内容。
2. 三种检查对象,别挤进一个支持率#
第一层是结构:一句话有没有 [1] 这样的数字标记,能不能找到引用记录,记录与答案是否对应。正则表达式适合做这种工作。它不会理解时间、条件、否定或因果,更不会判断一篇文档有没有支持整句话。
第二层是句子与一组证据:把这一句关联的资料放在一起,是否支持句子中的全部事实性内容。本文直接读取上游的句子标签。它是一次既有评审的结果,并非脚本在本次运行中读懂了证据。
第三层是句子与单条引用:这一条证据支持了多少。上游人工评审说明区分无支持、部分支持和完整支持。部分支持未必没有价值:一个复合句可能需要两条资料共同支撑。但如果其中一条只支持半句,不能把它展示成单独证明整句话的万能引用。
考虑一个明确标为教学例子的句子:“设备支持离线检索,并且断网后仍能自动同步。”材料甲只说离线检索,材料乙说联网后同步。找到两个相同关键词,检查不出断网条件被改写;两个引用也不能把错误条件拼成正确答案。这里的例子用于说明审查对象,没有计入后面的真实档案统计。
命题级审计应继续把复合句拆成最小可核查命题,保留条件、时间、主体与数量,再分别寻找支持。本文档案提供的是句子级标签,不能从它倒推出每个命题的真值,更不能编造命题级准确率。因此本次实现停在句子与引用层;这是可复现范围的边界,不把尚未完成的细粒度标注写成已经测过。
3. 先数清题、句子和引用发生次数#
本次完整读取所选配置的 100 个问题记录,没有按答案好坏抽取子集。其中 99 个有非空输出,1 个为空;另有一个名为 overall_results 的汇总键,它不是第 101 道题。空输出保留在问题级分母中,按零分计入;句子级分母只包含档案划分的 236 个句子单位,不能凭空给空回答制造一句话。沿用的分句中包含只有引用标记的单位和未带引用的拒答表达;本文不重新分句,也不把每个单位都当成独立事实命题。
| 检查对象 | 分子 / 分母 | 本次结果 |
|---|---|---|
| 有数字引用标记的句子 | 205 / 236 | 86.86% |
| 上游人工标签认为完全被支持的句子 | 168 / 236 | 71.19% |
| 上游自动标签认为完全被支持的句子 | 171 / 236 | 72.46% |
| 有引用、但人工标签未判完全支持的句子 | 37 / 205 | 18.05% |
| 所有句子都被人工标签判为支持的回答 | 59 / 100 | 59% |
这些行的分母有意写出来。37/205 只问“已经挂了引用的句子里,还有多少没获得完整支持标签”,不能写成全部句子的失败率;59/100 把完整回答作为单位,也不会被长答案多写几句话而放大权重。句子级失败不等价于整句事实为假,可能是证据不足、引用错配、条件遗漏或既有标注分歧,具体原因需要回看原文。
若先计算每道题内部的句子支持比例,再对 100 道题等权平均,人工结果是 74.69%,自动结果是 75.35%。前表的 71.19% 和 72.46% 则先汇总所有句子,属于句子微平均。较长答案在微平均中占更多单位;宏平均让每道题权重相同。两个结果都可计算,但报告必须说明关注的是“平均一题”还是“平均一句”。空答案的零分约定也要随指标一起保留,不能在打印表格前顺手过滤。
引用处还有一个更隐蔽的岔口。236 个句子的文本中出现 306 次数字引用标记,存档里却只有 290 个已评分的引用记录。按固定版本 eval.py 的默认上限核对,每句最多计入前三次引用;共有 10 句超过三次,合计有 16 次不在当前计分记录内。这里验证的是数量形状与默认规则一致;存档的引用记录缺少可直接核对的原始编号字段,程序不据此保证每一条的身份映射绝对正确。
其中还有 2 个句子重复使用同一引用编号。本文按标记出现次数和存档记录次数统计,没有偷偷去重,也没有把重复编号当成两篇不同文档。若产品想评价“不同证据来源的覆盖”,就需要另一种去重后的定义,并且重新审查关联关系;不能直接改分母而沿用当前标签。
290 个引用记录的原始人工标签分别为:完整支持 195 个、部分支持 58 个、无支持 37 个。这个三分表不能直接改名为“引用精确率”。上游 analyze.py 还有一条组合规则:句子整体先获支持,单条引用再至少有部分贡献,才计为接受。
按这条规则重新计算,人工接受 228/290,引用记录微平均为 78.62%;逐题先平均、空记录按零再汇总,则为 76.58%。自动接受 218/290,微平均 75.17%,逐题宏平均 74.36%。部分支持的 58 条里,有 34 条所在句子获整体支持,24 条没有;这些数字说明单条标签与整句标签怎样共同影响结果,不意味着脚本重新证明了每条证据的必要性。
档案里甚至存在 1 条“单条引用完整支持、所在句子却未获完整支持”的记录。审计器保留这个不一致线索,不擅自改标签让表格整齐。汇总字段也不是万能真值:本文从底层记录重算,不把文件中的旧总分直接当成本次结果。
4. 自动评审与人工记录,差在哪些句子#
把人工句子标签作为对照行、自动标签作为列,236 个配对单位得到下面的表。这里的“对照”只是统计方向,不预设人工标签绝对正确。
| 上游句子标签 | 自动未判支持 | 自动判支持 | 行合计 |
|---|---|---|---|
| 人工未判支持 | 51 | 17 | 68 |
| 人工判支持 | 14 | 154 | 168 |
| 列合计 | 65 | 171 | 236 |
两边有 205/236 个句子判断相同,即 86.86%;但这个比例恰好与前面的引用标记出现率相同,只是数值巧合,两个指标没有同一个含义。对照人工记录看,自动判支持而人工未判的有 17 句,反向有 14 句,总共有 31 句分歧。不能把 171−168=3 当成“只有三句需要复查”。
报告也保留引用记录组合规则下的四格计数:双方都不接受 37 条,自动接受而人工不接受 25 条,人工接受而自动不接受 35 条,双方接受 193 条,合计 290 条。两层分歧分别定位不同问题,不适合混在一个分母里。
这里的“人工”指上游公开文件给出的标签,不能被扩大解释为本次组织了多位标注员或证明了所有人的共识。程序不把重复引用当作独立标注员,不把同一句的多条证据当作多张支持票,也不根据输出好坏筛掉难例。
自动与人工有分歧时,单看总支持率接近,会遗漏方向相反的错误。一个系统在某些句子上过于宽松,在另一些句子上过于保守,两种偏差可能在平均数里互相抵消。审计报告保留问题 ID 和句子序号,供读者回到原始材料逐一检查;它不通过多数票宣告某一个判断必然正确。
更不能拿这些旧档案为当前评审模型做能力背书。没有重跑模型,没有新样本,也没有领域迁移检验。对真实业务,下一步是拿固定的当下输出和证据,按明确定义重新标注,记录分歧原因,再考察新评审器。本文得到的只是一个可运行的接入点:让每一项结果都能追到它实际对应的材料。
5. 让重跑先失败在输入,而不是成功地产生错表#
附件只依赖 Node.js 内置模块。实际执行环境为 Linux x86_64、Node.js v24.19.0。先下载这几份文件到同一目录:
- download.mjs:固定来源与字节校验
- source.mjs:固定来源清单与校验函数
- audit.mjs:输入校验与统计程序
- test.mjs:合成边界与真实存档回归测试
- results.json:本次实际运行结果
- README.md:来源、命令和指标定义
node download.mjs
node audit.mjs > reproduced.json
node test.mjs
下载器将固定源文件写入操作系统临时目录,打印实际路径;审计器无参数时读取同一默认位置。若已有该提交的源文件,可运行 node download.mjs --from /绝对路径/源文件.json,仍须通过相同哈希验证;也可用 node audit.mjs --input /绝对路径/源文件.json --out reproduced.json 显式指定读写位置。下载与审计均不需要 API 密钥。完整命令和文件清单见 README。
输入校验应优先检查评审数组与句子数一致,标签属于文档规定的集合,问题身份不被重名覆盖,空输出与空句子记录一致。它不应该在遇到无法解释的形状时自动截断数组,然后照常给出漂亮百分比。本文对上游的特殊结构逐项声明:汇总键不是问题、空答案保留在问题分母、引用计分有数量上限。真实业务若采用不同规则,应该显式换版本,而不是改一行过滤条件却沿用同一个指标名称。
边界测试使用单独构造的微型合成数据,包括字段缺失、标签非法、配对错位与分母变化等情况。合成数据只用于测试程序行为,不作为模型效果数据。 真实存档结果则独立回归,避免只验证一个特意为公式写出来的顺利例子。哈希校验也有独立测试,源文件被截断或替换时应停止,而不能悄悄把另一批数据计入同一份报告。
6. 回到知识库:保存能解释争议的最小记录#
把这个实验接到自己的 RAG 管线,不要先追求一个统一的“可信度”。先保存问题身份、实际答案、证据身份与版本、句子或命题边界、引用关系、评审规则和原始评审结果。隐私材料保存在受控位置,公开调试附件使用脱敏材料;一个可复现报告不需要把用户文档全量复制到公开仓库。
结构检查与语义检查分别报告。失效引用可以直接定位到编号;部分支持则需要展开句子,说明缺的是哪项条件;空答案应保留为可见结果,同时另行区分合理拒答、服务失败与无意空输出。本文原始档案不足以解释空输出的运行原因,因此没有把它自动判为安全拒答。
证据集合也要保留审查范围。若系统只让评审器看前三条引用,页面却展示五条,报告应该同时写明“展示了多少”和“检查了多少”。需要审查全部引用时,增加预算、重跑评审、重新记录分母;不能靠把旧分数贴回全部引用旁边完成扩容。
系列前四篇分别处理证据召回、切块身份、候选融合和发布门禁。这一篇补上生成结果与支持证据之间的连接:从真实存档里还原分母、审查范围与分歧,让下一次谈“引用支持率”时,可以直接打开记录问清楚它支持了什么。
系列导航:
- 第一篇:证据召回与答案支持
- 第二篇:切块与稳定证据映射
- 第三篇:两路词法、RRF 与重排
- 第四篇:评审校准与离线发布门禁
- 本篇:真实历史输出的引用审计与统计重放
- RAG 主题文章
想讨论自己的引用记录,可以从 AI 实践路线了解知识星球的讨论方向;公众号入口在关于页。带上脱敏后的句子、对应证据和失败类别,会比单独一张总分截图更容易找到问题。
本文文字、示例代码与图解由 AI 辅助制作;上游数据、人工标签与自动评审结果的来源及本次运行边界已列明。本文不代表对任何当代模型或生产知识库的质量认证。

