高校实验室内网论文AIGC合规校验系统踩坑与自研实现全记录

📅 2026/7/6 14:12:22 👁️ 阅读次数 📝 编程学习
高校实验室内网论文AIGC合规校验系统踩坑与自研实现全记录

上周接了实验室管理处的紧急需求,要搭一套完全离线的学术论文AI生成率筛查系统,绝对不能把用户上传的论文传到公网。

刚开始找现成方案试手,下了GPTZero、原创度小助手、团象AICG检测、文心检测、智谱内容校验一堆工具,全跑了一遍,没用,要么强制传文件到公网推理,要么中文误判率高到离谱。

没办法只能咬牙从零开始自研,前后踩了二十多个坑,花了三周终于把全流程跑通,给大家捋下完整的实现细节。

我们首先拉齐了三个硬性需求,所有后续开发都围绕这几个点走,缺一个都过不了内审。

第一是数据绝对闭环,所有推理、计算全在内网服务器完成,不能有任何外发数据包,连DNS请求都不能出内网网段。

第二是中文论文场景适配,自动过滤目录、公式、参考文献、致谢这些不该计入检测的区域,不能把引用的内容乱算成AI生成。

第三是精度达标,对全人工撰写的专业内容误判率必须低于10%,不能冤枉好好写论文的学生。

一开始做文本预处理模块的时候,我图省事直接用PyMuPDF提取PDF里的全部文字。

跑了10篇历史论文测试,有7篇把页眉页脚、公式OCR乱码、文末的图书馆馆藏号这些无关内容全混进了正文。

出来的检测结果自然完全没法看,直接推翻了这个最简方案。

后来我们选了LayoutLMv2的轻量版,用标注好的3000篇中文学术论文微调,做版面元素分类任务。

可以把PDF页面里的内容自动划分成正文文本块、公式块、图表标题块、引用块、目录块5个类别。

我们把微调后的全部权重下载到本地服务器,全程不触发任何公网加载请求,代码示例如下:

from layoutlmft.models.layoutlmv2 import LayoutLMv2ForTokenClassification import fitz # 加载本地离线微调权重,无任何外部网络请求 model = LayoutLMv2ForTokenClassification.from_pretrained("./local-layoutlmv2-finetune-academic")

微调后的模型体积只有1.8G,在普通的16G内存服务器上推理一篇100页的论文耗时不到10秒。

跑完版面分类之后,我们还加了一层规则过滤:连续3行以上带有[1][2]上标引用标记的段落,直接划入引用区不计入后续检测。

最后用120篇未参与训练的历年硕博论文做测试,正文提取的准确率能到97.2%,基本没有无关内容混入。

很多早期的AIGC检测方案,单靠ppl(困惑度)一个指标判断,面对GPT 4o、Claude 3生成的专业内容直接失效。

我们测试的时候发现,大模型生成的严谨学术文本,困惑度和资深教授手写的文本重叠度超过60%,根本没法用单一指标区分。

最终我们敲定了6个维度的组合本地特征,全部不需要外部接口,在内网就能完成计算。

第一个也是核心区分度最高的特征,是中文语义碎片熵。

我们用馆里2018-2023年收集的12万篇全人工撰写的学位论文,统计了全部连续5字词的ngram共现频次表。

大模型生成的文本表达极度平滑,ngram的分布集中在高频区间,而人工写作会有不少临时想到的个性化表述,碎片熵值明显更高。

最开始我们踩了个大坑,一开始用维基百科的通用中文语料训的ngram表,把很多全人工写的公式推导段落直接判成AI生成。

后来换成专属学术语料库之后,这个问题直接彻底消失,两类样本的熵值分布重叠度不到7%,区分度拉满。

对应的核心计算代码片段如下:

import numpy as np from collections import defaultdict # 加载本地预统计的学术语料5-gram频次表 ngram_freq = defaultdict(int) with open("./academic_ngram_5.txt", "r", encoding="utf-8") as f: for line in f: k, v = line.strip().split("\t") ngram_freq[k] = int(v)

第二个特征是标点分布偏离度,我们统计不同标点在每百字中的出现频次,和人工学术语料的基线值做比对。

测试的时候我们发现一个很有意思的规律:大模型生成的中文学术内容,分号使用率比人工低40%,逗号使用率反而高18%。

大模型几乎不会出现人工写推导过程时常用的破折号补充解释、括号标注临时变量的表达习惯。

第三个特征是专业术语跳跃度,我们把本学科的知识图谱导入系统,检测文本中跨学科术语的无意义共现情况。

大模型经常会为了凑字数,把不同领域的专业术语做无逻辑拼接,人工写作的术语关联度完全符合学科常识。

第四个特征是手写表述波动度,统计同一语义下不同表述方式的出现频次。

全人工写的长论文里,必然会出现同一个含义用不同话术表达的情况,比如前文写「综上所述」后文写「综上可得」。

大模型生成的文本通篇表述一致性极高,几乎不会出现这种自然的表述波动。

剩下两个特征分别是指代混乱度、逻辑断点分布,组合之后用轻量化的随机森林分类器做最终的AI率判定。

整个核心检测模块的模型体积加起来不到2G,普通服务器上跑一篇10万字的论文,总耗时不到30秒。

我们拿了200篇完全没参与过训练的盲测样本做精度验证,结果远超过预期需求。

其中50篇全人工手写的论文正文,只有3篇被误判,误判率1.5%,远低于10%的要求红线。

50篇大模型纯生成的学术正文,100%全部识别出来,没有一例漏判。

剩下100篇是学生提交的AI生成后人工改写的样本,AI内容占比从10%到90%不等。

最终系统输出的AI率数值,和真实人工标注的比例误差平均在6.2%,完全满足内网合规筛查的要求。

部署阶段又踩了新坑,一开始我图省事用Flask搭了个极简接口,测了下小文件没问题。

一测大的100M以上的扫描版PDF上传,直接把服务器内存占满,最多同时跑3个任务服务器就卡到完全无响应。

后来我们改成基于Celery的异步任务队列,上传的文件先落地到本地临时目录,用进程池做并发数限制。

4核16G的入门级服务器,现在可以同时稳定跑8个检测任务,内存占用峰值不超过60%。

对应的核心配置片段如下:

from celery import Celery import os app = Celery('aigc_check', broker='redis://localhost:6379/0', backend='redis://localhost:6379/0') app.conf.task_serializer = 'pickle' app.conf.worker_max_tasks_per_child = 10

整个消息队列用的Redis服务也完全部署在内网离线环境,没有配置任何外网映射端口。

后续我们又接入了学校统一的内网CAS身份认证,不需要单独搭账号体系,不同学院的管理员只能查看本院学生的检测结果,完全满足权限隔离要求。

现在全系统跑了快两周,已经完成了今年应届硕士毕业生的全部答辩前论文筛查工作,没出过一次数据泄露事故。

说句实在的,之前找工具阶段浪费了快一周时间,试的那堆工具连最基础的完全离线部署都做不到,全是花架子。

最后自研的这套方案不但把需求全部落地,原本申请的2万工具采购经费,省下来直接买了4T的SSD存新爬的学术语料了。

接下来我们打算微调下Qwen2的7B小模型,加个专门的改写痕迹识别模块,针对那种把AI内容全量替换同义词的降重手段,现在的方案识别率还有提升空间。