☰
代码审查与微重构:用Prompt构建可控的AI代码优化流程
2026/10/15 20:49:02 网站建设 项目流程

每次提完代码的自审需求,团队成员都会丢来一句“帮我看看这段代码,顺便优化一下”。起初我都是在IDE里逐行读,后来直接把代码丢给大模型,但问题马上来了——没有约束的“顺便优化”经常把同事的代码改得面目全非,遇到业务逻辑复杂的模块还容易出现“改一帧崩全局”的尴尬。直到我把“代码审查”和“微重构”拆成分阶段的prompt,并把边界条件、风险清单、输出格式全部写清楚,整个流程才真正稳下来。

这个“代码审查微重构prompt”的核心价值就是把两件事合并成一条可控的流水线:先让模型做严格审查并输出问题清单和风险等级,再在这个基础上进行最小粒度的重构,最后给出完整的变更说明。它适合两类人:一类是每天要review别人代码、又被各种风格差异折磨的开发者,另一类是习惯先让AI看一眼自己代码再提PR的个人开发者。

1. 整体设计思路:为什么要“先审查、再微重构”

1.1 审查和重构天然是配套动作,分开做只会浪费时间

我以前发起过的prompt需求经常是“请帮我审查这段代码,然后顺便优化一下”,听起来很自然,但实际执行结果几乎不可用。大模型在同一个回复里既要判断问题又要动手改,往往会因为注意力分配不均导致某一边做得很差。最常见的情况是:它只罗列了两三个表面的格式问题,然后大刀阔斧地重写了一遍代码——变量重命名了、循环改成了列表推导式、嵌套if换成了guard clause,逻辑上好像也说得通,但一跑测试发现原有的异常处理分支被悄悄删掉了。

后来我把流程拆开,先让它只做审查并输出结构化的发现清单,等清单确认后再进入重构阶段。这个改变带来的效果立竿见影:第一,模型在审查时会老实很多,不再想着“反正最后要改,审查随便说两句就行”;第二,我可以先看清单里的问题等级,决定哪些值得改、哪些不值得动,避免不必要的代码变动;第三,一旦确定了问题清单,重构成了一个“照着方案执行”的动作,模型的输出稳定性明显提升。

1.2 微重构的粒度边界需要靠prompt框架来划定

“微重构”这个词听起来简单,但执行起来最大的坑是范围蔓延。一次重构动辄把几十行代码推倒重来,这在大型业务代码中是致命的。我必须让模型明确“最小变更”“行为保持”“单一目标”这几个硬性约束,并且告诉它什么情况属于“越界”。

在设计prompt时,我把微重构的目标定义为以下四点:优化可读性、消除重复代码、拆分过长的函数、提升局部逻辑的清晰度,同时规定所有改动必须以“不改变函数的输入输出行为和异常路径”为前提。如果模型发现要实现某个优化需要动到函数签名、改数据结构或者调整模块间的协作方式,它必须停下来在“风险提示”里说明,而不是自作主张去改。这个“提出但不执行”的机制是我反复调出来最有效的。

1.3 为什么不用“直接让模型重写整个文件”的方案

有人会问:既然大模型能力强,直接让它输出优化后的完整代码不就好了吗?答案是不行,至少在真实工程场景里不行。完整重写意味着模型要依靠自己的理解在高层面上重建代码结构,而它对这个项目的业务背景、历史包袱、隐性约束都一无所知,重写往往会引入很隐蔽的逻辑偏差。而微重构是在原代码基础上做局部手术,每一处改动都像在已有的骨架上加固而不是拆了重搭,出错时也更容易定位和回滚。

另外,从审查到重构的这个split设计还有一个额外好处:它有效缓解了“模型尽力讨好用户”的倾向。当你直接说“优化代码”时,模型默认你想看到改动,于是即便代码已经不错它也会硬挤一些修改出来;而审查阶段的目标是找问题、提风险、做评判,模型不需要为了“显得有用”而强行改码,输出的真实性高很多。

2. 核心细节解析:prompt中必须包含的关键模块

2.1 角色与目标设定

好的prompt不是上来就把代码贴给大模型,而是先创立一个清晰的执行框架。我的prompt第一段是角色设定:“你是一名有十年经验的代码审查专家,关注代码正确性、可维护性和性能风险,同时具备节制的重构能力。”这比你直接说“帮我看看代码”更能引导模型进入专业状态。

