Come gestire retry idempotenti in un workflow AI
Il provider non risponde entro 20 secondi. Il workflow segna errore e riprova. Poco dopo partono due email identiche.
Il timeout descrive ciò che ha visto il chiamante. Non dimostra che il provider non abbia eseguito l'azione.
Per gestire retry idempotenti in un workflow AI devi decidere tre cose prima del primo errore: quali guasti sono ripetibili, quale chiave identifica la stessa intenzione e come riconciliare una scrittura con esito sconosciuto. Poi aggiungi limite, backoff e un percorso terminale. Si chiama retry decision table.
Il retry viene dopo la classificazione
Il monitoraggio vede timeout. La decisione di retry deve capire dove si è fermata l'azione.
Una SOP per il workflow AI dovrebbe separare almeno cinque classi:
| Classe | Esempio | Azione sicura predefinita |
|---|---|---|
| Input non valido | manca un campo obbligatorio | blocco, nessun retry |
| Errore permanente | permesso negato | blocco ed escalation |
| Guasto transitorio prima della scrittura | connessione rifiutata | retry limitato |
| Rate limit | HTTP 429 con indicazione | attesa e retry limitato |
| Esito sconosciuto dopo l'avvio | timeout dopo l'invio | riconciliazione, poi decisione |
Riprova tre volte descrive un contatore senza semantica, non una policy.
Il monitoraggio del workflow in produzione fornisce segnale e contesto. La tabella decide l'azione. Se la classe resta sconosciuta, il default sicuro è fermarsi.
Costruisci la chiave sull'intenzione
AWS definisce idempotente un'operazione che può essere ripetuta senza effetti aggiuntivi. Nei suoi esempi il chiamante invia un identificatore unico, e il servizio lo usa per riconoscere la stessa richiesta.
La chiave deve rappresentare l'intenzione di business, non il singolo tentativo tecnico.
Per una email di conferma ordine potrebbe essere:
order-confirmation:order-884:version-1
Per creare un ticket da un caso:
support-ticket:case-2048:v1
Se ogni retry genera una nuova chiave, hai solo numerato i doppioni. Se riusi la stessa chiave per un'intenzione diversa, rischi di scartare un'azione legittima.
Salva insieme chiave e impronta dei parametri rilevanti. La stessa chiave con payload diverso deve produrre un conflitto esplicito, non un successo ambiguo.
Verifica il contratto del sistema chiamato
Una chiave inviata a un provider non crea idempotenza da sola. Il provider deve dichiarare come la tratta, per quanto tempo la riconosce e quale risposta restituisce a una ripetizione.
Controlla quattro proprietà:
- la chiave è accettata dall'operazione che modifica lo stato
- una ripetizione restituisce lo stesso effetto logico
- un payload diverso con la stessa chiave viene rifiutato
- la finestra di validità copre il tuo tempo massimo di retry
Se il sistema non espone un contratto idempotente, devi mettere il controllo nel tuo dominio. Puoi conservare intenzione, stato e ricevuta prima di avviare nuovi tentativi. Ma attenzione al bordo: registrare la chiave e completare la scrittura sono due operazioni distinte. Se il tuo servizio controlla entrambe, AWS raccomanda di trattarle in modo atomico.
Quando non puoi farlo, la promessa cambia. Non dici una volta sola. Dici rileviamo e riconciliamo i possibili duplicati.
Conserva lo stato del side effect
Per ogni azione che scrive, usa stati che descrivono ciò che sai davvero.
idempotency_key: support-ticket:case-2048:v1
action: create_ticket
state: delivery_unknown
attempt: 2
provider_request_id: req_71bf
last_error: timeout_after_send
next_action: reconcile_by_request_id
Tre stati bastano per evitare molti errori:
not_started: nessuna chiamata mutante è partitastarted_unknown: la chiamata è partita, manca la confermaconfirmed: esiste una ricevuta verificabile
failed può essere usato solo quando sai che l'effetto non è avvenuto o che non deve essere ripetuto. Un timeout generico non offre quella prova.
Riconcilia prima di riprovare un esito sconosciuto
La riconciliazione cerca la prova dall'altra parte. Usa la chiave idempotente, il request ID del provider, un identificatore del record o un endpoint di stato.
L'ordine è semplice:
- cerca una ricevuta locale già confermata
- interroga il provider con un riferimento stabile
- confronta l'effetto trovato con l'intenzione originaria
- conferma, blocca per conflitto oppure autorizza il retry
Se non esiste un modo affidabile per cercare l'effetto, il caso passa a una persona. Meglio un hold visibile che un secondo pagamento, una seconda email o un secondo record.
La checklist di verifica di un output AI resta separata. Può dirti che il contenuto è adatto. Non dimostra che l'invio sia avvenuto una volta sola.
Limita i tentativi e allarga l'intervallo
AWS Well-Architected consiglia di controllare e limitare i retry e di usare backoff con jitter. Il motivo è pratico: cento workflow che riprovano nello stesso istante possono peggiorare il guasto che cercano di superare.
Definisci per ogni classe:
| Campo | Esempio operativo |
|---|---|
max_attempts | 4 tentativi totali |
base_delay | 2 secondi |
max_delay | 60 secondi |
jitter | casuale entro l'intervallo |
retryable_codes | 429, 502, 503 |
terminal_action | coda di verifica |
I numeri dell'esempio non sono una soglia universale. Dipendono da servizio, impatto, finestra utile e limiti del provider.
Usa anche un budget complessivo. Quattro tentativi su tre step in cascata possono diventare molte più chiamate di quanto sembri. Il retry va contato lungo il workflow, non nodo per nodo senza coordinamento.
Prepara il percorso terminale
Dopo l'ultimo tentativo il workflow non deve restare in running per sempre.
Salva il caso in una coda con chiave, classe, tentativi, ultimo errore, riferimenti e azioni consentite. Se l'azione ha esito sconosciuto, vieta il pulsante retry finché la riconciliazione non è conclusa.
Il NIST AI RMF prevede processi di contingenza per guasti o incidenti di sistemi AI e dati di terze parti ad alto rischio. La tua coda terminale è una parte concreta di quel piano. Indica anche il fallback: procedura manuale, servizio alternativo già approvato oppure sospensione.
Nessun fallback implicito. Passare da un provider a un altro può cambiare dati, qualità, costi e condizioni d'uso.
Testa il retry provocando il guasto
Il test felice dice poco. Servono errori inseriti nel punto giusto.
Prova almeno questi casi:
- errore prima della chiamata mutante
- timeout dopo che il provider ha completato l'azione
- due worker con la stessa chiave nello stesso momento
- stessa chiave con parametri diversi
- provider indisponibile oltre il limite massimo
Per ciascuno verifica effetto finale, numero di chiamate, ricevuta e stato terminale. L'obiettivo consiste nel dimostrare che l'azione prevista è avvenuta una volta oppure che il caso è rimasto fermo senza effetti aggiuntivi.
Un esempio completo
Un workflow crea una bozza di risposta e poi apre un ticket. Il contenuto supera il QA. La chiamata al ticketing parte con la chiave support-ticket:case-2048:v1, ma il client riceve timeout.
Il workflow registra started_unknown. Non crea una nuova chiave. Dopo 10 secondi cerca il request ID e trova il ticket TK-9931. Confronta case ID e tipo di richiesta, salva la ricevuta e chiude confirmed.
Un secondo worker arriva nel frattempo. Legge la stessa intenzione in stato started_unknown e non invia. Aspetta la riconciliazione.
Il modello ha scritto il testo. La sicurezza del retry arriva dal contratto operativo, non dal modello.
Misura gli effetti, non il verde dei nodi
La metrica più importante è duplicate_side_effect_count, che deve restare a zero. Affianca tentativi per classe, casi started_unknown, tempo di riconciliazione, code terminali e azioni manuali.
Conta anche i retry evitati perché l'errore era permanente. Un sistema che riprova meno ma classifica meglio può essere più affidabile di uno che recupera molti errori dopo averli amplificati.
Il KPI di conversione della pagina resta l'invio form aggregato con source_page e source_form. La lettura SEO richiede almeno 28 giorni comparabili dopo un eventuale rilascio.
Gli errori che creano doppioni
Il primo è riprovare ogni eccezione. ValidationError e PermissionDenied non guariscono aspettando.
Il secondo è generare la chiave dentro il tentativo. Ogni esecuzione sembra nuova.
Il terzo è segnare failed su un timeout post-invio. Il retry successivo può duplicare un effetto già avvenuto.
Il quarto è annidare retry in ogni nodo. La moltiplicazione resta nascosta finché il provider rallenta.
Il quinto è non chiudere mai. Dopo il limite serve uno stato terminale, un owner e una prossima azione.
Think, Build, Enable applicato ai retry
Think: elenca side effect, classi di errore e danno del doppione. Scegli dove l'esito può diventare sconosciuto.
Build: definisci chiave, stati, riconciliazione, backoff e limite. Inserisci guasti prima e dopo la scrittura.
Enable: consegna la retry decision table a chi opera il workflow. Fagli gestire un timeout senza aprire l'editor dell'automazione.
Un retry sicuro sa quando non deve partire. L'insistenza non lo rende più affidabile.
Domande frequenti
Idempotente significa exactly once?
No. Significa che ripetere la stessa intenzione non deve produrre effetti aggiuntivi nel contratto definito. La garanzia reale dipende dal sistema, dalla persistenza e dai punti di guasto.
Posso usare l'hash del payload come chiave?
Solo se il payload rappresenta davvero l'intenzione stabile. Timestamp o campi casuali cambiano l'hash, mentre due intenzioni diverse potrebbero avere contenuti uguali.
Quanti tentativi devo fare?
Non esiste un numero universale. Parti da impatto, durata utile, limiti del provider e costo delle chiamate. Poi prova il comportamento sotto guasto.
Cosa faccio se il provider non permette la riconciliazione?
Ferma le scritture con esito sconosciuto e passa il caso a una persona. Se il rischio lo consente, progetta un controllo locale o cambia il contratto prima di automatizzare.
Da quale workflow partire?
Scegli quello che può creare un doppione costoso o visibile. Il servizio automazioni e agent AI di MAIKER HUB parte dal punto di scrittura, provoca il timeout e costruisce il retry attorno alla prova.