上周运营组甩给我一个苦差。

3万篇从公域采集来的技术稿,要把里面AI生成的车轱辘话、凑字数的冗余段落全清掉。

要求清洗完的内容保留所有技术细节、参数、代码,一点不能丢。

一开始我还想偷懒找现成的工具解决,省得自己写代码。

结果前前后后试了GPTZero、Originality.ai、Content at Scale、Crossplag、Copyscape五六个工具。

要么要翻墙、要么按量报价贵到离谱,本地版直接要8G显存,根本跑不动我手头那台2C1G的闲置云服务器。

试了整整一周,所有工具全用得很憋屈,完全满足不了需求。

干脆放弃找第三方方案,自己撸了一套轻量的自研清洗流水线。

前后花了三天踩坑调试,最终跑起来峰值内存才700多M,完美适配低配置服务器。

首先我先把需求边界划得特别死,避免做无用功。

我们要做的不是判断“这篇内容是不是AI写的”,而是精准删掉AI生成逻辑下高频出现的无意义冗余内容。

没有要求100%全量识别,只要把冗余内容清理率做到90%以上,误删核心技术信息的概率低于0.5%就算达标。

我找了公司数据集里存的10万条公开AIGC生成的技术内容样本。

跑了一遍统计,最后定位到三类最高频的冗余特征,占所有AI生成空话段落的96%。

第一类是句子级语义重复率超过60%,前后两句话表述的信息点完全一致,没有任何新信息输入。

第二类是无意义连接词堆叠,整段一半以上字数都是“综上所述、值得一提的是”这类完全没用的过渡套话。

第三类是段落信息熵低于阈值,整段扫下来没有任何专业术语、数字参数,全是正确的废话。

定位完特征之后,我第一时间排除了用大模型来做清洗的思路。

要是调用GPT-3.5这类对外接口,3万篇1000字的内容算下来接口费要大几百块,排队跑还要3天。

而且我手头的2C1G服务器,连最低配的7B开源大模型都加载不动,一开就直接OOM挂掉。

最后敲定的技术路线全程不用大模型,全靠轻量算法组合实现。

只用到jieba分词、numpy数值计算这几个轻量依赖,连百兆以上的预训练文件都不用额外下载。

所有依赖加起来安装包体积不到20M,对低配置服务器极度友好。

pip install jieba numpy tqdm

这里额外说一句,我全程没有用网上开源的中文全量词向量表,那个文件大小动辄几个G。

我自己整理了一份裁剪后的300维中文基础词向量表,仅保留技术内容相关的常用12000个词汇,整个文件才11M。

加载完全部向量到内存里才占不到300M空间,完全不会给小服务器造成压力。

接下来分模块拆解核心实现逻辑,第一个模块是冗余连接词精准过滤。

一开始我图省事,直接用网上通用的中文停用词表来处理。

结果跑了几十篇就出问题,“服务器、端口、进程”这类核心技术关键词也被当成停用词删掉了,洗出来的内容全是碎片。

我直接删掉了那份通用表,自己整理了一份AIGC专属的冗余连接词表。

前后一共筛选出127个AI生成内容最高频出现的无意义套话,没有包含任何一个技术相关词汇。

对应的核心处理代码非常简单,运行效率极高:

REDUNDANT_CONNECTORS = {
    "值得一提的是", "从某种意义上来说", "综合来看",
    "总而言之", "不难看出", "事实上", "换言之"
}

def drop_dup_connectors(line: str) -> str:
    for con in REDUNDANT_CONNECTORS:
        line = line.replace(con, "")
    return line.strip()

实测这段逻辑的处理效率,每万行文本的匹配耗时不到0.3秒。

比用正则表达式做全局替换的性能还要高3倍以上,完全不会拖慢整体流水线速度。

第二个模块是句子间语义相似度校验,用来删掉前后句语义完全重复的冗余内容。

这里我没有用BERT类的句向量模型,那种模型光加载预训练权重就要占几个G内存,2C1G机器根本扛不住。

我用的是轻量版词向量平均算法:把一句话里每个词的300维词向量取出来,累加之后除以总词数,直接得到整句的句向量。

之前踩过一个大坑,最开始用的裁剪词向量表漏了很多计算机专属术语。