目标设定也很关键,我会明确说出这次任务的范围:“先完整审查我提供的代码片段,按严重程度输出问题清单;审查完成后再根据确认的问题清单进行微重构。”这个顺序和输出预期是prompt的骨架,必须写在最前面,因为大模型对“先做什么、再做什么”的指令比“同时做好几件事”要听话得多。

2.2 审查维度与输出清单

审查维度决定了模型会关注哪些角度。如果只说“看看这段代码有什么问题”,模型大概率只给你找语法瑕疵、命名问题这些表象。我自己整理过一份维度清单,直接写进prompt里:

  • 正确性:是否存在边界条件未处理、空指针/空值风险、并发安全性问题、逻辑分支遗漏
  • 可读性:命名清晰度、函数是否过长、代码注释是否必要且准确、结构是否易于理解
  • 性能:是否有明显的多余计算、循环内重复调用、可缓存而未缓存的热点
  • 安全:是否存在注入风险、敏感数据泄露、不可信输入处理不当
  • 健壮性:异常处理是否合理、依赖外部资源是否有超时/降级机制

针对每个维度都要求模型用列表形式输出“文件位置+行号级别描述(有的话)或函数名+具体问题+严重程度+修改建议”,这样我能直接对照源码做判断。严重程度我常用P0/P1/P2三级:P0是会导致错误或事故的,P1是明显不合理的实现或重要改进点,P2是建议性的风格优化。

2.3 微重构执行规范与边界控制

重构阶段的prompt是整套框架里最需要精细化控制的部分。我给了四个硬性规范:

第一,改动必须是局部的、最小化的。模型要尽量复用已有变量名和函数名,除非原命名严重误导或与规范冲突,否则不展开重命名。

第二,不允许改变对外接口。函数签名、类名、模块导出、返回值的结构和语义都保持不变。这个约束能避免模型顺手把调用方全改了的连锁反应。

第三,每次重构必须带清晰的diff式说明。在输出的代码块前后标注“改动点-原因”,让我一眼看出哪里动了、为什么动。

第四,如果发现审查清单里有某些问题无法在不触及外部接口的前提下解决,比如需要改变数据模型才能修好,模型要输出“超范围提示”,把问题留给开发者决策,而不是自己绕路硬改。

这四条规范缺一不可。我吃过亏的是第三条没写清楚时,模型重构完代码连个注释都不留,我根本分不清它改了哪些地方,code review时无从查起。后来把“diff说明”作为强制的输出格式放在prompt中,这个问题就解决了。

2.4 输出格式模板与交互流程

为了让模型输出更可靠,我在prompt里写死了输出格式模板,包括:

  • 审查结果:按P0/P1/P2分级的问题清单,每项包含位置、问题、理由、建议修法
  • 重构计划:基于审查清单列出你想改哪些点,用什么方式改,计划耗时或难度预估(可选)
  • 重构后代码:只贴被修改过的函数/代码块,不要展示整个文件
  • 变更说明:逐条列出实际改动,标注对应的问题编号,以及每处改动的目的和风险
  • 超范围记录:任何超出原审查模块边界的潜在问题,统一在此记录

这种模板化输出让后续人工review的效率大大提升。我可以在问题清单阶段直接回复模型“P2级别的先不改,P1里面的第2条我不同意你的修改建议,用另一种方式改”,然后让它带着新的指令进入重构阶段。多轮对话的稳定性也因此高了很多,因为每轮都有固定的输出框架可以比对。

3. 实操过程与核心环节实现

3.1 完整prompt模板示例

下面是我目前一直在用的prompt模板。要注意的是,这段prompt不是一次成型,而是经过多轮试错才稳定下来的。使用时可以直接复制,替换掉“待审查代码”部分的代码即可:

