写作指纹:用AI实现作者识别与风格匹配的工程实践

发布时间:2026/11/4 3:43:22
写作指纹:用AI实现作者识别与风格匹配的工程实践

如果你做内容审核、搞原创度检测,或者管着一个投稿平台,一定遇到过这种头疼事:一篇匿名爆款文全网转载,你拿不准真正的首发作者是谁;一篇稿子被人投诉洗稿,人工比对读下来眼都快瞎了;或者你手上攒了一堆作者的稿子,想按风格给新任务配对人,但只凭印象拍脑袋,根本不靠谱。

这类问题的本质,都是同一个:怎么从文字里判断“谁写的”。过去这是编辑靠语感做的事,说玄也玄,现在它能被拆成一套可计算的流程,核心载体就是“写作指纹”。AuthorMatch AI做的就是这件事——把“文章是谁写的”从模糊的感觉变成量化的指标,用AI提取作者的行文特征,再完成作者识别、作者匹配、风格归因等一系列任务。干这行的、管内容平台的、做学术期刊编辑的,还有对NLP感兴趣想练手的开发者,都可以从这个项目里拿到一套能直接落地的思路。

下面我按自己实际搭建的路径,把整个项目的设计思路、核心原理、实操细节和踩过的坑完整拆一遍。

1. AuthorMatch AI到底解决什么问题

1.1 名字拆解:匹配的不只是“作者”

项目名叫AuthorMatch,严格说包含两层能力,很多人第一次听到会误以为只是“认人”,其实它能干的事更多。

第一层是作者识别,英文里叫Authorship Attribution。给一段未知文本,判断它出自某个候选作者中的哪一位。这是最经典的任务,相当于给文章做“笔迹鉴定”。第二层是作者与任务匹配,也就是Author-Task Matching。给一篇待定稿件,从作者库里找出写作风格最契合的人;或者反过来,给一个作者画像,推荐最适合他风格的内容类型。这两层能力共享同一套底层的文本特征引擎,差别只在上层任务的输出形式。

我一开始只做了第一层,后来发现实际使用时,客户更常问的是“这篇稿子适合哪个人写”或者“这篇稿子是不是某个人写的”。所以,项目最终形态是把识别能力和匹配能力合在一个服务里,底层是同一个特征库,上层拆成两个接口。这样的设计也符合业务直觉:你只要能算出作者风格向量,就能算向量之间的相似度,识别是“拿它和谁最像”,匹配是“和谁最合适”,本质是同一个数学问题。

1.2 为什么现在特别需要它

早些年判断作者靠编辑人工读稿,那时候稿量小,可行。现在内容平台一天涌进来几十万篇投稿,加上AI写作工具泛滥,几百字的小作文可能有一半不是人写的。这时候靠人来判断“谁写的”“是不是抄的”,成本高到不现实。

一个很现实的场景:平台收到一篇匿名投稿,疑似某个大V换马甲发的。人工判断需要整篇通读,还要熟悉那个大V既往的风格,效率极低。但AuthorMatch可以用大V的历史文章做特征提取,把新稿子转成特征向量,算一个相似度,几秒钟出结论。再比如洗稿投诉,经常是原文被改得情节一样、句子全换,查重系统查不出来,但作者独特的句式偏好、标点习惯、虚词使用频率这些底层特征很难全部改掉,AuthorMatch正好补上这一环。

加上现在AI写作内容暴增,“AI味”反而成了一个可识别的特征簇。AuthorMatch不仅能区分人,还能区分人和机器,这对内容平台的风控来说,是刚需中的刚需。所以我把它定义成:作者识别、风格匹配、人机内容区分三合一的AI文本分析引擎。

2. 写作指纹:作者特征从哪来

2.1 人的写作习惯藏在哪些细节里

很多非技术背景的人以为“风格”是文笔、是修辞,但做NLP的人知道,真正稳定的作者信号,恰恰是那些作者自己都没注意过的“小习惯”。这里面的门道如果你要自己从零做,可以从下面这几个维度入手。

首先是功能词使用频率。像“的、了、是、在、和、就”这类词,它们不承载具体主题内容,但每个人的使用频率很稳定。有人爱用“的”,有人爱用“了”,这种差异在数据上极其显著,而且换主题也不变。相比之下,内容词(名词、动词)会随主题剧烈变化,不适合做作者特征。

其次是句式长度分布。同一个作者写句子,平均长度、长短句交替的模式通常比较稳定。有人通篇短句,有人习惯写带从句的长句,这个可以通过统计句号、分号、逗号之间的小句长度来量化。

