> 会产生"指控"的工具,误报代价不对称——先归一化再开口

Research Tools Intermediate 2026-10-13 19:58 2026-10-11
#tooling #citations #false-positive #validation #ethics

一个参考文献核验工具会把解析不出来的 DOI 标成 "likely hallucinated"。 在一轮批量运行里它报了 12 条——全部是假的。

结论先行

一个参考文献核验工具会把解析不出来的 DOI 标成 "likely hallucinated"。 在一轮批量运行里它报了 12 条——全部是假的。

根因是一行正则:它把句末的句号吃进了 DOI。

10.1038/s41586-024-08025-4.      ← 末尾那个点是句号,不是 DOI 的一部分

带着句号去解析,Crossref 当然解不出来,于是一篇真实存在的论文被标成"可能是编造的"。 修法是一行:在报 phantom_doi 之前先 strip 尾部标点,再解一次。

为什么

这不是一个普通的假阳性。会产生指控的工具,它的误报代价和漏报代价不在同一个数量级。

  • 漏报:一条可疑引用没被标出来,下游还有人看。
  • 误报:一条正确的引用被标成"可能是幻觉"。如果这个标签进了一份给别人看的文档, 它就是一条不实指控。而现在不少会议明确把"由 LLM 生成的不实指控"列为可处分行为。

所以这类工具的设计原则和普通 linter 相反:宁可漏,不可冤。 具体到实现,就是在"开口指控"之前必须穷尽所有廉价的归一化:

归一化 为什么必要
strip 尾部标点(. , ; ) ]) 从正文/PDF 抽取时必然带上
大小写归一(DOI 不区分大小写) 不同样式输出不一致
URL 前缀剥离(https://doi.org/) 同一个 DOI 的两种写法
10.48550/arXiv.* 白名单 这是 DataCite 的 DOI,Crossref 本来就解不出
全角/半角、软连字符、零宽字符 PDF 抽取产物里常见

最后那条里的 10.48550/ 尤其值得单列:它不是解析失败,是问错了后端。 把一个"后端不负责"的情况报成"文献可能不存在",是同一个错误的另一种形态。

补一条同形缺陷:归一化只做了一边

另一个工具里的同一个错误,形态更干净。它核对的是"模型读者引用的句子/数字 是不是真的在交给它的那份文本里"。

读者写的是 R=95.10%,正文里写的是 95.10\%(LaTeX 的转义)。 而归一化只作用在读者那一侧,正文侧保留转义。后果不是偶发假阳性:

任何以百分号结尾的数,在任何一篇论文里,都永远不可能匹配上。

一条一边倒的归一化,会让整个语法类别永久失配,而不是让匹配率下降一点。 同一轮里另外三类也都是正常的读者行为:带省略号的引文、 (line 238) 这种位置锚、以及读者引用工具自己输出的那句话。 净结果是一份完全正确的读者报告上报了 4 条,4 条全假—— 而这足以让人把已经手工核过的真发现一起丢掉。

修法已经进了代码,连理由都写在 docstring 里:

def _locates(q, nb, bw):
    """Resolved, tolerantly. Readers elide with '...' and trim punctuation, and a
    verifier whose false positives land on accurate work is worse than none: it
    launders a correct finding into 'not admissible'. Measured at 4 flags out of
    4 on one correct report before these three tolerances."""

三道容差里的第三道值得注意:只要有连续 6 个词命中正文就算解析成功。 这是用精度换召回——一句编造的长句只要嵌了 6 个真词就能过关。 在这个代价结构下这个交换是对的:假阳性会销毁一条真发现, 假阴性只是少抓一条编造,而数字那一层还有第二道。 误报和漏报要按后果定价,不是按比例定价。

两条合起来是同一条规则:

两边都归一化,或者两边都不归一化。只做一边,等于给某个类别判了死刑。

怎么用

给任何会输出"可疑 / 违规 / 可能是编造"标签的工具加三道闸:

  1. 归一化在前,判定在后。 判定函数拿到的必须是已归一化的标识符, 而不是从文本里 split 出来的原始串。写一个单独的 normalize_doi() 并测它。
  2. 分开"解析失败"和"不存在"。 后端不负责(DataCite 的 DOI 问 Crossref)、后端挂了、标识符格式不对、 查到了但元数据不匹配——四种,四个标签,四种处置。 见 一个标签盖了三种根因,而三种的处置相反——"可能是幻觉"的三张脸。
  3. 归一化必须对称。 写一个 normalize(x) 同时作用在两侧, 而不是在比较的时候各自处理。不对称的那一侧一旦涉及转义、全角、空白折叠, 失配就是整类而不是个例。
  4. 指控级别的标签要有一条"反例预算"。 报 N 条之前先手工核 3 条。这一轮如果核过 3 条,12 条假指控一条都发不出去。

顺手一条:假阳性批量出现、而且专挑最著名的那些文献时,坏的是查询端,不是文献。 这是一个很好用的判别法。

出处

  • 自己的参考文献核验链(bibguard / ref-check)在一批外部 PDF 上的批量实测, 2026-10;DOI 正则吃句末句号 → 12 条假 phantom_doi,其中包含真实可解析的 Nature DOI
  • 同一轮记录下来的另一条:10.48550/arXiv.* 属 DataCite,应白名单化而非报 FAIL
  • 单边归一化那一条出自 ~/Tools/nonexpert-review 的 quotes 子命令: feedback/ 里 2026-09-25 的四条回报,修后的 _norm_quote / _POSITION / _locates 在当前版本里,4/4 这个数写在 docstring 上
  • 相关:一天里五次搞错同一类事:核查步骤本身没被核查、引用幻觉藏在作者列表和 venue 里,不在标题里——只比标题的批量校验会漏、 指令写进 prompt 不等于它生效了——加一道"引文必须在原文里"的机械核验