拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

三专家提示词让AI审代码:从“整体不错”到挖出十几个真问题

三专家提示词让AI审代码:从“整体不错”到挖出十几个真问题

开工。这篇我打算从一个真实经历切入——年前我调AI审代码,只得到一个“整体不错,建议补充注释”的废话结论。后来我改成同时给它三个身份,由“挑刺的架构师”“找漏洞的安全专家”“补测试的测试工程师”分头审同一段代码,结果十分钟挖出十几个真问题。这篇文章要把这个思路完整拆开:为什么有效、提示词怎么设计、实际跑起来是什么样、怎么落地到日常流程,以及这类方案在真实工程环境里的边界和坑。

1. 让 AI 换 3 个专家身份审你的代码:问题背景与场景还原

先说个真实的场景。某天我提交了一个 Java 的批量文件处理模块,改完不放心,顺手打开聊天窗口,让 AI “帮我看一下这段代码有什么问题”。它返回的结果大致是:代码逻辑清晰、命名规范、建议增加注释、注意空指针。这几句话对不对?对。有没有用?基本等于没说。“注意空指针”这种建议放在任何一段 Java 代码上都成立,它根本不知道我的代码里哪个环节真的存在空指针风险。

后来我换了个思路:不找 AI “看一下”,而是让它换上三个专家身份,分别按照各自领域的标准去审同一段代码。跑完一轮之后,输出的差异非常明显——“挑刺”角色的意见集中在可维护性和隐性 bug,“找漏洞”的视角集中在路径遍历、资源泄漏和正则耗时上,“补测试”的视角则直接列出了十几个未被覆盖的分支和边界条件。同一段代码,三种身份,三条完全不同的审查线路。

这个现象背后的逻辑并不玄乎。AI 模型在对话中会依据上下文里的“角色设定”激活不同的知识组织方式和表达重心。你对它说“帮我看看代码”,它默认进入的是“通用编程助手”模式,输出的是平均水平的安全建议;但你明确告诉它“你现在是 code review 里那个最招人烦的挑刺同事”,它的输出会不自觉地偏向挑刺。这三种模式——通用助手、专注挑刺、专注安全、专注测试——在能力边界上其实差异不大,差异在于注意力配比。

所以这篇文章的核心是:如何设计一套可复用的“三角色审查”提示词,让 AI 用三个专家身份分别完成挑刺、找漏洞、补测试,最后把三份结果合并成一份可执行的整改清单。我会给出完整提示词模板、实测实际输出示例、整合流程和避坑要点,适合正在做代码评审、准备发版本前自检、或者想把手头静态检查工具查不出来的问题筛一遍的开发者。

2. 三个角色的分工逻辑:为什么是“挑刺、找漏洞、补测试”,以及各自的技能树

在给提示词之前,得先想清楚一个问题:为什么偏偏是这三个角色?为什么不是“架构师”“性能专家”“重构大师”?

因为这三个角色对应着代码交付前最容易翻车的三条线:代码质量线、安全线、测试覆盖线。大部分团队里,这三条线分别由不同的人和工具负责,评审流程中通常有专门的人盯代码风格和结构,有安全人员盯漏洞,有测试人员盯覆盖度。把这三个视角同时塞进一次 AI 对话里,模拟的正是一个小型评审会议的分工结构。

2.1 挑刺角色:负责代码质量和隐性缺陷

“挑刺”这个角色对应的是代码评审里最不讨喜、但价值最高的那类意见。它关心的问题包括:这段代码将来被其他人维护的时候会不会看不懂?这个分支逻辑有没有冗余?这个函数是不是已经违背了单一职责?这个变量命名是否准确传达意图?这个异常处理是否有吞异常的问题?以及,这段代码里是否存在那些静态检查工具查不出来的“味道”,比如面向实现编程、过度耦合、隐藏的时序依赖。