你是一名拥有十年经验的代码审查专家,擅长从正确性、可读性、性能、安全和健壮性五个维度审查代码,同时具备克制而精准的微重构能力。 任务流程分两个阶段: 第一阶段:代码审查。严格按以下维度审查我提供的代码,不要急于修改,先输出结构化问题清单。 第二阶段:微重构。根据我批准的问题清单,做最小范围的重构修改,并输出完整变更说明。 审查维度: 1. 正确性:边界条件、空值/空引用、并发、逻辑分支遗漏、异常路径。 2. 可读性:命名、函数长度、注释质量、结构清晰度。 3. 性能:不必要的计算、重复调用、可缓存的逻辑、热点路径。 4. 安全:注入风险、敏感信息、不可信输入、加密或访问控制。 5. 健壮性:异常处理、外部依赖、超时与降级机制。 输出格式要求: 审查阶段请严格输出: ### 审查结果 按严重程度分类的问题清单: - [P0] 位置+问题描述+风险理由+修改建议 - [P1] 位置+问题描述+风险理由+修改建议 - [P2] 位置+问题描述+风险理由+修改建议 重构阶段请严格输出: ### 重构计划 列出你计划修改的点以及修改方式。 ### 重构后代码 只展示修改过后的函数或代码块,保持其他部分不变。 ### 变更说明 逐条列出实际改动,引用问题编号,并说明每处改动的目的与潜在风险。 ### 超范围提示 如果某些问题涉及改动函数签名、数据模型、模块接口或调用方,无法在局部重构中解决,请在这里说明,不要直接修改。 微重构硬性规范: 1. 最小变更原则:保持对外接口不变,尽量复用原有命名,不做风格性重写。 2. 行为不变原则:不得改变输入输出行为、异常路径、边界处理语义。 3. 谨慎改动原则:如果不确定某处改动是否会引入风险,保留原样并把风险列入“超范围提示”。 4. 禁止扩大范围:不得顺带修改无关代码,不得重构与你确认清单无关的部分。 待审查代码: [在这里粘贴你的代码片段]

3.2 实操示例:一个真实的审查与重构过程

拿一个典型的反馈型代码片段来演示这个prompt的实际效果。假设我有下面这段Python代码,是一个简单的用户登录尝试控制逻辑:

def login_attempt_check(user, max_attempts, lock_minutes): attempts = get_attempts(user.id) if attempts >= max_attempts: lock_user(user.id, lock_minutes) return False else: increment_attempts(user.id) return True

用上面的prompt跑一遍后,审查阶段输出的问题清单大致会是:

  • P0:attempts用>=判断,但从业务上看“第N次失败后是否允许继续尝试”的边界定义不清;如果max_attempts传0,就会直接锁用户,缺少入参校验。
  • P1:函数没有区分“本次登录失败”和“之前已经超过阈值”两种场景,返回值True/False的语义在调用方容易混淆。
  • P1:每次调用都无条件increment_attempts,但登录成功后的清空操作没有放在这个函数里,次数会越攒越多。
  • P2:else分支在提前return后其实可以省略,减少嵌套层级。

基于这份清单,我可以决定是否批准重构。如果这张清单里有些问题我不想处理(比如业务需求本来就是“永远不清空次数”),就可以在第二轮回复时告诉模型“P0第2条不用改,P1两条都改,P2改”。模型会重新调整计划再执行。

进入重构阶段后的输出大致是:

def login_attempt_check(user, max_attempts, lock_minutes): if max_attempts <= 0: raise ValueError("max_attempts must be positive") current_attempts = get_attempts(user.id) if current_attempts >= max_attempts: lock_user(user.id, lock_minutes) return False increment_attempts(user.id) return True

变更说明里会标注:改动点1为新增入参校验,对应P0问题;改动点2为移除else分支,对应P2优化;改动点3为局部变量名调整,便于语义明确;同时“超范围提示”里会建议把登录成功后的attempts清空逻辑纳入调用方处理,因为本函数无法独立完成。整个过程从审查到重构完全可控。

3.3 如何把prompt接入日常代码评审工作流

实际使用时,我不会每一段代码都走完整流程。目前我沉淀了一套接入方式,效率比纯手工review高不少:

第一步,从版本控制工具里导出需要review的diff,只选改动过的函数级别片段,不要整个文件丢给模型。为什么?因为大模型在长上下文中容易被无关代码带偏,而且上下文窗口有限,放太多文件会导致关键信息被稀释。

第二步,在prompt模板里贴上必要的前置上下文。比如这个函数所在的模块是做什么的、有没有特定的代码规范、是否有历史背景需要注意。没有这些上下文时,模型可能会按通用的最佳实践来审查,结果跟项目实际风格差很多。

第三步,先跑一轮审查,拿到P0和P1问题清单后再决定是否进入重构。如果代码本身质量已经不错,可能直接结束流程;如果问题多,再针对问题做多轮重构。不要一上来就要求模型“审查后直接重构”,这样你等于放弃了中途干预的机会。

