在 Trae 里用 Skills 加速 API 30 老项目 Java→Kotlin 迁移

5 阅读7分钟

为什么在 Trae 里 Skill 比 Rules 更合适

Trae 的 Rules 是全量加载的:一旦开启对话,所有规则都会被注入并持续占用上下文窗口。你的迁移项目会涉及大量文件分批转换,如果把所有约束都塞进 Rules,每次对话都在消耗 token,Agent 反而容易分心。

Skill 是按需加载的:Trae 的 Agent 先扫描所有技能的简要描述,判断当前任务相关后才加载完整内容。这意味着你可以把迁移守则写得很细(包括 Gson 注解要求、@JvmStatic 规则、平台类型处理),而不用担心它干扰你日常写业务代码的对话。

Trae 官方明确建议:如果你在反复输入同一段提示词,就应该把它做成 Skill。你的 Java→Kotlin 迁移约束正是这种情况。

第一步:在 Trae 中创建迁移专用 Skill

Trae 的 Skill 本质是一个包含 SKILL.md 的文件夹,项目级 Skill 放在项目根目录的 .trae/skills/ 下。创建方式有两种:直接在对话里让 Trae 生成,或手动写文件。迁移这种有严格约束的场景,手动写更可控。

在项目根目录执行:

bash

mkdir -p .trae/skills/java-to-kotlin-migration

然后创建 .trae/skills/java-to-kotlin-migration/SKILL.md。

SKILL.md 内容模板(针对你的 API 30 项目)

markdown

---
name: java-to-kotlin-migration
description: 本项目 Java 转 Kotlin 时使用。当用户要求转换 Java 文件、审查 Kotlin 混编代码、或处理迁移相关问题时触发。适用于 compileSdk 30、AGP 版本锁定的老项目。
---

# Java 转 Kotlin 迁移约束

## 环境约束
- compileSdk 30,禁止建议升级 compileSdk 或使用新版本语言特性
- Kotlin 版本上限 1.8.x,不要生成仅高版本支持的语法
- 项目使用 Gson 做 JSON 解析,注解处理器仍在 kapt 阶段

## 转换规则

### 空安全(最高优先级)
- 禁止生成 `!!`,除非原 Java 代码有明确判空且逻辑上不可能为 null
- Java 侧返回值若无 `@Nullable`/`@NonNull` 注解,一律视为可空(`?`)
- 转换后的人工审查必须逐方法检查平台类型是否裸奔

### Gson + data class
- 所有参与 JSON 映射的 data class 字段必须加 `@JvmField` 和 `@SerializedName`
- 生成 data class 后,同步生成一个反序列化单元测试(用真实 JSON 断言关键字段)
- 如果原 Java 类使用了 `@SerializedName`,Kotlin 侧必须保留

### Java ↔ Kotlin 互调
- 会被 Java 调用的 Kotlin 伴生对象方法,加 `@JvmStatic`
- 有默认参数的 Kotlin 方法,如果 Java 侧会调用,加 `@JvmOverloads`
- Kotlin 集合传给 Java 前,确认对方是否会修改;会修改则用 `mutableListOf`

### 异步代码
- 不生成新的 `AsyncTask` 或裸 `Handler`
- 原代码中的 `AsyncTask` 标记出来,建议迁移到 `lifecycleScope` + 协程,但不强制本轮完成

## 输出要求
- 转换完成后,用三行格式报告风险:
  1. 发现的问题
  2. 运行时可能的影响
  3. 修正建议
- 不要输出“更简洁”“更现代”这类模糊评价

这个 Skill 的 description 字段是关键。Trae 官方文档指出,description 决定了技能能否在正确时机被触发。你需要在里面包含“转换 Java 文件”“迁移”“Kotlin 混编”这些关键词,这样当你对 Trae 说“帮我把这个 Presenter 转成 Kotlin”时,Agent 才会加载它。

第二步:判断是否需要导入社区 Skill

Trae 兼容开放的 Agent Skills 标准,可以从社区导入 Skill。理论上你可以从 GitHub 找 JetBrains 或 Android 社区维护的 Java→Kotlin Skill。

但有一个现实问题:社区 Skill 不知道你的 compileSdk 是 30,不知道你在用 Gson,不知道你的 AGP 版本锁死了。它们能帮你写出“语法正确的 Kotlin”,但写不出“在你项目里能跑的 Kotlin”。

