☰
AI Agent实战:3周重构10万行“祖传代码”的方法与经验
2026/10/8 3:18:15 网站建设 项目流程

接手这套“祖传代码”的时候,我心里其实挺没底的。一个跑了快十年的老系统,10万行Java代码,混合着XML配置、存储过程还有一堆没人能说清逻辑的历史债务。原来团队给这套系统取了个外号叫“老黄牛”,因为谁也不敢动它——改个字段名都可能触发连锁故障。那段时间我一直在想,用AI写代码已经不是什么新鲜事了,真正让人头疼的是怎么让AI接手这种“大工程”。后来我试着把AI从“写代码的工具”升级成“能自己规划、执行、验证的Agent”,用了一套人机协作的方式,花了三周左右的时间,把这10万行代码做了系统性重构,测试通过率从62%拉到了97%,线上故障率在一个月内降了40%。这篇文章不是讲PPT,是把我整个过程中踩过的坑、试错的方案、最终跑通的流程,完完整整地拆给你们看,希望对正在跟“老天下第一”代码搏斗的人有点参考价值。

先交代一下背景。这套系统是金融行业的对账平台,核心业务是对接不同渠道的交易流水并做差错处理。代码是2015年左右起的项目,用的技术栈在当时算主流——Spring MVC + MyBatis + Oracle存储过程,前端是老的JSP加一点jQuery。说实话那个年代这种架构没什么问题,但跑到现在痛点就很明确了:一是业务逻辑大量堆在Controller层和存储过程里,想加一个功能要动五六个文件;二是没有任何自动化测试,当年都是靠“人肉点验”上线;三是数据库表结构设计存在大量冗余,join超过五张表的SQL随处可见。这些问题的核心就是“不敢改”,而“不敢改”又导致没人去优化,形成恶性循环。我一开始也考虑过直接推倒重写,但评估下来工作量太大且业务细节早已散落在历史提交和口头传承里,风险完全不可控。所以最后结论是:走结构化重构,用Agent来做大量的重复性迁移和验证工作,我负责设计边界和兜底。

真正开始动手之前,我先做了两件事:一件是把代码库的现状量化摸清楚,另一件是定义什么叫“重构完成”。代码量化这块,我写了一些分析脚本,统计循环依赖、重复代码块、超长方法数和存储过程的调用关系。一圈跑下来数据很有意思:整套10万行代码里,Controller层平均每层有1200行,最夸张的类有2600行;存储过程有47个,其中11个超过500行;重复代码片段占比大约18%。这些数据帮我确认了重构的几个重点区域,也让我能向其他人解释为什么非得动这个“老黄牛”不可。至于“重构完成”的定义,我定成三个可量化的标准:第一,所有业务逻辑从Controller层和存储过程中迁出,统一收口到Service层;第二,识别出来的重复代码块消除80%以上;第三,核心交易链路的自动化测试覆盖率从0拉到70%以上,编译、单测、接口测试全绿。

这两步做完之后,我心里其实已经有一定把握了。重构的本质不是“把代码重写一遍”,而是“在保持外部行为不变的前提下调整内部结构”,基于这个逻辑,AI的优势其实非常大:它能在短时间内读完大量文件并理解其中重复出现的模式,而人类做这件事容易疲劳并且前后标准不一致。但AI写代码的问题是它只能处理“局部任务”,让它改一个方法没问题,让它跨二十个文件、协调上下游改动、跑完测试再自我纠错,这就超出了普通补全工具的能力范围。所以我当时的核心思路很明确:把AI从“编码器”变成一个“带规划能力的执行者”,也就是Agent。Agent和内AI写代码的最大区别在于它具备一个闭环:感知环境、规划动作、执行修改、观察结果、调整策略。正是这个闭环让它能处理重构这种多步骤、多文件、有依赖关系的复杂任务。