这个角色的价值在于,它不只看“代码能不能跑”,而是看“代码能不能被长期维护”。我给这个角色的提示词里加了一个关键约束:必须给每个挑刺点标注影响等级和修改建议,不想听泛泛而谈的“建议优化结构”。

2.2 找漏洞角色:负责安全视角的缺陷分析

“找漏洞”角色对应的是安全审查员视角。它关注的不是代码风格,而是攻击面。具体来说:用户输入是否被信任?文件路径是否存在遍历风险?外部输入有没有可能造成命令注入?正则表达式是否存在灾难性回溯?资源有没有可能泄漏?权限校验是否被绕过?敏感信息会不会出现在日志里?Token、密钥有没有被硬编码?以及序列化、反序列化、SQL 拼接这一类经典高危位置。

这个角色的关键约束是:每个疑似漏洞都必须标注漏洞类型、触发条件和修复方向,并且要求“宁缺毋滥,拿不准的也要标注”,让后续人工复核有据可依。实际跑下来会发现,这个视角经常能发现静态扫描工具报不出来的“业务逻辑型漏洞”,比如某个接口本该鉴权却因为中间层统一处理而漏掉,这种问题工具很难定位。

2.3 补测试角色:负责测试盲区扫描

“补测试”角色对应的是测试工程师的视角。它要做的事包括:检查现有测试覆盖了哪些路径;识别代码中的分支条件、边界值、异常分支、空值分支是否被测试覆盖;判断现有测试用例在断言上是否足够强(只验证没报错,不验证输出内容,这种断言其实很弱);以及,针对尚未覆盖的代码路径给出可落地的测试用例思路。

这个角色的独特价值在于,它能结合前两个角色的输出反向推导“哪些地方需要加测试”。比如挑刺角色说“这个函数有 5 个分支”,补测试角色就能据此列出至少 5 条测试用例;找漏洞角色指出“这个接口可能被恶意输入攻击”,补测试角色就能把对应的安全用例补进去。

这三个角色的组合,本质上覆盖了代码评审的“正确性”“安全性”“可验证性”三个维度,协作起来比单角色追问全得多。

3. 手写三套“专家提示词”:完整模板、设计意图和逐行拆解

下面直接给模板。这套提示词我试了多个模型(GPT 系列、Claude 系列、以及部分国产模型),稳定性和输出质量都不错。核心设计原则有三个:

  • 角色定义必须清晰,但不要长篇大论,AI 不需要两百字的身份小传,需要的是职责边界和输出格式。
  • 必须强调“基于事实,不要编造”,这能明显减少 AI 在审查时脑补不存在的代码行为。
  • 必须指定输出格式和最小内容单位,否则它容易给你一段漂亮的总结性废话。

3.1 挑刺角色提示词模板

我实际用的版本长这样:

你是一个参与代码评审的资深工程师,性格非常较真,专门负责挑代码设计层面的毛病。请审查下面这段代码,按照以下维度逐条输出意见: 1. 可读性与可维护性(命名、注释、函数长度、表达式复杂度) 2. 设计问题(单一职责、耦合度、扩展性、重复代码) 3. 隐性缺陷(边界条件、状态变更顺序、资源管理、并发隐患) 4. 冗余与性能(不必要的计算、可合并的逻辑、可简化的结构) 输出要求: - 每条意见必须包含:具体位置(行号或函数名)、问题描述、影响等级(高/中/低)、修改建议。 - 不要输出“整体来说代码很好”这类总结性内容。 - 拿不准的问题也要列出来,标注“存疑”即可,千万不要编造。 - 优先输出影响等级为高的意见。 代码: ```你的代码
设计这个提示词时,我注意了几个细节。第一,“性格非常较真”是有效词,它引导模型在输出时减少“礼貌性修饰”,很多模型默认输出会带“建议您可以考虑”,这会导致意见不够锋利。第二,“不要输出整体来说代码很好”这句话很重要,删掉试试就知道,模型会在末尾加一段“overall”式总结,占篇幅且无信息量。第三,要求标“存疑”是为了让 AI 在证据不足时不要强行编造风险点,这点后面会详细讲。 ### 3.2 找漏洞角色提示词模板 安全角色的提示词需要更严格,因为安全领域的误判代价高,修复一个不存在的问题浪费的也是时间。我的版本:

你是一名资深应用安全工程师,正在对一段即将上线的代码做上线前的安全审查。请以威胁建模的视角审查代码,重点检查以下方向:

  1. 输入验证(外部输入是否被信任、边界是否校验、路径是否可控)
  2. 注入类风险(SQL、命令、模板注入、反序列化、路径遍历)
  3. 认证与授权(权限校验位置是否正确、是否存在默认信任)
  4. 敏感信息泄漏(硬编码密钥、日志输出敏感字段、可预测ID)
  5. 资源与可用性(内存/连接泄漏、正则回溯、死循环、超大输入)
  6. 依赖与集成风险(调用外部服务的凭据处理、协议处理)

输出要求:

  • 每一条疑似漏洞都必须标注:漏洞类型、可能被利用的方式、影响等级、修复建议。
  • 如果不能确定是否存在漏洞,请明确写“需人工确认”,不要直接跳过。
  • 不要输出泛泛的安全建议,必须在代码中找到具体位置并引用。
  • 你只负责找漏洞,不要输出改进代码风格的建议。

代码:

这个角色的核心要素是“威胁建模视角”。如果不加这个前缀,模型会把它当成普通代码检查,输出“注意 SQL 注入风险”这种正确但没有定位能力的废话。加上“威胁建模”四个字之后,模型会更倾向于模拟攻击路径:输入从哪个函数进来、中间经过怎样的处理、最后在哪里触达危险函数,这在实际输出中的差别非常大。 ### 3.3 补测试角色提示词模板 补测试角色的提示词需要让它“看到”代码中可以被测试的路径,同时结合前两个角色的输出生成测试补充建议。

你是一位测试工程师,参与代码评审时你的职责是从测试覆盖角度审查代码。请分析这段代码,输出:

  1. 现有测试盲区:找出没有被正常路径、异常路径覆盖到的分支、边界和异常场景。
  2. 高风险待测点:指出如果某个条件判断错误、某个外部依赖失败、某个输入超出预期时,代码可能出现的异常行为。
  3. 不可测试的代码片段:指出哪些逻辑难以编写测试,并给出重构建议使其可测。
  4. 测试用例清单:基于以上发现,列出需要补充的具体测试用例,每条必须包含:测试目标、前置条件、执行步骤、预期结果。

额外要求:

  • 假设项目使用 pytest/xxx(按实际项目技术栈替换)作为测试框架,给出落地测试代码的大致结构。
  • 只关注如何验证和覆盖,不要评论代码风格。
  • 不确定是否能测的分支,标注“需确认”。

代码:

这个角色输出的核心其实是“测试用例清单”这一项。它能在一定程度上替代人工想测试点的工作,尤其是边界值这类容易遗漏的部分。实测里我发现,它对循环边界、空容器、超大值、超小值这类场景的判断相当可靠,但对业务规则的推导仍需要人工把关,因为模型并不真正理解你的业务目标。 ### 3.4 提示词选型的几个通用原则 如果你要改写这套提示词,有几个原则值得记住: - 角色身份里包含“负责什么”,不要包含“不负责什么”。不负责的内容在输出时反而容易被忽略。 - 输出格式的指令必须放在角色定义之后,距离越近约束力越强。 - 职责边界要尽量单一。让挑刺角色去找安全漏洞当然可以,但它会稀释原本的输出质量。 - 如果你用的模型支持 system prompt 和 user prompt 分离,把角色定义放 system,把代码放 user,效果稳定很多。 ## 4. 跑一次完整实测:同一段代码,三个专家各审出了什么 理论说太多没用,直接看一次真实运行的输出对比。我准备了一个故意埋了问题的 Python 函数,展示三个角色的实际输出方向。为了不引入版权风险,这里是我自己写的示例,但问题类型和真实项目一致。 示例代码是一个“根据用户输入路径批量处理日志文件”的函数: ```python import os import re import json from flask import request LOG_DIR = "/var/log/myapp" PATTERN = re.compile(r"(\w+)=(\w+);") def parse_log_to_dict(filepath): result = {} with open(filepath, "r", encoding="utf-8") as f: for line in f: m = PATTERN.search(line) if m: key, value = m[1], m[2] if key in result: result[key].append(value) else: result[key] = [value] return result @app.route("/upload", methods=["POST"]) def upload(): filename = request.json.get("filename") if not filename: return {"msg": "missing filename"}, 400 full_path = os.path.join(LOG_DIR, filename) data = parse_log_to_dict(full_path) return json.dumps(data), 200

