如果你最近一直在用AI编程工具写代码,可能已经遇到过一个很熟悉的烦恼:AI给出的代码能跑、功能也对,但放进项目仓库里总有一种“不对劲”的感觉。它可能把一个已经封装好的请求方法扔在一边,自己又包了一层HTTP;可能因为一个小需求,悄悄引入了一个几MB的第三方依赖;更常见的是,它把团队约定俗成的写法完全无视,生成一版“语法正确但风格违和”的代码。这种感受很像团队里来了一个能力很强、但完全不了解项目规则的新人,你需要盯着它一点点把规矩补上。我最近就在项目里做了一件事:新增了一份专门给AI制定的代码规范,把团队的隐形约定、红线禁区、基础架构约束全部显式地写进规则文件,并接入到日常使用的AI编程工具里。这篇文章就聊聊我为什么这么做、规范里到底写了什么、以及落地过程中踩过的坑。
1. 为什么非要单独给AI定一套代码规范
1.1 人类的规范和AI能执行的规范,是两回事
传统的代码规范通常长这样:“命名要有意义”“函数要符合单一职责”“接口要做好参数校验”。这些话人看得懂,因为人会结合具体业务场景、项目背景去理解,知道什么时候该变通。但AI没有这种“项目记忆”,每次对话都是从一个全新的上下文开始,它能看到的只是当前打开的文件、最近的对话、以及被明确喂给它的信息。如果你不对它做约束,它就会按照训练数据里的“通用最佳实践”去写代码,而不是按照你们项目的“自定义最佳实践”去写。
所以给AI制定规范,本质上不是把人类规范换一个文档格式,而是要完成一次“知识显性化”:把团队里那些写在代码评审记录里、存在老同事脑子里的隐性约定,变成AI能够逐条读取并执行的显性规则。我真正动手之后才发现,这件事比想象中复杂,但也比想象中有价值。
1.2 没立规矩之前,我见过三次翻车
第一次翻车发生在给一个列表页加筛选功能的时候。项目里明明有封装好的 request 方法和列表接口通用的 usePagination 组合式函数,AI 完全没有参考旧代码,自己写了个 axios 调用、自己管理分页状态、自己做了 loading 展示。功能是好的,但代码风格和项目里其他十几个列表页完全不同,维护成本一下子就上去了。
第二次翻车更典型。业务需要一个数组按某个字段排序,AI 顺手在 package.json 里加了一个 lodash 依赖。我们项目之前为了控制体积,特意把所有需要 lodash 的用法收拢到了 utils 里,这个约定对人是隐性的,但对AI来说根本不存在。它按“推荐实践”选择了最省事的方案,结果直接踩了项目的红线。
第三次翻车让我决定彻底落地规范:AI 在修复一个按钮样式问题时,连续改了三个不相关的文件,包括公共主题配置和全局 CSS 变量,导致线上样式整体变化。这个问题的根子不在AI笨,而是它根本分不清“这个任务允许改动的范围”到底在哪。这三件事结合起来,结论非常清晰:AI是一个没有项目常识的超级执行者,你必须在它动手之前,把所有“常识”喂给它的上下文。
1.3 给AI的规范,本质是一种约束性的上下文工程
把规范写进规则文件,和写一段优秀的提示词,底层逻辑是一样的:降低模型输出空间里的不确定性。人写代码遇到不熟悉的地方会问同事,AI不会主动问,它只会尽可能生成“看起来合理”的内容。规范的存在,就是把“看起来合理”强行拉回“项目里真实存在”的轨道上来。
想明白这一点之后,我的思路就从“写一份代码规范文档”变成了“做一套AI编码约束系统”。它至少要覆盖几个维度:技术栈边界、现有模式的参考入口、文件修改范围、质量与安全底线、输出前的自检流程。下面我会一个个拆开讲。
2. 给AI的代码规范,应该包含哪些模块
2.1 技术栈与依赖红线:先锁死AI的“工具箱”
AI在生成代码时有一个惯性:倾向于使用训练数据里出现频率最高的库,而不是你们项目实际在用的库。所以规范里的第一优先级就是锁定技术栈和依赖。我在规范里放了明确的白名单和黑名单,例如:
| 约束类别 | 规则示例 | 原因 |
|---|---|---|
| 框架版本 | 项目基于 Vue 3 + TypeScript 5,禁止输出 Vue 2 语法 | 防止新旧语法混用 |
| 日期处理 | 统一使用 dayjs,禁止新增 moment | moment 已停止维护且体积大 |
| 依赖白名单 | 除项目已有依赖外,禁止自行引入 npm 包 | 控制包体积与安全风险 |
| 样式方案 | 必须使用项目的 SCSS 变量与 mixin | 保证主题一致 |
这里的表达不能太含糊。比如“尽量减少新依赖”就不行,AI无法判断“减少”到什么程度;要用“禁止新增”“必须使用”这样无歧义的词。刚开始大家的规范普遍太软,AI执行起来就会松弛,后来我把所有规则全部改成祈使句,效果立刻提升了。
2.2 模式优先:让AI先找到“同类代码”再动手
AI犯懒的时候,生成代码的逻辑是“听起来合理就行”,而不是“项目里同类代码怎么写的”。要纠正这种倾向,最好的办法是把“参考现有实现”写进规范,并且给出具体的路径。我在规范里专门加了一条规定:
- 动手之前,先搜索项目中与当前需求最相似的现有实现;如果已有工具函数、组合式函数、公共组件,必须优先复用,禁止重复实现。
- 新增页面或组件时,先阅读同目录或相邻目录下已有的两到三个文件,保持结构、命名、注释风格一致。
光这样还不够,我还在规范附录里列了一个“常用工具函数清单”,把 formatDate、request、usePagination、fileDownload 这些高频工具的路径和用法写清楚。AI每次读规则文件时都会看到这张清单,重复造轮子的概率大幅下降。实测下来,这条规则对“AI写出来的代码像不像团队自己人写的”影响最大。
2.3 边界约束:明确“AI不能碰哪些文件”
这一条是吸取第三次翻车教训之后补上的。AI天然没有“改动范围”的概念,给它一个任务,它有时会顺手把看起来相关的公共文件改掉。所以规范里必须画一条清晰的安全边界。我在项目根目录维护了一个“禁止AI修改”的名单,包括:
- 全局配置文件:package.json(新增依赖须由人工完成)、vite.config.、tsconfig.json
- 公共样式入口:styles/global.scss、theme 相关变量文件
- 基础设施代码:request封装、路由守卫、权限校验模块
- 类型定义:全局 .d.ts 文件
同时规定,如果任务确实涉及这些文件,AI必须在回复里明确说明“需要修改公共文件XXX,原因是什么”,由人来最终决定。这条规则听起来简单,实际作用非常大,相当于给AI加了一道“先请示再动工”的流程。
2.4 错误处理、日志与安全基线:兜住质量底线
AI写代码还容易在两个地方出问题:一是错误处理太粗暴,二是日志信息约等于没有。规范里我加了几条最低要求:
- 禁止写空的 catch 块;捕获异常后必须给出至少一条可读的日志或向上抛出。
- 日志必须包含业务上下文,例如操作对象ID、失败原因,禁止只输出“error occurred”。
- 涉及用户敏感信息、Token、手机号等字段时,禁止直接输出到日志或接口报错信息。
- 涉及鉴权、支付、用户数据导出的功能,必须使用团队已有统一实现,禁止自己另写一套。
这里特别要提一个现象:AI对“安全基线”的理解通常停留在“不能有SQL注入”这种通用层面,而项目里的自定义安全约定,比如“管理端接口必须走某个鉴权中间件”,它完全意识不到。不把这类约定写进规范,AI就一定会漏。
2.5 输出自检:让AI交代码前先过一遍“自问清单”
最后,规范里还设计了一段“提交前必查”清单,要求AI在生成完整代码后逐条自检:
- 是否引入了新的第三方依赖?如果有,是否已经过人工确认?
- 是否复用了现有的工具函数或组件,而不是重复实现?
- 是否修改了边界文件中不允许修改的内容?
- 错误处理是否完整,有没有未捕获的异常路径?
- 新增代码是否与现有代码风格一致(命名、缩进、注释语言)?
一开始我以为AI不会认真执行这种自检,实测下来发现,把“自检清单”放在规范文件的末尾,与代码生成指令放在同一个上下文中,AI确实会像过流程一样逐条检查,很多低级错误能在生成阶段就被拦下来。
3. 实操过程:把规范真正落进工具链
3.1 规范文档怎么设计,AI才“看得进”
写规范这件事,最大的误区是把它写成“给人看的文档”。给AI看的规范要满足三个特点:短、具体、可执行。
先说“短”。AI的上下文窗口是有限的,规则文件如果超过几千字,越靠后的内容越容易被忽略。我给自己的要求是,强制规则控制在二三十条以内,每条一句话说清;附录里的工具清单可以长一点,但核心规则必须精简。再说“具体”。不能写“注意复用”,要写“项目已有 common/request.ts,所有HTTP请求必须通过它发出”;不能写“注意日志规范”,要写“禁止输出只有error字符串的日志”。最后是“可执行”。每条规则都应该能让AI判断出“我到底有没有违反”。如果一条规范写出来,AI看完了还是一脸懵,那它基本等于没写。
我当时落地的规则文件开头是这么写的:
# AI 编码强制规则(MUST) 你是本项目的一名开发工程师。在生成、修改代码前,必须先阅读以下规则并严格遵守。如果规则与你的通用知识冲突,以本文件为准。 1. 技术栈:项目是 Vue 3 + TypeScript 5。禁止生成 Vue 2 Options API 风格代码。 2. 复用优先:动手前先搜索 utils/、hooks/、components/ 下是否有现成实现,有则必须复用,禁止重复实现。 3. 依赖红线:禁止自行在 package.json 中新增第三方依赖。确需新增时,在回复中明确说明原因,由人类确认后手动添加。 4. 文件边界:禁止修改 package.json、vite.config.*、styles/global.scss、types/ 下的全局类型文件。如任务确实需要,先输出修改方案,不直接修改。 5. 错误处理:禁止空 catch。所有失败路径必须有可读日志或向上抛错。 6. 敏感信息:禁止将 token、手机号、身份证号等敏感字段写入日志或错误信息。 7. 风格一致:新增组件/页面时,先阅读同目录下 2-3 个现有文件,保持命名和结构一致。 8. 完成前自检:输出前逐条对照以上规则,自动修正不符合项。这一段规则写好后,我再根据具体项目情况补充附录。附录里放了工具函数清单、目录结构说明、常用组件清单,帮助AI在具体任务里找到参照物。
3.2 三种方式把规范注入AI工具
规范文档写得再好,不放进AI实际读取的上下文里也等于零。目前我在项目里尝试了三种方式,读者可以根据自己用的工具选一种或叠加使用。
第一种是Cursor的规则文件。在项目根目录建一个.cursor/rules/目录,把规范写进ai-coding-rules.mdc,Cursor在读取代码库时会把规则文件的内容作为上下文加载。规则文件顶部可以用 frontmatter 声明适用范围,比如只对 TS/TSX/Vue 文件生效。这种方式体验最好,规则是项目级的,团队成员clone下来就能用。
第二种是GitHub Copilot。需要在仓库根目录放一个.github/copilot-instructions.md,Copilot会把这个文件作为代码补全和对话时的项目级指令。语法上直接用Markdown就可以,核心规则和上面类似。配置简单,适合团队统一维护,缺点是它主要影响补全和简单对话,对复杂任务的控制力不如Cursor那么强。
第三种是自定义Agent或AI编程插件,比如 Cline、Continue、或者团队自研的Agent。这类工具通常在设置里有一个系统提示词或规则文件路径,我可以把同样的规范内容粘贴进去做全局指令,也可以指定读取项目中的AGENTS.md之类的文件。如果你在用开源工具,把规范直接放进仓库根目录的AGENTS.md,很多主流Agent框架会自动读取,算是一个跨工具的通用做法。
3.3 验证AI有没有遵守:评审、脚本、抽查三件套
规则写进工具,不代表AI就会百分百执行。我见过太多人以为配好文件就完事了,结果PR里依然充满违规代码。所以验证环节一定要跟上。我的做法是三层:
第一层是代码评审。每次AI生成的PR,我都会重点看 diff 里是否有“新引入依赖”“重写现有工具函数”“修改边界文件”这三类苗头。只要出现一次,就把对应的真实案例补充到规范里,让规则更有针对性。第二层是自动化检查。类似“禁止新增依赖”这种硬性规则,光靠人工看不过来,我写了一个非常简单的 CI 脚本,比对 package.json 的 diff,如果发现依赖列表有变化就自动标记,要求人类确认后才能合并。另外用 ESLint 和 TypeScript 严格模式把基础质量问题兜住,AI再怎么写,也绕不过编译和静态检查。
# 检查 package.json 是否在 PR 中被修改(用于人工确认依赖变更) if git diff HEAD~1 --name-only | grep -q "package.json"; then echo "package.json 被修改,需要人工确认是否新增依赖" exit 1 fi第三层是抽查统计。我每周随机抽两到三个AI完成的PR,对照规范清单逐项打分,把违规率记下来。说实话,刚开始那几周违规率非常高,有三分之一的PR至少有1条违规。等规范迭代到第二三周,明显下降到十分之一以内。这个过程没法一下子到位,但方向是对的。
4. 实战中遇到的典型问题与排查技巧
4.1 规则太长,AI容易“读完就忘”
项目里的规则越加越多之后,我很快遇到了新问题:AI偶尔会把前面的规则忽略掉,尤其是在处理复杂任务时,上下文被大量代码块占满,规则文件里的内容会退到一个比较“弱”的位置。排查后我做了两件事。
第一,把规则拆成“核心强制规则”和“附录参考信息”两部分,核心规则保持在20条以内,附录里的内容不强制每次加载。第二,在用户任务里补充一句“请先阅读项目根目录 AGENTS.md 中的AI编码规则,再开始分析”,相当于给AI一个明确的检索指令,让它在当前会话里主动读取规则文件。这个办法对长会话的帮助很大,尤其是当任务比较复杂、AI需要多轮对话时,主动提醒它回读规则能显著减少中途跑偏的情况。
4.2 AI经常“表面遵守规则,细节照旧放飞”
有一段时间,AI确实不再新增依赖了,但它在 dayjs 的用法上还是写得很奇怪,代码风格跟项目原有代码一眼就能看出差别。问题出在规范的颗粒度上:我只写了“用dayjs”,但没有告诉它项目里真正流行的用法是什么。后来我在规范附录里补充了大量“正例对照”。
// 反例(不推荐) const day = dayjs(date).format('YYYY-MM-DD'); // 正例(项目统一写法) const day = formatDate(date, 'YYYY-MM-DD');这种“反例加正例”的写法,比单纯写“使用统一日期工具”有效得多。AI在生成时会把正例作为模板来模仿,而不是把抽象规则“翻译”成自己的理解。现在我把“反例加正例”作为规范写作的固定格式,效果非常明显,尤其是对风格一致性要求高的项目,这招基本是必备的。
4.3 规则之间存在冲突,AI会“卡住”或“选错方向”
规范写多了以后,会出现互相打架的情况。最典型的是“禁止引入新依赖”和“优先使用成熟的第三方库”这两条规则同时存在,某天需求是处理一个PDF导出,项目里没有现成库,AI就不知道该怎么办了,最后随便选了个方向。后来我在规范里加了优先级说明:当规则冲突时,以“不新增依赖”为最高优先级;如果确有必要,必须走“先请示人工”流程。
这个改动很小,但解决了AI决策路径不稳定的问题。现在规范里每条规则旁都标了优先级,强制规则大于参考规则,AI在执行时有了明确的取舍依据。建议大家在写规范的时候,一定要预判规则之间可能的冲突场景,提前把优先级写清楚,否则AI很容易在边界情况里做出让人意外的决定。
4.4 规范多久更新一次比较好
我给团队定的节奏是:每周代码评审结束后花半小时更新一次规范。更新的时候只做两类操作:把AI常犯的新错误写成反例补进去,把已经不再符合现状的旧规则删掉。尽量避免大范围重写,因为规范变动太大会降低AI执行的稳定性,它需要一定的时间去适应新版本。
还没写完的另一个心得是:规范本身也要纳入版本管理,最好由负责AI工具落地的人统一维护并记录变更原因。团队里其他人如果对规则有意见,直接在合并请求里讨论,不要在群里口头说一句就完事,说完了规则没有进文件,下一次AI照样踩坑。规范从“第一次写完”到“真正稳定可用”,中间至少要经过两三周的迭代,这个迭代过程本身就是团队对项目“隐性知识”的一次大梳理。
最后再分享一点个人经验。给AI制定代码规范这件事,真正难的不是写文档、也不是配置工具,而是把团队多年的隐性经验“翻译”成AI听得懂、能执行的话。我前前后后迭代了差不多一个季度,规则文件越来越长,但AI产出的代码却越来越像团队自己人写的,那种不断纠正、不断收敛的过程,是很有成就感的。如果你们团队刚开始做这件事,我的建议是别想着一步到位。先挑最痛的三五条规则写进文件,跑起来,再根据PR和评审里的真实案例慢慢补。规范不是束缚AI的枷锁,它是让AI真正融入项目的一本“团队手册”。