我的建议是:先不装社区 Skill,用自己写的 java-to-kotlin-migration 跑通第一批文件。如果发现 Trae 在通用 Kotlin 惯用法上确实有欠缺(比如 collections 操作、扩展函数写法),再考虑导入一个辅助 Skill。但核心的迁移约束,必须由项目级 Skill 承载。

第三步:用 Trae 的对话方式触发迁移

Skill 建好后,不要指望它“自动生效”。你需要在对话中明确让 Trae 使用它。Trae 的 Agent 会根据 description 判断是否加载,但你可以更直接:

转换单个文件时:

text

使用 java-to-kotlin-migration 技能,把这个文件转成 Kotlin。
文件路径:app/src/main/java/com/example/UserProfile.java

批量转换时:

text

使用 java-to-kotlin-migration 技能,按以下顺序转换这批文件:
1. UserProfile.java
2. UserRepository.java
3. UserListPresenter.java

每转完一个,先告诉我风险点再继续下一个。

审查已转换的代码时:

text

使用 java-to-kotlin-migration 技能,审查这个 Kotlin 文件有没有迁移风险。
重点检查:!! 的使用、平台类型裸奔、Gson 字段注解、Java 调用入口的 @JvmStatic。

Trae 的 Skill 支持在对话中通过名称触发,你也可以在设置中确认 Skill 已启用。

第四步:把 AI 最容易犯的错写进 Skill 的“负向条件”

Trae 官方关于写好 Skill 的文档里有一条核心原则:模型最容易犯的错误不是“不知道怎么做”,而是“不知道什么时候该做”或“不该做什么” 。你的迁移 Skill 需要明确告诉 Trae 禁止事项。

在上面模板的基础上,可以追加一段:

markdown

## 禁止事项
- 禁止建议升级 compileSdk 或 AGP 版本作为解决方案
- 禁止在未补 @Nullable/@NonNull 的 Java 代码上直接转换(先提示用户补注解)
- 禁止把 Kotlin 的不可变集合直接传给 Java 方法而不做可变性确认
- 禁止在迁移的同时重构业务逻辑(迁移和重构必须分开做)

最后一条尤其重要。AI 倾向于“顺手优化”,但在迁移场景下,行为一致性比代码漂亮重要一万倍。如果你让 Trae 转一个 Presenter,它顺手把回调结构也改了,你就很难判断线上问题到底是迁移引入的还是重构引入的。

第五步:稳定性验证在 Trae 内的操作方式

Skill 只能约束 AI 的输出,不能保证运行时正确。在 Trae 里做迁移,验证环节建议这样安排:

每批转换后,让 Trae 帮你生成验证清单。在对话中说:

text

根据 java-to-kotlin-migration 技能的要求,列出这批转换文件的验证清单。
包括:需要补的 Gson 单元测试、需要检查的 Java 调用入口、需要手动验证的页面。

然后不要完全信任 Trae 生成的测试代码。Gson 反序列化测试必须用你项目里真实的 JSON 样本跑一遍。AI 生成的断言可能只覆盖了理想情况,而老项目的 JSON 字段经常有缺省值、类型不一致的坑。

一个务实的时间线

结合 Trae 的 Skill 机制和 API 30 老项目的现实:

第一天:写好 SKILL.md,用一个中等复杂度的工具类(不是核心 Presenter)试跑转换。人工审查 Trae 的输出,调整 Skill 里的约束表述,直到它不再生成 !! 或不带 @JvmField 的 data class。

第二到三天:迁移数据模型层。这是风险最低、收益最明显的部分。每转一个模型类,让 Trae 同步生成 Gson 测试,跑通再进下一个。

第四天起:按依赖顺序迁移 Repository 和 Presenter。每批转换后,在 Trae 里让 Agent 列出“需要手动验证的运行时场景”,然后去真机/模拟器上走一遍。

核心原则不变:Skill 解决的是 AI 输出的一致性问题,但“能不能上线”的判断权必须留在人手里。Trae 的 Skill 机制让你不用每次重复粘贴约束,但不会替你承担线上崩溃的责任。

最后一句

在 Trae 里做 Java→Kotlin 迁移,最有价值的不是找一个“万能 Skill”装上,而是花半小时把项目的真实约束写成一个项目级 Skill。compileSdk 30、Gson、kapt、空安全规则——这些东西 Trae 不会自己知道,但一旦写进 SKILL.md,它每次帮你转换时都会遵守。这半小时的投入,会在后续几十次转换里持续返还。