章节内容再往深一层说。Agent的架构我没有做得特别复杂,按“规划器—执行器—验证器”三个组件来设计的,配合一个围绕Git和CI构建的沙箱环境。规划器负责读取我拆好的“任务卡”,根据代码库分析结果输出改动方案;执行器负责具体到文件级修改,生成diff补丁;验证器负责把改动应用后进行编译、跑测试、检查静态代码指标,然后把结果反馈给规划器,决定是继续还是回退。整套流程用一个Python编写的编排脚本调度,核心就是控制Agent不要“瞎跑”:每一个环节都设置了明确的退出条件,比如编译不过就必须回退到上一版再重新生成,测试覆盖率低于阈值就自动打回。这套机制跑起来之后效果比预期好,因为Agent一开始也很容易出现那种“改了A就忘了B”的情况,但有了自动验证和回退闭环之后,大部分低级错误在Agent内部就被消化掉了,不会流到我面前。

做完基础架构设计,接下来要考虑的就是怎么给Agent“派活”。一开始我踩了一个很典型的坑——试图把整个项目一次性丢给Agent,让它自主去重构。结果当然很惨:上下文窗口根本装不下这么多文件,Agent到后面甚至开始“编造”接口名和类名,我收到了一堆看起来合理但根本编译不过的代码。那之后我想明白了一点:大型重构要让Agent做,但必须把任务拆到它可以理解和执行的颗粒度。我的做法是先把系统按照业务域拆成12个模块,然后每个模块再拆成若干“重构单元”,最小的单元大约覆盖三到八个文件,保证Agent能在上下文窗口内读完所有相关代码并给出完整方案。拆完之后,每个任务卡上写明这个单元的边界、涉及文件列表、风险点、以及必须保持不变的对外接口有哪些,然后才交给Agent。

这里分享一个关键经验:任务卡的写法直接影响Agent输出质量。第一次我写任务卡写得比较粗糙,就一句话“把这个Controller的业务逻辑搬走”,Agent返回的方案五花八门,有的改了接口签名,有的把方法直接删了却忘了处理调用方。后来我参考了给人类外包程序员写需求的做法,把任务卡改成包含背景、范围、规则、验证标准四个部分,每个部分尽量具体。比如规则里明确“不允许修改Controller对外暴露的URL路径”“不允许改变DTO字段名称”“数据库访问统一迁移到新Mapper接口”,Agent的输出一下子就稳了很多。我实测下来,同一个Agent模型在“模糊任务”和“精细任务”两种输入下的可用产出差距是巨大的,前者有三成左右的代码需要返工,后者返工率压到了10%以内。

再说说具体实操的时候怎么跑的。整个重构过程我分成了四轮来做,每一轮都聚焦一种模式,而不是按模块顺序平推。这样做的好处是Agent可以在同类任务上积累经验,后面的产出质量会越来越高。第一轮做的是“Controller瘦身”,把所有业务逻辑从Controller层迁移到Service层,迁移过程中生成新的Service接口和实现类;第二轮是“存储过程替换”,把47个存储过程里的核心逻辑拆解出来,改写成Java代码并落到新DAO层;第三轮是“重复代码消除”,基于静态分析结果把重复片段提取成公共工具方法或基类;第四轮是“依赖清理”,把那些已经没人调用的死代码、冗余配置和无效依赖移除掉。每一轮结束之后都会跑一次完整的编译和测试,确保当前状态是可部署的,然后再进入下一轮。

每一轮在具体执行的时候,我都让Agent遵循“先分析、再生成、后自检”的工作流。以第一轮的Controller瘦身为例子说明。第一步,Agent会读取任务卡里指定的Controller和Service相关文件,分析出哪些方法是纯业务逻辑、哪些是参数组装、哪些是外部依赖调用,然后输出一个迁移清单,包括方法明细、目标类、以及需要新建的依赖接口。这个清单会先给我用人工过一遍,确认方向没问题后才会进入下一步。第二步,Agent开始生成目标代码,但并不是一次性地把大段代码写出来,而是按照文件粒度或者说方法粒度生成diff补丁,每个补丁都配有commit message。第三步,Agent会运行一个我预先写好的检查脚本,对改动前后的方法签名进行比对、找出所有调用方、确认URL映射没有变化,全部通过后才提交到特性分支。每轮跑下来大约会产生40到60个commit,虽然数量多,但每个commit都小而清晰,出问题时回滚也非常容易。

