别让 AI 乱写代码:用规则文件给编码助手立规矩

📅 2026/7/11 13:28:08
别让 AI 乱写代码:用规则文件给编码助手立规矩
AI 编码助手确实能大幅提速但用过一段时间的人都会发现一个共同的烦恼它很聪明但记不住规矩。同一个助手这次话聊得好好的代码质量不错换个话题、换个会话它可能又用了一套完全不同的写法。团队协作时这种随机性会被放大成实实在在的返工成本。这篇文章想聊清楚一件事怎么把团队踩过的坑、达成的约定变成 AI 每次都会主动遵守的规矩而不是每次现场口头提醒。一、先说痛点AI 编码助手最容易让人抓狂的四个场景1. 风格漂移同一个助手今天写代码习惯用camelCase的 getter 命名明天可能换成一套更啰嗦的命名方式这次生成的 Controller 直接返回了 Entity下次又老老实实做了 VO 转换。没有约束的话AI 的输出风格会随着上下文、提示词的微小变化而漂移团队代码库越用越像拼贴画。2. 隐性约定丢失每个稍微上了年头的项目里都有一堆只有老员工才知道的规矩比如Controller 层不能直接查数据库必须走 Service或者某个金额字段传输前必须做精度处理。这些约定从来没有写进任何文档AI 自然也无从知晓——于是它一本正经地写出了一段技术上能跑但踩了隐性红线的代码等到评审或者上线才暴露问题。3. 红线被无视更让人心惊的场景是AI 完成一次代码修改后贴心地自动执行了git commit git push或者自己顺手跑了一遍测试、甚至改了几条数据库数据来验证效果。这些操作单独看都不算错但如果没有事先获得确认很容易在没人注意的时候造成不可逆的后果。4. 重复交学费如果你观察过团队里不同人跟 AI 的对话会发现大家都在提示词里反复重复同样的话“记得用我们项目的这种写法”“别忘了这里要加权限校验”。这些经验从来没有被沉淀下来每个人都在为同样的坑重新交一次学费AI 换一个会话就把这些叮嘱忘得一干二净。二、解决方案把约定写成 AI 会自动读的规则文件核心思路并不复杂**不要指望在每次对话里现场教育 AI而是把团队的约定写成文件让 AI 在每次会话开始时就自动加载它们。**主流 AI 编码工具基本都支持这类机制只是各家读取的文件名和位置并不统一——具体怎么在多个工具之间协调第三部分的教程会展开讲。这里先抓住四个通用的设计原则跟用什么工具无关。1. 分层加载红线全局生效细则按场景触发规则不是越多越好也不是一个文件囊括所有。比较实用的做法是分两层全局强制规则不管在改什么文件、聊什么话题都必须遵守典型的就是安全红线比如不能未经确认执行破坏性操作场景触发规则只在特定场景下才需要加载比如只有改动数据库相关代码时才提醒金额字段必须用什么类型、精度怎么处理这样既保证红线不会被稀释又避免所有规则堆在一起互相干扰、上下文过载。这个分层思路不只体现在规则内容上规则文件本身也要分层——单个文件不是越写越长就越好。一份跨工具主规则文件超过 150200 行就该考虑按模块拆成多份根目录留通用约定具体模块的细节下沉到对应子目录里教程第 1 步会给出具体做法。2. 可执行而不是写百科很多人写规则文件的第一反应是把项目规范文档搬过来——这恰恰是最容易失败的做法。规范文档是给人看的规则文件是给 AI 用的操作指令两者的写法完全不同。一条好的规则应该包含反例和正例的直接对比而不是抽象的原则陈述。比如与其写请遵守良好的命名规范AI 根本不知道良好具体指什么不如直接给一段错误写法和一段正确写法让它照着模仿。3. 反馈闭环一次踩坑一条新规则规则文件不是一次性写完就完事的静态文档。理想的状态是每当评审中发现一次 AI 写的代码有问题或者线上出了一次因为 AI “自作主张导致的小事故就应该把这次教训直接转化成一条新规则追加进去。这样规则库会随着团队实际踩坑的经验持续变厚而不是停留在上线那天写的几条”。4. 统一入口物理收敛别让闭环闭到了不同的地方这一点容易被忽略但恰恰是团队用了不止一种 AI 工具之后最先暴露的问题反馈闭环说踩坑就补规则但补到哪去同样重要。你在 Claude Code 里说记住这个它默认写进CLAUDE.md你在 Cursor 里说把这个变成规则它默认建一个新的.mdc文件。用得越久同一条约定就越容易在几份文件里各长出一个不完全一致的版本最后没人知道该信哪份。靠大家记得手动同步是不可持续的得从结构上把写的落点收敛成一处让工具专属文件本身不承载独立内容而是指向共享文件的引用CLAUDE.md用AGENTS.md导入并在每份入口文件顶部写一条明确的落点元规则告诉 AI 新的通用规则该往哪写。教程第 6 步会给出具体做法。三、动手教程给一个虚构项目 acme-order-service 立规矩光讲原理有点空我们用一个虚构的通用 Java 微服务项目acme-order-service一个订单服务走一遍完整流程从零建立规则、验证生效到把一次踩坑经验转化成新规则。步骤 1建立规则文件这里要先纠正一个容易踩的坑不同 AI 编码工具读取规则的文件名和目录并不统一随手建一个自己起名的目录只有你自己在用的那个工具会读换个同事用别的工具规则直接形同虚设。目前几种主流工具的实际情况是工具实际读取的文件说明多数工具原生或兼容项目根目录的AGENTS.md目前最接近通用标准的一份很多工具都会自动读取Cursor.cursor/rules/目录下的.mdc文件Cursor 专属格式支持按文件路径精确触发也兼容读取AGENTS.mdClaude Code根目录的CLAUDE.md优先认自己这份不会自动读AGENTS.md需要手动引用实用的做法不是选一个正确答案而是分层把团队通用的约定写进AGENTS.md作为跨工具的主规则只有确实需要某个工具专属能力比如 Cursor 按文件类型精确触发时才补一份该工具专属的文件并在里面引用AGENTS.md避免同一条规则维护两份。对于acme-order-service目录结构大致是acme-order-service/ ├── AGENTS.md # 跨工具主规则团队里用什么工具都先读这份 ├── CLAUDE.md # 只有一行 AGENTS.md让 Claude Code 复用同一份规则 └── .cursor/ └── rules/ └── safety.mdc # 仅 Cursor 团队成员需要按需精细化补充有一点要提前说清楚不能把AGENTS.md本身做成一个目录——工具查找的是这个精确文件名改成同名目录反而会让工具找不到文件。真正支持的做法是允许在子目录里再放一份AGENTS.md工具会读取离当前编辑文件最近的那一份。等acme-order-service规模变大、比如拆出一个支付模块之后目录会长成这样acme-order-service/ ├── AGENTS.md # 根目录通用约定、命名规范、安全红线 ├── CLAUDE.md ├── .cursor/rules/safety.mdc └── payment/ └── AGENTS.md # 支付模块专属补充只写这个模块特有的东西这里有个坑不同工具处理根目录 子目录都有AGENTS.md的方式并不一致——有些工具会把根目录到当前目录逐级拼接读取子目录那份是追加有些工具只认最近的一份、直接忽略上层文件。所以payment/AGENTS.md里不要重复抄根目录已经写过的规则只写支付模块特有的内容通用红线永远只在根目录维护一份不然又会回到规则被写散、多份内容各自漂移的老问题。步骤 2写第一条规则——Service 层命名规范在AGENTS.md里新增一节内容不写空泛原则直接给对比示例# AGENTS.md ## Service 层命名规范 - Service 接口以业务名 Service 结尾禁止用模糊词如 Stuff、Helper、Manager - 接口方法用具体动词开头create / cancel / query禁止用 handle、process、doXxx 这类模糊动词 - 入参使用具体的 Request/DTO 类型禁止直接传 Map 或多个零散参数 ### 反例 public interface OrderStuff { void doOrder(String id, String type, MapString, Object data); } ### 正例 public interface OrderService { OrderResult createOrder(CreateOrderRequest request); void cancelOrder(Long orderId); }如果团队里有人用 Claude Code只需要在根目录建一个CLAUDE.md内容就一行AGENTS.md这样 Claude Code 每次启动也会把AGENTS.md的内容合并进来不用把规则再抄一份。步骤 3写第二条规则——高危操作红线同样追加到AGENTS.md## Git 操作红线 严禁在没有用户明确同意的情况下执行 git commit、git push 或其他会 修改远程仓库状态的操作。 完成代码修改后 1. 先展示改动内容摘要 2. 等待用户明确说提交或可以了之后才允许执行 commit 3. 任何情况下都不允许自行执行 push这条属于任何场景都必须遵守的红线如果团队主力用 Cursor可以再补一份.cursor/rules/safety.mdc把同样的红线设成始终生效不依赖模型主动判断要不要读取作为双重保险--- alwaysApply: true --- # 高危操作红线同 AGENTS.mdCursor 强制加载版 严禁未经用户明确同意执行 git commit / git push。步骤 4实测验证规则写完不代表就万事大吉一定要验证它真的生效。找一个小改动让 AI 去做比如给订单服务加一个查询订单状态的接口观察两件事它生成的接口和方法命名是否符合刚写的命名规则而不是随手起个OrderUtil.getStatus()改完代码之后它是否老老实实停下来等你确认而不是自己执行了git commit如果发现规则没生效先排除文件放错了工具认的位置这个最常见的坑比如用的是 Claude Code却只写了.cursor/rules/确认文件位置没问题后通常是规则内容还是太抽象、缺少具体的反例正例对比——回到步骤 2、3 把规则写得更具体。如果团队里有人用 Cursor、有人用 Claude Code最好都各自验证一遍别假设一个工具测通过了别的工具也一定生效。步骤 5把一次踩坑经验变成新规则假设过了几天评审时发现 AI 生成的一段代码里把一个可能抛异常的调用套了个空的try-catch异常被原地吞掉业务出错时完全没有日志可查。这就是一次典型的隐性约定——不允许吞异常从来没写下来。这时候不要只是在这次对话里说一句别这样写而是立刻把它变成一条新规则追加到AGENTS.md## 异常处理规范 严禁出现空的 catch 块或吞掉异常不做任何处理的写法。 ### 反例 try { orderClient.notify(order); } catch (Exception e) { // 忽略 } ### 正例 try { orderClient.notify(order); } catch (Exception e) { log.error(订单通知失败, orderId{}, order.getId(), e); throw new OrderNotifyException(订单通知失败, e); }因为写进的是AGENTS.md而不是某个工具专属的文件团队里不管谁在用什么工具这条新规则都能同步生效不需要再手动同步到别的文件。这样下次同类问题出现的概率就会明显降低——规则库会随着真实踩坑经验持续增厚而不是停留在项目刚启动那天写的那几条。步骤 6防止规则被写散——把落点从结构上收敛掉规则库用了几个月之后最容易出的问题不是没有规则而是规则散在好几个地方而且互相不一致。最简单也最该先做的动作是加一条落点元规则写在每份入口文件的最上面 新增的通用业务规则、命名规范、安全红线一律写进 AGENTS.md。 本文件只允许存放该工具专属能力相关的内容不允许新增业务规则。CLAUDE.md和.cursor/rules/下的文件顶部都加上类似的一句相当于给 AI 一个该往哪写的明确指引而不是让它凭默认行为决定。即便加了元规则也建议每隔一段时间人工扫一眼各工具专属文件里有没有混进本该属于AGENTS.md的内容——这算是规则库的技术债清理跟代码重构是一个道理不做的话规则库迟早会积累到没人敢信任的地步。小结规则文件解决的是记忆问题把团队达成的约定、踩过的坑变成 AI 每次会话都会自动带上的隐性上下文而不是靠人一次次现场提醒。它不需要多复杂的工具核心就是四点——分层加载、可执行的反例正例、闭环反馈、把落点从结构上收敛掉。最后这一点在只用单一 AI 工具时不明显但团队用的工具一多往往就是决定规则库能不能长期维护下去的关键。下一篇我们会聊另一个问题光有规则还不够AI 还需要记住这个项目本身的业务逻辑和历史决策这就是知识库自动沉淀要解决的事。