先说实话,我之前对“AI 辅助开发”这事的评价一直很拧巴:写点单元测试、补个注释没问题,但真要让它按团队规范从零生成一个 Spring Cloud 微服务模块,基本靠不住。问一句答一句,问十句上下文就乱了,最后出来的代码还是要大改。直到我认真用了 Trae 国际版的 Agent Skill 功能,把团队里那套“新建微服务”的规矩固化成技能包,情况才真正不一样。现在我在某个电商后端项目里新建支付回调服务,一条指令就能拿到一整套带 Nacos 注册、OpenFeign 调用、统一返回结构、全局异常处理的基础工程,省下的是实实在在的大半天时间。
这篇文章不聊虚的,我会把 Agent Skill 的机制讲清楚,然后完整演示怎么针对 Spring Cloud 场景写一个属于自己的 Skill,包含可以直接抄的 SKILL.md 结构和 Java 代码示例,最后是我在实际项目里踩出来的最佳实践和避坑经验。
1. 先搞清楚:Spring Cloud 场景下,通用 Agent 为什么越帮越忙
1.1 我的真实工作流:一天要建两三个微服务模块
做后端的人应该都有这种体会,尤其是在微服务架构里,“新建一个服务”这件事的重复度极高。
就拿我们某个模拟电商中台项目来说,每上一个新业务域——订单、库存、支付回调、优惠券、用户积分——都要先搭一个 Spring Cloud 子模块。流程永远是那几步:创建 Maven 子工程、写 pom.xml 引入一堆 spring-cloud-starter、配置 bootstrap.yml 连 Nacos、写启动类、加统一返回 Result、加全局异常处理器、建实体类和 Mapper、写 Service 和 Controller、再配一个 OpenFeign 客户端去调别的服务。
你说这是技术难点吗?真不是,都是熟练工操作。但它就是很烦,每次都要小心翼翼,改包名、换端口、调 dataId,漏一个就够你排查半天。我一开始也用对话式 AI,把需求粘贴进去让它生成,结果发现它越帮越忙。
1.2 传统问答式 AI 的三大痛点
第一是上下文漂移。AI 会话聊长了,它会慢慢“忘记”你一开始定的规则。比如第一轮你说了“统一返回 Result ,错误码首位代表服务模块”,生成到第三个文件的时候,它可能就自顾自地抛了个 RuntimeException,返回变成了裸对象。
第二是隐性规则无法沉淀。团队里的约定通常不在代码里,而在口头和文档里。比如“Feign 接口必须带 fallback”、“每个服务必须有 health 检查端点”、“分页参数必须用 PageQuery 而不是 PageNum/PageSize”。你每次开新会话,都得把这一堆重新敲一遍,漏一条它就放飞自我。
第三是风格不统一。同一个团队,不同模块由不同会话生成的话,命名习惯、注释风格、异常处理方式会五花八门。这不是 AI 的问题,是知识没有被固定成持久化资产。
传统问答式 AI 和 Skill 机制的差异,我用一张表总结一下:
| 维度 | 对话式 AI 临时生成 | Skill 机制 |
|---|---|---|
| 知识来源 | 会话内的临时上下文 | 项目内持久化的技能文件 |
| 规范一致性 | 靠运气,容易漂移 | 每次加载同一套指令,稳定 |
| 复用性 | 基本为零,每次重新描述 | 团队级复用,一次编写到处使用 |
| 维护方式 | 无版本概念 | 可纳入版本库,随项目演进 |
| 适用场景 | 一次性探索、问答 | 高频重复、规则明确的工程动作 |
所以我的结论很直接:通用 Agent 适合“发散”,Skill 适合“收敛”。Spring Cloud 微服务脚手架恰恰是规则极其收敛的场景,太适合做成 Skill 了。
2. Agent Skill 到底是什么:一份给 AI 的“岗位说明书”
2.1 一份 SKILL.md 的完整结构
Trae 国际版的 Agent Skill,核心是一个 Markdown 文件,通常叫 SKILL.md,放在项目的特定目录下(我后面会讲目录组织)。这个文件的头部有一块 YAML 格式的元信息,里面最关键的是name和description,然后是正文的instructions。
我写了一个简化但结构完整的示例,你可以直接感受一下:
--- name: spring-cloud-module-creator description: 当用户要求新建Spring Cloud微服务模块、生成服务脚手架、添加子模块时使用。 version: 1.0.0 --- # 角色与目标 你是一个精通 Spring Cloud Alibaba 微服务架构的 Java 后端工程师, 负责按照本项目统一规范创建新的微服务模块。 # 通用规则 1. 所有新模块必须注册到 Nacos,服务名使用 kebab-case,如 pay-callback-service。 2. 统一使用 Result<T> 作为接口返回体,禁止直接返回实体对象。 3. 必须包含全局异常处理器 GlobalExceptionHandler。 4. 必须包含 OpenFeign 客户端声明,且每个 Feign 接口都要配置 fallback。 5. 代码注释使用中文,类和方法必须写 Javadoc。 # 输出要求 生成完成后,用表格列出: - 模块的路径、端口、服务名 - 已生成的文件清单 - 需要人工补充的业务代码位置看出来了吗?这个文件本质上就是给 AI 的一份“岗位说明书”。name是技能的唯一标识,description是触发条件,instructions是行为准则。Agent 在接收到用户请求时,会扫描项目中可用的 Skill,通过description判断当前任务匹配哪个技能,然后加载对应的指令。
2.2 description 是门面,instructions 是脊椎
很多人一开始写 Skill 容易犯一个错:把description写得很抽象,把instructions写得很随便。实际上这两个字段各有各的讲究。
description决定 AI能不能找到这个 Skill。它要覆盖用户可能使用的各种口语化表达。比如你的技能叫spring-cloud-module-creator,那么 description 里最好出现“新建服务”“生成模块”“创建微服务”“搭一个服务”这类自然表达,否则用户说“帮我搭一个支付回调服务”,AI 可能都意识不到该加载这个技能。
instructions决定 AI把事情做成什么样。这里不需要写得像律师条款,但要写清楚关键约束。我的经验是:把“绝对不能做什么”放在最前面,然后是“必须包含什么”,最后是可选的“推荐写法”。因为大模型对指令的理解是概率性的,越靠前的内容权重越高。
2.3 Skill 能带模板文件和脚本,不只是文字
SKILL.md 只是技能的入口,一个真正的 Skill 文件夹里还可以放模板文件、代码片段、参考配置、甚至可执行脚本。
典型的目录结构长这样:
.skill/ └── spring-cloud-module-creator/ ├── SKILL.md ├── templates/ │ ├── pom.xml.tpl │ ├── bootstrap.yml.tpl │ ├── Result.java.tpl │ └── GlobalExceptionHandler.java.tpl └── references/ └── 依赖版本对照表.md模板文件的价值在于把“正确的代码”直接固化下来,而不是让 AI 凭记忆生成。比如统一返回类Result<T>,你希望它长什么样就提前写好,AI 拿过来填充变量就行。这比在 instructions 里用文字描述“返回体要包含 code、message、data”要可靠得多。
我当时建这个技能的时候,就是把团队里已经稳定运行的两个服务代码抽出来,去掉业务逻辑,只保留骨架,做成模板。这样生成的代码风格想不统一都难。
3. 动手定制微服务模块生成 Skill:从分析重复动作开始
3.1 把“新建一个微服务”拆成固定动作清单
写 Skill 之前最重要的事,不是打开编辑器写 Markdown,而是先把你要固化的动作拆清楚。我以 Spring Cloud Alibaba 技术栈为例,把团队里“新建一个微服务”的固定动作拆成了一张表:
| 序号 | 动作 | 产出文件 | 关键规则 |
|---|---|---|---|
| 1 | 创建 Maven 子模块 | pom.xml | 继承父工程,锁定 Spring Boot / Cloud / Alibaba 版本 |
| 2 | 配置启动引导 | bootstrap.yml | 配置 Nacos 地址、namespace、group、dataId |
| 3 | 编写启动类 | XxxApplication.java | @SpringBootApplication+@EnableDiscoveryClient(新版可省) |
| 4 | 统一返回体 | Result.java | code / message / data,含 success、error 静态方法 |
| 5 | 全局异常处理 | GlobalExceptionHandler.java | 捕获业务异常、参数校验异常、兜底异常 |
| 6 | 数据库访问层 | 实体、Mapper、Service | MyBatis-Plus 风格,统一继承 BaseEntity |
| 7 | 对外接口 | Controller、DTO | Restful 风格,统一 Result 包裹 |
| 8 | 服务间调用 | FeignClient | 指定服务名、路径,配置 fallback |
| 9 | 配置补充 | application.yml | 端口、数据库连接、Redis、MQ 开关 |
这张表就是 Skill 的灵魂。你会发现,我需要 AI 做的事其实非常明确,根本不是让它“创造”,而是让它“按照规则填空”。
3.2 编写 SKILL.md 指令的要点
有了动作清单,SKILL.md 的instructions就可以写得很扎实。我分享一下我的写法技巧。
一开始不要追求一次性写完,我建议你把指令按优先级分层。第一层写“全局铁律”,第二层写“模块模板说明”,第三层写“交互约定”。
比如全局铁律部分,我会这样写:
# 全局铁律(不允许违背) - 服务命名 kebab-case,模块目录与 artifactId 保持一致。 - 端口分配遵循约定:订单服务 8081,支付回调 8082,用户服务 8083, 新服务根据业务域在 8080-8099 范围内选择未占用端口。 - 所有 Controller 方法返回 Result<T>,业务异常抛 BizException。 - 所有 FeignClient 接口方法必须声明 fallbackClass,不允许裸奔。 - 所有实体类继承 BaseEntity 并包含 createTime、updateTime 字段。这些规则你不需要解释为什么,AI 也不需要理解为什么,它只需要照做。但你要确保这些规则确实符合项目现状,否则 Skill 生成的代码会水土不服。
3.3 保证风格统一的模板设计
模板是整个 Skill 里最花时间但最值得的部分。我拿Result.java.tpl举个例子,它基本是直接从线上服务里抠出来的:
package ${packageName}.common; import lombok.Data; @Data public class Result<T> { private int code; private String message; private T data; public static <T> Result<T> success(T data) { Result<T> result = new Result<>(); result.setCode(200); result.setMessage("success"); result.setData(data); return result; } public static <T> Result<T> error(int code, String message) { Result<T> result = new Result<>(); result.setCode(code); result.setMessage(message); return result; } }模板里的${packageName}是变量,AI 在生成时会根据模块实际包名进行替换。这种模板文件的好处是,你可以把团队对代码的审美直接固化进去,不需要在指令里反复强调“要加 @Data、要写无参构造”之类的细节。
3.4 版本管理与迭代
SKILL.md 本质是代码的一部分,一定要纳入版本管理。我在frontmatter里加了一个version字段,每次改完发版都会更新。
我踩过的一个教训是:不要在 AI 生成的代码基础上反复口头纠正。比如 AI 生成了某个模块,你告诉它“这里不对,应该改成 xxx”,它会改对,但下一次触发 Skill 时它还是会按旧的 SKILL.md 来。正确做法是:把纠正结果反向补进 Skill 模板或指令里,升级版本号,而不是依赖 AI 的会话记忆。
4. 实战案例:一句话生成“支付回调服务”
4.1 场景设定
假设我们要在某个电商后端项目里新增一个“支付回调服务”,它的职责是接收支付网关的异步通知,校验签名,调用订单服务更新订单状态,然后落库留痕。
按照传统流程,从建目录到能跑通基础接口,一个熟练工也得折腾小半天。现在我们用前面写的 Skill,全程只需要一句话。
4.2 我给的指令
在 Trae 国际版里,我输入的是:
用 spring-cloud-module-creator 技能帮我新建一个支付回调服务, 服务名 pay-callback-service,端口 8082,数据库表 prefix_pay_callback, Feign 需要调用订单服务 OrderService 的更新支付状态接口。这里有个使用技巧:我显式说出了技能名,这能确保 Agent 优先加载目标 Skill。即使 description 匹配机制没触发,显式点名也不会走偏。
4.3 Skill 实际产出的核心文件
AI 加载 Skill 后,按照模板和指令生成了完整模块。我挑几个代表性的文件展示一下。
首先是pom.xml的关键依赖片段:
<dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-spring-boot3-starter</artifactId> </dependency> </dependencies>然后是bootstrap.yml:
spring: application: name: pay-callback-service cloud: nacos: config: server-addr: ${NACOS_ADDR:127.0.0.1:8848} file-extension: yml namespace: ${NACOS_NAMESPACE:mall-dev} group: DEFAULT_GROUP profiles: active: devController 层的代码,Skill 完全按照统一返回结构来生成:
@RestController @RequestMapping("/api/pay-callback") @RequiredArgsConstructor @Slf4j public class PayCallbackController { private final PayCallbackService payCallbackService; @PostMapping("/notify") public Result<Boolean> handleNotify(@RequestBody PayCallbackNotifyDTO notifyDTO) { boolean result = payCallbackService.processCallback(notifyDTO); return Result.success(result); } }Feign 客户端的声明也不含糊,带 fallback:
@FeignClient( name = "order-service", path = "/api/order", fallback = OrderClientFallback.class ) public interface OrderClient { @PostMapping("/updatePayStatus") Result<Boolean> updatePayStatus(@RequestBody UpdatePayStatusDTO dto); }4.4 验证清单
AI 生成完代码后,Skill 的指令要求它输出一份验证清单。我拿到的清单长这样:
- 模块路径:
pay-callback-service/ - 服务端口:8082,服务名:pay-callback-service
- Nacos 命名空间:mall-dev
- 已生成文件:pom.xml、bootstrap.yml、application.yml、启动类、Result、GlobalExceptionHandler、PayCallbackRecord 实体、PayCallbackMapper、PayCallbackService、PayCallbackServiceImpl、PayCallbackController、OrderClient
- 需要人工补充:签名校验逻辑、实际 SQL 映射、回调幂等处理
这个清单非常关键。它让我不用逐个文件去检查,就能知道哪里是“自动生成”的骨架,哪里是“必须人工介入”的业务点。
4.5 检查与微调
生成代码并不意味着直接能用,我的习惯是跑一次编译检查:
mvn -pl pay-callback-service -am compile这一步能发现依赖坐标错误、包路径问题。如果编译报错,直接把错误贴回给 Agent,让它对照 Skill 模板修复。正常情况下,骨架代码是没问题的,真正要写的只剩业务逻辑。
这套流程跑完,我的真实体会是:原来 3 小时起步的重复工作,现在压缩到半小时以内,其中大半时间还是花在写支付签名校验这种真正的业务逻辑上,跟脚手架没有任何关系了。
5. 让 Skill 成为团队资产:组织、共享与组合使用
5.1 skills 目录的放置与命名
Skill 文件放在哪里,直接决定它能被谁使用。我目前用的是项目级.skill目录,放在代码仓库根目录下,跟着 Git 走:
mall-backend/ ├── .skill/ │ └── spring-cloud-module-creator/ └── services/ ├── order-service/ └── pay-callback-service/项目级的优势是团队自动同步。新同事拉下代码的那一刻,就拥有了团队所有的技能资产,不需要额外安装配置。
命名上,我建议遵循领域-动作-对象的格式,比如spring-cloud-module-creator、mybatis-plus-mapper-writer、openapi-spec-generator。这样在技能列表里扫一眼就知道是干什么的。
5.2 Skill 的“单一职责”
我强烈建议一个 Skill 只干一件事,而且把它干到极致。不要写一个“全栈万能助手”技能,也不要让spring-cloud-module-creator顺便帮你写 Dockerfile、配 CI 流水线。一旦技能职责太多,指令就会膨胀,AI 的执行质量就会下降。
我见过一个反面案例:某个团队把一个技能写成了 2000 多字的“百科全书”,包含代码风格、部署规范、数据库设计规范、前端联调事项。结果 AI 加载后反而不知道该优先执行哪条,生成的代码顾此失彼。
正确的做法是拆细。比如我可以把“生成 Feign 客户端”单独做成一个 Skill,专门处理跨服务调用的代码生成和 fallback 编写;把“生成单元测试”再做成一个 Skill。这样的话,不同的任务可以精准匹配不同的技能,互不干扰。
5.3 多 Skill 配合使用
Skill 之间是可以配合的。比如我用spring-cloud-module-creator生成骨架之后,紧接着可以激活另一个技能spring-cloud-test-writer给 Controller 生成单元测试。
实践中的做法是连续下指令:
先用 spring-cloud-module-creator 生成 pay-callback-service 骨架, 然后用 spring-cloud-test-writer 给 PayCallbackController 补单元测试, 测试数据要 Mock,不要连库。两个技能各管一段,场景切换很自然。不过要注意,一个会话同时加载太多 Skill 会挤占上下文,建议一次突出一个主技能,辅助技能不超过两个。
5.4 团队评审与测试
Skill 也是代码,也需要 Code Review。我建议把 SKILL.md 的变更当成普通代码变更来走评审流程。
测试 Skill 有一套土办法:建一个临时目录,准备一份固定需求描述,然后用不同版本的参数反复触发技能,检查输出是否稳定。如果连续三次输出结果基本一致,说明这个 Skill 是收敛的。如果每次生成结果差异很大,那就要回头检查指令是否写得太模糊。
我会把这类测试的输入输出存到.skill/tests/目录下,作为回归用例:
.skill/ ├── spring-cloud-module-creator/ │ ├── SKILL.md │ ├── templates/ │ └── tests/ │ ├── case_order_service.md │ └── case_pay_callback_service.md每次改完 SKILL.md,就重跑一遍这些用例,对比输出。这比自己脑补“应该没问题”要靠谱得多。
6. 使用 Skill 的真实感受与避坑经验
6.1 我踩过的坑
第一,description写得太“官方”,导致技能不触发。最开始我写的是“当需要按照项目架构规范创建符合 Spring Cloud Alibaba 体系的微服务模块时使用”,结果我输入“帮我新建一个用户服务”的时候,Agent 完全不加载这个技能,因为描述里没有出现“新建”“用户服务”这类关键词。后来我把 description 改成“当用户要求新建Spring Cloud微服务模块、创建子服务、生成服务脚手架时使用”,触发率立刻上来了。
第二,instructions 太长了。我曾经试图把团队的全部规范都塞进去,结果 AI 反而抓不住重点,生成结果还不如不加载技能。现在我严格控制在 300 到 600 字左右,只保留“铁律”和“输出要求”,剩下交给模板文件。
第三,生成的代码里如果出现了模板残留的占位符,绝大多数情况是模板文件里的变量命名和指令里的变量不一致。比如模板里写$packageName,指令里却用{包名},AI 就容易顾此失彼。解决办法是固定一套占位符规范,比如全部用${xxx},并在指令里明确“所有模板变量必须全部替换,不允许残留”。
第四,不要指望 Skill 一次写对。它需要像产品一样迭代。我第一个版本的技能生成出来的模块端口分配和 Nacos namespace 全部写死,后来通过反馈修正了模板,才变成用环境变量控制。
6.2 边界认知:Skill 是放大器,不是创造者
用了一段时间之后,我最大的体会是:Skill 的价值不在于“让 AI 替你做决定”,而在于“把团队已经验证过的决定固化下来,让 AI 做高保真复制”。
它适合处理规则明确、重复度高、容错率要求不高的工作,比如脚手架生成、Mapper 编写、DTO 转换、标准化接口代码。它不适合处理需要深度业务理解、架构权衡、跨系统设计的工作,比如数据库表设计合理性判断、分布式事务方案选型。在这些事上,我会关掉 Skill,用自己的脑子想清楚,再让 AI 去执行。
还有一点要提醒:Skill 生成代码之后,一定要本地编译和走一遍基础测试。AI 生成的骨架虽然大体可靠,但依赖版本不对、Import 漏掉这类低级错误还是可能出现。把它当成“30% 完成度的初稿”而不是“成品”,心态会平稳很多。
6.3 一个小技巧:把常用约定写进 Skill,而不是写进口头禅
最后分享一个我很受益的做法。以前我引导 AI 时总喜欢在对话里加一句“记住我们的规范”,但事实证明,这句话一点用都没有——一旦会话滚动起来,它什么都记不住。现在我彻底不用这种口头禅了,所有规范都写进 SKILL.md,让文件替我说。
另一个小技巧与组织方式有关。我建议你的 Skill 模板文件单独维护,不要和各业务模块混在一起。我在.skill/spring-cloud-module-creator/templates/里维护的模板,实际上是从线上服务里逐层抽出来的最佳实现,相当于把“团队里最厉害那个人的经验”沉淀成了可复制的代码资产。哪怕 AI 工具以后迭代了、换了新的模型,这套模板依然能跟着项目迁移,不绑死在工具上。
如果你现在的工作流里也有那种“每周都要重复三五次、规则还特别死”的工程动作,真心建议花一个下午把它固化成 Skill。不用追求一上来就做很宏大,从一个你重复最多次的动作开始,生成、试错、迭代,几次之后你会回来感谢自己。