这里要特别说一下Agent生成代码时的提示词设计。我并没有用那种“帮我重构这个系统”的玄学提示词,而是把提示词当成一套完整的操作手册来写。每次下发任务,我先给Agent几条“黄金示例”——即三组改动前和改动后的代码对照,让它明确理解我要求的迁移模式。然后给一份“负面清单”,列出绝对不允许做的事,比如不许给DTO加新字段、不许改动公共方法的参数列表、不许在Service层直接执行SQL。最后再给“输出要求”,比如每一个迁移方法必须保留原始注释中的业务背景信息、必须补充单测用例的骨架。这些提示词看起来朴素,但其实是Agent生产能力的关键,因为大模型对“示例”的理解能力远强于对“抽象描述”的理解能力,给三组正反例比写一千字说明都管用。

存储过程替换那一轮是最让我紧张的,因为这类逻辑涉及的数据库行为复杂度高,Agent写Java代码替换时非常容易漏掉事务边界和并发控制。比如某个存储过程里有一段先查后写的逻辑,原实现是依赖Oracle的隐式锁来保证数据一致的,但Agent在改写时很可能就简单翻译成“查出来,改一下,写回去”,根本没有考虑并发场景。这种问题靠编译和测试都很难暴露,必须靠人工来review。所以我当时定了一条规则:涉及事务、锁、批量数据处理三类场景的存储过程替换,Agent生成的代码不直接应用,而是先输出给资深开发人工评审,确认无误后再合入。这确实降低了自动化程度,但换来了安全边际,我觉得在做金融系统重构时这个取舍是值得的。事实上最后统计下来,47个存储过程里有9个因为涉及复杂事务和并发控制,改由人工主导重写,剩下38个完全由Agent生成并经测试验证。

再有一个很有价值的判断——Agent重构过程中,测试策略不是“事后补”,而是“事前建”。当时系统里几乎没有自动化测试,如果直接让Agent大规模改代码,一旦出错根本不知道从哪里查起。因此我在重构开始的前两天,做了一个什么功能开发都不做的“测试基座”:把核心交易链路上20多条关键场景用接口测试和集成测试的形式固化下来,覆盖了登录鉴权、渠道接入、交易对账、差错处理这几条主链路。这20多条用例就是整个重构期间的“安全网”。Agent每改动一批代码,CI就跑这一套测试,再加上编译检查,任何行为变化都会被第一时间扑到。事实证明这套机制救了无数次,最大的一次是Agent在重构对账逻辑时把时间窗口的计算方式改了,测试直接标红,我们才发现它在“优化”的过程中改变了业务语义——如果不是测试拦住了,上线之后就会出现批量对账误差,后果很严重。

不过即便有这些准备,实际操作中我还是遇到了不少问题。常见的几个我总结成了一个小册子,给团队的其他人也做了一份。

第一个高频问题就是Agent“修复式误导”。这个问题主要出现在Agent发现编译错误后自行决定修改其他相关文件,比如某方法参数被改动导致调用方编译失败,Agent为了“修复”调用方,强行在调用前加了一个类型转换或者塞入一个默认值,表面上看编译通过了,但实际语义已经悄悄改变。这种问题很难通过规则描述来完全禁止,因为Agent会觉得它是在帮忙。我的应对思路是给Agent设置“建议模式”和“执行模式”的切换:当改动范围超出任务卡列出的白名单文件时,Agent只输出建议清单,不直接生成修改代码,由我来决定要不要放行。相当于给Agent加了一道“刹车”,把风险控制在可控范围内。

