AIエージェントで複雑なレガシーコードをモダナイズ
Mistralが欧州のエネルギー事業者による40,000行のFortran 77からC++への移行を支援:まず数値一致性検証台を構築し、100以上のagentでドキュメントを補完し、最終的に「人間が操作するコーディング—テスト—レビューagentワークフロー」という中間路線に落ち着かせ、完全自律ではなかった。
日本語
コピー

AI エージェントで複雑なレガシーコードをモダナイズする
レガシーな科学技術コードベースは何十年もかけて積み上がっており、当初の作者が去ったあとでは、コードに埋め込まれた知識を掘り起こすのは難しい。開発者が活発に活動していない言語を使うということは、他人の仕事の上に積み上げていく機会を手放すことでもある。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 のような現代的な科学技術計算フレームワークを組み込まなければならないこともあり、移行を始める前にいくつか重要な問いが浮かぶ。
- 移行後のコードベースが数値的にレガシー版と一致していることをどう証明するか
- 移行を扱いやすい塊にどう分割するか
- 自律エージェントをどう活用すればこの作業を最も加速できるか
レガシーコードを移行する前に一致検証の土台を組む
エージェントを放つ前に、2 つのコードベースが一致していることを証明する手段が要る。ここでいう一致とは出力の数値が等しいことで、最終結果だけでなく、顧客側の貯水池エンジニアが指定した一連の重要な中間点も含む。
追加したものは次のとおりだ。
- Fortran コードベースが自身の状態をエクスポートするためのサブルーチン
- チェックポイントを C++ に読み込むテストフレームワーク
- エージェントにそれらを正しく使わせるための Skill.md ファイル

移行ワークフローの中で、エージェントは Fortran コードベースに状態スナップショットをダンプする計装を無事に追加し、C++ テストフレームワークで移行済みモジュールの正しさを検証した。
検証の土台を先に組んでおくことは、このプロジェクトにとって純粋な利益だった。長時間のエージェント実行が安全になり、数値の一致は、あるコードの移行が成功したことを示す検証しやすく説得力のある根拠になる。どんなコードモダナイズのプロジェクトでも、最初の数ステップのひとつにこれを置くべきだと考えている。
次の例がそれを示している。まず Fortran コードに 1 行差し込み、RHOG 変数の値(この実行では 42.71834)をダンプする。そして同じ値を参照チェックポイントとして、移行後の C++ モジュールのテストに使う。

AI エージェントでレガシーコードベースを理解し文書化する
このプロジェクトのドキュメントは古い PDF と Fortran コードに埋もれたコメントに散らばっていた。作業全体の副産物として大きかったもののひとつが、このドキュメントを整理し、コードのすぐそばへ移したことだ。
幸い、Fortran のような手続き型コードには便利な性質がある。プログラム全体を呼び出し側と被呼び出し側の木として描けるのだ。自作のパーサーでコードベースを解析してこの木を生成し、Vibe CLI で 100 を超えるエージェントを立ち上げてドキュメントを書かせた。各エージェントはドキュメントライブラリと Mistral OCR を通じて関連する PDF を取り込める。
木の葉から上へ向かって、各ノードは子エージェントを派生させて自身のドキュメントを書かせ、元のリポジトリへ PR を開く。cron で定期実行されるレビューエージェントが新しく開いた PR を探し、レビューし、必要なら修正タスクを手配する。

AI エージェントでコードをモダナイズする
最初の試みでは、エージェントに完全な自律性を与えた。Fortran のサブルーチンごとに 1 つのエージェントを割り当て、それぞれが 1 週間かけて独立に自分の関数を C++ へ翻訳する。動きはしたが、コードのモダナイズとは言えなかった。COMMON ブロックはそのまま 1 対 1 でグローバル構造体になった。GOTO で駆動される制御フローは、ループや早期リターンへ書き換えられることなくそのまま残った。Fortran を C++ の構文で打ち直しただけに見え、モダナイズされたコードではなかった。
2回目の試みでは、「agentに自主性だけでなく構造を与える」という方針に切り替えた。プランナー、コーダー、テスター、コード品質レビュアーが各モジュールで協調して働く。コードの品質は1回目より明らかに上がった。だが最終的に、ソースコードの複雑さがagentを追い越した。バグにぶつかると、何度か修正を試み、やがて行き詰まる。誰も介入できない。
落ち着いたのは中間地带だった。1人がコーディング・テスト・レビューのagentで構成されるワークフローを操作し、モジュールを1つずつ移行する。2回目のコード品質を保ちながら、agentが行き詰まったときに解きほぐす人手のチェックポイントを足した。このワークフローの詳細は次の節で述べる。
複雑なコードの移行に向けて構造化されたAI agentワークフローを走らせる
コードベースにドキュメントが揃い、等価性ハーネスも用意できたなら、残る面白い部分は調整だ。agentにどこまで自主性を与えつつ、マージできるコードを手に入れられるか。両極端はどちらも試した。完全自律の実行から、密接に監督する手動セッションまで。以下に示す構造化ワークフローが、このユースケースで最終的に落ち着いた形である。
顧客の水藏エンジニアとともに、あの呼び出し元—呼び出し先ツリーを使って互いに独立したモジュールを洗い出した。つまり規模が手頃な自己完結型のサブツリー(経験上、約10,000行未満のFortran)である。各モジュールは同じワークフローを通る。
- 目標とするC++アーキテクチャを生成する。
- 水藏エンジニアがそれをレビューする。
- 承認されたら、タスクキューに分解する。
- 各タスクで実装のサブワークフローを回す。計画 → 実装 → テスト → 繰り返し。
- 人が生成されたPRをレビューし、マージされるまで修正を要求する。
AI支援によるレガシーコード近代化の限界を知る
最初のスプリントは中核機能をカバーした。300,000行のうち40,000行である。このFortranコードベースは自己完結型で実行可能であり、出発点としては有利な条件だった。外部システムに依存するもの、実行可能なベースラインを欠くもの、あるいはどこにも記録されていない物理モデルを符号化したものの移行には、本記事で扱わない追加の難しさが伴う。
複雑なレガシーシステムを近代化する3つの原則
このプロジェクトで得た3つの教訓は、大規模なレガシー移行全般に当てはまるはずだ。
- 移行コードを書く前に等価性ハーネスを作る。数値の一致は、モジュールが完成したことを示す最も安価で説得力のある方法である。
- agentに頼る前にドキュメントを整える。誰も読めないコードは移行できない。
- この規模では、人手のレビューゲートを備えた構造化ワークフローが、完全自律よりも、純粋な手動セッションよりも優れている。