像“Redis持久化、TCP四次挥手”这类固定术语被拆成了单字,算出来的相似度完全失准。

我后来手动补了2100个常用计算机技术术语的自定义向量,直接把术语语义匹配准确率拉到了97%以上。

核心的余弦相似度计算代码如下,全靠numpy内置向量化方法运算:

import numpy as np

def calc_sent_sim(vec1: np.ndarray, vec2: np.ndarray) -> float:
    dot_product = np.dot(vec1, vec2)
    norm_a = np.linalg.norm(vec1)
    norm_b = np.linalg.norm(vec2)
    return dot_product / (norm_a * norm_b) if norm_a * norm_b > 0 else 0.0

实测在我的2核服务器上,单进程每秒可以完成1200组句子对的相似度计算。

完全不需要调用任何第三方大模型接口,所有运算都在本地完成,没有任何延迟。

我把判定阈值设在了0.7,前后两句相似度超过这个值的话,直接删掉后一句,只保留信息密度更高的那句。

第三个模块是段落信息熵校验,专门用来过滤整段全是空话的无效内容。

最开始我踩了个差点出线上事故的坑:直接用全局字符熵来计算熵值。

结果很多全是英文代码、端口数字、版本号的纯参数段落,被系统判定为低信息熵直接删掉。

后来我重写了熵值计算的加权逻辑,只要段落里出现数字、英文代码字符,就自动给最终熵值乘2倍的权重。

彻底避免了把核心技术参数、代码片段误判为无效内容的问题。

重写后的熵值计算核心代码如下:

import math
from collections import Counter

def calc_entropy(text: str) -> float:
    char_counter = Counter(text)
    entropy = 0.0
    total_len = len(text)
    for cnt in char_counter.values():
        prob = cnt / total_len
        entropy -= prob * math.log(prob, 2)
    # 技术参数、英文代码类内容自动加权
    has_special = any(c.isdigit() or ord(c) > 127 for c in text)
    return entropy * 2.0 if has_special else entropy

我用1000条标注好的样本反复测试阈值,最终把过滤阈值定在了3.2。

有效技术段落的熵值普遍在3.5以上,纯AI生成的空话段落熵值基本都低于2.8,误判率只有0.18%,完全符合我们的业务要求。

把三个核心模块串起来之后,我还用多进程调度做了性能优化。

一开始单进程跑全量3万篇内容,预计要6个多小时才能跑完。

后来我用Python内置的multiprocessing库开了4个worker进程,绑定仅有的两个CPU核心做任务分片。

全程CPU占用稳定在65%左右,完全没有跑满导致服务卡顿。

核心的多进程调度代码写起来也非常简单:

from multiprocessing import Pool
from tqdm import tqdm

if __name__ == "__main__":
    all_doc_list = load_all_collected_docs()
    with Pool(processes=4) as pool:
        result_list = list(tqdm(
            pool.imap(process_single_doc, all_doc_list),
            total=len(all_doc_list)
        ))
    save_all_cleaned_result(result_list)

最终全量3万篇内容跑完只花了1小时42分钟,比最开始的单进程方案快了3倍多。

我后来随机抽了1000篇处理完的内容做人工校验,结果特别惊喜。

冗余套话、重复内容的整体清理率达到了92.3%,所有的代码片段、技术参数、实验结论都完整保留,没有一处误删。

而且整个脚本跑下来,服务器的峰值内存占用才780M,我甚至还能在后台挂个Nginx跑点其他小服务,完全不浪费资源。

这段时间我还在做后续的优化,打算把向量计算的热点逻辑用Cython重写一遍。

初步测试下来重写之后的计算速度还能再提升40%,后续就算要处理10万级别的内容量,半天之内也能全部跑完。

最后说句掏心窝子的,很多时候小团队碰到小体量的定制化需求,真没必要去凑第三方工具的热闹。

之前我前前后后花在找工具、注册账号、调试配置上的时间加起来有一周多。

最后自己写这套脚本,加上踩坑调试也才花了三天,不仅完全适配自己的业务场景,后续要加新特征修改逻辑也特别方便。

省下来的第三方工具年费、接口费,都够我给这台小服务器续一整年的带宽了,性价比高到离谱。

更多推荐