1. 存量代码改造的真实困境与破局思路手里维护着一套跑了三年以上的业务系统代码量不大不小大概二十万行出头。每次产品提新需求我最怕的不是从零写新模块而是在那些盘根错节的老逻辑里动刀子。改一个订单状态流转的判断条件可能牵出支付回调、库存扣减、消息推送三条链路加一个字段得翻遍五六个文件确认没有硬编码的兼容逻辑。这种活儿干多了人会变得保守能绕就绕能加 if 就加 if最后代码越来越臃肿谁都不敢删。这就是典型的“散装 AI”困局——不是说用了 AI 工具就叫智能化而是 AI 能力被零散地塞进各个角落IDE 里挂一个补全插件终端里跑一个对话式助手浏览器里开着另一个模型页面每个工具都只看到代码的一个切片彼此之间没有上下文传递更没有统一的编排逻辑。你问它“这个函数改完会影响哪些调用方”它只能基于当前打开的文件猜猜完你还得自己去验证。效率提升有限风险反而因为“看起来很快”而被低估。我后来想明白一件事存量代码改造的本质不是“生成新代码”而是“在约束条件下做最小必要变更”。这跟外科手术的逻辑一模一样——切口要小路径要准术前要有影像导航术中要有实时监测术后要有恢复验证。对应到工程实践就是需要一套SKILL 编排体系把检索、分析、变更、验证这几个动作拆成独立的技能单元用工作流把它们串起来让每个环节的输出成为下一个环节的输入而不是靠人去搬运上下文。这套思路的核心价值在于三点。第一降低认知负荷你不需要同时记住十个文件的关联关系编排层会帮你把依赖图拉出来。第二控制变更半径每个 SKILL 只负责一件事改什么、不改什么边界清晰。第三可回滚可验证每一步都有中间产物出问题能定位到具体环节而不是面对一坨混合了 AI 生成和手写逻辑的代码发呆。适合读这篇内容的人我大致分两类。一类是跟我一样在一线维护业务系统的工程师手头有历史包袱想用 AI 提效但不敢放开手脚。另一类是对 agent 编排感兴趣但还没找到落地场景的技术人你可能看过不少框架文档但缺一个“拿真实代码练手”的切入点。下面我会把整套方法拆开讲包括我踩过的坑和最终稳定下来的操作流程。2. SKILL 编排体系的核心设计与选型逻辑2.1 为什么是 SKILL 而不是单体 Agent市面上 agent 框架不少有走通用对话路线的有走函数调用路线的也有走多智能体协作路线的。我试过几种最后落到 SKILL 编排上原因很实际存量代码改造的每个环节对模型能力的要求不一样。检索阶段需要的是“广撒网、高召回”模型得愿意多列候选哪怕有些是噪音。分析阶段需要的是“深推理、强约束”模型得基于确定的调用关系做判断不能发散。变更阶段需要的是“精准生成、风格一致”模型得模仿现有代码的命名习惯和异常处理模式。验证阶段需要的是“严格比对、不放过边界”模型得像个挑剔的测试员。如果你用一个单体 agent 从头跑到尾要么提示词长得离谱导致中间步骤被忽略要么模型在某个环节“自由发挥”把整个流程带偏。SKILL 编排的好处是每个技能单元可以独立配置模型、独立调参、独立评估。检索用温度高一点的模型分析用推理能力强的模型变更用代码专精的模型验证用规则引擎加模型复核。各司其职互不干扰。注意不要一上来就追求全自动编排。我最初试图让整个流程无人值守结果在分析环节模型给出了一个“看起来合理但实际错误”的调用关系后面所有步骤都建立在错误前提上。后来改成关键节点人工确认整体效率反而更高。2.2 编排层要解决的核心问题上下文传递与状态管理SKILL 之间怎么传数据这是编排设计里最容易翻车的地方。我见过两种极端做法。一种是全部走自然语言上一个 SKILL 输出一段描述下一个 SKILL 自己解析。这种做法灵活但不可靠模型每次解析都可能丢信息。另一种是全部走结构化 JSON字段定义得死死的稍微复杂一点的代码关系就表达不了。我最终采用的方案是混合传递核心元数据走结构化字段辅助信息走自然语言摘要。举个例子检索 SKILL 的输出包含一个affected_files数组每个元素有file_path、match_type、confidence三个固定字段同时附带一段reasoning文本说明为什么认为这个文件相关。分析 SKILL 拿到结构化字段做精确遍历同时读reasoning理解检索意图避免机械匹配。状态管理方面我用一个轻量的本地存储记录每个 SKILL 的输入输出快照。这样做的好处是当变更 SKILL 生成的补丁导致测试失败时我可以回退到分析 SKILL 的输出检查是不是调用关系判断错了而不是从头再跑一遍检索。整个流程的中间状态大概占用几 MB 空间对于单次改造任务来说完全可以接受。2.3 工具选型不追新看接口稳定性具体工具上我没有绑定某个特定框架。编排层用的是一个基于 Python 的轻量调度器核心逻辑不到三百行主要做三件事按顺序调用 SKILL、传递上下文、记录状态。每个 SKILL 本身就是一个独立的 Python 函数或者命令行工具通过标准输入输出或者本地文件交换数据。模型侧我同时接了两个来源一个本地部署的代码模型用于检索和变更生成一个云端 API 用于分析推理。本地模型响应快、成本低适合处理大量候选文件的初筛云端模型推理强适合做调用链分析和影响面评估。两者通过编排层切换对上层 SKILL 透明。实操心得本地模型的上下文窗口往往比云端小检索 SKILL 如果一次性塞太多文件内容进去本地模型会截断。我的做法是检索阶段只传文件路径和函数签名不传完整实现等分析阶段再按需加载具体代码块。2.4 安全边界哪些事绝对不让 AI 做这套体系跑通之后我给自己划了几条红线。第一数据库 schema 变更绝不交给 AI 直接生成迁移脚本可以让它分析影响但最终脚本必须人工编写和审核。第二涉及资金流转的核心逻辑AI 只能做只读分析不能生成变更补丁。第三任何删除操作无论是删代码还是删配置必须有人工确认环节。这些红线不是对 AI 能力的不信任而是对存量系统复杂性的敬畏。你永远不知道某个看起来没人调用的函数是不是在某个定时任务里被反射调用了也不知道某个配置项是不是在运维脚本里被引用。AI 看不到这些“代码之外”的依赖但一次误删就可能引发线上事故。3. 核心 SKILL 的拆解与实操要点3.1 检索 SKILL如何做到高召回又不淹没关键信息检索 SKILL 的任务很明确给定一个变更需求找出所有可能受影响的代码位置。听起来简单做起来坑很多。最直接的做法是拿需求描述去代码库里做全文搜索但自然语言和代码标识符之间的语义鸿沟很大。你搜“订单取消”代码里可能叫cancelOrder、orderCancel、abortTrade、revokePurchase甚至就是一个status -1。我的检索 SKILL 采用三层策略。第一层是标识符扩展用一个轻量模型把需求描述翻译成可能的代码标识符列表包括驼峰、下划线、缩写等变体。第二层是调用图遍历从已知的入口函数出发沿着静态分析生成的调用图向外扩展两到三层把间接依赖也纳入候选。第三层是变更历史挖掘查最近半年内修改过相关文件的提交记录把那些“经常一起改”的文件也加进来。三层结果合并后按置信度排序取前 N 个进入分析阶段。N 的取值我一般设在 15 到 20 之间太少容易漏太多分析阶段成本扛不住。置信度计算不复杂标识符精确匹配权重最高调用图距离越近权重越高历史共现次数越多权重越高。具体权重值可以根据项目特点调我用的是一组经验值跑了几次之后微调过两轮。常见问题检索 SKILL 容易把测试文件也大量召回。我的处理方式是在候选列表里单独标记测试文件分析阶段可以选择跳过但变更阶段必须把测试文件纳入因为改了实现不改测试CI 会直接挂掉。3.2 分析 SKILL调用链梳理与影响面评估分析 SKILL 拿到检索结果后要做的是“理解这些文件之间的关系”。我让它输出三样东西调用链路径、数据流方向、变更影响等级。调用链路径是从入口到出口的完整函数调用序列用缩进树的形式展示。数据流方向标注每个环节的数据是“只读”、“写入”还是“读写”。变更影响等级分三档高影响意味着改动会波及多个模块或涉及核心状态变更中影响是单模块内的逻辑调整低影响是纯展示层或日志层的修改。这里有个关键设计分析 SKILL 不直接读完整文件而是按需加载。它先读函数签名和注释判断这个函数是否真的在调用链上确认后再加载函数体做详细分析。这样做的好处是 token 消耗可控一个中等规模的改造任务分析阶段加载的代码量大概在五千行左右不会把模型上下文撑爆。影响等级的判断逻辑我写成了规则加模型复核。规则部分看几个硬指标调用链深度超过三层、涉及数据库写操作、被超过五个其他模块引用满足任意一条就至少是中影响。模型复核部分让分析 SKILL 对规则结果做一次确认如果模型认为规则误判了可以调整等级但需要给出理由。3.3 变更 SKILL微创手术式的代码生成策略变更 SKILL 是整个流程里最需要克制的环节。我的核心原则是能加不改能小改不大改能局部不全局。具体操作上变更 SKILL 拿到分析结果后先判断每个受影响文件需要做哪种类型的修改。如果是新增逻辑优先在现有函数末尾追加不动原有代码结构。如果是修改逻辑优先用条件分支包裹新逻辑保留旧路径作为 fallback。如果是删除逻辑必须标记为“待人工确认”不自动执行。生成补丁时变更 SKILL 会读取目标文件的上下文包括 import 语句、命名风格、异常处理模式、日志格式。我给它加了一个“风格锚点”机制从目标文件里抽取三到五个代表性代码片段作为 few-shot 示例让模型模仿这些片段的写法。实测下来加了风格锚点之后生成的代码在命名和格式上跟原文件的差异明显缩小review 时少了很多“这看起来不像我写的”的别扭感。注意事项变更 SKILL 生成的补丁不要直接写入源文件。我的做法是先生成到临时目录跑一遍语法检查和静态分析确认没有低级错误后再由人工 review最后才合并。这一步多花五分钟能省掉后面半小时的排查时间。3.4 验证 SKILL自动化检查与人工复核的边界验证 SKILL 分两层。第一层是自动化检查包括语法解析、类型检查、单元测试、静态扫描。这些用现成的工具链就能做编排层负责按顺序调用并收集结果。第二层是模型复核让模型对比变更前后的代码检查是否有逻辑遗漏、边界条件未处理、异常路径被破坏等问题。模型复核的提示词我打磨了很久。最初只是简单地说“检查这个变更是否正确”模型给出的反馈很泛。后来改成结构化提问变更是否覆盖了所有调用路径是否引入了新的空指针风险是否改变了原有异常抛出行为是否影响了并发安全性每个问题要求模型给出“是/否/不确定”三态回答并附上具体代码行号作为证据。自动化检查通过但模型复核标记“不确定”的情况我会重点人工看。这种地方往往是边界条件或者隐含假设测试用例没覆盖到但线上可能触发。反过来自动化检查失败但模型复核认为“变更逻辑正确”的情况我会先检查测试用例本身是不是需要更新而不是直接改代码去迎合测试。3.5 编排层的调度逻辑与异常处理编排层本身不复杂但异常处理必须做扎实。我的调度器定义了每个 SKILL 的超时时间、重试次数和失败策略。检索 SKILL 超时了就扩大搜索范围重试一次分析 SKILL 失败了就回退到人工指定调用链变更 SKILL 生成失败就换一个模型重试验证 SKILL 不通过就阻断流程并输出详细报告。每个 SKILL 的输出都带一个status字段取值包括success、partial、failed。编排层根据status决定下一步走向。partial状态表示 SKILL 完成了任务但结果不完整比如检索 SKILL 找到了候选文件但置信度普遍偏低。这种情况下编排层会提示人工介入而不是硬着头皮往下跑。整个流程的日志我保留得很详细每个 SKILL 的输入输出、耗时、token 消耗都记录在案。跑过十几次改造任务之后我根据日志数据调整了各阶段的超时阈值和重试策略现在整套流程的端到端成功率大概在八成左右剩下两成需要人工干预的情况基本都是需求本身描述不清或者代码里有历史遗留的“魔法逻辑”。4. 完整实操流程从需求到合并的端到端演示4.1 场景设定与需求拆解假设我们有一个电商后台系统现在要加一个需求订单取消时如果订单已经发货需要自动生成一条退货工单并通知仓储系统拦截物流。这个需求涉及订单模块、退货模块、消息模块三个领域存量代码里订单取消逻辑分散在三个文件里退货工单的创建接口已经存在但调用方式不统一。我把需求拆成四个可验证的子任务。第一找到所有订单取消的入口和分支。第二确认每个入口在“已发货”状态下的现有行为。第三在现有行为基础上追加退货工单创建逻辑。第四确保消息通知走统一的发送通道不新增硬编码的调用。拆解完之后我把子任务描述输入编排层启动检索 SKILL。检索 SKILL 输出的候选文件列表包含十二个文件其中订单相关六个退货相关三个消息相关两个还有一个公共工具类。置信度最高的三个文件是订单取消的主流程文件、退货工单的服务类、消息发送的工具类。4.2 检索与分析阶段的实操记录检索阶段耗时大约四十秒本地模型跑了三轮标识符扩展静态调用图遍历了四层历史提交记录查了最近三个月。输出的候选列表里有一个文件引起了我的注意一个看起来跟订单无关的定时任务文件置信度排第七。点开reasoning一看原来这个定时任务会扫描“超时未支付”的订单并自动取消虽然跟“已发货取消”场景不同但共用了一部分状态判断逻辑。分析 SKILL 拿到候选列表后先加载了置信度前八的文件做调用链梳理。输出结果里订单取消的主流程有三条分支用户主动取消、客服后台取消、定时任务取消。其中用户主动取消和客服后台取消会走到“已发货”判断定时任务取消不会。退货工单创建接口有两个版本一个老的直接操作数据库一个新的走服务层封装新代码应该用新的。影响等级评估结果订单主流程文件标记为高影响退货服务类标记为中影响消息工具类标记为低影响定时任务文件标记为低影响但需要确认。分析 SKILL 给出的理由是定时任务虽然不直接触发退货工单但它的取消逻辑如果跟主流程不一致可能导致状态判断出现分叉。实操心得分析 SKILL 标记“需要确认”的地方我一般会人工看一眼。这次确认的结果是定时任务确实不需要改但它的存在提醒我在变更主流程时要保持状态判断逻辑的一致性避免出现“用户取消走退货、定时取消不走退货”这种不一致行为。4.3 变更生成与补丁审查变更 SKILL 拿到分析结果后针对三个文件生成了补丁。订单主流程文件的补丁是在两个取消入口的“已发货”分支里各加了一段调用退货工单服务的代码同时加了一个 try-catch 包裹确保退货工单创建失败不影响主取消流程。退货服务类的补丁是新增了一个方法封装了工单创建和消息通知的组合逻辑。消息工具类没有改动因为现有接口已经满足需求。补丁生成后先跑了语法检查和静态分析通过。然后我人工 review发现订单主流程的补丁里两个入口的代码几乎一样只是变量名不同。这其实是原代码本身就有的重复变更 SKILL 忠实地模仿了这种重复风格。我犹豫了一下要不要顺手重构最后还是决定不动因为这次改造的目标是加功能不是清理历史债务。重构可以另开一个任务混在一起做风险太大。风格锚点机制在这里发挥了作用。变更 SKILL 生成的代码在日志格式上跟原文件一致异常处理用了项目里统一的BusinessException没有引入新的异常类型。命名上用了createReturnOrder而不是generateReturnTicket跟现有代码的用词习惯匹配。4.4 验证阶段的自动化与人工复核验证 SKILL 先跑了单元测试订单模块的测试全部通过退货模块有两个测试失败。查看失败原因原来是退货工单创建接口的 mock 数据没有更新测试用例里期望的调用次数跟实际不符。这是预期内的失败更新 mock 数据后重新跑通过。模型复核阶段验证 SKILL 提出了三个问题。第一订单主流程的 try-catch 里只记录了日志没有做补偿或重试如果退货工单创建失败后续是否需要人工介入第二退货服务类的新方法里消息通知是同步调用如果消息服务响应慢会不会拖慢订单取消接口的响应时间第三定时任务取消的订单如果已经发货现有逻辑是直接取消不退货这个行为是否需要跟主流程对齐这三个问题我逐一确认。第一个问题退货工单创建失败确实需要人工介入我在日志里加了告警标记运维可以监控。第二个问题消息通知改成异步发送用现有的消息队列。第三个问题定时任务取消已发货订单的场景理论上不应该存在因为定时任务只处理未支付订单但为了保险我在定时任务里加了一个断言日志如果出现已发货订单被定时取消会打出警告。4.5 合并与上线后的观察补丁合并前我把整个变更 diff 又过了一遍确认没有遗漏。合并后跑了全量回归测试通过。上线后观察了三天订单取消接口的 P99 响应时间没有明显变化退货工单创建成功率符合预期消息通知的异步化也没有出现积压。这次改造从需求拆解到合并上线总共花了大概两个半小时其中人工 review 和确认占了四十分钟左右。如果纯手工做我估计至少要半天而且很容易漏掉定时任务那个边界情况。SKILL 编排的价值在这里体现得很明显它不会累不会因为看了太多文件而失去耐心每个环节的输出都有记录可追溯。5. 常见问题与排查技巧实录5.1 检索阶段漏召回的三种典型情况第一种是命名差异过大。需求说“取消”代码里写的是voidOrder标识符扩展没覆盖到。我的应对方式是在检索 SKILL 里加了一个同义词库把常见的业务动作词做了映射比如取消对应 cancel、void、abort、revoke、invalidate。同义词库不需要很大覆盖高频场景就行剩下的靠调用图遍历兜底。第二种是间接依赖被忽略。比如某个工具函数被反射调用静态分析看不到调用边。这种情况检索 SKILL 很难自动发现我的做法是在分析阶段让模型检查“这个文件是否被其他文件以字符串形式引用”虽然不能百分百覆盖但能捞回一部分。第三种是配置和脚本文件。代码检索往往只搜.java、.py这类源文件但实际影响面可能包括.xml、.yaml、.sql甚至 shell 脚本。我在检索 SKILL 里加了一个文件类型白名单把常见的配置和脚本后缀也纳入搜索范围虽然会增加一些噪音但漏掉配置变更的代价更大。5.2 分析阶段调用链断裂的排查思路调用链断裂通常表现为检索找到了文件 A 和文件 C但分析 SKILL 找不到 A 到 C 的路径。可能的原因有三个。一是中间文件 B 没有被检索到导致链路断了。二是调用关系是动态的比如通过接口实现类调用静态分析只能看到接口方法。三是代码里有条件编译或者特性开关某些调用路径在特定配置下才生效。排查时我会先让分析 SKILL 输出它找到的所有路径然后人工看断裂点在哪里。如果是中间文件缺失手动把文件 B 加入候选列表重新分析。如果是动态调用让模型基于接口定义推断可能的实现类。如果是条件编译检查配置项确认当前生效的路径。避坑技巧分析 SKILL 的提示词里要明确要求它输出“未找到路径”的情况而不是强行拼凑一条看起来合理的链路。我早期版本没加这个约束模型为了“完成任务”会编造调用关系导致后面变更阶段改错地方。5.3 变更生成后代码风格不一致的修正方法风格不一致的表现包括命名习惯不同、日志格式不同、异常处理方式不同、注释风格不同。修正方法分两步。第一步是加强风格锚点从目标文件里多抽几个片段覆盖不同的代码结构比如 if 分支、循环、异常捕获。第二步是在变更 SKILL 的提示词里明确列出项目的编码规范要点比如“日志必须用log.info(xxx, param{}, param)格式”、“异常必须包装成BusinessException并带上错误码”。如果风格问题反复出现可以考虑在编排层加一个“风格检查”环节用规则引擎扫描生成的补丁发现不符合规范的直接打回重生成。这个环节我目前是手动做的因为风格问题不算高频而且人工判断更灵活。5.4 验证阶段测试用例失败的分类处理测试失败分三类。第一类是预期内的失败比如 mock 数据没更新、断言条件需要调整。这类失败处理起来最快更新测试代码即可。第二类是预期外的失败说明变更引入了未预料的行为改变。这类失败必须停下来分析不能简单改测试去迎合代码。第三类是环境问题比如依赖服务没启动、数据库连接超时。这类失败重跑一次通常就能排除。我的处理顺序是先看失败用例的数量和分布如果集中在某个模块大概率是预期内失败。如果分散在多个模块可能是变更影响面比预期大。如果失败用例里包含核心链路的测试优先级最高必须彻底查清。5.5 编排流程本身的性能优化整套流程跑一次大概五到八分钟其中检索和分析占了大头。优化手段有几个。一是并行化检索 SKILL 的三层策略可以并行跑最后合并结果。二是缓存静态调用图和历史提交记录可以缓存不用每次重新生成。三是增量分析如果两次改造任务涉及的文件有重叠可以复用上一次的分析结果。我目前只做了并行化缓存和增量分析还在规划中。并行化之后检索阶段从四十秒降到了二十秒左右效果比较明显。缓存的话要注意失效策略代码库有提交时缓存必须更新否则分析结果会过时。5.6 常见问题速查表问题现象可能原因排查动作解决方式检索结果为空标识符扩展未覆盖检查同义词库补充映射词扩大调用图深度分析链路断裂中间文件缺失或动态调用查看断裂点手动补充候选文件或推断实现类变更补丁风格不符风格锚点不足对比原文件增加锚点片段明确编码规范测试用例失败mock 未更新或逻辑变更分类失败原因更新测试或回退变更流程超时模型响应慢或文件量大查看各阶段耗时并行化、缓存、缩小检索范围模型复核不确定边界条件或隐含假设人工确认补充测试或加防御性代码6. 个人实操体会与后续扩展方向这套 SKILL 编排体系我用了大概半年跑了二十多次存量代码改造任务最大的体会是AI 在存量代码改造里的价值不在于“写得多快”而在于“看得多全”。人脑的短期记忆容量有限看五个文件就开始混淆变量名看十个文件就忘了最初的调用关系。SKILL 编排把“看”这个动作拆成了可重复、可追溯的流程每个环节的输出都落在纸面上你随时可以回去查。另一个体会是编排的粒度比框架的选择更重要。我见过有人用很重的框架做编排每个 SKILL 都要定义复杂的接口和协议结果维护编排逻辑本身的成本超过了改造代码的成本。我的调度器只有三百行SKILL 之间用文件交换数据简单粗暴但够用。工具是拿来解决问题的不是拿来炫技的。后续我打算往两个方向扩展。一是把编排流程产品化做成一个内部工具让团队里其他工程师也能用。这需要把配置项抽出来把日志和报告做得更友好还要加权限控制防止误操作。二是接入更多验证手段比如模糊测试、差异测试、运行时行为对比让验证 SKILL 的覆盖度更高。目前验证主要靠单元测试和模型复核对于并发场景和性能回归的覆盖还不够。最后分享一个小技巧每次改造任务结束后把整个流程的日志和最终 diff 存到一个固定目录按日期和需求编号命名。过几个月回头看你会发现很多改造模式是重复的比如“加字段”、“加状态”、“加通知”这几类需求占了大多数。把这些重复模式提炼成模板下次遇到类似需求时检索和分析阶段可以直接复用历史结果效率还能再提一截。