我自己还会把输出中的问题清单复制回代码评论系统,作为review意见草稿。模型语言通常比人工写的更结构化,我再稍微改一下措辞就能发给同事,省掉了打字的功夫。当然,每条意见我都会再确认一遍,毕竟模型会看走眼,有时候会把本来就合理的写法当成问题。

4. 常见问题与排查技巧实录

4.1 模型审查不全面,漏掉关键逻辑问题

这是使用审查类prompt时最先遇到的头疼问题。模型经常把注意力放在浅层命名和格式上,对深层逻辑漏洞视而不见。我的排查思路是:先检查是不是prompt里的审查维度列表太笼统。如果只写了“请全面审查”,模型大概率会默认“全面”等于“看看语法和命名”。

解决方法是把每个维度下面再拆出几个具体子问题,比如在安全维度下写“是否存在SQL注入、路径遍历、不安全的反序列化、敏感硬编码”,在正确性维度下写“是否处理了None、空列表、超大数字、并发修改”。这个“子问题列表”是在几轮实测里慢慢补全的,补完以后审查命中率明显提升。

另外,如果模型给的清单全都是P2级别的风格建议,而没有任何P0/P1,大概率是因为它没理解代码逻辑。此时我会在prompt里补充一句“如果代码逻辑自己能跑通,请明确指出你的判断依据,并在不确定的地方提问,不要假设上下文。”这个约束能逼着模型把注意点放在逻辑推理而不是表面修饰上。

4.2 重构后“看着很干净,跑起来就报错”

这是重构阶段最容易翻车的情况。模型改后的代码格式漂亮、结构优雅,但一执行就异常。最常见的原因有三个:一是模型对原代码的隐含副作用理解不到位,比如某个局部变量原本在下面还被用到,结果被它优化没了;二是异常处理路径被压缩,原本某个分支会捕获特定异常,重构后用统一的兜底处理吞掉了细节;三是模型使用了它自己“想象”的API,比如把标准库的某个行为记错了,导致运行时错误。

针对这个问题,我在prompt中加入了三个防御机制:第一,要求模型在“变更说明”中列出每处改动的测试影响面,提示我该跑哪些case;第二,要求“重构后代码”必须是完整可运行的函数片段,而不只是局部几行;第三,也是最关键的——我会在prompt末尾追加一句“如果对原行为的某个细节不确定,请保留原写法并在超范围提示中说明”。这一句能有效减少模型自作主张“优化”造成的隐性破坏。

4.3 上下文窗口不够,长文件被截断

代码review时经常遇到一个函数几百行甚至一个文件上千行的情况,直接粘进prompt很容易突破上下文限制,或者让模型因为信息过载而输出质量下降。我常用的处理办法是“按函数拆分审查”:让模型每次只review一个函数或一个独立模块,而不是整个文件。如果函数之间有关联,我会在prompt里用文字简单描述关联关系,而不是把所有代码都塞进去。

如果文件确实需要整体审查,我就会采用“先整体扫一遍,再聚焦关键函数”的两段式流程:第一轮让模型输出“该文件的功能概览、模块间依赖、可疑风险点”,第二轮再逐个函数深入审查。这样即使上下文窗口有限,也能覆盖核心逻辑,而不是因为塞不下而漏掉关键点。

4.4 多轮对话后模型开始“自由发挥”

prompt用很多轮之后,模型有时会越来越“放飞”。比如在第二轮重构时,它会无视第一轮确认的问题清单,自己又追加了很多额外的改动。这是典型的多轮对话漂移问题。

我的应对策略是:在每轮新指令之前,先重新粘贴一遍“微重构硬性规范”部分,而不是默认模型记得上一轮的要求。虽然这会多占用一点token,但稳定性提升非常明显。另一种做法是开一个新的对话窗口,把上一轮的问题清单复制过去,作为新对话的输入上下文。这比在同一条对话里不断追加指令要稳得多。

4.5 输出结构不稳定,清单一会儿是表格一会儿是列表

模型输出格式不统一是prompt工程中最常见的问题之一。有时候它会跳出我定义的“### 审查结果”标题,自己用小标题组织内容,甚至直接讲一段话然后给代码,完全不按模板来。

我的处理办法是强化模板的引导:不仅写清楚输出格式,还在prompt里附一段“输出示例”,给模型看一个短小的样例。比如:

### 审查结果 - [P1] `login_attempt_check` 函数中 uses `>=` comparison, check the boundary at max_attempts=0 …

