Nel corso degli anni, il nostro sistema ha svolto un compito con notevole precisione: trasformare domande in linguaggio naturale in chiamate API. Gli utenti principali consistevano in analisti, gestori di account e dirigenti operativi che, pur conoscendo i dati necessari, avevano difficoltà a raccoglierli manualmente da quattro dashboard, due strumenti di BI e un costruttore di report Salesforce. Grazie al nostro sistema, potevano semplicemente digitare la richiesta in inglese puro. Per esempio, una richiesta come “Compila un rapporto sul volume delle vendite da gennaio a marzo 2026 per la regione Northeast, suddiviso per città” veniva tradotta in una chiamata API da cui il sistema poteva agire:
{
"description": "L'utente ha richiesto il volume delle vendite per il periodo specificato, ecco la chiamata API per ottenere la risposta",
"apicall": "/api/salesvolume",
"post_body": {
"start_date": "2026-01-01",
"end_date": "2026-03-31",
"region": "northeast"
}
}
Il resto della pipeline segue una procedura ingegneristica tradizionale. Il sistema invia la chiamata verso i backend corretti, che si trovavano integrati con portali di reporting interni, Salesforce e alcuni servizi interni, applica un modello linguistico di grandi dimensioni (LLM) che genera una query JSON per filtrare e modellare la risposta, e infine la consegna tramite email, come documento DRIVE o come grafico nel browser.
Entro la metà del 2025, il sistema era responsabile della generazione di centinaia di rapporti al mese. Questi rapporti venivano utilizzati da dirigenti, analisti e condivisi con stakeholder esterni. Era diventato il metodo standard per molte squadre nell'ottenere dati su base casuale.
Il contratto tra il modello LLM e il resto del sistema rimane il formato JSON strutturato come in esempio:
{
"description": "L'utente ha richiesto il volume delle vendite per il periodo specificato, ecco la chiamata API per ottenere la risposta",
"apicall": "/api/salesvolume",
"post_body": {
"start_date": "2026-01-01",
"end_date": "2026-03-31",
"region": "northeast"
}
}
Tutto ciò fu costruito su Claude Sonnet 3.5 inizialmente, e successivamente aggiornati a 3.7 e 4.0 senza problemi. Nel momento in cui si lanciò Sonnet 4.5, però, la squadra aveva smesso di preoccuparsi della stabilità e prevvedibilità dei modelli di LLM nel risolvere un problema che ritenevamo semplice.
I nuovi aggiornamenti al modello stavano diventando una routine, simile a incrementare la versione minore di una libreria ben comportata. Poi arrivarono i cambiamenti con 4.5, causando problemi significativi in una percentuale degna di nota di richieste. Il modello iniziò a incorporare il contenuto di post_body nel campo description:
Modalità di fallimento
Due principali tipi di guasti emergono da questa situazione:
- Primo, i parametri di filtro non arrivavano mai all'API. Il sistema interpretava post_body come la fonte di verità per il corpo della richiesta, il quale tornò vuoto. Così, la chiamata API era fatta senza filtro del periodo o della regione. A seconda dell'API specifica che veniva chiamata, il backend restituiva il volume delle vendite per l'intero periodo o per tutte le regioni, oppure generava un errore 500.
- Secondo, il modello iniziò a porre domande di chiarimento nella sua risposta. Questo era un comportamento nuovo. Le versioni precedenti adottavano sempre un approccio migliore sull'approssimazione e restituivano un oggetto strutturato. Con Sonnet 4.5, più cauto, iniziammo talvolta ad ottenere una domanda invece di un risultato. Il nostro sistema non aveva mai previsto questa opzione. La sua costruzione era basata sull’assunzione che ogni invocazione del modello generasse una chiamata API. Non esisteva alcuna componente con un esser umano coinvolto né un modo per salvare richieste parzialmente completate. Questo portò ad interruzioni multiple nel sistema downstream.
Rollback a 4.0
Rolling back a 4.0 era più complicato di quanto non dovesse essere. Nel frattempo tra i caricamenti di 4.0 e 4.5, la nostra squadra aveva aggiunto nuove API di integrazione, tutte verificate con 4.5. Tornando al modello precedente, dovevamo rilasciare nuovamente il test per ogni integrazione rispetto a 4.0, sotto stretto periodo.
Perché la disciplina tradizionale non si applica
L'ingegneria software si basa sull'abilità di limitare gli effetti di un cambiamento. In pratica, quando aggiorni un driver o una libreria, leggi le note di rilascio per capire se ci sono modifiche che rompono il sistema. I test unitari raffigurano il comportamento, il sistema può essere abbastanza deterministico da prevedere il risultato. Il raggio di malfunzionamento è limitato.
I sistemi supportati da modelli LLM non rispettano questo assunto. La funzionalità del modello che genera la tua uscita non è sotto il tuo controllo. Non puoi verificare una modifica passando da una versione 4.0 a 4.5. Essa rappresenta una sostituzione completa della funzionalità su cui il tuo sistema dipende.
Raggio d’azione infinito
Parliamo di "infinite blast radius": un cambiamento il cui effetti downstream non possono essere elencati in anticipo perché lo spazio di ingresso (linguaggio naturale) e i modi di guasti (qualsiasi cosa il modello possa modificare) sono entrambi illimitati.
Analisi del fallimento
L'analisi post-morte ha rivelato che il nostro prompt aveva sempre sofferto di ambiguità. Avevamo sempre chiesto al modello di restituire un oggetto JSON con tre campi, spiegando il funzionamento di ognuno, ma non avevamo mai specificato esplicitamente che il campo description doveva essere una stringa naturale e mai riportare rappresentazioni serialize di altri campi.
Le versioni precedenti del modello riuscivano a inferire questo limite di contesto. Sonnet 4.5, evidenziando una migliore capacità di utilizzare formati aiutando l'utente, ha deciso che fornire la richiesta nel description rendesse il risultato più utile. Dalla prospettiva del modello, era una ragionevole interpretazione di una richiesta ambigua. Ma questo violò le supposizioni fondamentali del nostro sistema.
Il problema non era nel modello, né tantomeno nei formati JSON richiesti. Il problema era nella nostra presunzione che il modello avrebbe sempre completato i vuoti in specifiche mancanti come aveva fatto in precedenza. Tre aggiornamenti avvenuti senza problemi avevano convinto il team che i vuoti fossero sicuri.