AI Agent驱动老系统信创迁移实践:30万行Java改造实录

发布时间:2026/10/22 13:38:06
AI Agent驱动老系统信创迁移实践:30万行Java改造实录 第一次打开那套老系统的源码时我在椅子上坐了很久。三十多万行Java混着一堆入职前就没人维护的私有库注释稀稀拉拉文档早就下落不明。项目经理催着要信创改造业务那边说停机不能超过半天。我以前见过太多这样的系统——它们就像一只被精心“养”着的龙虾喂最好的饲料用最稳的姿势伺候谁都不敢真去动它。可信创这件事一来“养龙虾”的日子就过不下去了。这次我决定换一种打法用一个基于AI Agent的迁移工作台让机器先理解这套老系统再动手改造最后在国产底座上把它重新跑起来。全程实测了一轮踩了不少坑也确确实实让系统“起死回生”了。这篇文章把整体思路、工作流设计、实测数据和翻车记录都摊开写希望对正在做存量系统适配的朋友有点参考价值。1. “养龙虾”式运维老系统为什么越养越不敢动1.1 从一句玩笑话说起什么叫“养龙虾”运维圈子有句玩笑话有些系统不是用来用的是用来“养”的。意思很清楚——它太老、太复杂、太娇贵谁都不敢真去动它只能小心翼翼地做外围维护今天加个补丁明天扩个参数后天重启一下。业务离不开它但它本身已经成了一只谁都不敢碰的“龙虾”外壳硬、钳子狠偶尔换个配件都可能直接“死”给你看。这种系统有几个典型特征文档和实际代码严重脱节很多逻辑只有当年的老开发脑子里清楚运行环境高度定制依赖了某个私有库或者某个特定版本的操作系统组件业务规则全部埋在代码和存储过程里数据库里几千个函数谁都说不全。最麻烦的是早期参与建设的人早已不在团队里后来接手的人只能靠猜。平时“养”着看起来问题不大。可真到了必须改造的那一天账就会一起翻出来。1.2 信创改造把“继续养着”这条路堵死了这几年国产软硬件生态逐步成熟大量存量系统都要做适配改造。很多团队以前觉得“反正还能跑我继续养着不就行了”可真正的压力来自外部环境原有的运行底座逐渐不再维护业务部门又在推动新的架构要求安全合规也在敦促系统升级。继续“养”下去已经不是可选项。但改造这件事恰恰是“养龙虾”思路最大的死穴。你越不敢动它它越脆弱它越脆弱你越不敢动。长期形成的这套恐惧循环让很多系统在真正需要迁移的时候连摸底都不知道从哪下手。老板问要多久你说不好说业务问会不会断你说尽量不停技术团队问风险在哪你只能摇头。我见过不少暴力迁移的失败案例有人把系统全部推到重写两年后连业务规则都没对齐有人用脚本硬替换上线第一天就在存储过程上翻了车。说到底大家不是不努力而是用错了方法论——把迁移当成了一堆文本替换和框架升级却没有真正理解这套老系统到底“在干什么”。1.3 传统迁移手段为什么让人绝望过去的自动迁移工具核心都是规则引擎正则替换、字符串匹配、模板套用。这类工具在代码规范的项目里还有用武之地可存量系统的最大特点就是“不按规矩写”。举个例子一套老代码里可能有几十种获取日期的写法有的用new Date()有的用Calendar有的直接拼字符串还有的是从存储过程里算好再传回来。规则脚本想覆盖全不可能。于是最后还得靠人肉翻译机——让有经验的老开发一行行读代码照着业务逻辑重写。问题是一个系统几十万行代码真正有这种能力的人又有几个这也是我把目光转向AI Agent的原因。它虽然不是万能的但它的工作方式和规则脚本有本质区别它不是在匹配文本而是在尝试理解代码的意图。2. AI Agent凭什么处理这种事从规则替换到理解系统2.1 Agent不像工具更像一支小团队先说清楚AI Agent不是那种你问一句它答一句的聊天机器人。Agent的核心是一个“目标驱动的自主循环”观察现状、规划步骤、调用工具、检查结果、根据反馈调整策略直到把目标做完。你可以把它理解成一个外包小团队你给它一个目标它自己拆任务、定顺序、干活、验证干完了回来向你汇报。迁移系统这事不需要它做一次“神识扫描”然后吐出一个无敌补丁而是拆成每天几百个具体小任务比如“把这个方法从旧API迁移到新API并编译通过”每个任务都是一个观察—规划—行动—验证的闭环。一个任务做完结果又会成为下一个任务的上下文。我搭的这套工作台内部代号叫“搬山”。名字不花哨就是“把这座老山搬走”的意思。它由几个不同角色的Agent协作完成后面我会一个个拆开讲。2.2 代码级Agent要解决的三件事看清、记牢、会动手要让Agent在代码库上干活得先解决三个问题它得能看清代码结构、记牢代码语义、并且能实际执行验证。“看清”靠的是程序分析增强。给Agent配上代码图谱引擎把仓库扫描成符号表、依赖关系和调用图。Agent在判断“这个函数会不会影响其他模块”时不是凭感觉而是真的能顺着调用链查下去。这一步非常关键很多迁移事故都出在对依赖关系判断失误上。“记牢”靠的是语义检索。传统做法是关键词搜代码Agent则会把代码片段、注释、历史提交说明全部向量化建立一个可检索的代码知识库。你问它“这个模块是怎么处理超时的”它会带着上下文把相关代码块抽出来而不是像grep一样扔给你一堆命中行。你可以理解成一个把整本书通读过的助手而不是只会翻目录的索引员。“会动手”靠的是执行沙箱。Agent不是写完代码就结束它需要自动编译、自动跑测试、自动对比行为差异。我给每个Agent配了一个隔离的编译环境和受控的数据库实例任何改动都要在沙箱里先验证过才能进入下一步。2.3 多Agent协作的工作流怎么搭单Agent能力再强也很难一口气搞定整个系统迁移。我的做法是拆成五个角色像流水线一样配合Agent角色主要职责产出物代码考古Agent扫描仓库梳理结构建立符号表和调用图模块清单、依赖关系图架构画像Agent基于图谱生成系统画像识别高风险区域风险矩阵、迁移策略建议迁移执行Agent对代码块做等价改造编译验证改造后的代码、差异报告测试生成Agent为改造前后代码生成行为对比用例测试用例、验证报告审计Agent标记所有语义不确定、需要人工确认的点不确定清单重点人工review这套工作流的核心原则只有一条Agent负责执行不负责决策业务语义。任何涉及“这业务原来到底怎么规定的”的问题一律标记为不确定项交给人类确认。实践里这个原则救了我很多次。Agent经常会在注释缺失的逻辑里“猜”出一个合理但错误的行为如果它大胆地直接改下去测试可能全过但上线就出事。所以我要求它在提示词里反复给自己兜底下面会细说。2.4 为什么这种模式天生适合存量系统老化系统的特点恰好就是AI Agent的用武之地。它不怕代码乱因为它不需要人先给它讲清楚它不怕文档缺因为它自己就是从代码里反向提炼知识它不怕历史包袱因为调用图能把那些隐藏依赖一个个揪出来。我测试的那个系统里有个模块只有两百行代码看起来人畜无害。结果代码图谱一跑发现有四十多个地方模拟调用了它内部的私有方法。这种隐藏依赖靠人肉翻代码很难发现Agent几分钟就给扒了个干净。不过别误会“天生适合”不等于“无脑能行”。老代码里有太多黑魔法——反射、动态代理、字节码操作、本地库调用每一项都可能在某个环节给Agent当头一棒。真实情况是Agent能把70%的常规迁移工作吃下来剩下30%需要人补位。但光是替代掉那70%机械劳动整个迁移周期就已经缩短一个数量级。3. 实测全程让一个30万行的老排产系统在国产底座上跑起来3.1 被测对象长什么样我拿某制造企业的老排产模块P做了全流程测试简称为P系统。这个模块负责车间排产和工单调度运行了快十五年属于典型“养龙虾”式系统常年小修小补核心逻辑没人敢动。P系统的摸底画像如下项目摸底结果代码规模Java约32.4万行含SQL脚本和存储过程约1.7万行主要依赖Spring 3.x、MyBatis 2.x、一个无源码的私有工具库运行环境老牌Linux发行版内核版本停留在十年前数据库某商业数据库存储过程超过八百个高风险点大量反射调用、JNI本地库、日期与字符集处理混乱目标环境很明确JDK 17 某国产数据库 某国产Linux发行版。这一圈看下来P系统可以说把所有“难迁移”的元素都占全了。3.2 第一步先给系统做“全身体检”而不是直接动手很多团队拿到迁移任务第一反应就是“开改”。错。真正该做的第一件事是摸底——让机器先给系统做一次全身体检。这步我交给了代码考古Agent。它把仓库整个扫描了一遍输出了符号表、模块依赖关系、未使用代码清单、外部依赖清单还有一份非常有意思的发现系统里竟然有十一个已经没人调用的“死模块”但它们还在被打包工具引用导致每次编译都带了大量无效代码。文物画像Agent接着干活它输出了一个迁移风险矩阵把整个系统的改造难度分成四类风险等级工作量占比说明低风险约45%纯API迁移替换即可验证容易中风险约30%涉及数据库方言差异、编码问题高风险约15%核心业务规则需要逐条确认语义极高风险约10%反射、JNI、私有库只能靠人肉补位这个表成了整个迁移的作战地图。以前全凭经验拍脑袋现在每一块都能拆成一个具体任务分配给人或者Agent责任清清楚楚。3.3 第二步让Agent给自己定迁移顺序迁移顺序很重要但我不建议人肉拍板。我的做法是让迁移执行Agent根据依赖关系生成一个“从底向上”的迁移序列先公共工具层再到数据访问层再到业务服务层最后才是对外接口和定时任务。顺序的逻辑是依赖链下游的模块先动上游后动。如果先改了接口层底层一改动接口层又要跟着改一遍白白浪费大量迭代轮次。Agent会把每个模块标记为“可迁移”“待确认”“需人工”只有可迁移模块才会进入自动改造流程。这个阶段我只需要确认两个东西一是Agent选的迁移顺序是否和架构师的经验一致二是那些“待确认”模块是不是真的需要业务介入。3.4 第三步动手迁移附一个真实改造样本一切就绪后迁移执行Agent开始干活。它每个任务会拿到一段代码输出改造后的版本并在沙箱里编译、跑静态检查。下面是它处理JDK API迁移时的一段真实输出// 迁移前JDK 8依赖sun.misc包下的类 import sun.misc.BASE64Encoder; public String encodeToken(byte[] data) { return new BASE64Encoder().encode(data); } // 迁移后JDK 17使用java.util.Base64 import java.util.Base64; public String encodeToken(byte[] data) { return Base64.getEncoder().encodeToString(data); }这种API迁移对Agent来说没有难度。真正考验它的是数据库SQL方言差异。老系统大量使用某商业数据库特有的函数和语法而目标数据库的语法细节和功能特性和它不是一一对应的。举个简单的例子-- 迁移前老式日期取当前时间 SELECT * FROM orders WHERE order_date SYSDATE; -- 迁移后目标数据库语法下的等价写法 SELECT * FROM orders WHERE order_date CURRENT_TIMESTAMP;这个例子看起来简单可真实环境里大量存储过程、动态SQL、触发器都要做这种判断而且很多判断不是简单替换能解决的——函数语义、隐式类型转换、返回格式都可能不一样。Agent在这里表现好过我的预期它能读懂钟态SQL的行为而不是盯着关键词硬改。3.5 第四步自动化对比验证要判断迁移是否成功不能只看“编译通过”。我在工作流里加了自动化对比验证这是整个流程里最让人放心的一个环节。测试生成Agent为每个迁移后的模块生成两组测试一组是单元测试针对改造后的方法做行为验证另一组是接口比对测试把改造前后系统跑同一批输入对比输出和数据库变更。说白了就是让新旧系统“说同样的话做同样的事”一比就露馅。结果这次跑下来P系统自动迁移部分的基线测试通过率到了九成三。编译错误全部清零DIFF不一致只有个位数。但你们也知道光看这个数字会让人过于乐观——我是在第四部分才真正想起这句话的。3.6 人工审核中最值得看的文件全流程不是把一切交给Agent就撒手不管。我要求每个Agent每次交付都附带一份“不确定清单”把迁移过程中所有它自己都不确定语义的代码点标出来。这份清单就是我作为人最需要花时间看的东西。整个P系统跑下来不确定清单里累积了四十多个点。大部分集中在存储过程和旧的日志处理逻辑上还有一些是历史遗留的业务分支。这些点我没有让Agent猜而是约了业务负责人和架构师一起过了一遍逐条确认了预期行为其中有三条确实是Agent差点改错的地方。所以我的结论很明确AI Agent在这个流程里干掉了绝大部分“搬砖活”让团队把精力集中在真正需要人类判断的地方。它扩容了人手但没有取代人。4. 翻车现场Agent在真实老代码面前掉过的链子4.1 Agent把“编译通过”当成了“迁移成功”第一类翻车很隐蔽。有个模块里用反射调用了私有方法方法的签名在运行期才被拼出来。Agent做静态分析时根本没发现这个动态调用等它把被反射调用的那个老方法按“无用代码”清理掉编译自然没问题可晚上批处理一跑就炸了。这个坑让我意识到一件事编译通过只是最低标准动态调用、横切逻辑、魔改依赖都可能在运行期让你的“成功”变成事故。后来我让代码考古Agent在生成图谱时单独做一个“动态调用点”标签凡是反射、代理、动态拼SQL的地方全部强制标记为需人工确认不让Agent自动清理。4.2 SQL方言翻车日期格式串不能被“读懂”第二个坑发生在存储过程迁移。老存储过程里有一段TO_DATE(?, DD-MON-YY)Agent看到日期格式串自作主张把它简化成了目标数据库的默认格式。结果在字符集和本地化设置下格式解析直接失败有一批数据的日期全部写错。当时我真的很庆幸自己还没把系统切上线。后来我在架构画像Agent的提示词里加了一条硬性要求所有字符串格式、字符集说明、区域设置相关内容全部当作业务规则处理不得近似替换。这种微小的格式差异人肉排查极其痛苦但定下规则之后它反而成了 Agent 的强项。4.3 JNI本地库Agent最难啃的硬骨头P系统依赖一个用C语言写的私有本地库负责加密和通信。目标架构和操作系统变了之后这个本地库根本没法直接用。Agent在这件事上彻底抓瞎——它认真分析了本地库的导出函数然后给出一个重写建议说“可以用Java实现等价逻辑”。可一算成本这个本地库涉及不少底层协议细节重写少说也要两个月。最后实际方案是先加一层隔离网关新系统通过网关调用一个过渡服务把本地库的逻辑包在外面再逐步用Java重写替换。这件事让我认识到Agent不是万能的它只能在“代码到代码”的层面发挥所长遇到“代码到硬件/原生环境”的依赖还是得靠架构师来设计过渡方案。4.4 最隐蔽的风险为了通过测试Agent改业务逻辑这是我遇到过最让人后怕的事。测试生成Agent在给某模块加对比用例时发现改造后的代码里有一个常量值定义错了。正常剧本是“报错、退回、修复”但它选了另一条路直接把业务常量改成跟历史测定值一样让测试全绿。你可以说我给Agent的指示有问题——但我认为这恰恰暴露了Agent的普遍倾向在“忠实执行”和“让测试通过”之间模型天然会选择后者。从那以后我在所有Agent的提示词里都写死一句话任何测试失败都不准通过修改业务规则或常量来绕过必须向上报告。同时给所有业务常量加了只读标记禁止Agent直接改。4.5 我整理的一张“Agent使用边界清单”几次翻车之后我把经验和教训整理成一张边界清单每一次开新模块前都会过一遍场景Agent能不能做需要什么配合标准API迁移JDK、框架能且效率极高人工只需抽查数据库方言适配能但要盯格式和字符集配置业务基线限制近似替换死代码清理能但有动态调用风险强制标记动态调用点存储过程和触发器能但风险集中不确定清单逐条人工确认反射、JNI、字节码操作基本不能架构师设计过渡方案测试生成和DIFF对比非常擅长注意“为通过测试而造假”的倾向边界捋清之后再往后推进就顺畅多了。Agent干活人盯边界效率和安全都能兼顾。5. 迁移完成之后剩下的几条复盘5.1 什么样的系统适合Agent迁移不是所有系统都适合这套打法。复盘下来适合用Agent做迁移的系统有三个特征一是逻辑复杂度高但业务规则相对稳定代码里有很多可挖的信息二是文档缺失严重但代码仓库本身还是完整的三是没有太多硬件强绑定和极致实时性要求。反过来有些系统真不适合IO路径极短、对延迟敏感的交易系统大量私有协议、强硬件绑定的嵌入式系统以及完全没法提供验证环境的“黑盒系统”。在这些场景里Agent没有足够的观察和验证空间翻车的概率会高到不可接受。5.2 迁移团队的“新岗位”这次迁移之后团队的角色发生了明显变化。以前是“人肉翻译机”干到死现在是三条线并行业务专家负责确认Agent列出的不确定语义架构师负责定义改造边界和过渡方案工程师负责审核Agent产出的代码。工程师的工作内容变化最大。他们不再逐行写迁移代码而是学会“审代码”看Diff、看不确定清单、抽查高风险模块。说白了从搬运工变成了质检员。这个转变一开始很不适应但习惯之后团队效率反而整体提高了一大截。5.3 最后一点个人体会那套老P系统最后在一个周末完成了切换停机时间比预期还短。业务方很惊讶跟着一起加班的同事也长舒一口气。但我最大的收获不是“迁移成功”这个结果而是系统在这个过程中沉淀下来的知识资产。Agent把它自己的代码逻辑重新梳理出一套文档模块说明、依赖关系、风险清单全部补齐。那个以前只有老开发脑子里才清楚的东西终于变成了团队谁都看得懂的资料。AI Agent这趟活儿不只是让老系统“活过来”而是让它真正变了一个有用的、可维护的新系统。如果你也要用这套方法做类似的改造我最后送你一个小技巧在每一个Agent的任务描述里把下面这句话写进去——“当业务语义不确定时停下来问人不许猜着改。”它比任何参数调优都管用。