Модернизация legacy-кода с AI-агентами: перенос Fortran 77 на C++
Перенос крупной научно-технической системы с Fortran 77 на C++ — это не механический перевод синтаксиса. В проекте Mistral для симулятора пласта на 40 000 строк ключевыми стали численная верификация, документация и структурированный процесс работы AI-агентов под контролем человека.
Научные и инженерные кодовые базы нередко живут десятилетиями. Их первоначальные авторы уходят, решения остаются лишь в комментариях, старых PDF и, что особенно неприятно, между строк самого кода. А когда язык теряет активную экосистему разработчиков, команда лишается и внешней опоры: библиотек, готовых практик, новых специалистов.
Mistral помогла европейской энергетической компании перенести 40 000 строк Fortran 77 в C++. Это был физически насыщенный симулятор резервуара — без полноценного набора тестов и без единой, аккуратно собранной документации. Знакомая картина для многих предприятий: система важна, работает, но трогать её страшновато.
Почему модернизация legacy-кода — это не перевод с одного языка на другой
Перевод синтаксиса сегодня в значительной степени решён. Современная модель способна за несколько итераций перенести отдельный фрагмент с одного распространённого языка на другой. Но миграция целой системы с процедурного Fortran на объектно-ориентированный C++ требует уже не перевода, а переосмысления архитектуры. И вот тут начинается настоящая работа.
Стандарт Fortran 77, как подсказывает название, появился в 1977 году. Код на нём несёт отпечаток своего времени: нет модулей, пространств имён и развитых структур данных. Состояние часто хранится в блоках COMMON — фактически в общей глобальной памяти программы. Тип переменной может определяться первой буквой её имени, поэтому банальная опечатка иногда не вызывает ошибки компиляции, а тихо создаёт новую переменную. Не самый приятный сюрприз в расчётном ядре.
Ниже — простой, но показательный пример линейной аппроксимации. Входы и результаты находятся в глобальных переменных блока COMMON, а IC считается целым лишь потому, что начинается с буквы из диапазона I–N. Имена, к тому же, ограничены шестью символами.
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, поставили перед командой несколько вопросов ещё до старта миграции:
Как доказать, что новая версия численно соответствует legacy-системе?
Как разделить большую миграцию на управляемые части?
Как применить автономных AI-агентов так, чтобы они ускоряли работу, а не создавали новый технический долг?
На практике это вопрос не только моделей, но и инженерной дисциплины: архитектура агентов, которые переносят код, должна заранее определять роли, границы полномочий, контекст и точки обязательной проверки.
Сначала — контур верификации, потом миграция
Прежде чем предоставить агентам свободу действий, команде нужен был надёжный способ сравнить две кодовые базы. Под соответствием понималось численное совпадение результатов: не только финальных значений, но и критически важных промежуточных состояний, которые отметили инженеры по разработке месторождений.
Для этого были добавлены:
подпрограммы для экспорта состояния системы Fortran;
тестовый фреймворк C++ для загрузки и сравнения контрольных точек;
файлы Skill.md с инструкциями для агентов по корректному использованию этого контура.

В ходе миграции агенты инструментировали Fortran-код для сохранения снимков состояния, а затем использовали C++-фреймворк, чтобы проверять каждый перенесённый модуль. Получился не просто набор тестов, а страховочная сетка для длительных запусков агентов.
Создание такого контура окупилось сразу. Численное соответствие стало легко проверить, а успешная миграция отдельного фрагмента — аргументировать без долгих споров. Для проектов модернизации это, пожалуй, один из первых шагов, а не приятное дополнение «на потом».
Например, в исходный Fortran-код добавили выгрузку значения RHOG — в одном из запусков оно составило 42.71834. Затем это значение использовали как эталонную контрольную точку в тесте для перенесённого C++-модуля.