模型看到具体示例后,会非常忠实地模仿这种格式。我试过很多次,加上输出示例之后,结构稳定性大幅提升,几乎不会再出现模板偏移。另外一个技巧是把“不要输出与模板无关的内容”写进prompt,虽然这是一个比较弱的指令,但在配合输出示例时会起正作用。

5. 进阶调优与场景扩展

5.1 根据项目语言微调审查维度

同一个prompt模板在不同语言上使用时需要调整审查子项。比如审查Python代码时,我会额外关注GIL影响下的多线程行为、是否用了可变默认参数、上下文管理器是否正确关闭资源;审查JavaScript/TypeScript时则关注异步流里有没有未处理的Promise rejection、闭包有没有意外捕获循环变量、可选链和空值合并的使用是否合理;审查Go时关注error有没有被吞掉、goroutine有没有泄漏、并发访问有没有加锁。

这个“语言微调”做起来很简单,只需要你在prompt的审查维度列表里增加对应语言的专项检查项。我习惯保留基础五维度,在其下挂一个“本语言专项”小节,跟随项目语言变化替换内容。这样不会丢失通用审查能力,又能兼顾语言特性。

5.2 结合单元测试让重构更有底气

微重构最怕的就是“行为不变”这个承诺无法验证。要让这个机制更可靠,一个很好的进阶做法是把prompt和测试结合起来。我现在的流程是:在重构之前先让模型基于原代码的公共行为写几个最小化的测试用例(只验证输入输出和异常路径),然后在重构完成后手动运行这些测试,确认结果完全一致。

如果你用的是支持测试的仓库结构,也可以把相关测试函数作为上下文贴给模型,让它清楚哪些边界行为必须保持。实测下来,测试先行能把重构后的“隐性回归”概率明显降低,尤其是对那种长函数拆分的场景效果更好。没有测试时,改动上线前一定要人工过一遍核心逻辑,不要盲目信任模型的“行为不变”。这不是模型能力不行,而是它对你业务语义的理解天然有盲区。

5.3 从“单次审查”扩展为“团队规范落地工具”

这个体验让我逐步扩展了prompt的使用场景:不仅是帮自己审查代码,还能把它当成团队代码规范的落地工具。你可以在prompt里附上团队自有的编码规范摘要(比如禁止在循环里查询数据库、所有外部接口必须做超时控制、DTO不允许直接暴露内部字段等),模型审查时会按照这些规则执行,而不是通用的“建议性最佳实践”。

这种方式尤其适合新成员接入团队的场景。新人写完代码后先用这套prompt自审一遍,很多低级问题在提交人工review之前就被拦住了,团队老师的压力也会小很多。我甚至见过一些团队把“用AI做前置自审”写进了PR合入检查清单的前置步骤,不过不管怎么用,核心还是那个原则:模型给出的是“疑似问题”,最终拍板和负责的仍然是开发者自己。

6. 我的实际体会与踩坑记录

6.1 prompt不是越长越好,但关键约束绝不能再精简

设计代码审查重构prompt时,我最初的想法是把所有规则都写进一句话里,以为这样够简洁。结果模型的输出随意得让我崩溃——它一会儿只审查不重构,一会儿把整个函数重写一遍。后来我把硬性规范和输出格式从描述性文字改成了可操作的条目和模板,效果立刻改善。

但我也发现一个悖论:我最早写的prompt有大量冗余段,比如“你是一位资深的代码审查专家,请通过你的专业能力帮助用户识别潜在风险”,这类话除了占token之外基本没用。真正有作用的是明确的任务阶段、可解释的审查维度、强制的输出模板和边界约束。所以现在我的prompt文字不算多,但每个元素都能对输出产生实际约束,只有那些真正影响模型行为的规则才值得被保留。

6.2 越关键的改动,越要带着“怀疑式提问”去看模型输出

用这个prompt工作了大半年之后,我最大的心得是:不要因为prompt写得好就相信模型的输出,尤其是代码审查领域,模型最大的风险是“一本正经地胡说八道”。它可能会在审查中给出一个看起来很专业的建议,但落地后反而破坏了原有设计。

我的习惯是,对于标为P0或P1的建议,一律打开源码重读一遍,确认模型的判断依据是否成立。对于重构后的代码,聚焦diff部分逐行检查,重点关注异常路径和边界条件。省下来的时间主要花在“不用从零构思审查角度”和“快速生成标准化review意见”上,而不是盲目全盘接受AI的结论。这个心态如果不摆正,再好的prompt工具也挡不住线上事故。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询