跳到主内容

AI正则质检闭环:从盲信大模型到样本自动验证 ​

问题背景:AI 生成正则为什么不能直接上线 ​

大模型生成正则表达式的速度很快,尤其面对“提取订单号”“校验工单号”“识别敏感手机号”等需求时,往往几秒钟就能给出一版结果。但正则的风险也恰恰藏在这种“看起来正确”里:训练数据中的写法可能过时,字符类可能漏写 Unicode 场景,贪婪匹配可能吞掉相邻字段,量词嵌套还可能触发灾难性回溯。

AI 正则质检的目标不是再请一个模型评价答案,而是建立一个可重复执行的工程闭环:先用标注样本自动验证,再把失败案例按风险和不确定性分流给人工审核,最后把审核结论沉淀为回归样本。这样,大模型负责提高规则生产效率,测试集和人工分级负责守住线上质量。

一个完整闭环包含四类输入:业务描述、候选正则、正例样本、反例样本;三类输出:通过率、失败明细、人工审核任务。

业务描述与样本正例/反例/边界例大模型生成规则候选正则与解释样本自动验证匹配/提取/性能风险分级阻断/复核/通过人工审核确认标签与规则回归样本库沉淀失败案例

方案对比:三种 AI 正则验收方式怎么选 ​

常见做法有三种。第一,纯 Prompt 复查,让模型解释正则并自我纠错,成本最低,但它无法证明边界样本是否通过,也容易出现“解释正确、代码错误”。第二,只做少量正例测试,能验证最常见输入,却无法发现误杀反例,例如把普通数字编号误识别为订单号。第三,样本集自动验证加人工分级,虽然前期要建设样本库,但能稳定发现漏匹配、误匹配、捕获组变化和性能退化,更适合生产环境。

建议把验收拆成三层:语法层检查是否可编译;语义层检查正例必须命中、反例必须拒绝、提取字段必须等于期望值;性能层用长文本和恶意构造文本检测回溯风险。自动验证无法判断“业务上像不像”时,再进入人工审核。

实操步骤:用 Python 搭建样本自动验证器 ​

下面以“物流工单号提取”为例。业务规则:工单号以 SF 或 YT 开头,后接 10 到 12 位数字,允许前面出现“单号:”等文字,但不能从更长的字母数字串中截断。

候选正则如下:

python
import re

pattern = r"(?<![A-Za-z0-9])(?:SF|YT)\d{10,12}(?![A-Za-z0-9])"
rx = re.compile(pattern)

逐段解析:

  • (?<![A-Za-z0-9]) 是负向后行断言,要求工单号前一位不是字母或数字,避免从 XSF123... 中截取出假单号。
  • (?:SF|YT) 匹配两个允许的前缀;?: 表示只分组、不捕获,避免污染提取结果。
  • \d{10,12} 匹配 10 到 12 位数字。注意它会优先匹配 12 位,边界由后续断言控制。
  • (?![A-Za-z0-9]) 是负向先行断言,防止 13 位以上数字或后续字母被截断。

接着构造带期望结果的样本。质检不能只存文本,还要存“应该匹配什么”,否则无法判断提取内容是否正确。

python
import time

cases = [
    {
        "id": "pos-1",
        "text": "您的顺丰单号:SF1234567890,请注意签收。",
        "expect": ["SF1234567890"],
        "label": "positive",
    },
    {
        "id": "pos-2",
        "text": "YT987654321012 已揽收",
        "expect": ["YT987654321012"],
        "label": "positive",
    },
    {
        "id": "neg-1",
        "text": "订单编号 SF12345 太短",
        "expect": [],
        "label": "negative",
    },
    {
        "id": "neg-2",
        "text": "ABSF1234567890 是被污染的编号",
        "expect": [],
        "label": "negative",
    },
    {
        "id": "boundary-1",
        "text": "SF1234567890123 超过十二位",
        "expect": [],
        "label": "boundary",
    },
]

def run_semantic_check(rx, cases):
    failures = []
    for case in cases:
        got = [m.group(0) for m in rx.finditer(case["text"])]
        if got != case["expect"]:
            failures.append({
                "id": case["id"],
                "label": case["label"],
                "text": case["text"],
                "expect": case["expect"],
                "got": got,
                "reason": classify_failure(case, got),
            })
    return failures

def classify_failure(case, got):
    if case["expect"] and not got:
        return "漏匹配:正例未命中,可能前缀、位数或边界过严"
    if not case["expect"] and got:
        return "误匹配:反例被命中,重点检查边界断言和字符类"
    return "结果不一致:检查贪婪匹配、捕获组或 findall 行为"