然后是标点使用习惯。别小看标点,这是很隐蔽的指纹。有人极度爱用逗号一逗到底,有人分号用得很频,有人引号使用密集,还有人对省略号和破折号有特殊偏好。我把标点频率做成一个特征向量后发现,在很多场景下它的区分度甚至高于词频特征。

最后是搭配习惯,也就是n-gram模式。比如“然而”还是“但是”,“一般来说”还是“通常”,这些连接词的选择和组合方式,是作者语言习惯的标记。字符级别的n-gram捕捉用字习惯,词级别的n-gram捕捉措辞习惯,组合起来就是一套完整的风格画像。

我之前和同行交流时总结过一句话:写作指纹不是看作者“写了什么”,而是看他“怎么写那些不重要的字”。这个思路直接决定了后续特征体系的搭建方向。

2.2 特征工程:把写作习惯变成向量

有了上面的观察,接下来就是把它们变成机器能算的数字。我实际使用的特征体系按四类组织,你在搭建时可以照着这个框架走。

特征类别具体特征说明
词汇特征词汇丰富度、类符形符比、平均词长、功能词频率捕捉用词层面的稳定习惯
句法特征句长均值/方差、小句长度分布、被动语态比例、连接词使用频率捕捉句式组织的偏好
字符特征字符n-gram分布、标点符号频率、大小写习惯捕捉拼写和标点的微观习惯
结构特征段落长度分布、段落数、换行习惯、数字与特殊符号使用捕捉排版和段落组织的差异

一个关键经验是:特征不是越多越好,而是要看稳定性。我最初堆了几百个特征,发现有些特征换个领域就失效。后来做法是,按主题分类分别做特征稳定性测试,只保留在多个主题下都稳定的特征子集,这样模型跨场景泛化能力明显提升。

特征工程里还有两个细节容易忽略。一个是文本长度归一化,不同长度的文章特征值波动很大,需要按段落或每千字为单位来统计,避免长文和短文不可比。另一个是模板内容过滤,很多文章带有固定的开头结尾、免责声明、引用格式,这些不属于作者风格,必须在预处理阶段清洗掉,不然模型会学到模板特征而不是作者特征。

2.3 匹配算法:怎么判断“像不像”

有了作者特征向量,接下来的问题是怎么判断“像不像”。这里没有唯一答案,我试过的路径有三条,各有利弊。

第一条是传统机器学习分类法:把所有候选作者的文本作为训练集,提取特征,训练一个多分类器(比如随机森林或XGBoost),预测时直接输出最可能的作者。优点是可解释性强,特征重要性一目了然;缺点是候选作者集合一旦变化就要重新训练,而且新作者加入需要全量更新。

第二条是向量相似度法:对每个作者构建一个风格向量(可以是特征向量平均,也可以是BERT句向量池化),新文本算出向量后,和库里所有作者向量算余弦相似度,取最高者。优点是不需要频繁重训,新增作者只要把历史文章向量加入向量库;缺点是相似度阈值需要仔细校准,阈值定高了漏判、定低了误判。

第三条是深度语义匹配:用BERT这类预训练模型做句子对匹配,输入“文本A + 文本B”,输出它们是不是同一作者。我实测下来准确率上限最高,但训练成本高,推理慢,而且对短文本的收益没有想象中那么大。

我最终采用的是混合方案:线上用向量相似度做快速召回,从几百个作者里捞出top10候选;再用深度匹配模型在top10里精排,输出最终判定。这样兼顾了速度和准确率。实话说,如果候选作者数量在50以内,纯向量相似度已经够用;一旦数量上去,混合方案的优势才明显。

3. 从零搭建:一个可复现的落地方案

3.1 数据集准备与预处理

做作者识别,数据是最关键的环节,也是最容易被低估的环节。我踩过的第一个大坑就是用主题混杂的语料训练,结果模型学的不是风格,而是主题。比如训练集里作者A写的全是科技文、作者B写的全是情感文,模型只需要判断主题就能分出作者,换个场景就完全失效。

正确的做法是平衡语料:每个作者的样本量尽量接近,同时每个作者尽量覆盖多个主题。如果做不到多主题,至少要保证训练集和预测集的主题分布一致。我在数据准备阶段还会做这几个动作:

  • 去除页眉页脚、固定模板段落、版权声明;
  • 统一编码格式,排除乱码干扰;
  • 过滤过短文本(少于200字的一般不参与训练,太短的稳定特征太少);
  • 做相似文本去重,避免同一篇稿子多次出现导致模型过拟合。

