返回文章列表

RAG 评测实战(五):引用都挂上了,证据真的撑住了吗?

芝士AI吃鱼2026年10月5日17 分钟阅读阅读统计加载中

一段答案后面挂满了方括号,看起来总比空口断言可靠。点开引用,资料也确实存在。检查似乎已经走完,直到有人追问:这条材料究竟支持了句子里的哪一件事?

引用可以装得很整齐,分母却可能悄悄换了。按题平均和按句平均不是同一个问题;正文写了五个引用,也不保证评测器真的检查了五个。要把生成后的答案交给上一篇的发布门禁,先得知道那一个支持率,究竟怎么算出来。

本篇不再编写合成回答充当模型输出。实际运行的是一套原创 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] 这样的数字标记,能不能找到引用记录,记录与答案是否对应。正则表达式适合做这种工作。它不会理解时间、条件、否定或因果,更不会判断一篇文档有没有支持整句话。

第二层是句子与一组证据:把这一句关联的资料放在一起,是否支持句子中的全部事实性内容。本文直接读取上游的句子标签。它是一次既有评审的结果,并非脚本在本次运行中读懂了证据。

第三层是句子与单条引用:这一条证据支持了多少。上游人工评审说明区分无支持、部分支持和完整支持。部分支持未必没有价值:一个复合句可能需要两条资料共同支撑。但如果其中一条只支持半句,不能把它展示成单独证明整句话的万能引用。

考虑一个明确标为教学例子的句子:“设备支持离线检索,并且断网后仍能自动同步。”材料甲只说离线检索,材料乙说联网后同步。找到两个相同关键词,检查不出断网条件被改写;两个引用也不能把错误条件拼成正确答案。这里的例子用于说明审查对象,没有计入后面的真实档案统计。

命题级审计应继续把复合句拆成最小可核查命题,保留条件、时间、主体与数量,再分别寻找支持。本文档案提供的是句子级标签,不能从它倒推出每个命题的真值,更不能编造命题级准确率。因此本次实现停在句子与引用层;这是可复现范围的边界,不把尚未完成的细粒度标注写成已经测过。

引用审计区分数字标记、句子与证据集合、单条引用和命题级支持;本次只重算既有句子及引用记录,没有重新执行语义评审。
AI 辅助制作的原创机制图。各层检查对象不同,结构校验不能替代语义判断。 查看大图 ↗

3. 先数清题、句子和引用发生次数#

本次完整读取所选配置的 100 个问题记录,没有按答案好坏抽取子集。其中 99 个有非空输出,1 个为空;另有一个名为 overall_results 的汇总键,它不是第 101 道题。空输出保留在问题级分母中,按零分计入;句子级分母只包含档案划分的 236 个句子单位,不能凭空给空回答制造一句话。沿用的分句中包含只有引用标记的单位和未带引用的拒答表达;本文不重新分句,也不把每个单位都当成独立事实命题。

检查对象分子 / 分母本次结果
有数字引用标记的句子205 / 23686.86%
上游人工标签认为完全被支持的句子168 / 23671.19%
上游自动标签认为完全被支持的句子171 / 23672.46%
有引用、但人工标签未判完全支持的句子37 / 20518.05%
所有句子都被人工标签判为支持的回答59 / 10059%

这些行的分母有意写出来。37/205 只问“已经挂了引用的句子里,还有多少没获得完整支持标签”,不能写成全部句子的失败率;59/100 把完整回答作为单位,也不会被长答案多写几句话而放大权重。句子级失败不等价于整句事实为假,可能是证据不足、引用错配、条件遗漏或既有标注分歧,具体原因需要回看原文。

若先计算每道题内部的句子支持比例,再对 100 道题等权平均,人工结果是 74.69%,自动结果是 75.35%。前表的 71.19% 和 72.46% 则先汇总所有句子,属于句子微平均。较长答案在微平均中占更多单位;宏平均让每道题权重相同。两个结果都可计算,但报告必须说明关注的是“平均一题”还是“平均一句”。空答案的零分约定也要随指标一起保留,不能在打印表格前顺手过滤。

引用处还有一个更隐蔽的岔口。236 个句子的文本中出现 306 次数字引用标记,存档里却只有 290 个已评分的引用记录。按固定版本 eval.py 的默认上限核对,每句最多计入前三次引用;共有 10 句超过三次,合计有 16 次不在当前计分记录内。这里验证的是数量形状与默认规则一致;存档的引用记录缺少可直接核对的原始编号字段,程序不据此保证每一条的身份映射绝对正确。