这段代码问题不少,但多数静态工具只能查出“未登录鉴权”这一层。三个角色实际输出的节选如下:

挑刺角色:

  • os.path.join(LOG_DIR, filename),如果filename是绝对路径,join会直接忽略前面的LOG_DIR,这是 path 处理里典型的坑,影响等级高。建议改为校验filename不得包含路径分隔符,或用os.path.realpath验证结果前缀。
  • PATTERN作为模块级正则编译问题不大,但(\w+)=(\w+);的匹配范围过窄,日志里如果有a.b=c这种键就会被跳过,需要确认是否符合业务预期,存疑。
  • parse_log_to_dict里result[key]从字符串变成字符串列表,类型不一致,后续如果有代码统一按result[key][0]读取就会踩坑,影响等级中。

找漏洞角色:

  • 路径遍历(CWE-22):filename未做任何过滤,用户传入../../etc/passwd可以直接读到系统文件。虽然返回值是解析后的字典,但<word>=<word>;的格式如果碰巧匹配某些敏感文件,信息就会泄露。修复建议:白名单校验文件名、禁止路径分隔符、使用 os.path.basename 取纯文件名。
  • 敏感信息/日志文件风险:这个接口没有任何鉴权,任何能访问服务的人都可以指定任意日志文件名,需要确认接口是否应置于内网或加 Token 校验。
  • 正则表达式虽然固定长,但\w+在超长输入上有一定回溯风险,若日志文件超大或某行超长,可能导致 CPU 占用异常,影响等级中。

补测试角色:

  • 条件分支盲区:if not filename只覆盖了“弹窗为空”的情况,没有覆盖filename是非字符串类型(如数字、数组)的情况,JSON 解析后可能直接抛异常,需要补用例。
  • 正则匹配盲区:日志行不匹配(\w+)=(\w+);时,该行会被静默忽略,没有测试验证这种情况是否符合预期。
  • 边界值用例缺失:空文件、单行文件、超大文件、无换行符结尾文件、含 Unicode 字符的行,都未在测试计划里出现。

三个角色加起来,基本覆盖了一个完整评审会议应该提出的所有类型问题。人工拿到这些输出后,优先修高等级问题,其余按优先级排迭代。

5. 三份报告如何合并:从分角色输出到一份整改清单

分别让 AI 输出完三份意见,只是一个开始。真正的价值在于把它们合并成一份可执行的整改清单。直接贴三份原文给开发同事看,大概率会被无视——信息太多,没有优先级,也没有负责人视角的“下一步动作”。

我建议的合并步骤:

  1. 把所有意见按“功能正确性 / 安全性 / 可维护性 / 测试完善度”四个维度归类,而不是按角色归类。
  2. 跨角色去重。很多问题会同时被“挑刺”和“找漏洞”提到,比如路径处理问题,两个角色都会报,合并时取等级最高那一条即可。
  3. 标注规则的“落地成本与收益”。修一个路径通配问题可能只需要十行改动,但补一个完整的权限体系可能是一个迭代的工时。不区分这一点,开发人员不会买账。
  4. 把“需人工确认”的条目标成待办,不要让“存疑”内容淹没在确定的问题里。