这里有个容易被忽略的细节:同一个人在不同场景下的风格差异可能大于不同人在同一场景下的差异。一个写正式报告的人,和他在聊天软件里的语气完全是两个人。所以构建作者画像时,文本来源要尽可能一致,或者按场景分组建模。我后来把作者特征库按“正式文章/社交媒体/评论短文本”分了三个子库,识别时先判场景再选模型,效果比单一模型好得多。

3.2 特征提取与模型训练

数据准备好之后,我通常用一个Python脚本完成特征提取和训练,结构清晰,也方便复现。下面这段代码是特征提取的核心框架,包含了词汇、句法和标点三类基础特征:

import re from collections import Counter def extract_style_features(text): """从单篇文本中提取写作风格特征""" # 预处理:统一全角/半角、清除URL和模板内容 text = re.sub(r'http\S+|www\.\S+', '', text) text = text.replace('\u3000', ' ') # 句子切分:按中英文句号、问号、感叹号切分 sentences = re.split(r'[。?!\.!?]', text) sentences = [s.strip() for s in sentences if len(s.strip()) > 1] # 词数统计(这里以词为单位,中文按字+词混合处理) words = re.findall(r'[\u4e00-\u9fa5]|[a-zA-Z]+', text) total_chars = len(re.findall(r'[\u4e00-\u9fa5]', text)) # 功能词频率(常用稳定功能词表) function_words = ['的', '了', '是', '在', '和', '就', '都', '而', '及', '着', '或', '被', '把', '让'] fw_freq = [] for fw in function_words: fw_freq.append(text.count(fw) / max(total_chars, 1)) # 标点频率 punct_count = [] for p in [',', '。', '、', ';', ':', '“', '”', '——', '…']: punct_count.append(text.count(p) / max(total_chars, 1)) # 句长统计(基于字符数) sent_lens = [len(s) for s in sentences] avg_sent_len = sum(sent_lens) / max(len(sent_lens), 1) var_sent_len = sum((l - avg_sent_len) ** 2 for l in sent_lens) / max(len(sent_lens), 1) # 词汇多样性:类符形符比 word_counter = Counter(words) ttr = len(word_counter) / max(len(words), 1) features = { 'num_sentences': len(sentences), 'avg_sent_len': avg_sent_len, 'var_sent_len': var_sent_len, 'ttr': ttr, 'avg_word_len': total_chars / max(len(words), 1), 'function_word_freq': fw_freq, 'punct_freq': punct_count, } return features

特征提取后,我会先拼接成一个一维特征向量,交给随机森林训练。实际训练时有一些参数选择值得说说:树的数量设500棵,max_depth设12,特征采样比例设0.3,这样在特征维度高时能有效防止过拟合。最重要的是训练集和验证集要按作者划分而不是按文本划分——如果同一作者的文本同时出现在训练集和验证集里,模型会记住作者特征,分数虚高得离谱,这个坑我一开始踩得很痛。

至于是否要用BERT,我的建议是:先从传统特征开始建基线。如果baseline的准确率已经达到95%以上,深度学习模型的提升空间有限,而且部署成本高不少。只有当候选作者数量多、文本风格接近、传统特征区分度不足时,再上深度模型才不亏。

3.3 服务化部署与实时识别

模型训练完成后,我把它封装成了一个FastAPI服务,线上提供两个接口:一个是单文本作者识别,一个是批量匹配。服务化部署的架构不复杂,但有几件事必须处理好。

第一件事是特征库的动态更新。作者库不是静态的,每天都有新作者入库,老作者有新文章。我的做法是作者画像向量存在一个支持增量更新的向量数据库里,每篇新文章入库时单独计算特征向量,合并进该作者的平均向量中。这样新增作者不需要重训模型,只需要扩充向量库,运维成本低很多。

第二件事是置信度输出。线上识别不能只给一个作者名,必须给置信度。我用的是“相似度差值法”:最高相似度减去次高相似度,差距越大可信度越高。比如最高相似度0.91、次高0.63,差值0.28,基本可以判定;如果差值是0.02,哪怕最高相似度0.90也得谨慎,因为说明这个文本和多个作者的风格都接近,可能是有意模仿或者中间人代笔。

