1. 为什么突然要给 AI 立一套代码规范
这事儿得从一次让我印象深刻的 code review 说起。团队引入 AI 辅助编程小半年,大家从最初的新鲜、质疑,到后来的“真香”,效率确实上来了。但问题也在悄悄积累——我那天 review 一位同事的 MR,改动文件有十几个,打开一看,风格五花八门:有的函数写得像个迷你论文,注释比代码还长;有的地方又极简到让人摸不着头脑,变量名就一个字母;异常处理有的吞掉错误,有的恨不得把堆栈打到用户脸上。更麻烦的是,AI 生成的代码里,有的按我们现有的分层规范来写,有的则自创了一套“私货”结构,看起来逻辑没毛病,但跟项目里的既有风格完全是两路人马。
这其实是所有深度使用 AI 编码的团队迟早要撞上的墙。我们得承认一个现实:当下的大模型编程助手,本质上是“概率性的代码生成器”,它给出的代码是基于海量公开代码库训练出来的“平均表现”,而不是基于你项目里的具体约束。你如果不给它画清楚边界,它就会拿平均水准来“糊弄”你。平均水准是什么?是网上各路教程、各类开源项目的混合体,风格割裂、防御层次不一、甚至有时连依赖的用法都是过时版本。
于是我们决定,在项目里专门给 AI 增补一份“代码规范”——不是替代原来的团队规范,而是叠加在人类规范之上,专门用来约束 AI 生成代码的行为。这份规范从本质上看,是一套写给大模型的“纪律条款”,它要解决三个核心问题:第一,让 AI 输出稳定且可预测;第二,让 AI 生成代码的“第一版”就接近团队评审标准,而不是把评审变成大型挑错现场;第三,让人类工程师从“给 AI 擦屁股”中解放出来,把精力留给真正的业务逻辑。
如果你所在的项目组也在用 AI 写代码,或者正准备引入 AI 编程工具,那么这篇文章里记录的思路、条款、落地方法,以及我们踩过的坑,应该能给你一个不错的参照。我会按我们实际推进的顺序来拆解:先从规范的内容设计说起,再讲怎么把它灌进 AI 的工作流程里,最后是执行中遇到的典型问题与排查手段。整个方案不需要你推翻现有的研发流程,也不需要你引入复杂的平台系统,一支 prompt 文件加上几条 CI 检查规则就能起步。
2. 给 AI 制定规范的设计思路与拆解
2.1 规范和给人类看的规范,本质上是两码事
给 AI 立规范,跟给团队新人写开发规范,表面上都是“约束行为”,但底层的逻辑完全不同。人类的规范文档,很多时候只写“要做什么、不要做什么”,剩下的靠人的常识、经验和上下文理解去补齐。比如我们原来的团队规范里有一条“禁止在循环体内执行数据库查询”,人看到了会点头,到了具体场景基本能判断什么算循环内、什么场景可以豁免。
但 AI 不一样。它对规范的执行是字面级的、缺乏常识补全能力的。你说“代码要整洁”,它能给你写出自认为整洁但其实很啰嗦的代码;你说“不要写重复代码”,它可能为了消除重复,硬抽出过度抽象的函数。所以给 AI 的规范,必须做到“机器可读、字面可执行、边界清晰”,不能依赖模型去理解你的“言外之意”。
我们内部把这条原则称为“对 AI 的规范要像对测试用例一样较真”。每一条规范都要能转换成一句不带歧义的指令,最好还能附带正反例。比如“禁止在循环内查库”这条,对 AI 要写成:“当你在 for、while、forEach、map 等循环体内部调用数据库查询方法时,必须先把查询结果提取到循环外的变量中,循环内只允许访问已获取的数据集合。”这样描述,模型在执行时才有明确抓手。
2.2 规范的层级结构:从全局红线到局部偏好
在设计规范文件时,我没有把它做成一个大而全的文档,而是拆成了三层,分文件存放。原因很简单:不同场景下,AI 需要关注的规范粒度不同。如果你把所有规则塞进一个庞大的上下文里,不仅 token 消耗大,模型还容易“注意力稀释”,该遵守的反而忽略了。
第一层叫“全局红线”,放在项目根目录的 AGENTS.md 或者 CLAUDE.md 里,是所有 AI 工具(不管是用 Copilot、Cursor 还是其他 Agent)都要加载的底线规则。这一层只写不可妥协的内容,比如禁止提交密钥、禁止删除他人代码、禁止使用已废弃的 API、必须通过哪些基础检查才能提交等。全局红线控制在 20 条以内,每条一句话,便于模型全部记住。
第二层叫“模块约定”,放在各子模块或软件包目录下。这一层约束的是某个业务域内的特殊规则。比如我们的支付模块里,要求金额字段一律使用整数分作为单位,禁止浮点数运算;比如订单模块里,状态变更必须走统一的状态机方法,不允许直接修改状态字段。这些约定跟着代码目录走,AI 在处理对应模块的代码时,能通过项目文件结构自动读取到相关约束。
第三层叫“风格偏好”,它不是强约束,而是为了让代码更接近团队手写风格的软性建议。这一层我会写一些类似“变量命名优先使用业务术语,而不是泛化的 data、info”“函数注释说明意图而不是复述代码”这类内容。风格偏好不需要 AI 强制遵守,但作为上下文注入后,明显能提升代码的可读性和团队的接受度。
为什么这样分?因为 LLM 的指令遵循存在“近因效应”和“特定性优先”现象。离当前任务越近的指令、描述越具体的指令,被遵循的概率越高。把全局红线放在根目录,把模块约定放在具体目录,就能最大化利用这个特性。
2.3 规范不是一锤子买卖:需要版本化与迭代
给 AI 的代码规范,在项目里应该拥有跟源代码同等的待遇——纳入版本管理、有变更记录、有评审过程。我们最开始犯的错,就是让规范停留在口头或者某个 wiki 页面里,结果规范更新了,AI 工具加载的还是旧版本,导致生成代码的行为漂移。
后来我们把规范文件放进了 git 仓库,任何改动都必须走 MR 评审。每次规范变更,我们会在文件头部的“变更日志”里写明改动点、原因和生效范围。这样做还有一个额外好处:当 AI 生成行为出现异常时,我们可以快速回溯“规范最近改了什么”,排查是不是某条新规范把模型带偏了。
迭代方面,我们保持一个比较务实的心态:规范不是越严越好,而是越“恰好”越好。如果某条规范频繁被 AI 违反,我们要分析是描述不够精确,还是这条规范本身就反直觉。如果一条规范长期零触发,也可以考虑删掉,减少上下文噪声。
3. 规范的核心内容与实操要点
3.1 通用红线清单:先把底线焊死
先展示我们目前在全局规范里实际使用的核心清单,并拆解每条背后的意图。你可以基于项目语言和技术栈增删:
- 禁止提交敏感信息:包括 API 密钥、数据库连接串、token、.env 文件内容。AI 经常在示例代码里顺手硬编码密钥,这条必须放在第一句。我们同时要求 AI 在发现代码中出现形如
sk-、AKIA、password =之类特征串时,主动替换为环境变量引用。 - 禁止引入未经确认的新依赖:AI 特别喜欢为了一个小工具函数去引入 lodash、day.js 之类的第三方库。我们的规范要求,如果现有代码或团队内部 util 已有等价实现,必须复用之;确实需要新增依赖的,AI 应生成带 TODO 标记的代码,并明确说明引入理由,等待人工评审。
- 不要删除或修改与当前任务无关的代码:AI 在重构时容易出现“顺手清理”行为,删掉一些它认为是冗余但实际被其他逻辑依赖的代码。规范明确要求:只能改动与本次需求直接相关的行。
- 新代码必须匹配现有代码风格:包括缩进、命名风格、是否分号、字符串引号等。我们约定 AI 在生成代码前,必须先读取同目录下至少一个现有文件来推断风格,而不是直接套用“最流行”的风格。
- 错误处理不允许空 catch:AI 默认生成不规范代码时,经常产出
catch (e) {}这种吞异常写法。规范要求 catch 块内至少要记录日志或抛出带有上下文的异常。 - 禁止硬编码魔法值:对于数字、字符串字面量,如果在一个函数里出现超过两次,就必须提取为命名常量。
每条红线我们都配套了一个正例和反例块,放在规范文件的最下方。你别小看这一招,给 AI 看正反例,比单纯描述规则的效果好一个量级。大模型本质上是模式匹配机器,你给它的“不该长什么样”范例越多,它跑偏的概率就越低。
3.2 模块级规范怎么写才有约束力
模块级规范的撰写,和全局规范有微妙的差异。这里的核心是:要绑定到具体的代码符号上,而不是描述抽象原则。比如你说“支付模块必须使用 BigDecimal 处理金额”,AI 能听懂但不知道边界在哪;你得写成“在 com.xxx.payment 包下所有涉及金额加减乘除的代码,必须使用 Amount 工具类,禁止直接对 float、double 类型执行算术运算。金额参数类型统一为 Long(单位:分)”。这样模型在执行支付相关任务时,就能准确命中约束。
另外一个要点是:模块规范要跟着业务文档走。我们在每个模块的 README 或 package 的约定文件里,用固定区块标注“AI 接入注意”,内容不超过 10 条。模型在读取模块代码时,通常会顺带读一下 README,这个区块就会自然进入它的上下文。如果模块改了关键设计,记得同步更新这个区块,否则旧约定会持续“污染”AI 的生成。
我还建议在每个模块规范里写一个“不做什么”的列表。LLM 在遵循指令时,对否定指令的敏感性通常低于肯定指令,所以否定性的规则要写得特别明确。例如“不要创建新的 service 类,除非现有 service 无法承载当前职责,且需在 MR 描述中说明重构理由”。这个“除非”写得越具体,AI 就越不会轻易越界。
3.3 风格偏好与硬规范的区别处理
风格类规范,比如“函数命名倾向动词开头”“注释用中文写”,我们单独放一个文件,不强制校验,但会让 AI 在生成完代码后自己 review 一遍。为什么要这样对待?因为风格类规则如果硬性约束,会大幅增加模型的思维负担,反而影响主逻辑的生成质量。AI 一旦把注意力放在“变量名是不是够优雅”上,就容易在核心功能实现上犯低级错误。
我见过有些团队为了追求“AI 生成代码零改动”,把命名规范、注释格式都写进强约束里,结果代码生成速度明显变慢,而且经常为了形式牺牲逻辑清晰度。所以我倾向于:让 AI 先保证正确性与可维护性,再在后续的人工评审过程中逐步调整风格问题。毕竟代码风格问题是可以靠格式化工具和静态检查自动兜底的,而逻辑缺陷和架构偏差才是真正需要人的判断力去兜底的点。
你可以把风格偏好理解为给 AI 的“气口”,适当留白让它发挥,反而能让生成的代码更像团队里一个老手写的。我们曾经在风格文件里加了一条“函数的圈复杂度超过 10 时,主动拆分”,这条反而带来不少积极作用,因为 AI 会在逻辑变复杂时自己停下来拆函数,而不是硬拗一个巨无霸。
3.4 规范落地:从 prompt 文件到工程检查
规范文件的载体,我们用的是 Markdown,文件名是 AGENTS.md,放在仓库根目录。主流的 AI 编程工具,像 GitHub Copilot 的自定义指令、Cursor 的 Rules,都支持加载项目内的规则文件。除了给工具加载外,我们还做了一层硬校验:在 CI 流水线里加入了针对 AI 生成“典型违规”的自动检查。比如用 gitleaks 检查密钥泄露,用 eslint 的 no-empty 规则检查空 catch。硬校验的意义在于:不能让规范只停留在“建议”层面,出现违规时要有客观的失败信号。
在 prompt 层面,我们会在需要 AI 生成代码的任务描述末尾,自动追加一句固定后缀:
注意:请严格遵循项目根目录 AGENTS.md 中的规则。如与当前模块 README 中的"A I 接入注意"冲突,以后者为准。生成完代码后,请对照 AGENTS.md 自查一遍,并在 代码注释中标注你认为可能有争议的设计决策。这句后缀看起来简单,其实起了很好的作用。前一句告诉模型去读取规范,第二句解决规范冲突时的优先级,第三句则让模型在生成时多一层自我审视,第四句为后续人工 review 提供了有价值的上下文。
4. 实操过程与核心环节实现
4.1 从零搭建规范文件:三步走
如果你现在要动手给项目制定 AI 代码规范,我建议按三个步骤推进,别想着一次到位。
第一步是“沉淀纠偏记录”。把过去一两个月里 AI 生成代码被 review 打回的问题,按照频率排序。最常见的问题优先写进规范。你不用猜 AI 会犯什么错,你手头的 MR 评论、bug 记录就是最好的素材。我们当时的高频条目是“不必要的依赖引入”“硬编码密钥”“重复实现已有工具函数”这三项,后来都成了规范里的头几条。
第二步是“起草与试行”。整理出一个 v0.1 版规范,挑一个中等复杂度的模块试行两周。注意不要全组铺开,找一个小模块的好处是:试错成本低,反馈周期短,你可以快速判断哪些条目有效、哪些描述有歧义。试行期间要记录 AI 生成代码的返工率——如果返工率没下降,说明规范没写到位,或者加载方式有问题,需要回头调整。
第三步是“全量推广与定期修订”。v0.1 稳定后,把规范同步到其他模块,并在每周的工程例会上留出 5 分钟,专门讨论“规范条款的有效性”。我们的修订周期是双周一次,每次改动一行也要走 MR,保持变更可追溯。
如果你觉得从零起草太耗精力,可以直接找一些公开的“AI 编码规范示例”作为底稿,再按项目情况裁剪。但不要原文照搬,别人的规范是基于他们项目的痛点和语言特性沉淀的,直接套用会在你的场景里水土不服。
4.2 关键参数与模板:直接抄作业的版本
下面我给出一个可直接参考的 AGENTS.md 简化模板,你可以在此基础上按需增删:
# AGENTS.md(项目 AI 编码规则) ## 全局红线(必须遵守) 1. 禁止提交任何敏感信息:API 密钥、token、连接串、密码等。 若在任务中出现,替换为环境变量引用,并注释说明所需环境变量名。 2. 禁止删除、注释掉与当前任务无关的既有代码。 如需调整,必须在 MR 描述中逐条说明理由。 3. 禁止为当前任务引入新的第三方依赖。 如确有必要,生成代码中使用 TODO(dep) 标记,并在注释里说明引入目的, 等待人工评审通过后方可合并。 4. 禁止在 catch 块中留空或直接吞掉异常。 必须至少记录日志(使用现有 logger),或抛出带业务上下文的异常。 5. 新代码的风格匹配同目录现有代码:缩进、引号、分号、命名风格均保持一致。 6. 禁止硬编码魔法值。同一函数内超过两次出现的字面量,提取为命名常量。 7. 禁止使用已废弃 API、已标记删除的方法、明显过时版本写法。 如不确定 API 是否仍受支持,在代码注释中标注 TODO(verify) 并说明你的顾虑。 ## 自查清单(代码生成完成后) - [ ] 是否读取了根目录 AGENTS.md? - [ ] 是否有硬编码的密钥或敏感信息? - [ ] 是否修改了与当前任务无关的代码? - [ ] 每个 catch 块是否都有处理逻辑? - [ ] 新代码风格与同目录文件是否一致? ## 冲突处理 当本文件规则与模块目录下 README 或模块规范冲突时,以模块规范为准; 如模块规范缺失,按本文件执行。这个模板的核心思路是“能用一句明确指令说清的,绝不用一段抽象描述”。你可以看到每条规则里,除了说明“不能做什么”,还给出了“应该怎么做”以及“无法避免时的兜底动作”。比如第 3 条里写“生成代码中使用 TODO(dep) 标记”,这就让 AI 在确实需要新依赖时,仍然有一个合规的出入口,而不是被迫违反规范或者干脆拒绝生成。
4.3 把规范接入 AI 工具:Cursor 与 Copilot 的实测
在实际接入环节,不同 AI 编程工具对规范文件的加载机制略有差异,我分别说一下我的实测体会。
如果你用的是 Cursor,在项目根目录放 .cursor/rules 文件,或者直接放 AGENTS.md,Cursor 都会自动识别并在对话上下文中加载。我个人的建议是:用 .cursor/rules 里的一个专门文件来放“面向 AI 编码约束”,把 AGENTS.md 放在仓库根目录也保留一份,这样即使团队里有成员用别的工具,规范依然是可发现的。Cursor 的 Rules 支持按文件路径做 glob 匹配,你可以把模块级规范放在对应目录下,实现“进入哪个代码目录就自动切换到哪套规则”,效果很不错。
如果你用的是 GitHub Copilot,自定义指令分为两种:一种是 GitHub 仓库设置的 .github/copilot-instructions.md,对所有开发者生效;另一种是个人在本地设置的自定义指令。仓库级的 instructions 适合放全局红线,因为它是跟随项目的,其他协作者 clone 下来就能用。Copilot 对规范文件的遵循程度不如 Cursor 的 Rules 那么“显式”,但实际测试下来,只要把规则写得足够清晰,它在生成时确实会更守规矩。
还需要提一下的是:规范文件本身建议使用英文和中文双语的关键词。因为当前主流模型对英文指令的遵循稳定性通常优于中文,但完全用英文写,团队阅读成本高。我们的做法是,规则标题用英文,详细解释用中文,关键约束词用英文。这样既提高了模型遵循率,也保证了团队成员的可读性。
4.4 通过 CI 让规范“带电”:硬性校验配置
如果只有 prompt 层面的软约束,AI 还是会时不时越界。我们要让规范在工程层面有“牙齿”。我在 CI 流程里加了三个轻量级检查:
第一个是密钥扫描,用的是 gitleaks。配置很简单,在流水线里加一步gitleaks detect --source .就够。这一步能把硬编码密钥的问题拦在合并前。过去一个季度我们抓到过 3 次 AI 生成的代码里带测试用密钥,全靠这一步兜住。
第二个是静态检查规则增强。在 ESLint 或对应语言的 Linter 配置里,开启 no-empty、no-duplicate-imports、no-unused-vars 等和规范直接相关的规则。注意把规则级别设为 error 而不是 warn,否则 AI 会倾向于忽略 warning。
第三个是依赖白名单检查。我们写了一个简短的脚本,读取 package.json 的 dependencies 字段,和一份白名单列表比对,新增依赖如果没有对应的 MR 关联编号,CI 直接失败。这个检查有点严格,但确实有效地刹住了 AI 随意引入依赖的毛病。
你可能会问:这些检查和给“人类”的规范有什么区别?其实检查规则和规范文件是同一套逻辑的一体两面:规范文件是用来“告诉AI应该怎么做”的输入,CI 检查是用来“验证AI实际做了什么”的输出。两者缺一不可。没有规范文件的 CI 检查,只是被动防御;没有 CI 检查的规范文件,只是纸上谈兵。
5. 规范执行中遇到的坑与排查心得
5.1 模型不读规范文件,怎么排查
这是最常见的坑。你把 AGENTS.md 写得天花乱坠,但 AI 生成代码时仿佛根本没看过。排查思路先从加载链路开始:检查工具是否正确识别了规范文件路径,比如 Cursor 的 Rules 是否匹配了目标目录。很多时候问题出在 glob 规则写错了,规则只对 src 目录生效,你在 tests 目录下让 AI 写代码,它当然读不到。
其次是上下文的截断问题。项目代码一多,AI 的上下文窗口被代码填满,规范文件可能被挤出注意力区。我们的做法是把规范里的最核心三条红线,在每次问答的任务描述里再重复一遍。比如:
注意:不引入新依赖;不硬编码密钥;不修改无关代码。这三句话成本极低,但能把 AI 的行为锚定住。你也可以尝试在让 AI 生成代码之前,先发一条指令“请阅读项目根目录 AGENTS.md,并复述其中前三条规则”,确认它真的读进去了。这一步虽多花几秒,但对稳定长任务的执行质量很有帮助。
5.2 规范之间互相打架怎么办
规范一多,就会出现自相矛盾的情况。比如全局规范说“禁止新增依赖”,但某个模块规范说“本模块必须使用某第三方库进行日期格式化”。当 AI 同时读到两条规则时,轻则随机选一条执行,重则产生混乱。
解决办法有两个。第一,在每份规范文件的开头,写清楚优先级。我们的约定是:模块规范 > 全局规范 > 工具默认行为。然后在全局规范里加一条兜底:“当本文件与模块规范冲突时,以模块规范为准。”这样模型在判断规则冲突时就有明确依据。
第二,尽量避免规则条目之间的“逻辑交集”。在新增一条规范前,先检索已有规则,如果新规则和旧规则描述的范围有重叠,要么合并,要么明确新旧规则的适用边界。我们每两周修订规范时,有一项固定任务就是“冲突扫描”,把可能互斥的条目列出来逐条梳理。
5.3 规范太细导致 AI“过度优化”
还有一个比较隐蔽的问题:规范写得太细,AI 在处理小任务时反而会过度设计。比如你要求“所有函数必须注明复杂度、必须有完整的 JSDoc、必须处理所有边界条件”,AI 在写一个简单的工具函数时,也会生成一大坨防御代码和注释,看起来无懈可击,实际上把简单的逻辑搞复杂了,可读性和维护性反而下降。
我们的应对方式是在规范里加一条“匹配任务规模”的原则:小改动保持小改动,不要为了满足规范而扩大代码体积。这条原则听起来像废话,但对模型来说很重要,因为模型倾向于“最大化满足所有约束”,一旦约束多,它就会通过堆代码来“求稳”。在规范文件里加入这条原则,相当于许可它在合理范围做减法,整体生成质量会更贴近人类工程师的判断。
5.4 如何评估规范有没有效果
最后聊聊怎么判断这套规范值不值得持续投入。我们用的核心指标有三个:
第一个是 MR 的一次通过率,也就是不含重大修改意见直接合入的比例。引入规范前,AI 参与较多的 MR 一次通过率大概在三成左右,规范稳定运行一个月后,这个数字提升到了接近六成。虽然不敢说全是规范的功劳,但趋势很明显。
第二个是 AI 相关 MR 的平均 review 耗时。之前每份 AI 相关 MR 的评审时间普遍要 20 分钟以上,因为要挑一堆风格和约束问题。现在大部分时间花在业务逻辑评审上,耗时降到了 10 分钟左右。
第三个是“规范违规”的密度,我们靠 CI 检查和人工标记统计。从初期每个 MR 平均 2~3 处违规,下降到现在的约 0.5 处。
我个人在实际推进这件事时还有一个体会:规范存在的意义不是把 AI 变成一个“不会犯错”的工具,而是让它的错误变得可预期、可控制。就像给一个非常能干但有点毛躁的新同事配了一本操作手册,你不指望他从此毫不犯错,但至少他犯错的方向、频率、半径都清晰可控,团队协作的摩擦系数自然就降下来了。如果你也在为项目里的 AI 协作头疼,不妨从这个方向试试,先别贪多,挑三五个痛点写成规则,跑两周看效果,再逐步迭代。