团队引入AI编程工具之后,我们的代码量确实涨了,但代码质量反而出现了肉眼可见的下滑。合并请求从每周个位数涨到每天十几个,代码评审会却从"过一遍逻辑"变成了"重新拆一遍结构"。一开始大家都质疑是不是AI的水平有问题,后来我想明白一件事——不是AI不行,是我们从没给它立过代码规范。
这个感受在团队里不是个例。AI生成的代码有个显著特征:单看没问题,放回项目里全是问题。类型注解写得很完整,但变量名语义混乱;函数能跑通主流程,但边界条件和异常分支处理得随缘;模块划分更是全凭模型当时的"心情"。原因不复杂——AI在生成代码时,默认遵循的是训练数据里的通用习惯,不是你项目里的工程约定。
所以我今天想聊的,是我们在项目中新增的那份"给AI制定的代码规范"到底怎么落地:它和我们写给人看的开发手册有什么区别、文件结构怎么组织、怎么做才能让AI每次开工都真遵守、以及在代码评审环节怎么把它变成团队共识。如果你正在带团队用AI做辅助开发,或者你个人天天在用AI写业务代码但总被代码质量困扰,这篇内容应该能给你一套直接能抄走的思路。
先把结论放在前面:给AI制定的代码规范,和我们过去写给人看的《开发手册》完全是两个物种。把它当成文档写是没用的,要把它当成一份"喂给AI执行规则集"来设计。
1. AI写代码最缺的不是能力,而是约束
1.1 "能跑的代码"和"能维护的代码"之间差了一条规范线
AI编程工具刚进项目那阵子,团队里流行一句话:"需求丢进去,代码跑出来"。效率确实高,但两个月后大家就笑不出来了。那些"跑出来"的代码,基本都长这样:一个函数写了八十行,中间夹着几处重复的fetch调用;组件拆分随心所欲,页面上两处高度相似的区块,一个抽了子组件,另一个直接复制了三遍;错误处理更是随缘,有些地方try-catch吞掉了异常,有些地方干脆什么都不管。
这些代码如果是一个人写出来的,评审一过就会被拉去谈话。但因为它们是AI生成的,大家反而陷入了一种奇怪的宽容心态:"AI嘛,能跑就行。"这句话就是质量崩坏的开始。
后来我们把仓库里AI参与生成的代码抽样统计了一下,发现一个规律:逻辑正确率其实不低,但代码结构的稳定性极差。同一个功能,让AI生成十次,十次的结构都不一样。这就是"能跑的代码"和"能维护的代码"之间那条鸿沟——AI天然倾向于"局部最优",它会把当前这一步写好,但不会像资深工程师一样,先想清楚边界条件、扩展点、模块边界,再去写实现逻辑。
人的这种约束感来自哪里?来自多年的工程训练和团队规范。AI没有这个积累,它只有你喂给它的上下文。所以我们面临的真实问题不是"AI不够强",而是"AI缺乏约束"。
1.2 人的规范不能直接丢给AI的三个原因
最开始我们偷了个懒,直接把团队那套开发规范文档丢给AI,让它照着写。结果效果惨不忍睹。后来反复对比,我总结出三个原因:
第一,人的规范大量依赖"隐性知识"。"保持代码简洁""合理拆分组件""注意代码可读性"——这些话人类看了秒懂,但AI拿到之后基本等于没拿到。因为在生成代码的逐token预测过程里,这种抽象指令很难转化为具体的输出约束,模型会回到自己训练数据里的默认习惯。
第二,人的规范像散文,没有条件触发机制。我们的开发规范写的是"接口返回的数据类型必须前端定义,不能直接用any",但AI不知道这句话在什么时机生效。它不会在生成接口调用代码时主动去查这个规则,除非你把规则放在和任务描述紧挨着的上下文里。
第三,人的规范描述的是"最终状态",AI的规范需要包含"过程指令"。人写代码时,规范是刻在脑子里的;AI写代码时,它需要你明确告诉它:开工前先读哪个文件、生成代码后要对照哪几条检查、发现冲突时怎么办。这些过程指令,传统的代码规范文档里根本不会写。
所以,给AI定规范,不能直接复用老手册,得单独设计一套。
2. 给AI看的规范文件,应该有一套独立的结构设计
2.1 从零散的"提示词碎片"到项目级规范文件
我们团队最早给AI下规矩的方式,是每个工程师自己在前缀提示词里写一段"你是xx前端专家,写代码时要先xxx再xxx"。这种方式有个致命问题:个人提示词只能约束自己,约束不了所有AI会话。同一段代码,上午用A工程师的提示词生成,结构是一种样子;下午用B工程师的提示词生成,又变成了另一种样子。代码风格不统一,后面所有环节都要为这个买单。
正确的做法,是把规范变成一个项目级的实体文件,放进代码仓库。现在社区里已经有不少约定俗成的叫法,比如AGENTS.md、CLAUDE.md,或者放在docs/agents/目录下。我在实际项目里验证下来,比较稳的结构是这样的:
. ├── AGENTS.md # 激活入口,AI每次开工先读这里 ├── docs/ │ └── agents/ │ ├── CODE_STYLE.md # 全局代码风格规则 │ ├── MODULE_RULES.md # 按技术栈/模块拆分的规则 │ └── TASK_FLOW.md # 任务处理流程(新功能/修bug/重构)AGENTS.md是入口文件,内容不用多,主要告诉AI三件事:必须阅读哪些文件、规则优先级怎么判断、遇到冲突时找谁确认。真正的规则内容拆到docs/agents/下,这样每个文件保持精简,AI在有限的上下文窗口里能精准命中当前任务需要的规则。
2.2 规则分层:全局规则、技术栈规则、任务规则
我见过不少团队把规范和提示词揉在一起,AI经常分不清哪条是硬性规定、哪条只是建议。这个问题靠分层解决。我们把规则分成三层:
第一层是全局规则。不管AI在写前端还是后端、新功能还是修bug,都必须遵守。比如:命名必须语义化、函数长度限制、禁止把密钥硬编码进代码、所有新增依赖必须注明用途。这一层规则对项目整体质量影响最大,数量必须精简,我建议控制在10到15条以内。
第二层是技术栈规则。前端项目和后端项目的规则差异很大,混在一起写,AI在生成后端代码时会莫名其妙地应用前端的约定。我们现在把React的组件拆分规则、Vue的响应式使用规则、Java后端的异常处理规则都拆开,AI在接到具体模块任务时,只注入对应技术栈的那部分规则。实测下来,规则混乱导致的"跨域污染"现象少了很多。
第三层是任务规则。不同任务类型对应不同的行为要求。开发新功能要遵守可扩展性约定;修bug必须补回归测试;重构代码要保持行为完全不变。这部分我们写在TASK_FLOW.md里,每个任务开始前由AI按任务类型自行读取对应段落。
2.3 每条规则的字段设计:编号、级别、示例、替代写法
规则不是一句话就完事的。我们迭代到第三版之后,每条规则固定出现了几个字段:规则编号、优先级、适用范围、规则内容、错误示例、正确示例、替代写法。下面用一个真实条目举例:
[P0] CODE-STYLE-001 命名必须语义化 适用范围: 所有模块 规则内容: 变量、函数、组件的命名必须准确表达其业务含义,禁止使用 a、b、temp、data、 res 这类无含义命名。 错误示例: const data = await fetchUserData(); 正确示例: const userList = await fetchUserData(); 替代写法: 如果短期内无法确定准确名称,请先重新审视该变量的业务含义,再取一个能描述 "它是什么"或"用来做什么"的名字,而不是随便写一个占位名。编号是为了Review时能引用,评审意见里说"这里违反CODE-STYLE-001"比说"这个命名不好"要有说服力得多。优先级是为了冲突仲裁,后面细说。示例是为了给模型提供输出格式参考,AI对"示范"比对"描述"敏感得多。替代写法是防止模型在遵守规则时出现"为了合规而乱写"的极端情况——之前我们写"禁止使用any",结果AI确实不用any了,但它写出来一堆复杂的泛型体操,这个问题后面踩坑部分重点讲。
3. 让AI"每次开工先读规范"的激活方法
3.1 规范文件不是摆件,需要显式地塞进AI上下文
很多团队做了规范文件之后,发现AI根本不看,于是断定"给AI定规范没用"。实际上大多数情况下,问题出在激活方式上。当前主流的AI编程工具,有些能自动读取AGENTS.md,有些不会。我们的经验是:不能依赖工具自动读,必须在每次会话的初始指令里显式要求。
我自己在AI辅助编码会话里的开场白是固定的:
你是一名参与本项目开发的高级工程师。开工前,请先阅读项目根目录下的 AGENTS.md, 以及 docs/agents/ 目录下的 CODE_STYLE.md、MODULE_RULES.md、TASK_FLOW.md。 如果这些文件不存在或读取失败,请立即向我确认,不要自行假设规则。 读完规则之后,请用三句话概述你将要遵守的核心约束,然后我们正式开始。让AI复述一遍核心约束这一步很关键。它不是为了形式,而是确保规则真的进入了模型的注意力机制。实测下来,做过复述的会话,对P0规则的遵守率明显高于直接丢文件的会话。
还有一个细节:长对话到后期,模型会"遗忘"早期规则。这是我们被坑了好几次才发现的。处理方式是,在任务进行中遇到关键步骤时,再重复一遍对应的规则引用。比如让AI改某个函数时,末尾加一句"注意,此函数必须满足CODE-STYLE-002,长度不能超过50行"。把规则"挪"到离生成点更近的地方,比一开始灌输一大堆更有效。
3.2 优先级与冲突裁决:AI遇见矛盾规则时应该停手问人
规则一多,冲突就不可避免。比如全局规则里有一条P0是"所有接口调用必须使用统一的request封装",但技术栈规则里又有一条是"页面级请求可以使用组件库自带的useFetch"。AI在生成代码时碰到这种冲突,如果规范里没写明裁决机制,它会随机选一条执行,结果就是代码风格再次分叉。
我们的做法是,在规范文件头部定义优先级语义:
优先级定义: - P0: 必须遵守,违反会直接导致代码无法通过CI校验或带来严重维护风险 - P1: 强烈建议,代码评审时会被重点检查 - P2: 参考项,适用于没有历史包袱的新模块 当规则冲突时,按下述顺序处理: 1. 遵守优先级更高的规则 2. 如果同优先级规则冲突,评估当前模块的历史代码习惯,优先与存量代码保持一致 3. 无法判断时,停止编码并向我提问,不要自行决定最后一条"无法判断时停下来问人"是整个裁决机制的兜底。AI的很多离谱操作,其实是在规则冲突时自作主张乱选导致的。允许它"合法地停下来",反而能减少大量返工。我们还在规范里专门加了一条:"任何情况下,不得因为遵守规则而修改需求本身的业务逻辑。如果规则与产品需求冲突,先停下来向用户确认,由人来决定是调整规则还是调整实现方式。"
4. 把规范变成代码审查的"通用语言"
4.1 提交前自检:让AI带着检查清单来交付
规范写出来、激活了,还不算完。我们遇到的新问题是:AI把活干完之后,不会主动去校验自己有没有违反规范。它就像个埋头赶作业的学生,写完了就交,根本不会回头检查。如果不强制自检,P0规则在生成阶段依然容易被漏掉。
解决方式是在TASK_FLOW.md里增加"AI交付前自检"环节,把它和PR描述绑定在一起。每次AI生成完代码,要求它按下面的格式输出自检清单:
### AI 自检清单 - [P0] CODE-STYLE-001 命名语义化: 通过 - [P0] CODE-STYLE-002 函数长度不超过50行: 通过(最长函数 43 行) - [P0] CODE-STYLE-003 禁止硬编码密钥: 通过 - [P1] MODULE-FE-003 组件拆分粒度: 通过 - 未遵守的规则: 无这个自检清单有几个好处。第一,它把抽象的质量要求变成了可见的交付物,Review的人不需要从头到尾读一遍代码就能快速定位问题。第二,AI在输出自检结果时,会倒逼它重新扫描一遍自己生成的代码,很多错误在这一步就被拦下来了。第三,这一过程在我们项目里实测下来,大大减少了"低水平重复犯错"——同一个P0规则,连续三次生成都违反的情况基本被消灭了。
4.2 Review时引用规则编号,把主观感受变成客观问题
引入规则编号后,代码评审的氛围也变了很多。以前看到AI写的不满意的代码,评审意见往往是"这个命名不好""这个函数太长""这块逻辑有点绕"。这类意见有个共同问题:主观,且没有标准。AI下一次生成时依然会犯同样的问题。
现在我们的评审意见统一改成这种格式:"这里违反CODE-STYLE-001,建议改为xx。""这个函数78行,违反CODE-STYLE-002,需要拆分。"规则编号成了团队内部的一种"通用语言",人和AI都基于同一套标准对话。更妙的是,我们后来尝试让AI做一次初筛评审,把"规范文件"作为评审标准,再去Review另一段AI生成的代码,它输出的问题清单比人肉Review还细致。当然,AI评审的结果我们不会直接采信,但作为第一道过滤网非常够用。
4.3 规范更新后,如何让AI重新对齐
规范不是写一次就完事的,它一定会随着项目演进而更新。这里有个特别容易踩的坑:AGENTS.md更新了,但正在运行的AI会话还抱着旧规则。你明明改了规范,AI今天生成的代码却还是按老规矩来做。
我们现在的做法是,规范文件头部强制标注版本号,每次修改都要更新版本号和变更记录:
# 规则集版本: v1.3.0 # 最近变更: v1.3.0 新增 MODULE-BE-004(事务边界规则);v1.3.0 放宽 CODE-STYLE-002然后,任何AI编码会话只要进行到"修改涉及规则内容"的阶段,都要先在对话里确认当前规则集版本。规则更新后,团队统一提醒大家重开会话,避免在旧上下文里继续干活。另外,所有规则文件都走Git提交,变更历史完整保留,出问题的时候能快速回溯是哪条规则变了导致AI行为异常。
5. 给AI定规范踩过的三个坑
5.1 规则贪多嚼不烂:一上来写120条,AI全"记住"了也就等于全忘了
第一个坑是我新手期踩的。刚开始给AI定规范时,恨不得把团队积攒的所有工程经验都塞进去,一口气写了120多条规则,从命名规范写到数据库索引设计。结果AI的表现反而退步了:规则太密,真正关键的约束被稀释,模型在生成代码时平均分配注意力,P0规则和P3规则看起来"重要性一样",于是P0规则也开始被违反。
后来我们做了一次大瘦身,P0规则砍到12条,P1规则控制在30条以内,P2规则全部移出AI上下文,只在文档里保留给人类参考。全量规则还是存在仓库里,但AI开工时只读取与当前任务相关的子集。这就好理解了:给AI的上下文不是仓库,是操作台,上面只能放这次任务要用的工具。
5.2 只写"禁止"不写"替代",AI会原地打转
第二个坑是规则语言太单向。我们最早写了很多"禁止xxx"的规则:禁止使用any、禁止使用内联样式、禁止在组件里直接写fetch。AI遵守得倒是很积极,但它会用一种很诡异的姿势来"规避"规则。禁止使用any,它就发明一大堆让人看不懂的泛型类型;禁止内联样式,它就每次render都动态生成一个样式对象;禁止在组件里直接写fetch,它就干脆把所有请求逻辑都塞进一个巨大的全局service里。
这些代码在规则意义上"合规",但维护起来比违规还痛苦。所以我们现在对每条"禁止"类规则强制配一个"替代写法",明确告诉AI:不让你走这条路,是让你走那条路。比如:
| 规则 | 错误做法 | 推荐做法 |
|---|---|---|
| 禁止使用any | const data: any = await getData(); | 定义明确的接口类型,用类型守卫收窄未知类型 |
| 禁止在组件里直接写fetch | useEffect里直接call api | 提取到hooks层,通过自定义请求hooks统一管理 |
| 禁止内联样式 | style={{ display: "flex" }} | 使用主题规范的className或design token |
加了替代写法之后,AI产出的代码质量才真正开始稳定。这条经验对AI特别重要——它不像人那样擅长"举一反三",你只堵路不给路,它就原地乱撞。
5.3 规则库不维护,新代码和老代码会慢慢分裂
第三个坑,也是最需要提醒的:规则库会和代码库一样腐烂。项目过了半年,当初定的某些规则已经不再适合新的技术栈了。比如我们之前有一条规则是"禁止使用可选链操作符",因为在当时的浏览器兼容策略下optional chaining需要额外编译配置。但后来构建体系升级了,这条规则就变成了纯负资产——AI遵守着一条过时规则,生成的老派代码和新代码风格越来越割裂。
现在我们把规则库当代码维护,每个迭代周期评审一次,做增删改记录。每次规则变更,都明确回答三个问题:新增了哪条、删除了哪条、为什么。变更记录写在AGENTS.md里,既不污染规则正文,又能给AI提供"当前规则版本"的意识。另外,不同模型的"规则理解能力"也有差异,同一份规范,A模型执行得很好,B模型可能频繁踩线。遇到这种情况别急着怀疑规范写得不行,可以先用评判标准更强的模型做规则审计,或者针对特定模型训练专门的规范skill,效果会好很多。
我在实际项目里最大的体会是:给AI定规范这件事,本质上是把团队的工程经验"外化"成机器能理解的执行文件。它不复杂,但需要耐心迭代,并且永远不要指望一份文档解决所有问题。规范文件是一等公民,需要有人持续喂养、修剪、更新。当你把AI从一个"偶尔惊艳的实习生"带成一个"稳定可靠的成员"时,回头看这趟折腾是值得的。