尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

SKILL编排实战:用AI对存量代码做微创手术

SKILL编排实战:用AI对存量代码做微创手术 你有没有过这种感觉让 AI 帮忙改一段运行了七八年的老代码改之前觉得“小意思”改完一看 diff整个人都不好了——它把你引以为傲的命名规范改成了另一种风格顺手删了两个看起来没用、实际上后台定时任务还在调用的方法测试环境直接冒烟。这种事我干过不止一次。以前用 AI 处理存量代码基本是“散装”状态临时从项目里抽几段代码贴进对话框AI 回答完再把结果粘回去全靠运气。后来我把一套叫 SKILL 的玩法引入了日常维护工作让 AI 按固定的“手术流程”去动老代码局面才彻底改观。这篇文章就聊聊我实际怎么用 SKILL 编排对存量代码做小步快跑的“微创手术”包括思路、文件怎么写、踩过的坑和能直接抄走的模板。1. 先聊聊“散装 AI”到底哪里不对1.1 散装的三个典型症状先说个真实场景。上个月同事接了个需求要在某个老支付服务里把日志脱敏规则调一调。他打开 AI 对话框把PaymentService.java整个贴了进去问了一句“帮我改改这里的日志”。AI 确实改了但它不知道这个服务的日志还要输出到审计系统直接改掉了%m的格式审计那边连续三天缺数据。这就是散装 AI 的第一个问题知识散装。AI 对项目的了解只来自你贴进去的那几段代码项目里沉淀的架构约束、合规要求、潜规则它一概不知。第二个问题叫流程散装。没有标准步骤没有前后检查点AI 想先改哪里就改哪里改完直接输出结果连编译都不一定保证。第三个问题叫结果散装。AI 每次给的答案都是“一次性产物”这次改了下次遇到同样问题又得重新说一遍。1.2 存量代码为什么特别怕散装存量代码和绿地上的新项目完全是两回事。新项目怎么折腾都行重构失败大不了删了重来。老代码不一样它身上背着线上流量、历史包袱和说不清道不明的隐式依赖。你不可能让 AI 对着一堆 10 年前的代码“自由发挥”哪怕这个 AI 再聪明。我见过太多人被这种自由发挥坑过AI 觉得某个catch块里吞异常写得不好顺手改成抛出异常结果把一个本该默默降级的非关键路径搞成了硬失败。“微创手术”的核心思想就是只动该动的地方其余一概不碰。要做到这点靠人盯着 AI 逐行改不现实必须把约束条件明确写进 AI 的执行规则里——这就是 SKILL 出现的原因。1.3 从“临时工”到“熟练工”一句话说清 SKILL它把 AI 从“每次都要重新解释需求的临时工”变成“看过操作手册、知道边界在哪的熟练工”。SKILL 本质上是给 AI 的一份结构化指令包里面写清楚这个技能什么时候用、执行哪些步骤、必须遵守什么红线、做完之后怎么验收。相比普通提示词模板SKILL 最大的区别在于可复用和可演进。提示词是一次性的纸杯SKILL 是可维护的工程资产。我刚把第一批 SKILL 放进仓库时团队里的人还觉得这不过是个“高级 prompt”。跑了两个月之后大家发现 AI 改代码的出活质量稳定了不少这种认知才慢慢扭转过来。2. SKILL 不是提示词模板是给 AI 的一份“手术方案”2.1 SKILL 文件里到底装了什么一个标准的 SKILL 在文件系统里就是一个目录。以我常用的 Cursor 环境来说目录结构大概是这样的.cursor/skills/ └── legacy-log-migration/ ├── SKILL.md ├── references/ │ └── migration-checklist.md └── scripts/ └── find_log4j.sh核心文件是SKILL.md它通常带一个 YAML 格式的文件头用来告诉 AI 这个技能是干嘛的、什么时候该被调用。文件头下面就是正文包含具体步骤、约束、注意事项和验收清单。我写的一个日志迁移类 SKILL 的简化版长这样--- name: legacy-log-migration description: 将存量模块的 log4j 1.x 日志调用迁移到 SLF4J保持业务逻辑零改动。 when-to-use: 用户提到“迁移日志”“换日志框架”“log4j 改 slf4j”时使用。 version: 1.0.0 --- # 日志迁移 ## 目标 在不改变业务逻辑的前提下把 log4j 1.x 的调用替换为 SLF4J。 ## 前置检查 1. 读取 pom.xml 或 build.gradle确认日志依赖版本。 2. 列出所有包含 org.apache.log4j.Logger 的文件。 ## 改造规则 1. 仅替换 import 与 Logger 获取方式。 2. 保留日志级别调用info/debug/error/warn原样。 3. 禁止格式化代码、禁止重命名变量、禁止移动方法。 4. 每次只处理一个包改完立即编译。 ## 完成标准 - 项目中不再存在 org.apache.log4j 的 import。 - 编译通过无新增错误。 - 日志输出内容与改造前保持一致。看到没这份 SKILL 的价值不在“教 AI 怎么写日志”而在规定 AI 不许干什么。改造规则第 3 条特别关键——禁止格式化代码、禁止重命名变量、禁止移动方法这三条把 AI 的“创作欲”死死摁住了。没有这些红线AI 很容易在迁移日志时顺手帮你重构整个类。2.2 为什么“存量代码微创”特别吃这套机制存量代码维护最大的痛点就是改动的不可控性。你没法保证 AI 理解了这个方法被哪个定时任务调用、那个字段被哪个报表依赖。SKILL 通过两种方式解决这个问题一是知识前置。把项目里的特殊约定写进 SKILLAI 在动手前先读取这些规则。比如我们老项目里有一条铁律支付模块禁止引入 Lombok必须手写 getter/setter。这条规则在代码评审时被强调过无数遍但 AI 不知道。我把这句话写进 SKILL 的“红线清单”AI 就再也没犯过。二是验收驱动。SKILL 的完成标准就是一股“紧箍咒”。AI 改完代码必须自己检查“是否还有残留引用”“编译是否通过”。以前散装状态下AI 改完交付就完事现在它得像走过场一样完成自检。这个过程看起来机械但对老代码来说机械反而意味着安全。2.3 SKILL 与 Agent 的边界热词里经常把 SKILL 和 Agent 混在一起聊。我自己的理解是Agent 是“能自主做事的员工”SKILL 是“员工手里的操作手册”。一个 Agent 可以按需调用多个 SKILLSKILL 本身不决策它只提供流程和方法。在实际操作中我倾向于让 Agent 做流程控制SKILL 做单个环节的标准化操作。比如 Agent 判断当前任务需要“影响面分析”就加载impact-analysis这个 SKILL拿到结果后再决定下一步加载哪个 SKILL。3. 编排把多个 SKILL 串成一条流水线3.1 为什么单个 SKILL 不够有同事问我我写了一个贼全的 SKILL把分析、改造、验证全塞进去了为什么效果还是不行答案是太长的 SKILL 会让 AI 的注意力分散。模型在上下文里塞一大堆指令执行到后半段往往忘了前面说了什么。我的做法是拆。把“改代码”这件事拆成几个单一职责的 SKILL再用编排机制把它们串起来。以改造类任务为例我通常拆成四段诊断 SKILL分析目标代码的影响面输出“哪些文件要动、风险点在哪”的清单。方案 SKILL基于诊断结果生成最小变更方案列出具体的改动点和顺序。执行 SKILL按方案逐个文件落地修改严格遵守红线。验证 SKILL编译、跑单测、检查 diff 范围输出验证报告。这么拆的好处是每个 SKILL 都很短AI 执行时“眼里只有一件事”不容易跑偏。更实际的好处是可复用。诊断 SKILL 不只是日志迁移能用接口升级、数据库访问层替换、第三方 SDK 版本更新都用得上。3.2 输出契约让前一个 SKILL 的结果喂给后一个编排不是简单地把几个 SKILL 的文档堆在一起关键是给每个 SKILL 定义清晰的输入输出契约。我习惯让前一个 SKILL 输出一段结构化 JSON后一个 SKILL 读取这段 JSON 作为输入。比如诊断 SKILL 的输出{ task: log-migration, scope: [ order-service/src/main/java/com/example/pay ], files: [ { path: PayServiceImpl.java, matchCount: 12, risk: low }, { path: RefundWorker.java, matchCount: 3, risk: high, reason: 同时在多个线程中持有 Logger 引用 } ], suggestedPlan: 先迁移无状态工具类再迁移业务类最后核对配置文件 }执行 SKILL 拿到这份 JSON 后只需要按suggestedPlan的顺序逐一处理files列表里的文件。这种“契约式编排”还有个好处诊断结果可以人工审核你觉得RefundWorker.java的风险等级评估不准改一下 JSON 再让 AI 执行就行不用重新跑一遍全流程。3.3 触发与执行怎么让 AI 知道该用哪套流程SKILL 的触发有两种方式。一种是自动触发靠 SKILL.md 文件头里的description字段。模型读到用户说“把 log4j 换成 slf4j”会和所有 SKILL 的 description 做匹配匹配上了就加载对应技能包。所以 description 必须写得像“项目经理下命令的口吻”直接、明确别绕弯子。另一种是手动触发适合那些你希望严格按流程走的场景。我在 Cursor 里会直接对 AI 说“加载 legacy-log-migration 流程按诊断-方案-执行-验证的顺序跑一遍目标目录是order-service/src/main/java/com/example/pay。”手动触发不依赖模型的判断力适合给新人演示或者处理那些风险特别高、不允许 AI 自作主张的任务。4. 实操给一个存量支付模块做日志迁移“微创手术”4.1 第一步先写诊断 SKILL摸清手术区假设我们的任务是老支付模块的日志框架从 log4j 1.x 迁到 SLF4J业务逻辑一行不许动。别上来就让 AI 改先让它“照个 CT”。我写的诊断 SKILL 大概是这样--- name: impact-analysis description: 分析存量代码影响面输出待改造点清单供后续变更使用。 when-to-use: 用户要求评估改动范围、搜索依赖、做影响面分析时使用。 --- # 影响面分析 ## 执行步骤 1. 扫描目标目录列出所有与改造目标相关的文件。 2. 每个文件记录 Logger 获取方式、调用次数、所在包路径。 3. 用 grep/ripgrep 搜索整个代码库找出反向上引用该模块的地方。 4. 输出 Markdown 表格文件路径、关键位置、改造难度、风险等级。 5. 基于风险等级给出建议执行顺序。 ## 红线 - 只做分析不得修改任何代码。 - 不得输出未经证实的依赖关系。注意这个 SKILL 的红线只有一句话只做分析不得修改代码。这是我最看重的约束分析阶段的 AI 一旦手痒改了代码后面的方案和执行全部白搭。诊断跑完后AI 输出的表格大概是文件涉及行难度风险备注PayServiceImpl.java45-58低低无状态静态 LoggerRefundWorker.java120-130中高多线程环境持有 LoggerAuditKafkaSender.java11-20低低无外部日志配置依赖log4j.properties全文件中中需要替换为 logback 配置看到RefundWorker.java标注的高风险我会在评审时特别留意这个文件必要时要求 AI 单独说明改动细节。4.2 第二步写执行 SKILL把手术刀探进去执行阶段的 SKILL 就是前面展示过的legacy-log-migration。它最重要的是明确交代“可以做什么、禁止做什么”。我把改造规则写成清单形式因为模型对“允许/禁止”的二元指令响应质量最高。规则里有一句很关键“每次只处理一个包改完立即编译。”这句话我是在翻了两次车之后才加进去的。原因后面在常见问题里细说。另外这个 SKILL 还包含了一个小脚本find_log4j.sh作用是扫描目录里是否还有残留的 log4j import。验收阶段模型会运行这个脚本脚本返回空结果才算通过。4.3 第三步用编排把两端接起来两个 SKILL 写好后编排就是“让 AI 按顺序执行”。在 Cursor 里我会新建一个.cursor/skills/目录把两个 SKILL 分别放好然后对 AI 说“先运行 impact-analysis分析对象是order-service/src/main/java/com/example/pay拿到结果后运行 legacy-log-migration按诊断输出的顺序执行最后运行 dependency-check确认没有引入新的 log4j 引用。”如果希望流程更自动可以再加一个简单的流程控制 SKILL里面只写步骤和每步调用的下游 SKILL相当于一个“指挥手册”。我实测下来的感受是拆成两步之后AI 在诊断阶段的“多管闲事”现象明显变少。以前一个完整指令让 AI 从头干到尾它会在改日志的时候顺手把RefundWorker.java的数据结构重构了。现在诊断和执行分离执行阶段输入的 JSON 里明确列了“只包含日志迁移相关文件”AI 就知道边界在哪了。4.4 第四步人工复核与回归测试守住最后一道门SKILL 再完善也不能让人彻底放手。每次改造完成后我会做三件事第一看git diff --stat。如果改动的文件数和诊断清单不一致直接返工。多余的文件改动是 AI “越狱”的最直观信号。第二逐段扫一遍 diff 内容确认没有出现“格式化噪声”。AI 改两行代码却给你格式化了整个文件这种 diff 无法 review我会立刻要求 AI revert。第三跑回归测试。老代码最怕静默行为变化所以我会用流量录制回放的方式对比改造前后的日志输出。这个过程我没有写进 SKILL因为不同项目的回放工具差异太大写成半自动的人工步骤更稳妥。5. 常见问题与排查技巧实录5.1 SKILL 经常不生效AI 就是不按文档走这是团队里被问得最多的问题。排查思路先看description。有些人的描述写得太文艺比如“这个技能用于提升代码质量帮助开发者更好地维护系统”AI 看得懂才怪。改成“用户要求迁移日志框架或替换 log4j 时触发”这种指令式描述命中率立刻上来。另外SKILL.md 正文里不要堆砌废话。AI 加载技能包是把它的一段内容塞进上下文正文越长越稀释注意力。责任边界、允许动作、禁止动作、验收标准四块就够。5.2 改完编译不过AI 把代码改坏了我遇到过最典型的翻车现场AI 在执行日志迁移时顺手把两个类的 import 顺序“优化”了结果触发 CheckStyle 报错接着它试图修复 CheckStyle 问题又改了一堆变量名最后整个模块编译失败。后来我在执行 SKILL 里加了一条强制步骤“每次只处理一个包改完立即编译并显示结果。”这条步骤本质上是把人的“小步提交”习惯强加给 AI。模型如果一次性改十几个文件再统一编译一旦报错它往往搞不清自己改了哪里破坏了什么。拆小步之后每次编译错误都能精确定位到刚刚改动的那几行。再提供一个排查技巧AI 改坏代码后先让它跑git diff把改动回的滚到一个干净版本再带着最后一个成功的编译日志重新执行。别让它基于当前的坏状态盲目继续修。5.3 团队协作时SKILL 放哪、怎么维护SKILL 一定要放进代码仓库跟着项目版本走。我放在.cursor/skills/下这样所有用 Cursor 的同事拉代码后自动同步。还在用 Continue 或 opencode 的同事就手动把目录拷到对应配置路径。维护上有两个心得。第一把 SKILL 本身的变更当代码 Review。AI 用 SKILL 改业务代码大家可能会习惯性忽略 SKILL 的 commit。但 SKILL 改错一个约束影响的是后续所有 AI 改动比单次代码改动的风险大得多。第二定期清理失效步骤。存量代码是活的半年前写的约束可能现在已经没用了。我每季度会抽时间过一遍 SKILL把那些“AI 执行时会卡住”或“已经不适配当前代码库”的规则移除。SKILL 维护得跟代码一样勤快它才能真正成为团队的工程资产。我在实际维护一个运行了 8 年的后端服务时最深的体会是SKILL 编排这套玩法的本质不是让 AI 变得更聪明而是让 AI 变得更规矩。聪明它本来就有规矩才是老代码最需要的东西。如果你想快速上手我建议别一上来就搭复杂流程先挑一类最机械、最重复的改造任务——比如改注释、换 import、统一命名——写一个最小的 SKILL跑通之后再慢慢扩展。踩过几次坑之后你会明白能把 AI 的“自由发挥”控制住比让它多干活更有价值。
返回列表