首页 / 资讯中心 / 文章详情

用Agent写正则?这些坑必须知道:实战避坑指南

用Agent写正则?这些坑必须知道:实战避坑指南 ★ FEATURED ARTICLE
1. 为什么我会掉进“Agent 写正则”这个坑说起来有点惭愧。上个月我接手一个老项目后端的文件上传接口一直用一段“历史悠久”的正则做后缀校验。那段正则写得很抽象缩进乱、分支多没人敢动。新需求要加几种文件类型我第一反应是“让 Agent 帮我写一段新正则改一下不就完了”。当时我用的还是一个接入了通用大模型的 Agent 框架顺手把需求一贴几秒钟它就给我吐了一段看起来挺工整的表达式还贴心地加了注释。我差点直接复制进代码库。好在最后时刻我多留了一个心眼把它丢进测试用例里跑了一圈。结果第一个用例就没过——一个合法的.jpg文件被拦下来了。接下来我又试了几个边界输入发现问题远比想象中多。这一段经历让我彻底明白了一个道理Agent 能写正则但它写出来的正则和你脑子里的“规则”往往是两个世界的东西。这也是我写下这篇博文的原因。如果你也在用 Agent 辅助写正则、做规则校验或者你正准备把“规则”这件事交给 AI 去自动化我会把这次踩坑的完整过程、背后的原理、以及我后来总结的避坑套路全部展开讲一讲。内容会涉及 Agent 交互、正则语法、规则评审、测试用例设计这几个方面希望能帮你少走点弯路。2. Agent 写正则的底层逻辑它到底“懂”什么2.1 Agent 生成正则的过程本质上是在做“模式补全”首先要搞清楚 Agent 写正则的原理。以当前大模型为底座的 Agent生成正则的方式不是“推导”出来的而是“预测”出来的。它会根据你的自然语言描述从训练数据里寻找“最相似的问答对”然后把最常见的正则写法作为输出。换句话说它是在做模式补全不是在执行逻辑推演。这就带来一个很反直觉的结论Agent 写得越流畅、注释越完整越说明它见过类似的训练样本。而这种“见过”跟“理解你的业务规则”是两回事。你告诉它“要求文件名后缀只能是 png、jpg、jpeg、gif”它能写出来但你告诉它“这个系统在 Apache 2 环境下运行文件名可能是中文、可能有空格、大小写不敏感”它就未必能把所有约束都编码进正则里了。在实际项目中业务规则往往不只是“允许哪几种后缀”这么简单。它还可能包含文件名长度限制、是否允许点号出现在中间、是否允许下划线、Windows 与 Linux 文件名兼容、URL 编码后的百分号字符、Unicode 文件名……这些规则叠加起来单靠一段正则去承载本身就是一个高风险设计。Agent 只负责生成“字面匹配规则”但承载不了的隐含约束它不会主动帮你识别。2.2 正则的“可读性”是个陷阱我还发现一个特别误导人的地方Agent 写出来的正则通常配了一堆注释看起来特别“正规”。但这恰恰是问题所在。真实生产环境里的正则经过几轮需求变更后往往已经演变成“能跑就行”的状态。你让 Agent 重新生成它会给你一份“从零开始”的干净版本却完全丢失了原来正则里那些隐含的兼容逻辑。举个最常见的例子。老正则里可能有[a-zA-Z0-9_\-]这种字符类为什么不是因为它当时只想允许这些字符而是因为早期某个版本有一个 bug文件名为中文时上传会乱码大家为了“省事”直接把中文禁了。这个背景没人写进注释。几个月后你让 Agent 基于“当前需求描述”重写它大概率不会保留这个限制于是中文文件名开始进入流程乱码问题卷土重来。所以我在实际使用中得出一个教训让 Agent 写正则之前一定要先把你现有的正则、历史变更记录、以及所有的边界测试用例一起喂给它而不是只给一份“简明需求”。否则你得到的不是“重写”而是“失忆”。2.3 Agent 的“自信”与真实错误率不匹配还有一个很普遍的现象Agent 生成的表达式往往没有经过充分的测试就交给了你而且它不会主动告诉你“这个正则可能需要更多边界测试”。你问它“这个正则对吗”它更倾向于回答“对”而不是“我测试了 100 个用例其中 3 个没过”。我把这种现象叫做“自信不匹配”。它源自语言模型的优化目标——生成流畅、合理、符合人类偏好的文本而不是“生成绝对正确的代码”。所以你要把 Agent 当成一个“非常善于表达但需要严格验收”的协作者。它写出来的正则只是第一版草稿不是可交付的成品。这个心态不建立起来后续所有步骤都容易翻车。3. 翻车现场实录Agent 写正则踩到的四个典型坑3.1 黑名单思路看着在拦截形同虚设我最先让 Agent 帮忙处理的是接口里那段后缀校验。当时的现状是后端本来有一个“不允许上传的可执行后缀列表”比如php、jsp、exe、sh等。Agent 很快就写出了类似这样的逻辑# 这是一个“黑名单”风格的正则示意请勿直接使用 import re blocked_ext re.compile(r\.(php|jsp|exe|sh|asp|aspx)$, re.I) def check_filename(filename): return not blocked_ext.search(filename)表面看没问题逻辑也简单命中黑名单就拦截。但这里有个致命缺陷——黑名单永远列不全。攻击者不会只用php后缀他可以用phtml、pht、php3、php5、PhP如果服务器配置了多后缀解析还会出现.jpg.php这种组合。更麻烦的是Apache 2 在某些版本配置下可能会把foo.php.末尾带点或foo.php%00之类的内容交给处理器。服务端正则要应对这种变体几乎是不可能穷举的。正确做法是改白名单只允许明确允许的后缀其余一律拒绝。# 白名单方案只允许配置列表内的后缀 ALLOWED_EXT {.png, .jpg, .jpeg, .gif, .webp, .pdf} def check_filename(filename): ext Path(filename).suffix.lower() return ext in ALLOWED_EXT这个案例给我的启示特别深。Agent 写代码时会按照“你让我拦截什么我就拦截什么”的逻辑执行但它不会判断你的底层设计是不是安全的。如果规则本身设计错了Agent 只是在帮你更高效地执行一个错误方案。所以让 Agent 写正则前先确认“规则的思路本身是否成立”。3.2 贪婪匹配校验通过但括号里全是问题第二个坑出在一个看似人畜无害的“表单字段校验”上。当时要校验一个“用户标识”规则是下划线开头后跟字母数字总长度 5-20 位。Agent 给的正则是pattern r^_\w$我一看就觉得不对。\w在 Python 的 re 模块里默认会匹配 Unicode 字母数字包括中文、大写、全角数字。于是_张三、_这类的输入也能通过校验。这里的问题不是“能不能匹配”而是“匹配的边界和字符范围与业务规则不一致”。更隐蔽的是另一个例子。我需要匹配一个“包含版本号的文件名”比如report_v2_final.pdf这样的结构。Agent 给出的正则是rreport_v\d_.*\.pdf。这个正则在很多输入上都表现正常但遇到report_v20241201_draft_v3_final_merged.pdf时它也能匹配。表面上没问题可如果你的意图是“只匹配 v 后面跟一个两位数的版本”那贪婪匹配和多段下划线的组合就会让规则范围失控。我后来用了一个笨办法把所有历史文件名跑一遍看看正则漏了什么、多吞了什么。结果发现有两类合法文件名被拦截一类是因为大写字母Windows 用户上传的文件名常带大写另一类是中文文件名。这两类单看正则都能匹配但组合起来就有问题。其实正则的“正确性”不能靠看只能靠“测试用例 输出比对”。这也是我后来反复强调的核心原则没有测试用例验证的正则都只是“待定草稿”。3.3 灾难性回溯规则没写错服务被拖垮这是最吓人的一个坑。度的把握正则看似写对了但在特定输入下引擎会进入灾难性回溯Catastrophic Backtracking直接导致 CPU 飙升、请求卡死。有一次我需要写一个校验“数字和字母混合的短码”的正则规则是“必须以字母开头可以包含数字和字母总长度 4-10 位”。Agent 给出了一个“看似更通用”的版本pattern r^([a-zA-Z0-9])$后面跟了一个$乍看没什么问题。但如果完全没有长度限制的输入进来比如用户传了一个 30 万个字符的字符串正则引擎就需要反复回溯来尝试不同的匹配方式性能急剧下降。这就是经典的嵌套量词导致的灾难性回溯。换成r^[a-zA-Z0-9]{4,10}$之后性能问题立刻消失。这个案例说明Agent 生成正则时常常为了“包容性”加上多余的量词分组而这恰恰是性能隐患的来源。你不仅要看“规则是否匹配”还要看“最坏情况下是否还能跑得动”。当然多数情况下问题不会那么极端但一旦出现线上卡顿排查起来非常费劲。3.4 字符编码与大小写被忽略的隐性规则最后一个高频坑是 Agent 生成的正则对大小写和 Unicode 的处理与业务预期不一致。最常见的是用户名校验。规则要求“小写字母、数字、点号、下划线”Agent 直接写了r^[a-z0-9._]$看着没问题。但现实业务里用户可能输入大写字母或者输入全角小写字母来自中文输入法或者行尾带上不可见字符。后果就是一方面合法输入被拒绝另一方面看起来“同形”的非法输入却能通过。这时候我会用一个很简单的辅助方法写一段超短脚本把候选输入全部跑一遍同时输出“输入内容 正则结果 实际字节码”通过字节码一眼看出是否有隐藏字符。隐藏字符比如零宽空格是正则和肉眼都特别容易漏掉的东西。import re pattern r^[a-z0-9._]$ for name in [test_user, Test_User, , test_user\u200b]: result bool(re.match(pattern, name)) print(repr(name), -, result)输出会直接展示第四种输入带了一个\u200b但你能看见的名称却是正常的。这种情况如果发生在登录、改名、路由匹配上会很隐蔽。Agent 无法主动替你考虑“用户会不会输入全角字符”它只能按照你描述的规则来写。4. 实操记录如何让 Agent 输出“可验收”的正则4.1 把“规则”翻译成“测试用例”再做反向提问经过三轮翻车后我开始改变用法。现在我的做法是不让 Agent 直接写正则而是先让它帮我把业务规则转换成一个测试用例清单。比如我给它一句话规则“文件名后缀只能来自白名单不区分大小写且文件名不能以点结尾。”我会要求它输出至少二十组输入样例覆盖合法、非法、边界、异常编码等场景。有了测试用例再让它写正则然后直接用这些用例去验证它写出来的东西。如果 Agent 足够强它会先拆分规则、再输出代码和测试脚本如果 Agent 能力有限至少我能拿着用例清单去人工写正则然后做对比测试。这套流程本质上是把“模糊的自然语言规则”转成“可以自动判定的用例契约”。4.2 Prompt 工程把需求描述“结构化”我给 Agent 下指令时不再说“帮我写个校验文件后缀的正则”而是这样写场景后端校验上传文件名仅允许白名单内的后缀。 系统环境Linux Apache 2文件名可能包含空格、中文、大写字母。 约束 1. 采用白名单校验不允许黑名单。 2. 后缀列表.png, .jpg, .jpeg, .gif, .webp, .pdf 3. 后缀匹配不区分大小写。 4. 文件名不能为空不能以点结尾不允许路径分隔符包含 / 和 \。 5. 输出 Python3 代码re 模块或 pathlib 均可并在代码中自带上文约定的测试用例。 请先输出测试用例清单再输出实现代码并在代码里添加运行时自检。这样写的好处是Agent 不再只根据“语义”来猜测需求而是被约束在明确的规则边界里。它生成的正则即使第一版不能全部通过测试预期也会更接近正确结果。而测试用例列表又会反过来帮我理解规则本身的漏洞——比如我发现“文件名不能以点结尾”这条其实很容易被忽略而 Agent 自己会在输出里强调它。4.3 多轮迭代把 Agent 当成“需要验收的实习生”我现在的习惯是Agent 输出第一版正则后我不会直接看代码而是先跑测试用例。如果没过我把失败的用例回贴给 Agent让它自己修。这个过程可以重复两三轮。有一次正是因为反复迭代我发现了re.match与re.fullmatch之间的语义差异——re.match只从开头匹配如果正则没有$就会导致“前缀合法但整体非法”的输入通过校验。而 Agent 在第二轮迭代里主动指出这一点并把代码改成了fullmatch。迭代过程里我会刻意加入“坏输入”案例——比如空字符串、超长字符串、包含换行符的字符串、包含百分号编码的字符串、包含空格的字符串。这些输入通常在业务里不容易被想到却恰恰是正则翻车的高发区。每次迭代我都记录失败用例数直到归零才算“初步可用”。4.4 用工具“可视化”正则再做人工审查即使测试用例全过我也不会马上上线。我建议你用支持正则可视化的工具比如 regex101、regexr把 Agent 生成的正则图形化展示一遍。这时候可以直观看到量词的作用范围、分组的嵌套结构、边界锚点的位置。看图形的意义在于你会发现很多正则写成一行时“看着对”但拆解成图形后量词的嵌套与分组关系可能跟你设想的不一样。比如([a-zA-Z0-9])这种嵌套量词图形化后会让你意识到它可能引发回溯风险。正则的“可读性”在图形化之后会大幅提升人工审查的难度也会降低。4.5 线上验证不要只做单元测试最后一步是回归真实请求。我会把新的校验函数接进一个灰度接口提前几天采集线上的历史请求日志把“曾经正常通过”的请求全部回放一遍看看新正则是否会误伤。这一步特别重要因为单元测试用例是我们构造出来的而线上流量的复杂性永远超出想象。有一次我自认为测试用例已经覆盖得很全了结果回放时发现大量文件名以数字开头、以多个点分隔的文件被拦截。原因是我给 Agent 的规则里没有体现“文件名可以包含多个点且最后一个点后面的才是后缀”。这种“规则描述不完整”导致的问题只有通过真实流量回放才能暴露出来。从那以后我把“线上日志回放”写进了自己的验收清单。5. 常见问题与排查技巧实录为了让你以后排查起来省点力我这里整理一份速查表。它来自我这几轮踩坑的总结也结合了群里同行分享的常见问题。如果你在使用 Agent 生成正则时遇到相似情况可以对照着查。症状可能的根因排查思路解决方向合法输入被拒绝字符范围过窄、未考虑大小写/中文/全角用字节码打印输入比对字符集改用 Unicode 属性或扩大字符类非法输入通过校验未使用全文匹配match vs fullmatch检查是否漏了$或\Z改用fullmatch或补锚点匹配结果与预期不符贪婪匹配留下多余分支图形化正则观察分组改懒惰匹配或固化分组特定超长输入导致卡顿嵌套量词引发灾难性回溯用超长字符串做压测重写为单层量词增加长度限制同一正则在不同语言行为不同各语言正则引擎差异分环境测试锁定语言后针对性调整规则描述正确但 Agent 输出偏差需求描述缺乏边界样例反推“测试用例先行”用用例清单约束 Agent上线后偶发误拦截线上流量包含未预测输入回放历史请求日志用流量回放做回归排查时我用得最多的工具是grep -P和 Python 的regex调试模式。尤其是 Linux 环境下可以用grep -P快速验证某个表达式对不对然后写一段批量脚本把几十个候选输入一次性跑完。具体命令大致是这样# 快速验证哪些文件名匹配白名单后缀 printf %s\n a.PNG b.jpg c.php d.tar.gz e.pdf \ | grep -iP \.(png|jpe?g|gif|webp|pdf)$注意grep默认按行处理如果输入文件来自 Windows行尾会带\r这个隐藏字符经常会让正则的$匹配失败。遇到这种问题先用sed s/\r$//清理再测试。6. 比“写正则”更重要的重新审视“规则”本身踩完这些坑之后我反而对“Agent 写正则”这件事有了新的理解。与其说问题出在 Agent 写不对不如说问题出在我把“规则”想得太简单了。业务规则从来不是一个正则能承载的完整规范。规则里有“显性约束”也有“隐性约束”有“数据格式要求”也有“安全底线要求”。所以我现在的习惯是把规则拆成三层第一层是“格式规则”用正则和字符串方法处理这部分可以交给 Agent 辅助生成第二层是“业务规则”比如某些文件名必须在特定目录下、某些用户不能上传特定类型这层用显式代码判断不要塞进正则第三层是“安全规则”比如白名单、路径穿越防护、文件内容嗅探这层跟框架和中间件配置强相关正则只能当辅助不能当主力。把这三层分清楚后再去用 Agent你会发现它的定位特别清晰Agent 是一个能快速生成“格式规则草稿”的助手而不是“业务决策者”。最终的验收责任永远在人这边。所以我在让 Agent 产出正则的时候会坚持一条原则可以有 Agent 的初稿但必须有人去审、去测、去跑回归。一旦少了这步任何看起来完美的正则都只是“未爆炸的坑”。如果你正准备用 Agent 来自动化“规则”相关的工作我的建议是从一个极小的规则开始试水比如“只校验一个表单字段的后缀”跑通这套“用例前置 迭代验收 流量回放”的流程再逐步扩展到更复杂的场景。个人体会是这个流程本身的价值远远大于 Agent 帮你省下的那几分钟写代码的时间。一次线上故障的成本基本够你手写几百条正则了。
阅读完成 · 觉得有帮助?
咨询建站