我给一个简单的合并表格式示例:

编号问题来源角色影响等级修复成本建议动作
1filename 路径遍历风险安全高低(半小时)改用 basename 白名单
2接口无鉴权安全高中(一个迭代)加 Token 校验
3result 类型不一致挑刺中低统一 value 为列表
4正则匹配范围过窄挑刺/测试中低确认业务规则或改正则
5空文件、非字符串 filename 测试缺失测试中低补 5 条用例

需要注意一个常见误区:不要指望 AI 直接输出一份能粘贴到缺陷跟踪系统里的完整报告。AI 的输出是“线索”,合并和评级必须人工做。原因在于模型对“影响等级”的判断没有项目上下文——它不知道这个服务是否暴露在公网,不知道这份代码是否核心链路,不知道团队现在的迭代压力。这些信息只有人知道。

这个合并环节做多了之后,你会发现一个很自然的模式:挑刺角色产生的问题数量最多,但高等级的少;安全角色输出条数少,但每条几乎都得处理;测试角色的输出是三种中最容易被开发人员接受的,因为它不指责代码,只提供用例。由“好修的先修、安全红线优先、测试缺失的下一轮补齐”顺序处理,效率最高。

6. 把“三专家审查”固化到日常流程里:增量审查、上下文隔离和工具化思路

特殊的、一次性的审代码,手动跑三遍提示词已经很好了。但如果要把它变成日常流程,就得考虑几个工程化问题。

6.1 全量审查 vs 增量审查:大仓库的上下文窗口难题

让 AI 审一个完整的项目仓库,尤其是一个模块几千行的情况下,“上下文超限”或“漏掉关键信息”会立刻出现。可行的做法是缩小审查范围:在 git diff 粒度做增量审查。只把本次改动涉及的函数、类和相关调用链贴给 AI,而不是把整个文件塞过去。

增量审查的另一个好处是控制 token 成本。一次提审一个几千字符的 diff,比提审一个几万字符的文件便宜得多,也更精确。实测中我发现,当代码量过大时,AI 会倾向于审查“前半段内容”而忽略“后半段”,这个问题在长文本中非常显著,唯一的解法就是切片。

6.2 会话隔离:不要一锅炖三个角色

有些人会图省事,一条消息里写“你既是架构师又是安全专家又是测试工程师,请分别从三个角度审这段代码”。实测下来这个方案效果很差,输出内容会互相干扰,最后得到一份四不像的综合意见,安全分析被风格建议稀释,测试输出被架构建议埋没。

建议的做法是三个角色分三个独立会话跑,或者至少在同一个会话里用三个独立的 user 消息,之间用明确的角色分隔文本隔开。会话隔离能保证模型在每一个输出窗口内完全按照当前角色要求工作。多角色协作的思路虽然现在很流行,但多角色协作不等于“一条 prompt 里塞多个角色”,而是多次调用的结果在外部进行整合。

6.3 工具化:写一个小包装脚本

如果你经常要跑类似审查,值得花半小时写个本地脚本,把三份提示词模板固化下来,通过命令行传入文件路径,自动输出三份文本报告。脚本可以很简单,核心逻辑就是读文件、构造三个 prompt、调用 API、分别写入输出文件。

这类脚本不必做得很复杂,唯一的工程建议是:把三个角色的 prompt 模板放在独立配置里,不要硬编码在代码里,因为模板迭代速度很快。你很快会发现,安全角色的提示词需要针对不同项目类型调整(Web 项目要加 OWASP 项、Python 项目要加对象注入点),把模板独立出来之后,维护成本低很多。

6.4 与现有检查工具做互补