第三件事是性能设计。向量相似度召回我用faiss建了索引,单次检索毫秒级返回top10;精排的深度模型用ONNX部署,单次推理控制在30毫秒左右。整套服务在单机4核8G的配置下,跑1000次识别大概需要50秒,这个吞吐量对大多数内容平台够用了。

下面是一段简化的API服务代码,展示了核心的识别逻辑:

from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() class AuthRequest(BaseModel): text: str top_k: int = 5 @app.post("/identify") def identify(req: AuthRequest): # 1. 提取待识别文本的特征向量 vec = extract_style_vector(req.text) # 2. 从向量库中召回 top_k 候选作者 candidates = author_index.search(vec, req.top_k) # 3. 精排模型对候选进行pairwise打分(略) results = [{"author": c.author_name, "score": c.score} for c in candidates] return {"results": results, "status": "ok"}

部署后我强烈建议加一层监控告警:每天统计识别结果的平均置信度、top1命中率抽样。如果某天平均置信度显著下降,大概率是线上文本分布变了,比如新出现了一类机器生成的文本模式,需要及时补充训练数据微调模型。

4. 踩过的坑与排查实录

4.1 短文本识别的翻车现场

上线没多久就接到一个需求:识别一段50字的微博评论是谁写的。当时我想当然地以为长文本能识别,短文本把阈值调低点就行,结果准确率直接掉到60%以下,和随机猜差不多。

原因是短文本里能提取到的稳定特征太稀疏。50个字可能只有十几个功能词,标点可能就一个句号,词汇多样性更是没有统计意义。这个问题的解法我当时试了几种,最有效的是“上下文拼接法”:如果待识别短文本出现在某个已知上下文里,比如是某篇文章的评论、某个话题下的发言,就把上下文文本拼接进去一起算特征,等于给短文本补了一段“背景风格”,准确率能回升二十多个百分点。还有一招是做特征扩展:用作者的历史短文本统计出该作者的“短文本风格档案”,识别时单独和短文本档案比对,而不是和长文档案比对。

所以如果你也要做短文本作者识别,建议先想清楚一个事:这个场景天生适合做识别吗。如果真的非做不可,务必分场景建模型,别拿一套长文模型硬扛。

4.2 风格漂移与多作者协作

另一个让我头疼的问题,是作者风格会漂移。同一个作者十年前在博客上的文风和现在的朋友圈文风差异巨大;一个作者写专业报告和写段子,用的标点习惯完全不同。如果只用一套历史画像去匹配他所有时期的作品,早晚出错。

我后来做了个按时间窗口切分的作者画像:每个作者每个季度单独计算一个风格向量,识别时先判断待识别文本可能属于哪个时期,再用对应的画像匹配。这个改动让老作者的作品识别准确率提高了将近15个点。

还有多作者协作的稿件,这在新闻稿和技术博客里尤其常见。一段文章可能有3个作者各写一部分,直接整篇匹配会失败。我的处理是分段识别:按段落切分,每段单独识别作者,再输出一个“作者贡献分布”。这东西在实际业务里很实用——比如平台要算稿费分成,或者做原创度判定,都很需要这种细粒度结果。

4.3 机器改写后的对抗与防御

做作者识别绕不开一个对抗场景:有人故意改写文本试图逃避识别。典型的操作包括同义词替换、语序调整、句式改写,甚至用AI工具先改写再发布。这里面有个好消息:写作指纹的特征很多藏在标点、功能词、句子长度分布这些“低语义”层面,常规的同义词替换对这些特征影响很小;即使是AI改写,也很难完全消除一个人的标点习惯和句式节奏。

但确实有高强度的改写能骗过模型。最有效的一种是“整篇仿写”——让另一个作者模仿目标作者的风格去写新内容。这种情况所有基于统计的方法都很难识别,因为它本质上是风格伪造。我的防御策略是加“风格一致性检验”:除了算和候选作者的相似度,还要算文本内部的一致性。如果前半段像一个作者、后半段突然换成另一个人的节奏,那这文本大概率有蹊跷,输出结果时标记为“疑似多人协作或AI改写”。

做这类对抗测试时,我强烈建议自己动手做一个简单的改写工具,把改写后的文本灌进测试集,看看识别准确率的变化。只有被攻击过、补强过的模型,上线才敢说心里有底,不然遇到恶意场景大概率一击即溃。

4.4 常见问题速查表

我把开发和上线期间遇到的高频问题整理成了一张表,方便对照排查。

