Thinking
Summary
Mistral ha aiutato un operatore energetico europeo a migrare 40.000 righe di Fortran 77 a C++. Scopra come è stato fatto e quali lezioni portare avanti.
Le codebase scientifiche legacy si accumulano nel corso di decenni e, quando gli autori originari se ne vanno, la conoscenza incorporata nel codice diventa difficile da recuperare. Inoltre, usare linguaggi senza un ecosistema di sviluppatori attivo significa perdere l’opportunità di costruire sul lavoro altrui. Mistral ha aiutato un operatore energetico europeo a migrare 40.000 righe di Fortran 77 a C++, un simulatore di giacimento ad alta intensità fisica senza suite di test e senza documentazione centralizzata.
Oltre la traduzione del codice nella modernizzazione del codice legacy.
La traduzione della sintassi da un linguaggio a un altro è un’attività in gran parte risolta. Chiedere a qualsiasi modello recente di tradurre uno snippet da un linguaggio non del tutto oscuro a un altro porterà probabilmente a un risultato accettabile in poche iterazioni. Tuttavia, migrare un sistema completo da un linguaggio procedurale a C++ orientato agli oggetti richiede refactoring architetturali che rendono l’attività non banale.
Fortran 77 è stato standardizzato nel 1977, come il nome può suggerire, e il codice scritto in questo linguaggio riflette direttamente quei vincoli: niente moduli, niente namespace, niente tipi strutturati. Lo stato risiede nei blocchi COMMON, memoria globale condivisa in tutto il programma. Le variabili sono tipizzate implicitamente in base alla prima lettera, quindi un nome scritto in modo errato crea silenziosamente una nuova variabile invece di generare un errore del compilatore.
Per un esempio semplice ma illustrativo, consideriamo la seguente implementazione di uno sviluppo di Taylor di primo grado. Input e output sono variabili globali in un blocco COMMON e IC è un intero solo perché il suo nome inizia con una lettera compresa tra I e N. I nomi delle variabili sono piuttosto criptici, poiché la loro lunghezza è limitata a 6 caratteri.
SUBROUTINE GASDEN INCLUDE 'common.h' DO 10 IC = 1, NCELL10 RHOG(IC) = ROG(IV) + DROG(IV)*(P(IC)-PTAB(IV)) ENDIn C++, sarà possibile sfruttare tipi espliciti, scrivere codice orientato agli oggetti e restituire un valore invece di effettuare una scrittura globale:
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]);}Gli array COMMON sparsi diventano un unico parametro GasProperties e il ciclo sulla griglia viene spostato al chiamante, quindi non c’è una corrispondenza riga per riga da verificare: è questo che rende difficile la verifica della migrazione.
Queste differenze strutturali, insieme al requisito di integrare framework moderni per il calcolo scientifico come PetSc, hanno sollevato alcune domande importanti ancora prima di avviare la migrazione:
Come dimostrare che la codebase migrata corrisponde numericamente a quella legacy
Come suddividere la migrazione in parti gestibili
Come usare al meglio gli agenti autonomi per accelerare il processo
Creazione di un harness di parità prima della migrazione del codice legacy.
Prima di lasciare agire liberamente gli agenti, avevamo bisogno di un modo per dimostrare che le due codebase fossero concordi. In questo caso, per "concordanza" si intendeva l’uguaglianza numerica degli output: sia i risultati finali sia un insieme di punti intermedi critici segnalati dagli ingegneri di giacimento del cliente.
Abbiamo aggiunto:
subroutine che consentissero di esportare lo stato della codebase Fortran
un framework di test per caricare i checkpoint in C++
Skill.md files to steer agents into using them correctly

Nei workflow di migrazione, gli agenti hanno strumentato con successo la codebase Fortran per scaricare snapshot dello stato e hanno usato il framework di test C++ per verificare la correttezza dei moduli migrati.
Costruire prima questa parte dell’harness è stato un investimento nettamente positivo per il progetto: ha reso più sicure le esecuzioni prolungate degli agenti, e la parità numerica è un argomento facile da verificare e convincente per dimostrare che una porzione di codice è stata migrata con successo. Riteniamo che questo debba essere uno dei primi passaggi in qualsiasi progetto di modernizzazione del codice.
L’esempio seguente lo illustra. Per prima cosa abbiamo inserito una riga nel codice Fortran per scaricare il valore della variabile RHOG (42,71834 in questa esecuzione), quindi abbiamo usato lo stesso valore come checkpoint di riferimento durante il test del modulo C++ migrato.

