Mistral: 40 тысяч строк Fortran 77 на C++ агентами

9 сентября 2026 года Mistral в посте Modernizing complex legacy code with AI agents разобрала, как Applied AI команда помогла европейскому энергооператору перенести 40 тысяч строк Fortran 77 на C++. Физический симулятор пласта, тестов нет, общей документации нет. Человеку с сайтом, ботом или ключом API полезное здесь не новый id модели, а схема: сначала harness на численное совпадение, потом документация рядом с кодом, потом агенты с человеком в контуре.

Официальный пост Mistral: Modernizing complex legacy code with AI agents, 9 сентября 2026
Шапка официального поста Mistral, снимок 14 сентября 2026. Источник: mistral.ai/news/legacy-code-modernization.

Авторы, Carlo Antonio Patti и Rasul Alakbarli, сразу отделяют перевод сниппета от миграции системы. Кусок с Fortran на C++ любой свежей модели обычно сходится за несколько итераций. Целый процедурный код в объектный C++, с интеграцией научных фреймворков вроде PetSc, это уже архитектура, а не синтаксис. Первый спринт закрыл ядро: 40 тысяч из 300 тысяч строк. Фортран был самодостаточным и запускался, это благоприятный старт. Если бы код зависел от внешних систем, не имел рабочего baseline или прятал физику, которой нет ни в одном PDF, история была бы другой, и в посте её не разбирают.

Fortran 77: COMMON, шесть букв, без модулей

Fortran 77 стандартизировали в 1977-м. Модулей нет, пространств имён нет, структурных типов нет. Состояние живёт в COMMON-блоках, общей памяти на всю программу. Тип переменной часто задаёт первая буква: имя с I–N считается целым. Опечатка не ломает компиляцию, она тихо создаёт новую переменную. Длина имени упирается в шесть символов, поэтому в тексте сплошные RHOG, DROG, PTAB.

В посте показывают подпрограмму плотности газа. Вход и выход сидят в глобалах, цикл по ячейкам внутри, IC целое только потому, что имя начинается с I:

   SUBROUTINE GASDEN
   INCLUDE 'common.h'
   DO 10 IC = 1, NCELL
10 RHOG(IC) = ROG(IV) + DROG(IV)*(P(IC)-PTAB(IV))
   END

В C++ тот же смысл выглядит иначе: явные типы, объект GasProperties, значение возвращается, а не пишется в глобал. Цикл по сетке уехал к вызывающему. Построчного соответствия больше нет, и именно поэтому «перевёл и сравнил diff» здесь не работает.

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]);
}

Parity harness до первого агента

До того как отпускать агентов, команда собрала способ доказать, что два кода совпадают численно. Совпадение в их формулировке, это равенство финальных результатов и набор критических промежуточных точек, которые отметили инженеры заказчика по пласту.

Добавили три вещи: подпрограммы, которые выгружают состояние Fortran; тестовый каркас, который поднимает эти чекпоинты в C++; файлы Skill.md, которые направляют агентов пользоваться этим каркасом, а не изобретать свою проверку. В одном прогоне в Fortran сбросили RHOG как 42.71834 и тем же числом проверили уже перенесённый модуль. Длинный прогон агента без такого якоря быстро превращается в «вроде компилируется».

Это не пакет в apt и не новый ключ. Зато это то, что можно унести в любой свой легаси, даже если Mistral в контуре нет: сначала научиться выгрузить состояние старого кода и сравнить числа, потом уже звать агента писать новый.

Vibe CLI и сто агентов на дереве вызовов

Документация проекта была размазана по старым PDF и комментариям внутри Fortran. Отдельный выигрыш всей истории, по тексту, как раз в том, что её собрали и положили рядом с кодом. У процедурного Fortran удобное свойство: программу можно нарисовать одним деревом caller–callee. Дерево получили своим парсером, затем через Vibe CLI подняли больше сотни агентов. Каждый тянул нужные PDF через библиотеки документов и Mistral OCR.

Шли с листьев дерева вверх. На узел порождали субагента: написать документацию и открыть PR в исходный репозиторий. Рядом крутился reviewer-агент по cron: смотрел новые PR, ревьюил, ставил задачи на правки. Картинка красивая. Ломается она там, где PDF врёт, комментарий в коде устарел, а субагент это склеивает в уверенный абзац. Человек в этой схеме всё равно читает PR, иначе дерево вызовов превращается в дерево галлюцинаций.

Неделя автономии: Fortran в синтаксисе C++

Первая попытка: полная автономия, один агент на Fortran-подпрограмму, перевод на C++ независимо, около недели. Код работал. Модернизацией это не было. COMMON стали глобальными структурами один в один. GOTO не разобрали в циклы и ранние return. Получился Fortran, перепечатанный синтаксисом C++.

Вторая попытка: структура вместо голой автономии. На модуль ставили planner, coder, tester и code quality reviewer. Качество выросло. Потом сложность исходника догоняла агентов: баг, несколько попыток починить, стопор, вмешаться некому.

Сели на середину. Человек ведёт workflow из coder, tester и reviewer, модуль за модулем. Качество второй попытки сохранили, плюс точка, где человека зовут разблокировать агента. Вместе с инженерами заказчика по дереву вызовов нарезали независимые модули: самодостаточные поддеревья, эмпирически меньше примерно 10 тысяч строк Fortran. На каждый модуль одна и та же лента:

  1. Сгенерировать целевую архитектуру C++.
  2. Просмотреть её с инженером по пласту.
  3. После одобрения разбить на очередь задач.
  4. На задачу: plan → implement → test → repeat.
  5. Человек ревьюит PR и гоняет правки, пока не вольётся.

Три принципа, которые Mistral выносит из кейса, короткие. Сначала parity harness, численное совпадение дешевле любых рассказов, что модуль «готов». Потом документация, иначе агенту нечего читать. На таком объёме структурированный workflow с человеком на воротах бьёт и полную автономию, и ручные сессии без агентов. Если у вас бот, WordPress и ключ, этот пост можно читать как разбор метода, не как кнопку «включить миграцию». Свой боевой сайт, SSH и root агенту из этого текста не скармливают: здесь физика пласта и 300 тысяч строк Fortran, не админка.

Источники и ссылки


Комментарии загружаются…