其中还有 2 个句子重复使用同一引用编号。本文按标记出现次数和存档记录次数统计,没有偷偷去重,也没有把重复编号当成两篇不同文档。若产品想评价“不同证据来源的覆盖”,就需要另一种去重后的定义,并且重新审查关联关系;不能直接改分母而沿用当前标签。

实际审计包含100题、236句、306次数字引用和290个已评分引用记录;1个空答案留在问题分母,16次超出前三条范围的标记不冒充已有标签。
AI 辅助制作的原创统计图。数字来自本次运行结果,只描述固定历史存档。 查看大图 ↗

290 个引用记录的原始人工标签分别为:完整支持 195 个、部分支持 58 个、无支持 37 个。这个三分表不能直接改名为“引用精确率”。上游 analyze.py 还有一条组合规则:句子整体先获支持,单条引用再至少有部分贡献,才计为接受。

按这条规则重新计算,人工接受 228/290,引用记录微平均为 78.62%;逐题先平均、空记录按零再汇总,则为 76.58%。自动接受 218/290,微平均 75.17%,逐题宏平均 74.36%。部分支持的 58 条里,有 34 条所在句子获整体支持,24 条没有;这些数字说明单条标签与整句标签怎样共同影响结果,不意味着脚本重新证明了每条证据的必要性。

档案里甚至存在 1 条“单条引用完整支持、所在句子却未获完整支持”的记录。审计器保留这个不一致线索,不擅自改标签让表格整齐。汇总字段也不是万能真值:本文从底层记录重算,不把文件中的旧总分直接当成本次结果。

4. 自动评审与人工记录,差在哪些句子#

把人工句子标签作为对照行、自动标签作为列,236 个配对单位得到下面的表。这里的“对照”只是统计方向,不预设人工标签绝对正确。

上游句子标签自动未判支持自动判支持行合计
人工未判支持511768
人工判支持14154168
列合计65171236

两边有 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。先下载这几份文件到同一目录:

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 管线,不要先追求一个统一的“可信度”。先保存问题身份、实际答案、证据身份与版本、句子或命题边界、引用关系、评审规则和原始评审结果。隐私材料保存在受控位置,公开调试附件使用脱敏材料;一个可复现报告不需要把用户文档全量复制到公开仓库。

结构检查与语义检查分别报告。失效引用可以直接定位到编号;部分支持则需要展开句子,说明缺的是哪项条件;空答案应保留为可见结果,同时另行区分合理拒答、服务失败与无意空输出。本文原始档案不足以解释空输出的运行原因,因此没有把它自动判为安全拒答。

证据集合也要保留审查范围。若系统只让评审器看前三条引用,页面却展示五条,报告应该同时写明“展示了多少”和“检查了多少”。需要审查全部引用时,增加预算、重跑评审、重新记录分母;不能靠把旧分数贴回全部引用旁边完成扩容。

系列前四篇分别处理证据召回、切块身份、候选融合和发布门禁。这一篇补上生成结果与支持证据之间的连接:从真实存档里还原分母、审查范围与分歧,让下一次谈“引用支持率”时,可以直接打开记录问清楚它支持了什么。

系列导航:

想讨论自己的引用记录,可以从 AI 实践路线了解知识星球的讨论方向;公众号入口在关于页。带上脱敏后的句子、对应证据和失败类别,会比单独一张总分截图更容易找到问题。

本文文字、示例代码与图解由 AI 辅助制作;上游数据、人工标签与自动评审结果的来源及本次运行边界已列明。本文不代表对任何当代模型或生产知识库的质量认证。

AI 实践

想把本文的问题继续做深一点?

知识星球里会继续整理相关案例、工程约束和问题讨论;具体内容以当前社区页面为准。

AI 实践知识星球加入海报,包含可扫描二维码
芝士AI吃鱼公众号二维码

关注「芝士AI吃鱼」公众号持续更新

在这里,我用「人话」和「漫画」拆解 AI 技术。公众号更新会同步整理到本站,方便继续阅读、检索和订阅。

点击二维码可放大,使用微信扫码关注。

芝士AI吃鱼

持续记录 AI 原理和工程实践

更多文章