Uso di agenti IA per comprendere e documentare codebase legacy.
La documentazione del progetto era dispersa tra vecchi PDF e commenti nascosti nel Fortran stesso, e uno dei maggiori vantaggi collaterali dell’intero lavoro è stato riconciliare questa documentazione e spostarla accanto al codice.
Fortunatamente, il codice procedurale come Fortran ha una proprietà pratica: l’intero programma può essere rappresentato come un unico albero chiamante-chiamato. Abbiamo generato questo albero analizzando la codebase con un parser personalizzato, quindi abbiamo usato Vibe CLI per avviare oltre cento agenti incaricati di documentarla. Ogni agente poteva acquisire i PDF pertinenti tramite librerie documentali e Mistral OCR.
Partendo dalle foglie dell’albero e procedendo verso l’alto, ogni nodo generava un subagent per documentarlo e aprire una PR sul repository originale. Un agente reviewer eseguito in loop secondo una pianificazione cron cercava le nuove PR aperte, le revisionava e pianificava attività di correzione quando necessario.

Utilizzo degli agenti IA per la modernizzazione del codice.
Al primo tentativo, abbiamo dato agli agenti piena autonomia: un agente per ogni subroutine Fortran, ciascuno incaricato di tradurre indipendentemente la propria funzione in C++ nell’arco di una settimana. Il risultato era funzionante, ma non poteva essere definito modernizzazione del codice. I blocchi COMMON sono diventati struct globali, uno a uno. Il flusso di controllo basato su GOTO è rimasto intatto invece di essere ristrutturato in loop o early return. Sembrava Fortran riscritto in sintassi C++ più che codice modernizzato.
Nel secondo tentativo, abbiamo affrontato il problema dando agli agenti una struttura, non solo autonomia: un planner, un coder, un tester e un revisore della qualità del codice che lavoravano insieme su ciascun modulo. La qualità del codice è migliorata in modo sostanziale rispetto al primo tentativo. Ma alla fine la complessità del codice sorgente ha raggiunto gli agenti. Incappavano in un bug, tentavano alcune correzioni e poi si bloccavano, senza nessuno disponibile a intervenire.
Siamo arrivati a una via intermedia: una persona che gestisce un workflow di agenti coder, tester e reviewer, migrando la codebase modulo per modulo. Questo ha preservato la qualità del codice del secondo tentativo, aggiungendo al contempo un checkpoint umano per sbloccare gli agenti quando restavano fermi. La sezione successiva descrive questo workflow in dettaglio.
Esecuzione di workflow strutturati di agenti IA per migrazioni di codice complesse.
Con la codebase documentata e l’harness di parità in funzione, la parte interessante rimasta era calibrare quanta autonomia potessimo affidare agli agenti continuando a ottenere codice pronto per il merge. Abbiamo provato entrambi gli estremi, dalle esecuzioni completamente autonome alle sessioni manuali strettamente supervisionate. Il workflow strutturato seguente è il punto a cui siamo arrivati per questo caso d’uso.
Lavorando con gli ingegneri di giacimento del cliente, abbiamo usato l’albero chiamante-chiamato per identificare moduli indipendenti: sottoalberi autonomi di dimensioni gestibili (empiricamente, meno di ~10’000 righe di Fortran). Ogni modulo seguiva lo stesso workflow:
Generare l’architettura C++ di destinazione.
Revisionarla con un ingegnere di giacimento.
Dopo l’approvazione, suddividerla in una coda di attività.
Eseguire un sub-workflow di implementazione per ogni attività: pianificazione → implementazione → test → ripetizione.
Una persona revisiona le PR risultanti e richiede modifiche fino al merge.
Riconoscere i limiti della modernizzazione del codice legacy assistita dall’IA.
Il primo sprint ha coperto la funzionalità core: 40.000 righe su 300.000. La codebase Fortran era autonoma ed eseguibile, una condizione di partenza favorevole. Le migrazioni che dipendono da sistemi esterni, non dispongono di una baseline eseguibile o codificano fisica non documentata da nessuna parte presenterebbero ulteriori sfide non trattate in questo post.
Applicare tre principi per modernizzare sistemi legacy complessi.
Tre lezioni tratte da questo progetto dovrebbero valere per qualsiasi grande migrazione legacy.
Costruire l’harness di parità prima di scrivere codice di migrazione: la concordanza numerica è la prova più economica e convincente che un modulo è completo.
Mettere in ordine la documentazione prima di fare affidamento sugli agenti, perché non si può migrare codice che nessuno riesce a leggere.
E, a questa scala, i workflow strutturati con gate di revisione umana superano sia la piena autonomia sia le sessioni manuali condotte direttamente da persone.
Stiamo assumendo
Il team Applied AI di Mistral è un gruppo di ingegneri che crea soluzioni full-stack basate sui modelli Mistral e sulla piattaforma Azienda. Rilasciamo soluzioni critiche e specifiche di dominio per risolvere alcuni dei problemi più difficili al mondo.
Se questo è il tipo di attività a cui desidera contribuire, si candidi per entrare nel nostro team.