Как AI-агенты помогли понять и документировать legacy-систему
Документация была разбросана по старым PDF, заметкам и комментариям внутри Fortran-исходников. Одним из самых полезных результатов проекта стало сведение этих сведений воедино и перенос документации ближе к коду. Ничего эффектного на первый взгляд. Зато жизненно необходимо.
У процедурного кода есть удобное свойство: всю программу можно представить как дерево вызовов. Команда построила такое дерево с помощью собственного парсера, после чего через Vibe CLI запустила более сотни агентов для документирования. Каждый агент мог обращаться к релевантным PDF через библиотеки документов и использовать Mistral OCR.
Работа шла от листьев дерева к верхним уровням. Каждый узел запускал субагента, который готовил документацию и открывал запрос на слияние в репозитории. Агент-рецензент по расписанию находил новые запросы на слияние, проверял их и, если требовалось, создавал задачи на исправление.
В подобных процессах особенно важны управляемый контекст, источники знаний и воспроизводимость. Для этого применяется агентная память и RAG: агент получает не случайный набор файлов, а релевантные документы, правила и историю решений.

Что сработало при модернизации кода AI-агентами
В первой попытке агентам дали полную автономность: по одному агенту на каждую подпрограмму Fortran, а на перенос в C++ — неделя. Результат запускался, но модернизацией его назвать было трудно. Блоки COMMON почти один в один стали глобальными структурами, а поток управления на GOTO сохранился вместо преобразования в циклы и ранние возвраты. Получился Fortran в костюме C++. Формально новый, по сути — нет.
Во второй попытке автономность сохранили, но добавили роли: планировщик, разработчик, тестировщик и ревьюер качества совместно работали над каждым модулем. Код стал заметно лучше. Однако на сложных ошибках агенты всё равно могли зайти в тупик: несколько попыток исправления, потом ещё несколько — и двигаться дальше некому.
В итоге выбрали компромиссный вариант. Человек управлял процессом работы агентов-разработчиков, тестировщиков и ревьюеров, последовательно перенося систему модуль за модулем. Это сохранило качество второй модели работы и добавило точку вмешательства, когда агенты буксовали. Иногда именно этого одного решения и не хватает, чтобы проект не увяз.
Такой подход близок к практике мультиагентной архитектуры с разделением ролей: специализированные агенты выполняют разные функции, а правила передачи задач и человеческое ревью не дают процессу превратиться в хаотичный чат нескольких моделей.
Структурированный процесс для миграции сложного кода
После подготовки документации и контура сравнения оставалось подобрать разумную степень автономности. Команда проверила оба полюса: полностью автономные запуски и ручные сессии с постоянным надзором. Для этого сценария лучше всего сработал структурированный процесс.
Вместе с инженерами-разработчиками месторождений команда использовала дерево вызовов, чтобы выделить независимые модули — самодостаточные поддеревья приемлемого размера, обычно до 10 000 строк Fortran. Каждый модуль проходил одинаковый путь:
Сформировать целевую архитектуру C++.
Проверить архитектуру вместе с профильным инженером.
После согласования разбить работу на очередь конкретных задач.
Для каждой задачи выполнить подцикл: планирование → реализация → тестирование → доработка.
Провести итоговое ревью запроса на слияние человеком и внести запрошенные изменения до слияния ветки.
Ограничения модернизации legacy-кода с помощью AI
Первый спринт охватил основную функциональность: 40 000 из 300 000 строк. Исходная Fortran-система была самодостаточной и запускалась, а это очень благоприятная отправная точка. Проекты, которые зависят от внешних систем, не имеют работающей базовой версии или реализуют физические модели без документации, будут существенно сложнее. Тут не стоит питать иллюзий.
Три принципа модернизации сложных legacy-систем
Опыт проекта можно свести к трём правилам, полезным для любой масштабной миграции.
Создавайте контур проверки соответствия до написания миграционного кода. Численное совпадение — самый недорогой и убедительный сигнал готовности модуля.
Приводите документацию в порядок до того, как начнёте активно использовать агентов. Нельзя надёжно перенести код, который никто по-настоящему не понимает.
Для задач такого масштаба структурированный процесс с обязательным человеческим ревью эффективнее и полной автономии, и ручной работы без автоматизации.
Наконец, в корпоративной среде процесс должен учитывать права доступа, аудит изменений и отраслевые ограничения. Эти требования стоит проектировать заранее — вместе с мерами безопасности AI-агентов и контролями соответствия, а не прикручивать в самом конце.
Если у вас тоже есть система, которую «страшно трогать», поможем с переработкой legacy-модулей силами AI-агентов — или научим этому вашу IT-команду.
Источник: mistral.ai