问题现象可能原因排查思路
准确率虚高,换场景就崩训练集主题和测试集主题重叠按作者划分训练/验证集,做主题平衡
短文本识别率极低短文本稳定特征稀疏上下文拼接、单独建立短文本画像
新作者加入后老识别结果变化特征库未增量更新导致向量偏移改为增量更新,重算受影响作者向量
相似度普遍偏高无区分度特征未做归一化或文本长度差异过大按千字归一化,过滤过短文本
机器改写文本识别率暴跌特征体系缺少句法和标点维度补充字符n-gram和标点频率特征
同一文本调用接口结果不稳定线上模型和训练模型版本不一致固定模型版本,统一特征提取参数

排查问题的时候,我建议先看特征分布,不要直接调模型。很多所谓的“模型效果不好”,实际是特征提取环节出的问题——比如文本预处理没做好、模板内容没清干净、特征归一化没到位。把特征分布打印出来看一眼,问题往往就清楚了。

5. 应用场景与影响范围

5.1 内容平台:从洗稿检测到作者推荐

对内容平台来说,AuthorMatch的价值不只是风控。一开始我主要做洗稿检测——平台投诉太多,人工看不过来,模型先把相似度高的都筛出来,再送人工复核。这样做人力节省了70%以上,每天的投诉处理量翻了三四倍。

但真正让平台觉得值的是作者推荐场景:平台上有一批签约作者,平台接到新选题,需要知道这个选题最适合哪个作者写。过去是靠编辑的印象分,现在可以把选题的关键词、预期风格转成文本描述,和作者风格向量库匹配,输出一个“作者的推荐排序”。实测下来,推荐的匹配准确率比编辑人工判断高了近两成,而且能覆盖编辑不熟悉的新作者。这就是从“识别”到“匹配”的自然延伸,也是这个项目名字里Match的由来。

5.2 学术与编辑场景:审稿人匹配与诚信检查

学术期刊的编辑岗,每天要处理两件事:一是判断来稿有没有学术不端,二是给稿件找合适的审稿人。前者是典型的作者识别场景,一篇文章换了关键词、改了摘要,但骨架没变,靠传统查重查不出来,靠AuthorMatch的风格特征能找出可疑的同源文本;后者是匹配场景,把稿件的语言风格和已发表论文的风格向量比对,推荐风格最接近的作者群作为审稿人候选,因为风格相近意味着领域相近、写法相近,审稿人对这类文章更有判断力。

我接触过一个做预印本平台的朋友,他们尝试用这套思路做匿名投稿的作者身份验证,主要防的是“论文工厂”批量生产伪原创论文。识别出的同源论文比例相当惊人,这类场景对学术圈的价值是很大的。

5.3 版权保护与内容溯源

内容溯源是另一个很有价值的方向。比如盗版小说网站会把正版内容做轻量改写后上传,章节顺序打乱、主角名字替换,这种情况下常规的文本匹配完全失效。但作者的章节节奏、对话描写习惯、场景转换的句式特征,这些很难全部改掉。按段落做作者画像比对,往往能精准锁定盗版章节和正版章节之间的风格关联,为维权提供技术依据。

我体验过最深的感受是:这类需求平时藏在看不见的地方,但一旦出事就是大事。提前把作者特征库建好,等于给内容资产上了一道隐形的保险,这是很值得投入的基础建设。

5.4 后续扩展方向

AuthorMatch做久了,能看见的自然延伸方向挺多。

一个是多模态特征融合。目前的特征体系以文本为主,但很多内容平台还有配图、排版、视频字幕,这些模态里也藏着作者的稳定习惯。把视觉排版特征和文本特征结合,识别能力能再上一层。另一个是实时增量学习。作者风格是动态演变的,未来可以做到每处理一篇新文章就自动微调该作者的画像,让模型跟上人的变化。

再往产品端看,现在很多团队在做AI写作工具,可以反向利用作者识别技术做“风格引导”:用户输入自己的历史文章,AI学习他的风格后,在写作时给出符合个人风格的提示词和句式建议。这等于让每个人都有了自己的“AI文风助手”,从识别走向生成,这个想象空间就大了。

我个人在这套系统上最深的体会是:越是在AI内容泛滥的时代,真实的人类风格越有价值。识别技术本质上在做一件事——守住“谁写的”这个基本事实。这个事实对内容平台是风控,对作者是权益,对读者是信任。所以这类项目技术门槛有,但真正难的是把特征做细、把场景做准、把阈值调稳。照着上面这套路子走一遍,踩过几个坑,你也能搭出一套能用的作者匹配系统。