ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

AI代码规范实战:用AGENTS.md约束大模型编程

AI代码规范实战:用AGENTS.md约束大模型编程 1. 为什么项目中需要一份“给AI的代码规范”1.1 真实场景AI进项目组之后发生了什么我最初也以为团队原本就有一套《代码规约》叠加上团队Code Review的习惯AI顶多算个“打字很快的初级工程师”翻不出什么浪花。但实际跑了两个月之后我发现问题恰恰出在“AI太听话”上。举一个最典型的例子我们后端的错误处理原先约定是“对外抛业务异常由全局异常处理器统一转换”。人写代码时会先翻一下项目里现成的Handler抽一个合适的异常类型往外抛。而AI生成的代码十次里有八次会在Service层自己写try-catch然后在catch里直接返回一个Map或自定义Result——它会主动帮你把“流程走完”而这种写法与我们现有架构是冲突的。但AI不知道或者说你知道AI不知道。更隐蔽的问题是风格漂移。AI不会像人一样在写代码时保持统一的“手感”同样的一个分页查询它这次用MyBatis-Plus的Page下次又用PageHelper第三次干脆在内存里手动subList。单看每次都像模像样合到一起就是技术债。最让人头疼的是AI生成完一段代码会在周边留下大量“我觉得可能以后会用到”的死代码、无用import、没接线的配置类。人的代码滥用会被人怼回去AI的代码滥用你得在Code Review时一行一行地跟它掰扯。所以当“项目中新增给AI制定的代码规范”这个任务摆到桌面上的时候我的第一反应是这不是把已有的规范复制一份丢给AI而是需要重新定义一套“AI也能读懂、愿意遵守、且可自动检查”的约束体系。这套规范不是写给人的是写给AI的更准确地说是写给“用AI写代码的人”和“审核AI产出的人”看的。1.2 通用代码规范与AI代码规范的核心差异想做好这件事得先搞清楚一个本质区别人读规范读的是“意图”AI读规范读的是“关键词”。人类工程师看到“请遵循项目现有的错误处理约定”会自动去搜索项目中ErrorHandler相关代码然后模仿类似写法。AI没有这个“常识推理”能力它对上下文的依赖极强如果你不把“现有约定”的具体路径、类名、示例代码贴给它它就会按训练数据里的最常见模式来写而训练数据里最常见的模式往往是“通用但不符合你们项目”的。再有一点人的代码规范可以写“酌情处理”“视情况而定”这种模糊表述到了AI那里几乎是无效指令。现在主流AI编码工具都依赖大模型进行文本生成大模型天然倾向“生成概率最高的Token”而不是“最符合团队约定俗成的Token”。所以给AI的规范每一条都应该尽量做到可以是、否、必须、禁止附上正例和反例最好再给出检查命令。另外还有一个常常被忽略的差异人对规范的执行是“主动的”是自己约束自己AI对规范的执行是“被动的”只在提示词或系统规则明确提到时才生效。换句话说通用规范是贴在墙上的标语AI规范是写进prompt里的操作系统级配置。理解这一点后面所有设计才顺理成章。2. AI代码规范应该管什么一份能落地的框架2.1 架构与模块约束先把边界画清楚这是AI代码规范里最优先、也最管用的一部分。AI不会像人一样在动工前先花十分钟理解模块边界它只会看当前对话中涉及的几个文件然后按“最快能编译通过”的方式把功能塞进去。结果就是本该写在Infrastructure层的Http调用出现在Domain层本该通过接口注入的依赖被new在某个工具类里本该走MQ解耦的逻辑被同步Feign一把梭。所以架构约束层我建议按这个清单来立规矩明确项目的分层结构如Controller/Service/Repository并告诉AI“新代码只能出现在对应层禁止从View层直接操作Repository”。明确跨模块依赖的方向例如“Domain层不依赖任何框架注解”“Infrastructure层可以依赖所有层但其他层禁止反向依赖”。明确依赖注入方式例如“必须使用构造器注入禁止Autowired字段注入也禁止在方法内部new核心Service对象”。明确对外接口的兼容性要求例如“修改对外开放的API时必须保留旧方法新增参数用重载禁止直接改方法签名”。这些约束如果只写成一句“请遵循项目架构”AI大概率还是看不懂。要让AI遵守最好给它看一眼实际的目录结构再给几个“允许做什么、禁止做什么”的对比示例。比如# 错误做法 public class OrderService { Autowired private OrderRepository orderRepository; } # 正确做法 public class OrderService { private final OrderRepository orderRepository; public OrderService(OrderRepository orderRepository) { this.orderRepository orderRepository; } }别小看这种对比示例AI对示例的模仿能力远强于对抽象指令的理解能力。我们是这么做的把规范文件里的每一条架构约束都拆成“约束描述 错误示例 正确示例 检查方式”后续实测效果很明显架构违规率直接降了一大截。2.2 实现与风格约定让AI的“审美”对齐团队这块最容易被误解很多人以为给AI定规范就是让它统一缩进、统一引号、统一分号有没有。这些格式化层面的东西其实交给ESLint、Prettier、Checkstyle这类工具去自动修复就够了没必要浪费AI的上下文窗口。真正需要写进AI规范里的实现级约束是那些“工具检查不出来、但对可维护性影响巨大”的东西。我按踩坑频率排个序给各位参考过度设计约束。AI特别容易把简单问题复杂化比如一个只用一次的转换逻辑它能给你建一个Factory加三个Strategy再加一个抽象接口。规范里要写死“如无抽象扩展需求禁止引入设计模式优先用平铺直叙的代码完成功能”。重复代码约束。人在写代码时重复了三段以上自己会烦AI不会你让它生成十个相似接口的实现它就能生成十份几乎一样的代码。规范里要写“当你在一个文件中需要写第二段相似逻辑时必须停下来思考能否抽取公共方法并把这个判断告诉使用者”。错误处理约束。这是AI翻车重灾区。规范里明确要求“禁止空catch块catch后必须记录日志并抛业务异常或返回统一错误体禁止用print/console.log做日志统一走Logger”。魔法值约束。AI生成代码时很喜欢写if (status 1)这种不会自动引用状态枚举。规范里可以强制要求“业务状态、类型等固定取值必须定义为枚举或常量禁止裸数字、裸字符串”。注释约束。AI生成注释有两个极端一种是完全不给注释另一种是每一行都写满“// 获取用户信息”这种正确的废话。规范里应该要求“只在核心业务逻辑、复杂算法、解释设计原因处写注释禁止为一行自解释代码加注释”。风格约定是“审美”问题不像架构约束能二分对错但如果完全放任代码库很快会变成AI风格的“混搭秀”。我的建议是把风格约束浓缩成10条以内可自动或半自动检查的硬规则并且定期让AI按新规范“自检”存量代码逐步收敛。2.3 提交与协作规范把生成痕迹变成管理抓手项目里新增AI代码规范如果只管“代码怎么写”而不管“代码怎么提交”会导致一个尴尬局面你根本分不清哪段代码是人写的、哪段是AI写的出了问题也没法复盘优化prompt。所以在规范里我专门加了一节“AI辅助开发提交约定”包含这几条AI生成或大幅修改的代码提交信息Commit Message需要加前缀[AI]例如[AI] 重构订单查询逻辑。这么做不是为了歧视AI而是后期想统计AI代码占比、回溯AI导致的问题时能直接grep出来。提交时要检查是否包含调试代码、临时文件、硬编码密钥特别是AI有时候会把测试环境IP或账号密码一起“礼貌地”写进来。单次提交尽量保持单一职责禁止一个提交里混杂“AI重命名变量 新增功能 修复旧Bug”这种三合一。AI生成的新文件提交前人工必须过一遍文件末尾和注释区AI经常会把对话过程中聊到的无关示例代码一并塞进文件。这一条说实话一开始团队里有人觉得“多此一举”。后来有一次线上故障排查半天发现某个缓存key的过期时间莫名其妙从30分钟变成了1分钟源头就是一个AI添加的“顺手优化”而且提交信息写的是“fix typo”。从那以后大家都老老实实打[AI]标记了。3. 如何把AI代码规范真正落地从文件到工作流3.1 用AGENTS.md把规则送进AI的上下文规范文档写好了怎么让它真正作用到AI的行为上这是整个项目里最关键的“最后一公里”。目前主流AI编程工具的规则加载机制各不相同但有一个趋势是通用的项目根目录下的AGENTS.md文件已经成为事实标准很多编码AI会在会话启动时自动读取它作为项目级指令。以我们项目为例AGENTS.md的结构大概是这样的# 项目AI协作规范 ## 技术栈 - Java 17Spring Boot 3.xMyBatis-Plus - 前端 Vue 3 Element Plus ## 架构红线 - 禁止在Controller中直接写业务逻辑 - 禁止在Domain层引用Spring注解 - 新代码只能新增Controller/Service/Repository/Model四类文件 ## 编码约定 - 依赖注入方式构造器注入禁止字段注入 - 错误处理禁止空catch禁止System.out.println - 枚举状态禁止裸字符串比较 ## 提交要求 - AI生成代码请在commit message中以[AI]开头 - 禁止提交.only调试文件、本地配置、密钥文件注意这个文件里每条规则都尽量是“非黑即白”没有“酌情”。同时我会在关键位置贴上正反例比如构造器注入的对错示例直接摆在规则下面。把规则放进AGENTS.md的原因是我发现如果你只在口头或群里说“AI生成的代码注意一下规范”AI是听不见的但如果你把规则写进项目根目录的这个固定文件主流编码工具会在每次会话建立时自动加载。这等于给AI戴上了一副“项目专用眼镜”它看到的上下文一开始就带着你的约束。3.2 提示词与规则文件的分工光有AGENTS.md还不够实际使用中还要区分哪些规则放进规范文件、哪些规则放进对话提示词。我的做法是规范文件放长期稳定、对项目整体都适用的硬性约束比如架构分层、依赖注入、提交格式。这类规则最好一年半年都不动。对话提示词放临时性、针对当前任务的要求。比如“本次只重构OrderService不要改动任何Mapper接口”“这个功能不要使用Stream用传统for循环if判断方便新同事阅读”。任务是临时的约束也是临时的。举个例子有一次我们要在存量老项目里加一个接口那个项目代码风格比较原始几乎全是过程式写法。如果按新项目的规范强行让AI生成一套“标准三层架构”反而会显得格格不入。这种情况下我在提示词里临时加了一句“请严格按照本文件现有Controller的写法新增接口保持风格一致不做架构调整”。AI遵守得很好。换句话说规范文件管“全局稳定”提示词管“局部变化”两者配合而不是混用AI才不会精神分裂。3.3 用自动化检查卡住底线规范写得再好如果只靠人肉Review迟早会破功。我的经验是AI代码规范落地一定要做“自动化闭环”至少在三道关卡上建立检查第一道是提交前本机检查。在开发者的Git钩子pre-commit里挂上格式检查和基础lint比如Java项目的Checkstyle、前端的ESLint。这一层解决的是缩进、命名、未使用变量这类低级问题AI生成代码基本能过。第二道是CI流水线检查。在分支合并到主干前跑一遍完整的静态扫描、单元测试、架构约束检查比如ArchUnit把AI最容易犯的“跨层依赖”“循环依赖”“过度设计”问题在后置阶段拦截下来。第三道是PR描述自动生成检查。现在很多AI编码工具能自动生成PR描述但描述里经常出现“优化了一些问题”“修复了bug”这种空话。我们会在规范里额外要求“PR描述必须包含变更原因、影响范围、测试情况”并在PR模板里写死这几个栏位。别小看这一步它倒逼AI在写代码时就想清楚自己改了什么、为什么改。自动化闭环的好处是规范不再是一份“看了就忘的文档”而是变成了代码库的一个“活”的约束系统。当规则被打破时会有人其实是机器人第一时间指出来下一次AI生成代码时就会小心很多。4. 执行记录与排查实录AI为什么总“不听话”4.1 上下文窗口被截断规则被“挤”出去在实际推行AI代码规范的过程里我发现最无语的问题不是AI不懂规则而是它在生成长代码时会“忘记”规则。原因用大白话说就是大模型的上下文窗口是有限的你塞给它的项目文件越多最初加载进来的AGENTS.md规范就越容易被“挤”出有效注意力范围。有一次我让AI重构一个涉及十几个文件的旧模块它刚开始几步都严格遵守了规范可改到第六七个文件时突然开始用字段注入还冒出了ESLint从没出现过的魔法数字。我看了一眼对话内容发现前面已经贴了大量旧代码而AGENTS.md的规则早被淹没在滔滔不绝的上下文里了。我的解决办法是把规范里最核心的三四条“红线”在每次对话的提示词里再重述一遍。比如每次会话固定开头加上请严格遵守以下项目红线 1. 构造器注入禁止字段注入 2. 业务代码禁止出现在Controller 3. 提交勿带调试代码 其余约束请查阅项目根目录AGENTS.md。亲测有效。核心约束重复三遍好过全面规则只出现一次。这个做法听起来有点笨但对于“AI会选择性失忆”这个特性来说是最靠谱的提醒方式。4.2 规则写得太“像人话”AI根本没法执行还有一次我在规范里写了一句话“请复用项目已有工具类避免重复造轮子。”结果AI连续三次提交的代码里都自己写了一套字符串格式化逻辑。后来我仔细复盘才发现问题出在“复用项目已有工具类”这句话太抽象了AI并不知道项目里有哪些工具类、在哪里、方法名叫什么。从那以后我所有的规范条目都强制要求“带路标”。不能只说“复用已有工具类”要说“项目公共转换工具在com.xxx.common.util.ConvertUtil包含toJson、toBean方法涉及JSON转换时请直接调用不要新增工具方法”。同理“统一异常处理”要改成“全局异常处理器位于com.xxx.common.exception.GlobalExceptionHandler业务异常类为BizException无法处理的异常请直接抛出BizException并附错误码”。AI吃“具体线索”不吃“抽象口号”。每一次规范条目修改我都会问自己一句如果是一个刚入职、对公司代码库一无所知的实习生他能看着这条规范找到正确做法吗如果连实习生都找不到那AI也找不到。4.3 规范与现实代码冲突时的“倒挂”推行规范后还遇到一类很有意思的问题因为存量代码本身就“不符合规范”AI在参照存量代码风格时会不知不觉把“旧风格”当成“新规范”。比如我们规范里要求“所有状态判断必须用枚举”但旧代码里有大量if (order.getStatus() 1)的写法。AI在改到旧代码附近时大概率会跟着旧代码的写法走因为它复制的上下文里充满了反例。这种时候光靠规范文件就不够了我会临时在任务提示词里追加一句“当前文件中出现的裸状态判断属于历史遗留问题请在本轮改动中一并替换为OrderStatus枚举不要模仿旧代码风格”。另外还有一种“倒挂”是工具层面的AI编码工具的自动补全比如Copilot是根据当前文件上下文推测代码的项目的存量代码风格会直接影响它的输出。这其实是存量技术债的又一次“复利”没办法一蹴而就。我的做法是在每个迭代里划出固定时间做“AI规范存量整改”把高频文件里最刺眼的几类问题先手动修正让AI身边的环境先干净起来后面它生成的代码也会跟着干净。做这份给AI的代码规范到现在我个人最深的体会是与其说这份规范是在约束AI不如说是在倒逼团队把“潜规则”显性化。以前很多约定都靠老人带新人、靠代码Review时口口相传现在为了让AI能理解我们不得不把架构边界、编码偏好、错误处理习惯一条条白纸黑字写清楚。这个过程一开始有点麻烦但执行一个季度后团队里新同学上手的效率明显快了Code Review的争论也少了很多。如果你们项目里也在大量使用AI编程工具我建议别等出问题再补规范从第一天就把这套“AI协作契约”立起来。规范不用一步到位先覆盖架构、依赖注入、提交标记这几条最痛的跑一个迭代再慢慢加。坚持下来你会看到AI从一个“有时候靠谱有时候闯祸的实习生”慢慢变成了一个“稳定守规矩的熟练工”。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进