AI生成文本检测是近期内容社区讨论得很热闹的一个话题。你经常能看到“一眼识别AI文本”的分享贴其中一个流传很广的梗是AI喜欢用 em dash也就是英文里的长破折号 “—”中文语境下对应全角破折号“——”。于是很多人把“破折号出现频率”当成判断AI文本的硬指标甚至做成脚本统计到几个破折号就自动标记为疑似AI生成。这个判断如果放在英文博客、营销文案里可能有一点道理但如果真的拿它当检测依据误判率会高得惊人。提示词一变模型输出的标点习惯就跟着变作者文风不同人类写作里的破折号也可能非常多。更关键的是破折号属于“浅层风格特征”它只能反映某个模型的某一个侧面并不能代表AI文本的普遍规律。真正值得关注的是文本内部的概率分布和统计特征。这篇文章不从“看起来像”出发而是从“可计算、可验证”的角度拆解哪些信号对AI生成文本更有区分度并给出一个可以本地运行的Python检测小原型。读完你可以理解 perlexity、burstiness、词汇多样性这些指标的实际含义也能自己跑一版检测流程搞清楚为什么单一特征会误判。1. 为什么“破折号检测法”不可靠先说清楚 em dash 是怎么变成“AI文本试金石”的。很多大语言模型在长文本生成时倾向于使用破折号来表达插入语、补充说明和语气转折。这可能是训练数据里高质量英文文本的常见风格也可能是模型在长序列建模时偏好的一种“安全”连接方式。于是人们发现AI生成的英文文章里破折号很多。但问题在于这个规律并不稳定。你可以在提示词里直接要求“不要使用任何破折号”也可以要求“拆成短句”模型输出会立刻变化。不同模型对破折号的偏好也不一样新版本模型经过对齐和风格控制后这类浅层特征往往会被修正。更现实的问题是人也会大量使用破折号。部分作者、编辑、学术写作者本身就有使用长破折号的习惯尤其在英文非虚构写作里em dash 是表达插入语和强调的常见工具。从统计角度看一个特征要成为检测依据至少需要满足两个条件区分度要足够大稳定性要足够好。破折号在两个条件上都表现不佳。对于一段中文文本情况更复杂——全角破折号到底是刻意使用还是输入法自动生成很难从标点数量里判断。因此破折号可以作为一个观察窗口但不能作为检测证据。真正更可靠的信号藏在更深的统计分布里。2. 更可靠的信号概率分布、突发性与语义模式要把“AI文本一眼假”这种主观感觉变成可测量、可计算的指标需要从语言生成机制说起。大语言模型生成文本的过程本质上是不断从概率分布中采样下一个 token。AI生成的文本倾向于选择高概率词整体路径比较“安全”所以模型对这段文本的预测难度较低人类写作则不同作者会引入口语、生僻词、隐喻、特殊术语这些内容会让语言模型的预测难度明显上升。这个差异对应的概念就是 perplexity中文常译作困惑度。通俗解释是模型看到一段文本时它有多“意外”。如果模型很熟悉这段文本的模式困惑度就低如果文本充满意外困惑度就高。AI生成文本整体困惑度偏低因为模型生成时就在走自己最擅长的路。另一个更重要的指标是 burstiness中文可以理解为突发性或波动性。人类写作有张弛有些句子信息密度高使用的词汇意外性强模型预测起来很吃力有些句子则是连接部分比较平淡。因此人类文本的句子级困惑度波动较大。AI生成的文本往往从头到尾保持“平稳质量”句子之间的困惑度差异很小。这个“平滑感”其实是比平均困惑度更有辨识度的信号。除了概率统计层面的特征语义层面也有一些可观察的模式维度人类写作常见表现AI生成文本常见表现能否用提示词改变词汇多样性因作者功底差异较大会出现特定领域的术语和口语用词正确但重复率高偏好通用词汇可以部分改变但本质概率路径难完全改变过渡与套话过渡自然偶尔使用不会密集出现频繁使用“首先”“其次”“总而言之”“值得注意的是”等模板可以改变但默认输出概率较高句子结构长短句交错节奏变化明显句式均匀信息密度稳定缺少节奏起伏可以改变但需要较强的提示控制认知立场有个人判断、不确定性、限定条件回避立场追求面面俱到缺少真实观点可以改变但模型本身倾向安全表述具体细节包含真实数字、时间、地点、动作细节细节偏泛化常用“一些”“许多”“各种”等模糊量词可以被提示但模型缺乏真实感知细节多为结构化的假细节把这些信号组合起来得到的不是某个单一的“AI嫌疑值”而是一份文本分布画像。检测工具要做的事情就是提取这些可计算特征再根据一组文本的分布去判断它们更像人类写作还是AI生成。3. 实验准备环境、数据与评估思路为了让讨论落地下面实现一个最小可运行的检测流程。这个流程不是工业级检测系统而是帮助你理解核心指标的教学原型。3.1 环境准备推荐使用 Python 3.8 或更高版本安装依赖pip install transformers torch scipytransformers 负责加载语言模型torch 提供推理框架scipy 用于统计计算。本文示例使用gpt2作为困惑度计算模型因为它是开箱即用的小型因果语言模型适合演示。如果处理中文文本建议换用支持中文的因果语言模型效果会更好具体模型名称请在运行时从模型库中选择不同版本可用性会有差异。3.2 数据准备准备两组文本一组是人工撰写的文章或笔记一组是AI生成的文本。每组 5 到 10 篇即可演示流程。注意文本来源必须合法合规仅用于个人学习、内容管理或学术研究。不要用这套方法去对他人文本做公开定性检测只能作为辅助手段。3.3 评估思路先对每篇文本分别计算多个特征再比较两组文本在特征分布上的差异。如果某个特征在两组之间有明显分离说明它有区分度如果分布重叠严重说明它不适合作为判断依据。这种“先看分布再定阈值”的思路远比拍脑袋定一个“破折号超过3个就是AI”要可靠。4. 特征量化用 Python 统计文本风格先从最容易实现的统计特征开始。创建一个features.py文件包含破折号计数、平均句长、词汇多样性、过渡短语频率等函数。# 文件features.py import re def count_em_dashes(text: str) - int: 统计长破折号英文 em dash、中文全角破折号 return len(re.findall(r—|—|–, text)) def avg_sentence_length(text: str) - float: 按常见句末标点切分句子返回平均句长词数 sentences re.split(r[.!?。], text) sentences [s.strip() for s in sentences if s.strip()] if not sentences: return 0.0 return sum(len(s.split()) for s in sentences) / len(sentences) def type_token_ratio(text: str) - float: 词汇多样性不同词数 / 总词数简称 TTR words re.findall(r\b\w\b, text.lower()) if not words: return 0.0 return len(set(words)) / len(words) TRANSITION_PHRASES [ 值得注意的是, 总而言之, 首先, 其次, 最后, 综上所述, 需要指出的是, 不难发现, in conclusion, notably, moreover, furthermore, additionally, ] def transition_frequency(text: str) - float: 统计常见过渡短语出现次数除以文本长度做归一化 count 0 lower_text text.lower() for phrase in TRANSITION_PHRASES: count lower_text.count(phrase.lower()) return count / max(1, len(text))这段代码的核心价值不是“高深”而是把“模板化”“套话多”这种主观感受变成可比较的数值。count_em_dashes用来观察破折号频率avg_sentence_length能反映句子节奏type_token_ratio可以衡量文本用词的丰富程度transition_frequency则用来捕捉那些最容易被AI默认输出的过渡套话。在实际使用中你不需要迷信任何一个指标。比如type_token_ratio和技术类文章之间就可能存在冲突技术文章本来就会反复使用术语TTR天然偏低但这不代表它是AI生成的。所以这些特征更适合作为多维输入而不是单点判据。5. Perplexity 与 Burstiness 计算基础统计特征容易实现但它们很容易被人类作者、文体差异干扰。更具区分度的是基于语言模型计算的困惑度和突发性。5.1 计算文本困惑度创建perplexity.py加载一个因果语言模型计算整段文本的困惑度。# 文件perplexity.py import torch from transformers import AutoTokenizer, AutoModelForCausalLM MODEL_NAME gpt2 tokenizer AutoTokenizer.from_pretrained(MODEL_NAME) model AutoModelForCausalLM.from_pretrained(MODEL_NAME) model.eval() def ppl_for_text(text: str) - float: 计算一段整体文本的困惑度perplexity encodings tokenizer(text, return_tensorspt) input_ids encodings[input_ids] with torch.no_grad(): outputs model(input_ids, labelsinput_ids) loss outputs.loss return torch.exp(loss).item()这里的原理是把文本输入模型同时把原文本作为标签模型会计算每个 token 的交叉熵损失取均值后再做指数运算得到困惑度。数值越小代表模型对这段文本的预测越轻松也就意味着文本在统计上更接近模型的“舒适区”。AI生成文本通常会有偏低的困惑度。5.2 计算句子级波动由于 AI 文本的“平稳感”更重要需要计算句子级困惑度的标准差即 burstiness。创建burstiness.py# 文件burstiness.py import re import statistics from perplexity import ppl_for_text def sentence_ppl(text: str) - list: 将文本按句子切分计算每个句子的困惑度 sentences re.split(r[.!?。], text) sentences [s.strip() for s in sentences if s.strip()] return [ppl_for_text(s) for s in sentences] def burstiness(text: str) - float: 句子困惑度的标准差衡量文本张弛度 ppls sentence_ppl(text) if len(ppls) 2: return 0.0 return statistics.pstdev(ppls)这里有个需要注意的细节短句子的困惑度往往非常不稳定单个短句可能因为缺少上下文分数波动很大。实际使用中建议把文本切分成合理的窗口比如每 2 到 3 个句子合并成一个小片段再计算困惑度序列。这样能减少碎句噪声让 burstiness 更有参考价值。另外gpt2对中文文本的支持并不好。如果处理中文需要换成中英文都支持或纯中文的预训练模型。这个替换不会改变整体流程只影响模型加载那一行代码。6. 综合评分一个可运行的 AI 文本检测原型有了基础特征、困惑度、突发性之后可以把它们组合成一个简单的检测函数。创建一个detector.py输出多维特征并给出一个从 0 到 100 的倾向性分数。分数越高代表文本在统计上越接近 AI 生成文本。# 文件detector.py from features import count_em_dashes, avg_sentence_length, type_token_ratio, transition_frequency from perplexity import ppl_for_text from burstiness import burstiness def detect(text: str) - dict: 提取多维文本特征并输出 AI 倾向分数 ppl ppl_for_text(text) bst burstiness(text) ttr type_token_ratio(text) trans transition_frequency(text) score 0 reasons [] # 困惑度越低AI 嫌疑越高 if ppl 25: score 25 reasons.append(overall_perplexity_low) elif ppl 40: score 15 reasons.append(perplexity_moderate) # 突发性越低AI 嫌疑越高 if bst 5: score 25 reasons.append(burstiness_low) elif bst 10: score 15 reasons.append(burstiness_moderate) # 词汇多样性越低AI 嫌疑越高 if ttr 0.45: score 15 reasons.append(ttr_low) elif ttr 0.55: score 8 reasons.append(ttr_moderate) # 过渡套话越密集AI 嫌疑越高 if trans 0.003: score 20 reasons.append(template_transitions) elif trans 0.001: score 10 reasons.append(some_transitions) # 破折号只作为参考不明显影响总分 dash_count count_em_dashes(text) if dash_count 5: score 5 reasons.append(many_dashes) return { score: min(100, score), perplexity: round(ppl, 2), burstiness: round(bst, 2), type_token_ratio: round(ttr, 3), em_dash_count: dash_count, reasons: reasons, }这个评分函数的阈值只是示例不应该直接写死到生产环境。不同模型生成的文本、不同语言、不同领域的人工文本在困惑度和突发性上差别很大。正确做法是先准备一批有代表性的样本分别计算特征分布再根据分布确定阈值。值得强调的是破折号在这个综合模型里只占很小的权重。这是有意为之破折号本身区分度不稳定强行提高它的权重只会增加误判率。7. 运行结果与判断框架在命令行里运行检测函数可以用一个简单的脚本读取文本文件并输出特征。这里不写复杂了直接演示调用# 文件run_detector.py import sys from detector import detect text sys.stdin.read() result detect(text) print(result)运行命令python run_detector.py sample_ai_text.txt预期会看到类似这样的输出{ score: 62, perplexity: 18.43, burstiness: 4.1, type_token_ratio: 0.483, em_dash_count: 6, reasons: [overall_perplexity_low, burstiness_low, ttr_moderate, some_transitions, many_dashes] }这只是一个示意输出实际数值取决于模型版本、文本长度和语料来源。你需要重点关注的是两个问题第一score是否偏高。如果一段人工撰写的文本因为某人习惯使用破折号、句子节奏稳定就被打出高分说明你的特征权重或阈值设置有问题。第二不要把score 80当成“这就是AI写的”的依据。这个分数只能说明“该文本在统计分布上更像AI生成文本”。它适合用来做风险排序比如内容审核场景中把高分段文本优先交给人工复核而不是直接做自动定性。一个更稳妥的判断框架是先看 perplexity 和 burstiness 是否同时偏低再看过渡套话是否密集最后才看词汇多样性和破折号。如果前两个特征都不满足破折号再多也说明不了太多。8. 常见误区和排查思路这套流程看起来简单实际运行时很可能遇到各种问题。下面列出几个高频问题。问题现象可能原因排查方式解决方案人类文本被频繁误判为AI作者文风本身就平稳、套话较多对比多篇同作者文本的特征分布引入更多特征不要只依赖困惑度和突发性破折号数量很高但最终分数不高破折号权重被有意降低或其他特征不匹配AI分布检查检测器返回的 reasons 字段这是预期行为说明综合判断比单一特征可靠perplexity 对短文本极不稳定单句缺少上下文模型预测难度大打印句子级困惑度序列使用 2 到 3 个句子合并的滑动窗口中文文本的分数明显失真使用 gpt2 处理中文分词和概率分布都不适配查看 tokenizer 切分结果换用支持中文的因果语言模型同一篇文章不同模型计算得分差异巨大不同模型的概率分布不同对比多个模型的困惑度排名固定检测使用的模型和版本不要混用AI 文本被改写润色后分数下降改写破坏了原始概率分布特征分析改写后的句子结构变化明确检测边界润色后文本可能无法可靠识别遇到误判时最忌讳的是继续调高某个特征的权重。更合理的方式是收集更多样本文例观察误判文本和正确识别文本在特征空间中的分布差异根据差异做针对性调整。9. 工程落地建议与风险边界如果你想把这套思路应用到真实业务中有几点需要提前想清楚。第一检测工具应该定位为“人工审核的辅助”而不是“自动定罪的法官”。内容审核、学术评审、招聘筛选这类场景中一个统计分数不能替代人工判断更不能成为单方面惩罚的依据。把检测结果标成“需要进一步核查”比“这是AI生成”更稳妥。第二持续监控模型更新和特征漂移。大模型更新频率很快旧版本模型生成的文本特征可能在新版本中完全消失。你需要周期性地用新样本重新校准阈值否则检测系统会逐渐失效。第三注意数据来源的合法合规。建设检测系统时用于训练的文本必须有合法来源包括自有语料、授权使用的公开数据集等。不要使用通过非授权方式爬取的数据。第四不要陷入“猫鼠游戏”。有些写作者会刻意调整文本让它更“像AI”或更“不像AI”这种对抗会影响检测效果。正确的心态是承认检测能力的边界只把它用在合理的辅助场景。第五如果你是普通内容创作者这套指标更实用的价值不在于识别AI而在于反观自己的写作风格。低困惑度、低突发性、模板化套话密集这些特征同样可以用来审视自己的文字是否太“平”。从这个角度说文本统计方法完全可以当作写作质量诊断工具。10. 总结与后续学习方向回到文章开头的问题什么暴露了AI生成文本不是破折号。破折号只是某个模型在某种提示下的浅层风格偏好而真正能提供稳定区分度的是文本的概率分布特征尤其是困惑度、突发性、词汇多样性和模板化表达的组合。AI生成文本并不是因为某个标点奇怪才被识别而是因为它整体的统计分布太“平滑”缺少人类写作中常见的低概率跳跃。建议你亲自跑一遍上面的流程准备 5 篇人工文本和 5 篇AI生成文本对比两组文本在困惑度和突发性上的分布差异。只有亲手看到数值分布你才会理解为什么“破折号检测法”不靠谱也才能真正建立对检测工具的判断力。后续如果还想深入可以沿着三个方向继续学习一是用提取到的特征训练一个小型分类器二是使用 token 级特征替代句子级特征提高检测精度三是把事实核查和风格检测结合起来从“文本是否可信”的角度做更完整的分析。