
先说一个我自己经历过的场景有段时间我在做内容合规方向的内部工具需要把 AI 批量生成的稿件按风险等级分拣出来再交给人工复核。最初我试过用另一个大模型当裁判效果时好时坏而且调一次要花不少 token排队跑一批文档经常把人等毛了。后来同事半开玩笑地说“既然你怀疑输出在骗人那不如先让几行字符串规则去盘问一下。”我当时心里挺不屑的——正则什么时候能看穿语义了但实在被成本逼得没办法就写了几十条规则试着筛了一轮结果让我很意外大约三成有明显“自说自话”特征的稿子根本不需要语义模型用模式匹配就能抓出来。今天这篇就想聊聊我后来怎么把这个思路做成了一道放在 LLM 输出侧的正则闸门。我不是要跟你说正则能替代语义审核那是吹牛。真正有价值的点在于LLM 输出的不可信往往不是整个句子、整个段落都不可信而是藏在一些非常固定的“语气瑕疵”和“结构瑕疵”里。而这些东西恰好是正则的甜区。1. 先说清楚“骗自己”是怎么回事自噬循环与幻觉的“合法化外衣”1.1 模型输出正在变成下一代模型的训练原料很多人讨论 LLM 幻觉时默认场景是人喂给模型的数据有问题或者模型没学过相关知识便强行作答。但你有没有想过一个问题现在网上铺天盖地的文章有多少已经是 AI 生成的了如果这些 AI 生成的内容又被人抓取、整理成数据集再拿去微调下一代模型会发生什么技术圈管这叫“自噬循环”也有人叫“模型坍缩”。一代模型在数据里留下一个错误说法第二代模型不仅没纠正还会基于这个错误说法继续“合理”推导把错误包装得更圆润。我见过一个离谱的例子一份 AI 写的行业综述里出现了“根据 CB Insights 2024 年 6 月的报告”我花十分钟找到了原始出处发现 CB Insights 根本没发过类似报告那个标题是模型自己拼接的。但句子结构、称呼、引用格式都是对的一眼看过去和真实引用几乎一样。这就是我标题里说的“骗自己”。不是说模型有主观恶意而是它从那些同样由 AI 制造的内容里学来了“引用应该长什么样”却学不会“这个引用是否真实存在”。等到这类内容再回流错误就变成了一种被复制的“记忆”。1.2 幻觉在文本上留下的痕迹一点都不隐蔽听上去很玄乎好像模型在撒谎这件事难以捕捉。但你把几十篇有幻觉的文章摆在一起对比会发现它们的输出有一种奇特的“紧绷感”。人写东西的时候会有犹豫、会有口语的毛边、会使用模糊程度不一的限定词而模型在编造事实时反而特别爱用斩钉截铁的绝对化表达。举个具体的对比人写“这个方向可能跟图数据库有点关系但我还没验证。”模型编造时爱写“这一领域主要由Neo4j主导其核心优势在于原生图存储与事务处理的深度整合。”第二句话里没有一处语法错误逻辑上也没有硬伤但它预设了“Neo4j主导”这个未经论证的前提并且用非常笃定的语气把它陈述出来。正则干的活就是先把这种“语气上的过度自信”和“上下文里缺少支撑证据”的错位信号标记出来。我后来把这类特征总结成四类下面第二部分会展开讲。这四类信号单独出现任何一个都不一定代表内容是错的但多个信号叠加的时候幻觉概率会明显上升。这道闸门的核心逻辑就是不追求零误伤只追求把高风险的稿件以高召回率拦下来让真人去复核。2. 这道闸门为什么用正则而不是大模型裁判2.1 语义审核在批量场景中的三个现实痛点不是说大模型裁判没用它在深度分析方面确实更强。但把它放在“第一道闸门”的位置上会有几个绕不开的问题第一个是成本。一次语义判断如果按输入 2000 字、输出 200 字来算在主流 API 定价下批量跑一万篇稿子的开销是肉眼可见的。排在业务高峰期时还会挤占真正需要模型智能的环节。第二个是延迟。内容审核管道通常要求文档进来后几秒内给出返回语义级裁判的响应时间往往不可控。你总不能因为裁判服务超时就把所有待审稿件压在队列里。第三个是黑盒风险。如果是给下游提供 API 能力你需要向调用方解释“为什么这篇稿子被判高风险”。大模型给出来的理由经常是“根据上下文判断该内容可能包含不实信息”。这种理由没法审计也没法沉淀成规则。但正则规则是透明的“你命中第 3 类信号 7 次阈值超过了。”直接把命中的原文片段贴出来就行谁看了都服气。2.2 正则能抓到的是人类作者的“刻意”和机器作者的“惯性”正则的局限大家都清楚它不理解上下文不掌握常识甚至搞不定同义改写。那为什么在检测 AI 内容可信度时反而有奇效关键在于人和机器的犯错模式不一样。人类故意写假话时会刻意规避明显的模式复用把谎话说得很“活”但 LLM 生成文本时的概率采样机制决定了只要语义相似它就更倾向于落入相同的表层句式。同一个意思人的表达可以换一百种花样模型的表层句法特征却往往收敛在有限的几类槽位上。“根据XX报告显示”是一种槽位“值得注意的是”是一种槽位“综上所述我们可以得出结论”也是一种槽位。槽位不是错误本身但它可以帮助你锁定某个位置然后去检查这个位置上的内容是否真的配得上这么硬的语气。这就是正则闸门的抓手。2.3 闸门不是替代品是前置于深度审核的粗筛层想清楚定位后我对这套规则的预期就变得很务实。它不负责告诉你“这篇文章是假的”它只负责告诉你“这篇文章在语气和结构上呈现出若干高 hallucination 风险特征建议进入下一步人工或语义模型复核”。如果一条规则命中了系统会把原文切片高亮展示给审核员。这种前置闸门的价值不亚于一个完整的语义审核器。因为大部分接入内容生成管线的团队真正缺的并不是最终那一下判断而是缺少一个能把每天几千篇稿子缩减到几十篇重点稿的低成本初筛器。把这道初筛放在 LLM 输出和业务展示之间用户的体感是“多了道保险”而不是“每次都要等半天审核结果”。3. 我把危险信号拆成了四类直接在文本模式上做标注那具体怎么在文本模式上做文章呢我在第一版里拆了四类正则信号。拆分的依据不是语言学理论而是我肉眼标注了几百段可疑文本之后总结出来的高频共性。你不需要跟我完全一致可以按你自己的语料去调整但这四类可以作为一个相对通用的切入点。3.1 A类信号无出处支撑的刚性论断这是最重要的一类。它的特征是句子在形式上被包装成了一个“有出处”的陈述但整篇上下文里并没有真正给出可验证的来源。典型模式我用表格列一下规则意图正则模式片段示意命中的文本示例引用类动词后接机构或报告(根据据机构名加年份的伪精确引用[A-Z][A-Za-z0-9\s]{1,20}?(研究院研究中心绝对化副词后接数字(毫无疑问毋庸置疑这个思路值得展开说说。正则只抓“形式引用”不判断引用是否真实存在这个矛盾怎么解决答案是把判断留给后续规则。比如一个名叫“某某研究院”的机构名出现了正则先把它圈出来接着另一条规则会检查全文是否出现过至少两条以上的独立来源名称。如果全篇只有一个“权威机构”死死撑着来源多样性不足这条规则组合就把该段落标记为“可疑引用”。3.2 B类信号变量置换式类比和“一本正经”的桥接句模型在编造不存在的机制时特别喜欢把“一种技术”和“另一种技术”在句子里强行做平滑过渡读起来没有任何逻辑跳跃但恰恰因为太顺了才显得可疑。标志性的表达有“其底层逻辑可以类比为”“本质上等同于”“从原理上看这与……高度一致”。我不是说这些表达一定出自幻觉文本人类作者也常用。但如果你统计一段文字里这类桥接句的密度会发现模型输出显著高于普通作者。原因是 LLM 生成时非常依赖 token 之间的高概率续写而这些桥接句在语料里出现的频率极高属于模型眼里的“安全路径”。我在实际规则里没有去禁止这些词而是对它们做了加权计分出现一次扣一点信任分出现三次以上才触发高亮。真正被我们标记成高风险的往往是“桥接句 无引用 绝对化陈述”三个特征挤在同一段的情况。这种组合模式下幻觉率极高。3.3 C类信号空转的宏观总结和陈词滥调堆叠很多 AI 生成稿件被判“假大空”不在于它说了假数据而在于它输出了一堆不可证伪的宏观话术。比如“赋能”“抓手”“闭环”“生态化反”这类词的密集出现往往说明模型在调用一套被过度训练的“官腔语料库”而它的具体信息量已经枯竭了。正则在这里做不了语义判断但可以统计“废话密度”。具体做法是维护一个表达库包含空泛名词和低频个性化表达。在模型输出之后计算“空泛词密度”公式是空泛词命中次数除以总词数。阈值我设的比较宽超过 15% 才提示低于这个值不打扰。这样做是为了避免误伤那些明明内容扎实但风格上也有点爱用术语的真人作者。3.4 D类信号逻辑连接词的单调性和机械排比这个特征平时很少有人提但在我看来非常有意思。真人写作时逻辑连接词的使用往往比较散——一会儿“但”一会儿“不过”一会儿直接不用连接词靠语序推进。模型却倾向于反复使用同一批低风险连接词比如“此外”“同时”“然而”在几千字里反复出现甚至隔两句就出现一次。对这种现象正则的抓取逻辑采用滑窗检测设定窗口 500 字如果同一个连接词出现三次以上并且它出现的间隔特别均匀就标记为“机械节奏”。这种规则看着不强实际用起来却能揪出一整类“AI 水货稿”。这类稿子内容也许没有错但它表达出来的思维深度和编辑程度都偏低不该让它直接流向用户侧。4. 落地成闸门正则规则怎么组织、怎么计分、怎么输出4.1 规则引擎的层次划分这块我建议直接把规则分成两层信号层和聚合层。信号层的规则只管一件事——抓某个文本模式返回命中位置和命中类型聚合层的规则才去组合多个信号判断最终要不要拦截以及拦截的级别。这样做的好处是以后单条规则想调整、想加新特征不用动核心流程。下面是一个规则引擎的简化示意用的 Python 风格伪码但在任何语言里实现起来都不复杂# signal layer: 每条规则都是 (pattern, weight, signal_type, desc) SIGNAL_RULES [ { name: assertive_citation, weight: 2.0, type: A, desc: 无出处的刚性引用, pattern: r(根据|据|援引|引自)[^。]{0,30}?(报告|研究|数据|统计) }, { name: absolute_number, weight: 1.8, type: A, desc: 绝对化副词后接数据, pattern: r(毫无疑问|毋庸置疑)[^。]{0,30}?\d(%|万|亿) }, { name: bridge_sentence, weight: 1.2, type: B, desc: 高概率类比桥接句, pattern: r(底层逻辑可以类比为|本质上等同于|从原理上看这与[^。]{0,20}高度一致) }, { name: empty_talk, weight: 0.6, type: C, desc: 空泛词命中, pattern: r(赋能|抓手|闭环|生态化反|全方位|多维度) }, { name: monotone_connector, weight: 0.4, type: D, desc: 机械连接词, pattern: r(此外|与此同时|然而) } ] # aggregation layer: 聚合信号 def evaluate(text): hits [] for rule in SIGNAL_RULES: for m in re.finditer(rule[pattern], text): hits.append({ rule: rule[name], type: rule[type], weight: rule[weight], span: m.span(), sample: m.group(0)[:80] }) score score_hits(hits, text) return classify(score), hits4.2 计分模型累加加权、密度惩罚和上下文抑制计算最终风险分时千万别只做简单加法要注意三个细节第一是长度归一化。一篇两百字的短文命中三个“空泛词”比一篇五千字的文章命中五个更扎眼。所以每一类信号的原始分先除以文本长度得到密度分再参与聚合。第二是同类信号的重复惩罚。连续命中同一个规则名第一次权重计入后续命中按 0.7、0.5、0.3 的衰减系数折减。为什么这样处理因为同一类模式连续出现说明内容越写越“机械化的空转”确实该扣分但不至于一条命中的分无限累加把整个文件打到不可看的程度。第三是上下文抑制机制。也就是说不是所有引用都该被扣分。如果检测到该段落前文存在类似“本文整理自XXX的访谈成稿经过对方确认”这样明确的外部信息佐证引用句的“无出处刚性引用”扣分权重降为原来的 30%。具体实现可以在命中后向上回溯 500 字看看有没有佐证词。4.3 三档输出策略最终输出不要做得太两极分化建议三档低风险直接放行不需要人工介入。中风险系统自动放行但推送给作者一个提醒让作者自己看一眼高亮片段决定是否修改。高风险自动拦截进入人工复核队列复核通过前不能对外展示。这个策略的核心是闸门并不是卡死了所有可疑内容而是给不同风险等级安排了不同的处理路径。尤其是中风险档能避免大量不必要的审核人力。5. 真刀真枪跑一轮验证集效果与调参注意事项5.1 用什么验证集来测正则有一个特别容易踩的坑你在手头的一百篇稿子上调出来的规则拿到新的领域语料上可能完全失灵。所以验证集的构建千万别只用自己生产的同领域数据。我建议至少包含四个来源一自己管线里的人工复核稿件二合作伙伴提供的“疑似高风险”样本三从开源数据集里随机挑的“正常文本”作为负样本四纯人工写作的高质量博客文章作为负样本第二组。负样本里混合真人内容很重要否则你调着调着就会把所有规则调到过拟合误伤极高闸门根本没法上线。我当时第一版规则在正样本上召回率达到 0.82但负样本误伤率高达 0.47也就是说每两篇正常文章就有一篇被误判为高风险。后来我意识到问题出在 D 类“机械连接词”规则上真人比较长的文章里也会频繁使用那些连接词。后来通过滑窗检测、同类惩罚的衰减系数调整误伤率才降到 12% 左右召回率虽然也掉了一点到 0.76但整个系统变得能用了。5.2 误伤案例分析真人写作用词也有“高密度时刻”有一次一篇人工深度调查文章被闸门判成高风险理由是 C 类空泛词密度超标。我一看原文标题就笑了——那是一篇正经的行业会议综述里面大量引用了嘉宾发言的原话嘉宾在台上就是爱讲“赋能”“抓手”这类词。正则分不清作者自己在说空话还是在引用一个说空话的人。那之后我加了一条前置处理规则如果检测到引号或冒号直接引语比例超过 30%先把引语部分切走只对非引语部分做密度分析。另一个经典误伤是编辑在文章开头放了免责声明“本文基于公开资料整理部分观点引自行业报告……”结果 A 类规则的“无出处刚性引用”命中了大量正文内容。实际上文章处处有出处只是出处都在文后的参考文献列表里。针对这种情况我把引用列表单独切出来并在正文计分时对“有对应参考文献编号的句子”做豁免处理。这个豁免的逻辑很粗糙但把一个最严重的误伤源解决了。5.3 不该省的小事对命中的语境片段做保留正则命中的只是几个词但审核员和下游系统需要的是上下文。我强烈建议在输出命中记录时不要只输出匹配到的二十个字而是把命中位置前后各扩展 200 字符作为“语境窗口”并且把窗口内的其他 B 类、C 类信号同时也标亮。这样人工复核时看到的就是一个完整句群而不是碎片。我曾经偷懒只保存命中片段结果人工复核员不得不一次次回到原始文档里去翻上下文效率折损大半。这也算是一个“机器可读不够人还得流畅审核”的经验。6. 更进一步正则与其他可信度校验手段怎么组合6.1 信号输出可以作为语义模型的先验特征单独的正则再细致也没法覆盖“事实性错误但语气相对克制”的那类内容。所以我会在闸门把高风险稿件摘出来之后在第二级引入一个小型语义模型对高风险稿件做更具体的事实一致性判断。正则闸门的输出不是终点而是给语义模型的“先验框”——告诉它重点检查哪几段。语义模型只针对这些高亮段落做 entailment 判断比让它全文扫读要准得多、省得多。具体来说把闸门输出的命中片段拼成一句话比如“本文在第 3 段出现绝对化断言涉及数据无来源第 5 段出现桥接类比缺上下文支撑”然后让语义模型去判断文案里的核心断言是否和已知知识库冲突。这种方式相当于把语义模型从一个“通读型阅读者”变成了“拿着线索查证的审计员”效果和成本都有改善。6.2 知识库正则为行业专有实体建立动态白名单除了对通用表达做检测你还可以把业务知识库里的专有实体表拉出来生成一组对照规则。比如在某个医学内容场景下模型经常编造“某药物已在各国获批上市”。跑正则之前先生成一张带正则化实体名的知识图谱表对所有药物名称做白名单匹配。不在白名单上的陌生拼写一旦出现直接进入高风险池。这个思路听起来像知识库校验而不是正则但落地时你会发现它完全可以做成正则样式(?:复方|单方)?[^。]{1,30}?(胶囊|片剂|注射剂)[^。]{0,20}?(获批上市|上市许可)再用后面捕获到的名称去查业务库。正则在这里的价值不是理解知识而是帮你完成从自然语言到结构化实体查询的“定位”工作。6.3 要不要考虑多语种变体如果你的内容管线覆盖英文、日文等语言正则闸门不是不能做但要小心字符编码和分词差异。中文在正则上有一个天然优势字与字之间没有空格词边界模式相对稳定。英文则要处理大小写、复数变形、时态变化等大量形态学问题只靠正则很容易被击穿。对于英文我的建议是正则只用于标点级别或句式层面的稳定性特征如引号使用一致性、引用格式不再试图用正则去抓“过度自信表达”这种语义倾向特征。英文内容需要另配一套基于分词和词性标注的规则引擎那又是另一套工程了。6.4 长期维护机制置信度基线与误报反馈闭环任何规则系统上线后都会面临同一个问题规则老化。模型的语言习惯会随着底座模型的更新而变化新的模型版本可能不再使用旧版爱用的那些高频表达或者学会了绕开你精心设计的规则。我见过不少团队做完一版就再也不动了半年后规则集沦为摆设。要避免这个问题建议做两件事。第一在系统里保留一段“规则灰度开关”按流量百分比放量每条规则单独看命中后的人工复核通过率。通过率低于 60% 的规则自动降级为观察状态。第二每周抽少量“高风险通过人工复核后被判定为正常”的案例反向生成新的特征词加入规则库。维护这套机制的工作量不大单人每周半天到一天就能完成但能让规则库保持生命力始终贴合最新模型的输出模式。7. 踩过的典型坑我在生产环境里遇到的三个反直觉问题7.1 规则冲突导致高风险稿件被降权放行听起来很简单一条规则加了 40 分一条规则减了 30 分本来应该拦截的稿子因为同时命中了一条“信任加分类规则”而净分数低于阈值。这种冲突在刚开始时根本想象不到直到一次审核员反馈“有一篇明显是模型编的稿件居然放行了”我们查看日志才发现它命中了“上下文抑制关键词”中的“经作者授权”这个片段而这句话其实出现在稿件的广告合作免责段落里和正文内容毫无关系。后来我加了两个约束一是“抑制词必须与被抑制的命中片段处于同一段”二是“抑制效果不跨命中类叠加”。这两个约束修掉一整类因为全局抑制导致的误放问题。7.2 正则与 LLM 输出格式的耦合问题模型在输出时经常加 markdown 格式常见的有加粗、列表、表格。你在纯文本上写的正则如果没先剥掉 markdown 标记会在匹配边界上出很多怪问题。比如“根据*报告显示”星号把文本切得七零八落规则可能匹配不到或者因为路径里有大量 markdown 表格符号导致一段长文本里多处命中重叠。解决方案是在进入规则引擎前先做格式清洗去掉 markdown 标记、去掉超链接保留链接文字、将无序列表符号替换为统一分隔符。清洗阶段和信标阶段分离之后正则规则的编写难度会显著下降因为不需要再考虑字符边界上的干扰。7.3 LLM 会“绕开”规则但绕开的样子也很明显真正让我觉得有意思的是部分较新的模型如果把温度调高会减少使用被规则高频命中的“空泛词”甚至整体降低绝对化陈述的密度。可一旦它们刻意绕开这些词新的输出往往会显得很“端着”——大量使用被动语态把原本是主语的机构名全部后置一个判断句能被拆成三段。我没有额外针对“被动语态”加规则因为闸门的重心始终是召回高风险内容它绕开了“空泛词密度”这类低权重信号并不代表它能绕过“无出处刚性引用”这种高权重结构信号。只要你的证据锚点足够硬换皮不换核的生成策略依然会撞上口子。8. 我不建议你照抄规则模板的几点想法最后这段可能跟很多“技术教程”的调性不一样。网上能搜到很多现成的 AI 检测正则模板包括一些检测 AI 内容的“风格指纹”清单。我也参考过一些但最终没有直接照搬。原因很简单语言是活的模型也是活的。一个月前的规则列表放到新的模型版本上去重率可能会掉 30% 以上。模板能给你的只是“从哪里开始看”的思路真正能打的规则集必须建立在你自己的语料和业务场景之上。我建议你从自己的负样本和正样本里各抽二十篇逐句过一遍找到那些频繁出现且在你人工审核时愿意打上“有点可疑”标签的句子形状再去结构化整理规则。你会发现这是一种比想象中更有效的训练方式——你用肉身当过一阵子规则引擎才会真正理解哪些槽位值得用正则去找哪些槽位需要交给更重的模型。如果你只是偶尔需要判断一篇 AI 生成内容的可信度那不需要搭完整的闸门服务写一个十几行的 Python 脚本把 A 类那几条规则放进去跑一遍就够了。多数时候刚性的“伪引用”和“绝对化数字”已经能帮你拦住大量肉眼看似流畅、实际站不住脚的文章。等你的用量和数据量上来需要把它做成带人工复核流程的生产组件再回来参考我上面讲的工程化细节。正则识别不了一句话背后的真假世界但它在“文本正式入口”这个位置上确实能帮你用极低成本拦住一批最危险的自动生成内容。它们或许不假到能被一眼看穿但你看过足够多案例后会发现它们的“假”从来不会毫无声音。