failures = run_semantic_check(rx, cases)
print("失败数:", len(failures))
for item in failures:
    print(item["id"], item["reason"], item["got"])

再加入性能检测。正则性能问题通常不在普通短句,而在重复量词、可选分组和长文本组合中暴露。

python
def run_perf_check(pattern, evil_text, timeout_seconds=0.05):
    rx = re.compile(pattern)
    start = time.perf_counter()
    rx.search(evil_text)
    cost = time.perf_counter() - start
    return {
        "cost_seconds": round(cost, 4),
        "passed": cost <= timeout_seconds,
    }

# 大量重复数字但不满足结尾边界,用来观察量词组合的回溯成本
evil_text = "SF" + "1" * 100000 + "!"
print(run_perf_check(pattern, evil_text))

这里的 \d{10,12} 是有限量词,且后面紧跟边界断言,通常不会形成指数级回溯;但如果模型生成 (\d+)+$、(.*,)*X 这类嵌套重复结构,就必须在性能层阻断。

人工分级:让审核资源花在高风险样本上 ​

自动验证的结果不应只有“通过/失败”。更实用的做法是分成四级:

  1. P0 阻断:反例命中,可能造成误拦截、错扣款、错误脱敏,直接禁止上线。
  2. P1 高风险复核:正例漏匹配,或核心字段提取错误,由规则负责人审核。
  3. P2 业务确认:正则行为确定,但样本标签存在歧义,例如历史单号是否算有效格式,由业务人员确认。
  4. P3 观察通过:全部样本通过且性能达标,先进入灰度日志,不立即全量生效。

可以用简单规则生成分级任务:

python
def triage(failures, perf_result):
    if not perf_result["passed"]:
        return "P0", "性能阈值超限,疑似灾难性回溯"
    if any(f["label"] == "negative" and f["got"] for f in failures):
        return "P0", "反例误匹配,可能扩大业务打击面"
    if any(f["label"] == "positive" for f in failures):
        return "P1", "正例漏匹配,影响召回"
    if failures:
        return "P2", "边界样本标签或期望结果需确认"
    return "P3", "自动验证通过,建议灰度观察"

level, advice = triage(failures, run_perf_check(pattern, evil_text))
print(level, advice)

人工确认后的样本要回流:新增反例防止规则再次放宽,新增正例防止修复时过度收紧。每次大模型生成的新版本都跑同一套回归集,才能形成真正的 AI 正则质检闭环。

避坑总结:生产环境里的具体注意事项 ​

第一,不要用 \\d 想当然地只表示 ASCII 数字。 在 Python 3 中,默认正则下 \d 能匹配部分 Unicode 数字字符,可能把阿拉伯文数字等也算作编号。若业务只要 0-9,应改成 [0-9]{10,12};常见错误现象是测试英文数字全部通过,线上却提取出含非 ASCII 数字的“单号”。

第二,不要只测 search 是否命中,还要校验完整匹配值。 很多 AI 生成正则包含多个捕获组,使用 findall 时返回元组,代码却按字符串处理,导致下游字段为空。修正方法是统一使用 finditer 并取 group(0) 或命名组 group("order_no"),在样本里填写精确期望。

第三,不要忽略前后边界。 典型错误是 (SF|YT)\d{10,12},它会从 ABSF1234567890 中提取出看似合法的片段。应增加 (?<![A-Za-z0-9]) 和 (?![A-Za-z0-9]),并把污染编号加入反例回归集。

第四,不要把性能测试放在很短的样本文本上。 普通句子通过不代表安全,嵌套量词遇到“几乎匹配但最后失败”的长文本可能急剧变慢。应为每类规则准备超长重复串、缺失终止符文本和大量分隔符文本,并设置毫秒级阈值。

第五,不要让人工审核只看正则本身。 审核人员必须同时看到业务描述、原始样本、期望结果、实际命中片段和失败类型。只给一串正则,人工很容易被复杂表达式震慑并点头通过;给足上下文,才能把经验转化为可回归的样本资产。

最终,AI 生成的是候选规则,不是上线承诺。把样本自动验证作为硬门槛,把人工分级作为风险闸门,再把每次审核结论沉淀回测试集,正则规则才能在高频迭代中保持可信。

免责声明

本工具与文章内容仅用于学习测试场景,优化建议仅供参考,请勿直接用于生产环境,由此产生的风险自行承担。