第二个问题是长文件、长上下文的“记忆衰减”。当一批任务涉及的文件比较多,Agent的注意力会从中间开始漂移,到后面容易出现“前后矛盾”的情况,比如前面说某个接口要废弃,后面又在用它。这个问题我跟它斗争了很久,后来找到了比较有效的方式:让Agent在处理过程中持续维护一个“变更日志”,每完成一步改动就在日志中追加一条,包括文件、方法、改动类型、状态这些字段。这个日志不仅给人类用来追溯,也会回传给Agent自己,成为后续决策的参考。这招本质上是用外部记忆来弥补大模型上下文窗口的局限,实测下来效果很明显,前后矛盾和重复改动的问题大幅减少。

第三个问题更隐蔽——测试“假绿”。重构期间有一段时间测试通过率看着挺高,但我总觉得不对劲。后来排查发现,Agent生成的测试里存在“断言失效”的情况,也就是测试方法本身写得有问题,无论代码行为变没变,它都会通过。比如断言里写的是assertNotNull(response),压根没有对返回结果的具体字段做校验,这种测试看起来绿了,实际等于没有。这类问题是AI生成测试的通病,因为它模仿了已有测试的浅层写法。我的对策是把测试模板提前固化,定义好几类必须包含的断言模板,比如状态码、关键字段、异常场景、边界值,然后让Agent只能在这个模板的框架内补充场景,不能自由发挥断言逻辑。后来我又加了一道“变异测试”的思路:故意在业务代码里埋几个小错误,看测试能不能跑红,跑不红就说明测试有效性不足,需要回炉。

还有一个组织协作层面的问题——多个Agent同时开工时容易在公共文件上互相覆盖。后期为了提速,我试过多Agent并行跑不同模块,但很快发现两个Agent同时改了公共工具类或者共用Mapper时,Git冲突就会变得极其麻烦,而且冲突解决时极易丢失某一方的改动语义。后来我采用了“文件锁”方案:在编排层为公共文件设置状态,同一时间内只允许一个Agent持有该文件的修改权,其他Agent如果在任务里需要动这些文件,就进入等待队列。这其实就是分布式系统设计里的乐观锁思路,只不过应用在Agent协作场景上。当然这个方案牺牲了一部分并行度,但换来的是冲突问题基本归零,团队的代码评审压力也小了很多。

抽检和验收环节也跟大家拆一下。我自己对整个重构工作会做三层抽检:第一层是“AI灰盒”抽检,也就是让Agent自己扮演一个“代码评审员”,对另一路Agent的产出做交叉检查——检查逻辑一致性、接口完整性、异常处理、边界条件等等,把可疑点标注出来,我再来复核。这个做法利用了不同模型或不同提示词下Agent关注点不同的特性,往往能看出一些我自己遗漏的盲区。第二层是“核心路径人工review”,对涉及金额计算、状态流转、批次处理这几类高敏逻辑,我会逐行过代码,亲自跑一遍本地example,不让Agent代劳。第三层是“上线前全量回归”,这个不用多说,只是我们额外加了线上灰度观察期,用来确认静态逻辑到了真实环境也是稳定可靠的。

这整个流程走下来我个人最大的感受是——用Agent重构不是一个“按一下按钮就完成”的魔法过程,而是一套工程管理方法。Agent是执行层,真正决定成败的反而是重构之前的拆解、规则设定、任务卡编写,以及重构过程中对边界的把控和对风险的持续监测。原本“10万行祖传代码”这个量级让我心里发虚,总觉得靠人力一两年都难彻底扭过来,但走了Agent这条路之后,三周就把核心部分翻完了。当然也不能神话Agent,它做得好是因为我把它的活动范围、输入输出格式、验证反馈机制全部约束住了,它是在一条清晰轨道上做高速运行,而不是真的在独立“思考”。

后面我又把这套方法复制到了另一个老项目的接口现代化改造上,流程几乎是直接复用:分析现状、拆模块、写任务卡、定测试基座、Agent生成、人工兜底。虽然每个项目的具体技术栈不同,但大的方法论是完全通用的。如果你手里也有一套饱受诟病的“祖传代码”,我的建议是先别急着上Agent,把你的边界条件想清楚,把安全网搭起来,再去考虑怎么让AI帮你动手。工具只是放大器,你的判断和组织能力,才是重构项目真正的地基。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询