Mistral AI 复盘如何用 AI 智能体迁移复杂遗留代码
Modernizing complex legacy code with AI agents.
Mistral AI 复盘了协助一家欧洲能源运营商将 40,000 行 Fortran 77 储层模拟代码迁移到 C++ 的实践。团队先建立数值一致性验证框架并整理文档,在尝试全自主迁移和多智能体协作后,采用人工监督编码、测试和审查智能体逐模块迁移的流程。首轮覆盖总计 300,000 行代码中的核心功能,原代码可独立运行;依赖外部系统或缺少可运行基线的迁移不在本次讨论范围内。
案例将数值一致性验证、文档整理和人工审查串成迁移流程,为复杂遗留系统如何划分智能体职责提供了可迁移的方法。
遗留科学计算代码库经过数十年的积累,一旦原作者离开,蕴含在代码中的知识就变得难以还原。此外,使用缺乏活跃开发者生态的语言,意味着错失基于他人成果继续构建的机会。Mistral 帮助一家欧洲能源运营商将 40,000 行 Fortran 77 代码迁移到了 C++。这些代码构成了一个涉及大量物理计算的油藏模拟器,既没有测试套件,也没有集中式文档。
遗留代码现代化:不止于代码翻译。
将语法从一种语言转换为另一种语言,在很大程度上已是解决了的问题。让任何较新的模型将一段并非极其冷门的语言代码转换为另一种语言,通常只需几轮迭代就能得到可接受的结果。然而,将整个系统从过程式语言迁移到面向对象的 C++,需要进行架构重构,这使得任务并不简单。
顾名思义,Fortran 77 于 1977 年完成标准化,用它编写的代码直接体现了当时的限制:没有模块、没有命名空间、没有结构化类型。状态存放在 COMMON 块中,即整个程序共享的全局内存。变量类型由名称的首字母隐式确定,因此,拼错名称会悄无声息地创建一个新变量,而不是触发编译器错误。
举一个简单但有代表性的例子,来看下面的一阶泰勒展开实现。输入和输出都是 COMMON 块中的全局变量,而 IC 之所以是整数,仅仅是因为它的名称以 I 到 N 之间的字母开头。变量名相当晦涩,因为其长度上限为 6 个字符。
在 C++ 中,可以利用显式类型、编写面向对象的代码,并通过返回值而非写入全局变量来输出结果:
分散的 COMMON 数组变成了单个 GasProperties 参数,网格循环也移到了调用方,因此无法逐行核对对应关系,这正是迁移验证困难的原因。
这些结构性差异,加上集成 PetSc 等现代科学计算框架的要求,使得在迁移开始之前,就有几个重要问题需要考虑:
如何证明迁移后的代码库与遗留代码库在数值上保持一致
如何将迁移拆分为可管理的部分
如何最有效地利用自主智能体来加快进程
在迁移遗留代码之前构建一致性验证框架。
在放手让智能体开展工作之前,我们需要一种方法来证明两个代码库的结果一致。在这里,“一致”是指输出在数值上相等,既包括最终结果,也包括客户的油藏工程师标出的若干关键中间节点的结果。
我们添加了:
用于导出 Fortran 代码库状态的子程序
用于将检查点加载到 C++ 中的测试框架
用于引导智能体正确使用上述工具的 Skill.md 文件

在迁移工作流中,智能体成功地为 Fortran 代码库添加了插桩,以转储状态快照,并使用 C++ 测试框架验证了迁移后模块的正确性。
优先构建验证框架的这一部分,为项目带来了净收益:它让智能体的长时间运行更安全,而数值一致性也易于验证,能够有力地证明一段代码已成功迁移。我们认为,这应当成为任何代码现代化项目的首批步骤之一。
下面的示例说明了这一点。我们先在 Fortran 代码中插入一行,用于输出 RHOG 变量的值(本次运行中为 42.71834),然后在测试迁移后的 C++ 模块时,使用同一个值作为基准检查点。

