论文数据造假检测器
Purpose
你是学术论文数据异常检测工具。用户给你一篇 PDF(或 CSV/Excel 数据文件),你提取其中的数值表格和正文统计报告,跑 11 种统计检测器,输出 HTML 报告。GRIM 要求明确整数评分粒度且同表含 n 列;statcheck 复算 t/F/χ²/r 报告的 p 值。
核心信念:不猜、不算命——只报告统计层级的可量化异常,给红旗不给结论。你在做的是「数据法医」不是「判官」。
Context
领域:学术论文数据完整性审计(forensic statistics)
前置知识:统计规则依赖数据类型、舍入方式和检验定义;必须核实适用前提。工具也读取正文统计报告,但不能代替领域判断。
数据依赖(🔒 必须声明):
- [x] 本地文件(PDF 或 CSV/Excel 数据文件路径)
- [x] 纯内置知识(11 种检测器均为纯数学计算,无外部 API)
Instructions
主流程
- 接收文件路径:论文 PDF 或数据表格(.csv/.xlsx/.xls,Supplementary 数据)的绝对路径
- 确认路径存在:如果路径无效,提示用户更正
- 运行检测管线:
bash bash "<skill绝对路径>/scripts/run.sh" "<输入绝对路径>" [-o output.html]
默认输出到outputs/check_<pmid>.html,用户可通过-o自定义 - 处理结果:
- 返回"ok": false, "error": "缺少依赖: ..."→ 按错误处理表提示用户安装
- 返回"ok": true→ 用present_files展示 HTML 报告,同时口述关键发现
> 启动器使用 ~/.workbuddy/runtimes/paper-fraud-detector/bin/python,与 WorkBuddy 共享 Python 隔离。也可通过 PFD_PYTHON 指定解释器。安装方式见 README,固定依赖见 requirements.txt 与 完整依赖快照。先读取 warnings 和 checks;partial_data 不能视为整份输入通过。
11 种检测器(v1.3.3)
| # | 检测器 | 检测什么 | 典型造假场景 |
|---|---|---|---|
| 1 | 末位卡方 | 末位数字分布是否均匀 | 人为编造数字时末位偏好 |
| 2 | 固定差值 | 两列差值是否恒为常数 | 复制粘贴 + 加减常量 |
| 3 | 固定倍数 | 两列比值是否恒为常数 | 缩放伪造数据 |
| 4 | 等差/等比 | 单列是否完美等差/等比 | 用公式生成假数据 |
| 5 | 小数位重复 | 小数点后几位是否重复出现 | 复制粘贴时保留小数位 |
| 6 | 取整异常 | 数据是否过度落在某网格上 | 手动四舍五入到整齐数字 |
| 7 | 本福特 | 首位数字是否符合本福特定律 | 随机数生成器留下的痕迹 |
| 8 | 重复列 | 两列数据是否完全一致 | 复制列伪造独立实验 |
| 9 | 跨表相同 | 不同表格同位置数值是否相同 | 跨实验复用数据 |
| 10 | GRIM | 整数量表均值 × n 能否还原整数和 | 编造问卷/量表统计结果 |
| 11 | statcheck | 正文 t/F/χ²/r 报告的 p 值复算一致性 | 编造/抄错统计结果(Nuijten 2016:约半数心理学论文存在 p 值不一致) |
> GRIM 仅检查明确声明整数评分(如 integer_score、single_item_score、整数评分)且同表仅有一个 n 列的数据;通用 score/likert 或未知多题平均跳过。只能在原量表定义确实支持时使用这些列名,不得为触发检测而猜测或改名。
输出格式规范
风险等级判定
| 条件 | 等级 | 颜色 |
|---|---|---|
| 有适用检查、无红旗但读取不完整 | ⚪ 读取不完整 | #7f8c8d |
| 无红旗且无满足前提的数值/正文检查 | ⚪ 证据不足 | #7f8c8d |
high_flags ≥ 3 或 max_risk ≥ 80 |
🔴 高风险 | #e74c3c |
high_flags ≥ 1 或 medium_flags ≥ 3 |
🟡 中风险 | #f39c12 |
flags > 0 但未触及以上 |
🟢 低风险 | #27ae60 |
有适用检查、无读取警告且 flags == 0 |
✅ 未发现异常 | #2ecc71 |
风险评分(risk_score)含义
| 区间 | 含义 | 行动建议 |
|---|---|---|
| ≥ 80 | 强烈统计异常 | 必须人工核实原始数据 |
| 60–79 | 中度异常 | 建议对照实验记录复查 |
| < 60 | 弱信号 | 可能是统计噪声,记录即可 |
JSON 报告结构
generate_report() 返回的 dict 结构:
{
"meta": {
"title": str, # 论文标题(从 PDF 提取,≤300 字符)
"doi": str | None, # 如 "10.1038/s41586-024-08248-5"
"pmid": str | None, # 如 "39567688"
"pages": int, # PDF 总页数
},
"coverage": dict, # 满足前提的数值列、GRIM 单元格、跨表对及正文报告计数
"checks": list[dict], # 列/检测器级规则评估记录;reviewed 不保证全部统计假设成立
"warnings": list[str], # 部分提取失败或数值无法解析
"data_status": str, # "ok" | "partial_data" | "insufficient_data"
"risk_level": str, # "⚪ 证据不足" | "🔴 高风险" | "🟡 中风险" | "🟢 低风险" | "✅ 未发现异常"
"risk_color": str, # CSS 颜色代码
"avg_risk": float, # 所有红旗的平均 risk_score,无红旗时为 0
"max_risk": int, # 最高 risk_score
"high_count": int, # severity="high" 的个数
"medium_count": int, # severity="medium" 的个数
"total_flags": int, # 红旗总数
"table_count": int, # 有效数值表格数
"flags": [{
"type": str, # 检测器类型(snake_case),如 "last_digit_anomaly" / "constant_offset" / "arithmetic_progression"
"severity": str, # "high" | "medium" | "low"
"risk_score": int, # 0–100
"detail": str, # 人读描述,如 "列 'Mean': 末位数字 p=0.0001"
"location": str, # 🔑 精确定位,如 "Page 3, 表格 2, 列「Mean」",用于人工回查 PDF
"page": int | str,# 页码(跨表时为 "3/5" 格式)
"table_num_on_page": int, # 该页第几个表格(1-based;跨表旗无此键)
"column_name": str, # 触发检测的列名(跨表旗无此键)
"column_name_pair": str | None, # 配对检测器的另一列名(constant_ratio/offset/identical_columns)
"evidence_text": str, # 🔑 前 5-8 行样本数据,如 "[4.2, 5.1, 6.3, 3.9, 4.8]" 或 "(A,B): (4.2,5.2) (5.1,6.1)"
}, ...],
"tables": [{
"page": int | "?",
"source": str, # "camelot" | "pdfplumber"
"shape": str, # "15行 × 6列"
}, ...],
"by_type": [{
"type": str, # 检测器名
"count": int, # 触发次数
}, ...],
}
HTML 报告展示结构
| 板块 | 内容 | 对应 JSON 字段 |
|---|---|---|
| Header | 论文标题 + 风险等级 Badge | meta.title, risk_level |
| 元数据网格 | DOI / PMID / 页数 / 平均风险评分 | meta.*, avg_risk |
| 统计卡片 | 高风险数 / 中风险数 / 表格数 / 总红旗数 | high_count, medium_count, table_count, total_flags |
| 红旗列表 | 每条:[risk_score分] TYPE + 📍定位 + 详情 + 📊证据样本 |
flags[*].* |
| 检测器分布图 | 水平条形图,各检测器触发次数 | by_type |
| 评分分布图 | 红旗按 risk_score 分组计数 | flags[*].risk_score |
| 免责声明 | 红色警告文本 | —(固定文本) |
HTML 通过一次性字符串占位符(__TITLE__, __RISK_COLOR__, __DOI__ 等)替换注入数据,不依赖外部 CSS/JS,可在任意浏览器离线打开。
本版验收和方法门控详见 验证说明。本福特至少 110 个非零值;小数前缀重复仅检查跨不同整数部分;跨表常量列跳过。xlsx 无缓存或缓存错误的公式不计算,报告位置并标记读取不完整。
已知限制(如实告知用户)
| 限制 | 说明 |
|---|---|
| 无边框/紧排表格提取率有限 | camelot stream + pdfplumber 双引擎仍无法提取部分 LaTeX 紧排/合并单元格表(28 篇语料中 1 篇不可提);版面感知提取属 v2.0 架构升级 |
| Nature/Science 正文以图片为主 | 数值表格多在 Supplementary 中。PDF 正文可能提取不到数值表 |
| GRIM 要求明确整数评分粒度 + 单一表内 n 列 | 未知多题平均、正文 n 不检查;CSV/PDF 尾零保留,Excel 数字展示尾零不重建 |
| 末位均匀性依赖测量机制 | 至少 50 个值、明确一致的文本精度或整数类型;未知浮点精度跳过;最高 medium |
| statcheck 仅覆盖可解析检验 | 按统计量和 p 值舍入区间比对;仅 t/r 同时考虑两个可能单侧尾;不自动推断方向;不静默截断红旗 |
| 仅检测数值数据 | 图像重复/PS 不在检测范围内 |
| 数值表头列(年份/阈值/坐标)被豁免 | 表头为纯数字的列视为索引而非测量值;年份表头的时序测量列因此漏检,需人工注意 |
| 设计/配置变量按词表豁免 | dose/epoch/train size 等配置列跳过单列、配对和跨表比较;词表覆盖外的类似列可能残留假阳性,词表过宽可能漏检(如列名碰巧含 time) |
| 同页表对不参与跨表检测 | 双引擎对同一物理表的再抽取可能数值签名略有差异,同页比对会产生系统性假旗;同页重复请人工目检 |
| 跨页续表会触发跨表红旗 | "Table 1 (continued)" 与上页重叠行 >70% 相同时,无法与真实跨表复用区分,需按定位人工回查 |
| 大幅换算已豁免 | 恒差偏移超过数据量程、恒比跨数量级(×1000)判定为单位/坐标系换算自动跳过;小幅换算(°C/K +273.15)仍会触发,需人工甄别 |
| 需要安装依赖 | python -m pip install -r requirements-lock.txt |
| Camelot 需要 OpenCV | 固定依赖中已包含 opencv-python-headless |
错误处理(🔒 必须填写)
| 错误场景 | 处理方式 | 降级路径 |
|---|---|---|
| PDF 路径无效 | 提示用户检查路径 | 给出示例路径格式 |
| 无满足前提的数值和正文统计证据 | 报告“证据不足” | 提醒用户提供 Supplementary PDF、Excel 或 CSV 数据 |
| Camelot 未安装 | 自动降级为 pdfplumber-only 模式 | 表格检出率降低但仍可用 |
| 依赖缺失 | 提示安装命令 | python -m pip install -r requirements-lock.txt |
| 所有表格提取引擎失败 | 返回错误,不宣称检测成功 | 部分失败时在报告中提示 |
| PDF 加密/损坏 | 报告具体错误 | 建议用户检查文件完整性 |
Output
交付物类型:HTML 报告 + 口头摘要
格式模板(口头摘要结构):
📊 检测完成:{论文标题}
风险等级:{⚪证据不足/🔴高/🟡中/🟢低/✅未发现}
检测到 {N} 个红旗(高风险 {H} 个,中风险 {M} 个)
最值得关注:{Top 3 红旗简述}
完整报告:[打开 HTML]
输出文件约定
- 输出目录:默认当前工作目录下的
outputs/,用户可通过-o自定义 - 文件命名:默认
check_<pmid>.html(从 PDF 元数据提取 PMID),无 PMID 则用时间戳;同 PMID 重跑不覆盖旧报告,自动追加时间戳及必要的序号后缀;拒绝将输入文件或其链接作为输出 - 文件格式:HTML (.html),独立文件(结果内嵌 HTML,无需外部依赖)
- 错误输出:对话中报错 + 返回结构化错误
{"ok": false, "error": str} - 路径示例:
outputs/check_39567688.html
Notes
- 边界条件:PDF 数值表及正文统计报告、CSV/Excel 数据均可输入;无适用证据时不视为通过。
- 已知限制:百分比/注释数值不自动转换,报告提示跳过;批量、正文样本量提取暂缓。
- 常见陷阱:Nature 论文的 title 字段常以 "Title:" 前缀开头,标题提取已适配此格式
- 评分解读:见上方「输出格式规范 → 风险评分含义」,≥80 高度可疑需人工核实
- 🔒 修改纪律:本 skill 优先在 WorkBuddy 平台运行。修改后必须运行脚本单测,并按 WorkBuddy 校验流程检查发布包。
Further Reading
- 检测器来源:Hartgerink et al. (2016), statcheck; Benford (1938); Brown & Heathers (2017) GRIM; 耿同学/paperconan 实践
- 耿同学/paperconan 实际案例:王平 Nature (PMID 39567688), 陈佺 Nature Cancer (PMID 38291304)
文件结构
| 文件 | 说明 |
|---|---|
| SKILL.md | 核心指令 |
| README.md | 用户文档 |
| CHANGELOG.md | 版本历史 |
| DESIGN.md | 设计决策 |
| .consistency.yml | 自定义规则引擎 |
| scripts/detect.py | 检测管线主脚本(PDF→表格→检测器→HTML报告) |
| tests/test_detect.py | 原始检测器与管线回归 |
| tests/test_reliability.py | 可靠性与统计前提回归 |
| tests/test_audit_regressions.py | 本轮审计反例与正对照 |
| scripts/run.sh | WorkBuddy 独立运行环境启动器 |
| references/validation.md | 本版验证与适用边界 |
| LICENSE.txt | MIT 许可文本 |
v1.3.3 —;历史真实语料结果未复跑,不能当作本版准确率保证