Gli agenti LangGraph hanno finalmente trovato un modo affidabile per mantenere il proprio stato dopo settimane di perdita silenziosa di dati. Dopo tre tentativi falliti di checkpointing — SQLite, object storage grezzo e una versione errata di ciascuno — l'autore è approdato a un pattern di aggiornamento atomico che impedisce agli agenti di ricominciare da capo ogni volta che arriva una richiesta.
Perché il checkpointing è importante per LangGraph
LangGraph consente agli sviluppatori di concatenare chiamate LLM in "agenti" riutilizzabili che possono ricordare ciò che è accaduto in precedenza in una conversazione. Questi agenti suddividono una richiesta dell'utente in sottotask, memorizzano i risultati intermedi e riprendono da dove si erano interrotti alla chiamata successiva. Se lo stato memorizzato scompare, l'agente ricalcola tutto, sprecando risorse di calcolo, aumentando la latenza e offrendo un'esperienza utente scadente. In un bot di produzione che gestisce messaggi Telegram, la perdita ha cancellato settimane di cronologia delle conversazioni.
La prima soluzione: SQLite saver
L' SqliteSaver integrato funziona bene quando un'unica istanza esegue l'agente. Scrive ogni checkpoint come un blob JSON in un file SQLite locale. I problemi sono iniziati quando lo sviluppatore ha aggiunto un nuovo campo al tipo AgentState e ha ridistribuito l'applicazione. I checkpoint esistenti, creati prima del cambiamento dello schema, non contenevano il nuovo campo. Poiché SqliteSaver non esegue mai una migrazione, LangGraph ha caricato il JSON incompleto, ha scartato i dati mancanti e l'agente è ripartito dall'inizio.
Punto chiave: l'archiviazione SQLite è uno strumento di demo, non una soluzione pronta per la produzione quando è necessaria l'evoluzione dello schema.
La seconda soluzione: Object storage
Per ottenere il controllo sul formato di serializzazione, l'autore ha scritto un saver personalizzato che caricava il checkpoint JSON su Oracle Cloud Object Storage. Questa mossa ha offerto la flessibilità di versionare lo schema manualmente, ma ha introdotto una nuova modalità di errore. Quando due richieste colpiscono lo stesso thread di conversazione simultaneamente, entrambe tentano di sovrascrivere lo stesso oggetto. I servizi di object storage sono ottimizzati per pattern di tipo "write-once, read-many"; non forniscono semantiche di sovrascrittura atomica. La race condition ha prodotto file JSON malformati o troncati, e l'agente ha nuovamente perso il proprio contesto.
Punto chiave: la semplice sovrascrittura nell'object storage non è sicura quando più worker possono accedere alla stessa chiave contemporaneamente.
La terza soluzione: Aggiornamenti atomici con versionamento
Il design finale e stabile combina due idee: numeri di versione espliciti e scritture condizionali basate sull'ETag dell'oggetto (l'identificatore checksum del servizio di archiviazione).
- Leggere il checkpoint corrente e catturarne l'ETag.
- Incrementare un campo di versione all'interno dell'involucro del checkpoint.
- Scrivere il checkpoint aggiornato utilizzando una richiesta condizionale che ha successo solo se l'ETag corrisponde a quello letto in precedenza.
- Riprovare l'intero ciclo di lettura-incremento-scrittura se la scrittura condizionale fallisce perché un altro processo ha modificato l'oggetto.
Poiché la scrittura ha successo solo quando nessun altro processo ha alterato il file, solo un worker può effettuare il commit di un nuovo stato alla volta. Il campo di versione facilita inoltre il rilevamento dei checkpoint obsoleti e la loro migrazione quando lo schema cambia.
Il pattern funziona con l'object storage che supporta scritture condizionali basate su ETag.
Lezioni per gli ingegneri AI
- Usare SQLite solo per i prototipi. Gli agenti in produzione necessitano di un archivio in grado di gestire i cambiamenti di schema e le scritture concorrenti.
- Pianificare autonomamente le migrazioni dello schema. I dizionari tipizzati descrivono le forme per l'analisi statica, ma non impongono la struttura a runtime.
- Trattare lo stato come una risorsa condivisa. I bug di concorrenza si manifestano come perdita silenziosa di dati; sono più difficili da debuggare rispetto alle eccezioni esplicite.
- Usare le primitive cloud. Le scritture condizionali basate su ETag offrono un locking ottimistico economico senza la necessità di un servizio di lock separato.
- Registrare ogni passaggio. I fallimenti silenziosi — come un campo mancante che LangGraph ignora — sono i più difficili da individuare.
Cosa riserva il futuro per il checkpointing di LangGraph?
Per i team che hanno già incontrato gli stessi ostacoli, la ricetta dell'aggiornamento atomico offre una soluzione rapida e a basso costo. Dimostra che una pipeline di produzione affidabile non richiede un archivio di stato pesante, ma solo una gestione attenta della concorrenza e del versionamento.
In sintesi: Un semplice involucro versionato unito a scritture condizionali trasforma un sistema instabile in uno affidabile, permettendo agli ingegneri AI di concentrarsi sulla logica dell'agente piuttosto che su un infinito debugging della perdita di dati.