使用 AI 智能体理解遗留代码库并为其编写文档。
项目文档散落在旧 PDF 文件和深埋于 Fortran 代码中的注释里,整个工作带来的最大附带收益之一,就是整合这些文档并将其移到代码旁边。
幸运的是,Fortran 这类过程式代码有一个便利的特性:整个程序可以绘制成一棵调用关系树。我们用自定义解析器解析代码库,生成了这棵树,然后使用 Vibe CLI 启动了一百多个智能体来为其编写文档。每个智能体都可以通过文档库和 Mistral OCR 获取相关 PDF 文件。
从树的叶节点开始向上推进,每个节点都会启动一个子智能体,为其编写文档并向原始代码仓库提交 PR。一个按 cron 计划循环运行的审查智能体会查找新提交的 PR,对其进行审查,并在必要时安排修复任务。

利用 AI 智能体实现代码现代化。
第一次尝试时,我们赋予智能体完全的自主权:每个 Fortran 子例程对应一个智能体,各自在一周内独立将其功能转换为 C++。结果可以正常运行,但称不上代码现代化。COMMON 块被一对一地转换成了全局结构体。由 GOTO 驱动的控制流原封不动地保留下来,而没有重构成循环或提前返回。它看起来更像是用 C++ 语法重新敲了一遍的 Fortran,而不是现代化的代码。
第二次尝试时,我们不再只是赋予智能体自主权,而是通过为其提供结构化分工来解决这个问题:规划者、编码者、测试者和代码质量审查者共同处理每个模块。代码质量比第一次尝试有了显著提升。但源代码的复杂性最终还是难住了智能体。它们遇到错误后,会尝试修复几次,然后陷入停滞,而此时没有人可以介入。
我们最终采取了折中方案:由人来操作一个包含编码、测试和审查智能体的工作流,逐个模块迁移代码库。这样既保留了第二次尝试的代码质量,又增加了人工检查环节,在智能体卡住时帮助它们继续推进。下一节将详细介绍这一工作流。
运行结构化 AI 智能体工作流,完成复杂代码迁移。
在代码库文档编写完成、等价性测试框架就位后,剩下有趣的部分就是调整可以赋予智能体多大的自主权,同时仍能产出可合并的代码。我们尝试了两个极端,从完全自主运行,到严密监督下的手动操作。下面的结构化工作流就是我们针对这一用例最终采用的方案。
我们与客户的油藏工程师合作,利用调用关系树识别独立模块,即规模可控的自包含子树(根据经验,Fortran 代码少于约 10’000 行)。每个模块都经过相同的工作流:
生成目标 C++ 架构。
与油藏工程师一起审查该架构。
获得批准后,将其拆分为任务队列。
针对每项任务运行一个实现子工作流:规划 → 实现 → 测试 → 重复。
由人工审查生成的 PR,并要求修改,直到这些 PR 被合并。
认识 AI 辅助遗留代码现代化的局限。
第一个冲刺阶段涵盖了核心功能:300,000 行代码中的 40,000 行。这个 Fortran 代码库自成一体且可运行,这是一个有利的起点。如果迁移项目依赖外部系统、缺乏可运行的基线版本,或在代码中实现了没有任何文档记录的物理原理,就会面临本文未讨论的额外挑战。
应用三项原则,实现复杂遗留系统的现代化。
这个项目的三点经验应该适用于任何大型遗留系统迁移。
在编写迁移代码之前,先构建一致性测试框架——数值结果一致,是证明模块已完成迁移的成本最低、最有说服力的证据。
在依赖智能体之前,先整理好文档,因为你无法迁移没人能读懂的代码。
而在这种规模下,设有人类审核关卡的结构化工作流程,既优于完全自主运行,也优于人工主导的手动操作。
我们正在招聘!
Mistral 的 Applied AI 团队由一群工程师组成,围绕 Mistral 模型和 Enterprise 平台构建全栈解决方案。我们交付面向特定领域、关乎重大成败的解决方案,以解决世界上一些最棘手的问题。
如果你希望参与这类工作,欢迎申请加入我们的团队。
来源:Mistral AI · mistral.ai