在真实工程流程里,AI 审查最适合的位置是“代码扫描工具之后、人工评审之前”。先用 SonarQube、Semgrep、pytest-cov 这类工具跑一遍确定性问题,再让 AI 三个角色做一轮“语义层”审查,最后把两份结果合并交给评审人。原因是静态工具确定性高但覆盖面窄,AI 覆盖面广但确定性低,两者互补效果最稳。

千万不要试图用 AI 审查完全取代人。至少在当前,AI 对业务语境的理解仍不可靠,它会漏掉与业务强相关的错误,也会在某些无关紧要的地方指出莫须有的问题,人工复核环节不可省略。

7. 实操后的真实边界:我自己踩过的几个坑和使用建议

最后分享几个实际操作中反复踩到的坑,供参考。

7.1 幻觉与“过度挑刺”:AI 会编造不存在的代码行为

有一次我审一段并发代码,AI 自信地指出“这里存在竞态条件,因为有两个线程同时操作同一个 map”。实际打开代码仔细看,那两个线程在操作不同的 map,只是变量名相似。AI 没有“亲眼”看到执行,它只是根据代码文本推断出了最可能的场景。这种错误在安全角色里尤其危险——如果基于一条不存在的漏洞去改代码,浪费的时间是双倍的。

对策就是前面模板里写的“拿不准就标存疑”,再加上人工复核。对每条输出,我都有一个默认心态:这条意见 60% 可能是对的,40% 可能是错的,不改代码,先改“思路”。

7.2 越大的代码块,越容易漏掉关键问题

我曾经一次性贴了 1500 行代码给 AI 做安全审查,结果它只认真查了前 400 行,后 1100 行几乎只给了两三条无关痛痒的意见。再跑第二个会话只贴中段和末段,又能找到不少真问题。这说明大上下文下模型的注意力分配并不均匀,实用性优先策略是“一次只审一个函数族”,超过 200 行就考虑切片。

7.3 提示词模板需要按语言和技术栈调参

同一个安全角色模板,审 Python 代码时需要补充 “反序列化(pickle/yaml.load)” 和 “对象注入” 相关项,审 Java 代码时需要补充 “表达式注入(SpEL/OGNL)” 和 “反序列化链”,审前端代码时则要贴近 “DOM clobbering、原型链污染、第三方脚本加载策略”。模板只是骨架,关键词库需要按项目类型迭代更新。

7.4 初版不要盲目信任“多 AI 协作”提效的姿势

现在很多吹“多个 AI 互相审查能提升准确性”的说法,实际体验是在代码这一场景中,多个模型协作并不等于 1+1>2。两个模型对同一个问题给出矛盾的修改意见时,开启第二轮互评会得到一个“综合结论”,但那个综合结论的置信度并不高。三专家协作的价值,是让一个模型在三个方向上把注意力分散开,而不是让多个模型群聊。

7.5 隐私与合规:敏感代码绝不外传

如果审的是公司核心业务代码,尤其涉及密钥、用户数据、内部架构的模块,最安全的做法是使用私有化部署的本地模型或企业内部网关,而不是把代码直接粘到公网平台的对话框里。这一点在接入任何第三方 AI 服务时都必须作为前置条件。如果没有私有化部署条件,至少做到:脱敏后再提交、去掉真实密钥、用通用变量名替换业务敏感的命名。

我个人的使用习惯是把这套三角色审查流程固定成“发版前夜的标准动作”——代码写完、自测通过、静态工具零报警之后,再跑一轮三角色审查,把它当成最后一道语义层面的自查网。跑出来的问题不直接改,先合并、再评级、带着评级去跟同事讨论。这套流程跑了小半年,最直观的收益是:线上问题数量下降不明显,但“类型”变了——很多低级但隐蔽的问题在发布前就被拦截掉了。至于那个“帮我看看这段代码”的对话,我很久不用了;因为我想要的从来都不是一句“没问题”,而是有人真的替我把每一寸代码都翻过一遍,如果那个人是 AI,那就给它三个不同身份。

返回列表