YAOTU INSIGHTS

若依框架AI代码生成不稳定?用Agent Skill三层结构+五步法搞定

若依框架AI代码生成不稳定?用Agent Skill三层结构+五步法搞定
最近一段时间我一直想把后台管理系统里那些机械重复的增删改查代码交给AI去写。框架用的是若依按理说这类业务代码模式非常固定应该是AI最擅长的事情。可真试下来却不是那么回事——你让AI生成一个用户管理模块它给出的Controller方法命名一会儿对一会儿错Mapper XML动不动少一段resultMap前端页面更是经常跟菜单配置对不上。反复调提示词折腾了好几个版本效果始终不稳定。后来我换了个思路不再把需求当作一次性对话丢给AI而是把“写若依代码”这件事整体打包成一个Agent Skill。配合我自己总结的黄金三层结构来做内部设计再用一套五步法逐步打磨AI输出的代码质量一下子稳定了很多基本能直接进编译环节。这篇文章就把这套方法完整拆开讲包括三层结构里每一层该放什么内容、五步法落地时的具体操作以及我在实测过程中反复踩过的坑。1. 直接让AI写若依代码为什么总是差一口气先聊聊为什么原来的方式不work。很多人跟我一开始一样把建表语句贴在对话框里然后写一大段“帮我生成若依的增删改查”AI确实能给你吐出一堆代码看着结构挺完整可用起来就露馅了。1.1 一次性提示词的三个死穴第一个问题是规则没有显式化。若依框架看着简单里面的隐含约定非常多Service接口要带I前缀Mapper接口和XML的namespace必须全限定名对应Controller继承BaseController之后返回AjaxResult还是TableDataInfo有严格区分。这些规则分散在AI的“印象”里而不是你的提示词里它就很容易凭感觉输出。第二个问题是流程不可控。生成代码本身是有顺序的先分析表结构再做类型映射然后生成实体类再生成Mapper、Service、Controller最后才是前端页面和SQL脚本。如果你不把顺序定死AI经常东一榔头西一棒子生成到一半还会自己“优化”掉某些步骤。第三个问题是经验无法沉淀。就算你一次性调出了好结果下次换一张表、换一个对话一切重新碰运气。你在调试过程中积累的那点修正经验全留在聊天记录里没变成可复用的资产。1.2 把“问AI要答案”变成“让AI调技能”对比一下直接对话和Skill方式的差异我用一张表来说明对比项直接对话方式Agent Skill方式输出稳定性换一次上下文就飘规范固定在Skill内部稳定输出若依规则掌握时好时坏靠运气规则显式写入规范层强制执行复用成本每次重新描述需求一次构建长期复用质量保障凭输出后人工检查Skill自带自查清单生成时同步检查扩展能力换个框架或风格等于重来维护多个Skill即可应对不同场景Skill的本质是把一套“怎么做好某件事”的经验结构化让AI在调用时严格按照这套结构来执行。它不是一段简单的提示词而是包含触发条件、执行流程、规范约束、示例样例的完整“技能包”。所以我现在不跟AI说“帮我写个订单模块”而是把一个写好的单表CRUD生成Skill挂载好然后把表结构丢给它。2. 黄金三层结构拆解一个若依生成Skill的内部构造我总结的三层结构分别解决三个问题什么时候调用这个技能、按什么步调做事、用什么风格产出。这三层缺一不可我管它们叫技能描述层、流程编排层、规范与样例层。2.1 第一层技能描述层这一层说白了就是Skill的“门面”。它要让AI和人都能一眼看懂这个技能管什么、不管什么、需要什么输入、产出什么结果。我一般用类似下面的内容填充这一层name: ruoyi-single-table-crud description: 根据数据库建表语句生成若依框架下单表CRUD全套代码。 适用于业务模块的后台管理功能输出后端分层的Java代码、前端Vue页面和菜单SQL。 不适用于复杂业务逻辑、流程审批、主子表结构。 input: table_name: 数据库表名例如 biz_order table_comment: 表业务注释例如 订单信息表 columns: - field: 字段英文名 type: 数据库字段类型 comment: 字段注释 required: 是否必填 optional: author: 代码作者注释默认 某开发者 output: - BizOrder.java 实体类 - BizOrderMapper.java 与 BizOrderMapper.xml - IBizOrderService.java 与 BizOrderServiceImpl.java - BizOrderController.java - index.vue 与 form.vue 页面文件 - menu.sql 菜单初始化SQL你别小看这一层。AI拿到你的Skill时首先读的就是这段描述。如果描述里没有明确“只处理单表”遇到主子表结构它就会不管三七二十一硬上最后生成一堆没法看的代码。边界写得越清楚AI越不会越界操作。2.2 第二层流程编排层第二层负责把生成过程变成一步步可执行的流水线。生成Java代码这件事最怕AI跳步骤。比如实体类还没定好它就先写了Mapper XML结果字段一对不上。我的流程编排层长这样解析建表语句提取表名、表注释、所有字段的名称、类型、注释与主键。将数据库字段类型映射为Java类型并确定实体属性命名。按顺序生成实体类、Mapper接口、Mapper XML、Service接口、Service实现类、Controller。根据后端字段信息生成前端Vue页面包括搜索条件、表格列、表单验证规则。生成菜单权限SQL菜单地址与Controller的RequestMapping保持一致。对照规范层执行自查输出完整的生成清单。每一步之间是强依赖关系。AI在这一层的引导下不会突然去研究时间复杂度也不会自作主张加一个跟若依毫无关系的工具类。整个过程就像工厂里的流水线每个工位做固定的事。2.3 第三层规范与样例层第三层存放的是“若依风格”本身。AI生成代码像不像若依就看你这一层喂了多少规则和样例。规则部分要细到字段级别。我整理了一套最基础的映射规范放在Skill里数据库类型Java类型若依风格约定varchar / charString较长字段增加Size长度校验int / integerInteger直接映射即可bigintLong主键字段加TableId(type IdType.AUTO)decimal / numericBigDecimal涉及金额时保留两位小数datetime / timestampDate前端默认用日期时间选择器text / longtextStringMapper XML中用#{remark}正常取值除了映射还需要放若干段完整样例代码。样例不用多一个典型的单表模块彻底吃透就够了。我会在规范层放一段标准的Controller代码让AI在生成时严格对齐RestController RequestMapping(/business/order) public class BizOrderController extends BaseController { Autowired private IBizOrderService bizOrderService; GetMapping(/list) public TableDataInfo list(BizOrder bizOrder) { startPage(); ListBizOrder list bizOrderService.selectBizOrderList(bizOrder); return getDataTable(list); } }样例的作用是给AI一个“锚”。纯文字规则它可能理解偏差但给它一段地地道道的若依风格代码它就能从中提取出返回类型、分页写法、注解风格这些信息生成的东西自然靠谱得多。3. 五步法落地从零构建可复用的若依代码生成Skill有了三层结构做骨架具体怎么把Skill造出来我总结了一套五步法每一步都有明确的交付物。3.1 第一步把输入边界和输出约定写成文档动手写Skill之前先拿一张纸把“这个技能做什么、不做什么”写清楚。我最初的版本野心太大想兼顾单表、树表、主子表结果哪个都做不精。后来砍到只管单表CRUD效果一下就上来了。这一步的交付物是一份边界文档核心内容包括支持的输入格式只接受建表语句或字段清单不接受模糊描述。支持的输出范围后端五层代码、前端两个Vue文件、菜单SQL。明确不做的事不生成业务校验之外的复杂规则、不处理主子表分页查询。默认约定包名、模块名、作者名等参数未提供时使用预设值。边界定得越死Skill之后维护起来越轻松。AI一旦接收了清晰的约束就不会把精力浪费在那些不该它操心的方向上。3.2 第二步把若依生成器的规则反向翻译成文本这一步是核心中的核心。若依框架自带代码生成器里面已经沉淀了一套完整的代码模板逻辑我们做的就是把那套逻辑“反向工程”成文字规则。我对照生成器的模板思路整理出一套规则草案内容包括类名生成规则表名去掉下划线并转大驼峰比如 biz_order 变为 BizOrdersys_user 变为 SysUser。分层类命名实体类直接用BizOrderMapper接口叫BizOrderMapperService接口叫IBizOrderService实现类叫BizOrderServiceImpl。XML命名空间必须是Mapper接口的全限定名写错一个字符整个项目启动就会报绑定错误。Controller路由RequestMapping用小写复数形式例如 /business/order与菜单地址保持一致。这些规则看起来琐碎但恰恰是AI最容易出错的地方。把规则文本写进Skill的规范层之后AI相当于有了一本“操作手册”不用每次都在脑海里猜若依是怎么约定的。3.3 第三步搭一个只覆盖单表CRUD的最小原型不要一上来就求全先把最小可用版本搭起来。我会挑选一张最简单的表比如一张只有主键、名称、状态、备注的字典类型表然后手动生成一遍完整的若依代码把这段代码当作标准答案。接着把三层结构按最小原型填写进去描述层写清楚输入模板流程编排层写六步流水线规范层放入映射表和几个关键样例。第一版Skill不需要考虑太多异常情况能用起来就行。我建议在这一步把所有内容放到同一个Skill配置文件里不要拆散。你可以用Markdown、YAML或者JSON都行重点是让AI在一个上下文里同时读到三层内容。拆太散的话AI阅读时容易丢失关键信息。3.4 第四步准备自测集正反例都要有Skill写完了不能拿同一张表测那样测不出问题。我准备了三张结构差异很大的表一张纯单表字段基本只有字符串和数字用于验证基础CRUD。一张包含BigDecimal金额、Date时间、长文本备注的表用来验证字段映射。一张字段很多、注释不全的“坏表”用来验证Skill在输入信息缺失时的表现。正例用来确认功能正常反例用来确认边界约束生效。比如对字段注释不全的表Skill应该明确提示补齐信息而不是自己脑补一堆含义。自测的方式很简单把这些表结构依次丢给挂了Skill的AI看它产出的代码能不能通过IDE编译能不能对应上若依的规范。第一版跑下来通常会发现不少问题比如类型映射漏了BigDecimal或者前端页面缺失时间日期选择器。这些问题会直接反馈到第三步的规则补充里。3.5 第五步用真实业务表做回归验收最小原型通过了就要拉真实业务表来验收。我个人偏好拿项目里新开发的业务模块来测试因为真实表的复杂程度远高于手工造的测试样例。验收标准我定得很硬性生成的后端代码在IDE中零错误编译通过。Mapper XML与Mapper接口全部正确绑定。前端Vue页面打开后能正常渲染查询、新增、编辑、删除功能可操作。菜单SQL执行后能正常挂载到角色权限。就这样跑十几个真实表之后规则库会越来越厚Skill的稳定度才会真正上来。我目前的规则库里已经有二十多条补充约定都是靠这一步慢慢积累的。4. 实战记录一个订单表从建表语句到全套代码光讲原理有点虚我拿一张实际开发中常见的订单表走一遍完整的生成过程。4.1 输入一张订单表的建表语句我准备的输入如下CREATE TABLE biz_order ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 订单ID, order_no VARCHAR(64) NOT NULL COMMENT 订单编号, customer_name VARCHAR(64) DEFAULT NULL COMMENT 客户名称, total_amount DECIMAL(10,2) DEFAULT NULL COMMENT 订单总金额, status VARCHAR(16) DEFAULT NULL COMMENT 订单状态, create_by VARCHAR(64) DEFAULT NULL COMMENT 创建者, create_time DATETIME DEFAULT NULL COMMENT 创建时间, update_by VARCHAR(64) DEFAULT NULL COMMENT 更新者, update_time DATETIME DEFAULT NULL COMMENT 更新时间, remark VARCHAR(500) DEFAULT NULL COMMENT 备注, PRIMARY KEY (id) ) COMMENT订单信息表;把这段SQL丢给AI同时点名“用挂载的ruoyi-single-table-crud技能处理”AI就会自动进入技能流程。4.2 Skill内部的执行流程是怎样的AI根据第二层的流程编排逐步处理第一步解析表结构明确表名biz_order、注释“订单信息表”、主键id、10个业务字段。第二步做类型映射其中total_amount映射BigDecimalcreate_time映射Date其余字符映射Stringid从bigint映射为Long并标注主键。第三步生成实体类带好TableName注解TableName(biz_order) public class BizOrder extends BaseEntity { private static final long serialVersionUID 1L; TableId(type IdType.AUTO) private Long id; private String orderNo; private String customerName; private BigDecimal totalAmount; private String status; private String remark; }因为createBy、createTime、updateBy、updateTime这几个字段和若依BaseEntity里自带的字段重名了Skill在规范层里有约定直接交由父类维护所以实体类里不再重复定义。这一步很关键很多第一次尝鲜的人在这里就翻车了要么重复定义字段要么漏了继承BaseEntity。之后AI继续按流水线生成Mapper接口、Mapper XML、Service、Service实现和Controller再根据字段信息生成前端页面和菜单SQL。4.3 生成结果长什么样质量怎么查生成的Controller代码风格对齐了若依规范RestController RequestMapping(/business/order) public class BizOrderController extends BaseController { Autowired private IBizOrderService bizOrderService; GetMapping(/list) public TableDataInfo list(BizOrder bizOrder) { startPage(); ListBizOrder list bizOrderService.selectBizOrderList(bizOrder); return getDataTable(list); } PostMapping public AjaxResult add(RequestBody BizOrder bizOrder) { return toAjax(bizOrderService.insertBizOrder(bizOrder)); } }生成完之后我会按一张固定的自查表逐项过检查项判定标准实体注解TableName与TableId齐全继承关系正确Mapper XMLnamespace匹配接口全限定名字段映射完整Service层接口带I前缀实现类注入Mapper并加ServiceController统一返回AjaxResult或TableDataInfo分页用startPage前端页面搜索区、表格区、分页区齐全字段与后端完全对应菜单SQL菜单路径与Controller的RequestMapping保持一致只要这一层检查通过我才会把代码合进工程。不是不信AI而是生成逻辑本身无法保证100%覆盖所有场景校验环节绝不能省。5. 反复踩过的坑给正在调Skill的人提个醒方案很顺但落地过程中坑也不少。我挑几个印象最深的说说免得你再踩一遍。5.1 表信息给半截AI就自动“补脑”我一开始偷懒传建表语句时漏了几列注释想着AI这么智能应该能猜出来。结果它真开始猜了把amount字段脑补成“余额”把status脑补成“启用禁用枚举”生成的前端页面对不上原始业务含义返工成本相当高。对策就是输入模板严格化。描述层明确写上“必须提供完整字段清单包括字段名、类型、注释、是否必填缺失信息时先向用户索要不得自行假设”。这一步能拦住大多数“自由发挥”。5.2 模板样例和项目版本漂移了Skill里存的样例代码跟实际项目的若依版本是强绑定的。我最初用一套老版本样例里面Controller还在用旧的返回结构而项目已经切换成新风格了生成的代码自然不合拍。解决办法是在规范层顶部标注版本信息比如“适配若依前后端分离版本V3.x系列”并且每次升级框架后同步更新样例代码。版本漂移这个问题很隐蔽建议在自测集的验收标准里加入“对照当前项目源码风格”这一项。5.3 生成不等于编译通过AI生成的代码不能当作文档看。我第一次测试时生成结果看着非常顺眼一编译直接红了原因是Mapper XML里某个字段类型写成了java.lang.String跟实际映射不一致。所以我现在的流程里固定加一步生成完代码后立即放到Maven工程里编译验证不通过就回去改Skill规则。编译本身就是最好的校验器它能把99%的隐藏问题暴露出来。5.4 多张表共用一个会话上下文串了在同一个对话里连续生成多张表的代码Skill容易出现上下文污染生成订单表时前面客户表的字段名偶尔会混进来特别是字段名相似的场景。我把这个问题想明白了之后就在流程编排层加了一条硬性规定每次生成新表前必须重新完整解析并复述本次的建表语句确认表名与字段与上一次无关。同时建议每张表单独开一个会话彻底切断干扰源需要对比时再手动汇总。个人现在实际操作中的感受是这套三层加五步的打法真正帮我把AI从“偶尔给个惊喜”变成了“稳定给个及格”而及格才是工程化的起点。我最近还在做第二个Skill目标是让AI根据需求文档自动把表结构设计出来再顺手接上这个若依生成技能两条流水线串起来开发效率又能往上走一截。如果你也在折腾类似的事情强烈建议从一张最简单的表开始把三层结构填满再用五步法慢慢磨别急着上来就搞全家桶。