Mistral 复盘用 AI Agent 将 40,000 行 Fortran 77 迁移到 C++ 的方法与教训
Solutions Modernizing complex legacy code with AI agents. Lessons from 40,000 lines of Fortran. September 9, 2026 By Carlo Antonio Patti & Rasul Alakbarli
Mistral 帮助一家欧洲能源运营商将 40,000 行 Fortran 77 的油藏模拟器迁移到 C++,该代码库无测试套件且文档分散。团队先搭建数值一致性验证工具,用 Vibe CLI 生成调用树并派出百余个 Agent 补齐文档,最终采用由人类把关的 coder、tester、reviewer 结构化工作流逐模块迁移。
来自一线项目复盘,给出了迁移前搭建数值一致性验证、用 Agent 分工工作流等可直接迁移到其他遗留系统改造的经验。
遗留科学代码库经过数十年的积累,当最初的作者离开后,代码中蕴含的知识便难以恢复。此外,使用没有活跃开发者生态系统的语言意味着错失在他人工作基础上继续构建的机会。Mistral 帮助一家欧洲能源运营商将 40,000 行 Fortran 77 代码迁移到 C++,这是一个物理密集型油藏模拟器,没有测试套件,也没有集中式文档。
超越遗留代码现代化中的代码翻译。
将语法从一种语言翻译到另一种语言在很大程度上是一个已解决的问题。让任何近期的模型将一段并非完全冷门语言的代码片段翻译成另一种语言,很可能在几次迭代后就能收敛到可接受的结果。然而,将整个系统从过程式语言迁移到面向对象的 C++ 会产生架构重构的需求,这使得任务变得不再简单。
Fortran 77 正如其名,于 1977 年标准化,用它编写的代码直接反映了那些约束:没有模块,没有命名空间,没有结构化类型。状态存在于 COMMON 块中——整个程序共享的全局内存。变量根据其首字母隐式确定类型,因此拼写错误的名称会静默地创建一个新变量,而不是引发编译器错误。
举一个简单但具有说明性的例子,请看以下一阶泰勒展开的实现。输入和输出是 COMMON 块中的全局变量,而 IC 之所以是整数,仅仅是因为它的名称以 I 到 N 之间的字母开头。变量名相当晦涩,因为它们的长度被限制在 6 个字符以内。
SUBROUTINE GASDEN
INCLUDE 'common.h'
DO 10 IC = 1, NCELL
10 RHOG(IC) = ROG(IV) + DROG(IV)*(P(IC)-PTAB(IV))
END
在 C++ 中,可以利用显式类型,编写面向对象的代码,并返回一个值而不是进行全局写入:
double gasDensity(const GasProperties& gas, double pressure) {
size_t i = lookup(gas.pressure, pressure);
return gas.density[i] + gas.slope[i] * (pressure - gas.pressure[i]);
}
分散的 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,而不是现代化的代码。
在第二次尝试中,我们通过赋予智能体结构而非仅仅是自主权来解决这个问题:一个规划者、一个编码者、一个测试者和一个代码质量审查者共同处理每个模块。代码质量相比第一次尝试有了大幅提升。但源代码的复杂性最终让智能体们难以招架。它们会碰到一个 bug,尝试几次修复,然后停滞不前,没有人可以介入。
我们最终找到了一个折中方案:由一个人操作一个由编码者、测试者和审查者智能体组成的工作流,逐个模块地迁移代码库。这保留了第二次尝试的代码质量,同时增加了一个人工检查点,在智能体卡住时为其解除阻塞。下一节将详细介绍这个工作流。
为复杂的代码迁移运行结构化的 AI 智能体工作流。
在代码库完成文档化并且一致性测试框架就位后,剩下的有趣部分就是调整我们可以赋予智能体多少自主权,同时仍能产出可合并的代码。我们尝试了两个极端,从完全自主的运行到密切监督的人工会话。下面的结构化工作流就是我们在这一用例中最终确定的方案。
我们与客户的油藏工程师合作,使用调用者-被调用者树来识别独立模块——即规模可控的自包含子树(根据经验,少于约 10,000 行 Fortran 代码)。每个模块都经过相同的工作流:
生成目标 C++ 架构。
与油藏工程师一起审查。
批准后,将其分解为任务队列。
为每个任务运行一个实现子工作流:规划 → 实现 → 测试 → 重复。
由人工审查生成的 PR,并要求修改,直到它们被合并。
认识到 AI 辅助遗留代码现代化的局限性。
第一个冲刺覆盖了核心功能:30 万行中的 4 万行。这个 Fortran 代码库是自包含且可运行的,这是一个有利的起始条件。依赖外部系统、缺乏可运行基线或编码了无处可查的物理规律的迁移,会带来本文未讨论的额外挑战。
应用三项原则来现代化复杂的遗留系统。
这个项目的三条经验应当适用于任何大型遗留迁移。
在编写迁移代码之前先构建一致性测试框架——数值一致是证明一个模块完成的最廉价、最有说服力的证据。
在依赖智能体之前先把文档整理好,因为你无法迁移没人能读懂的代码。
而在这种规模下,带有人工审查关卡的结构化工作流,既优于完全自主,也优于手动驱动的会话。
我们正在招聘!
Mistral 的应用 AI 团队是一群围绕 Mistral 模型和企业平台构建全栈解决方案的工程师。我们交付高风险的、特定领域的解决方案,以解决世界上一些最困难的问题。
如果你希望参与这类工作,申请加入我们的团队。
来源:Mistral AI:News(网页) · mistral.ai