Pause e riavvii
Perché una chiamata al modello già pagata non si ripete mai, qualunque worker riprenda il ticket.
Un harness può mettere in pausa un turno tramite il ledger (una soglia di budget o una tool call ripetuta). Il motore scrive la pausa in anticipo, e riprende l'harness dal suo ultimo token, così una chiamata al modello pagata non si ripete. Lo stesso token fa continuare un turno dopo la morte di un worker. L'harness lo salva dopo ogni risposta del modello e ogni risultato di tool, così un crash ripete al massimo la chiamata che era in volo.
Il broker
Il broker fornisce la coda, il lock per ticket, gli eventi tra processi e i segnali di
annullamento. Il plugin afe-redis lo implementa su Redis.
- uno Stream con un consumer group per la coda.
SET NX PXcon rinnovo controllato dal proprietario per il lock.- pub/sub per le notifiche e gli annullamenti.
- chiavi a scadenza per gli heartbeat dei worker.
Il broker non trasporta mai il contenuto di un evento. Un worker registra l'evento nello store e manda solo un ping al broker. Ogni processo rilegge il buco dallo store per numero di sequenza, così nessun evento si perde o si duplica.
Quando un worker muore
Un worker che muore perde il suo lock (scade), e il suo job non confermato viene riconsegnato a un altro worker. Quel worker continua il ticket dal suo ultimo checkpoint. Un nodo il cui risultato era stato scritto in anticipo, ma il cui checkpoint non era stato salvato, non viene richiamato.
Un job consegnato tre volte senza essere finito mette il ticket in needs_human, con la
motivazione "gave up after 3 attempts" e le azioni retry, take over e close. retry lo
rimette in coda con il contatore dei tentativi azzerato. All'avvio, ogni worker accoda anche un
job di recupero per ogni ticket running che nessun lock tiene.
Un'interruzione del broker non ferma un worker. Il ciclo di claim, l'heartbeat e l'iscrizione ai
cancel registrano la classe dell'errore una volta e riprovano, così il processo resta vivo.
/readyz segnala broker finché Redis non torna. Un cancel inviato durante l'interruzione ferma
comunque il ticket al confine del nodo successivo.
Una risposta appartiene alla sua richiesta
Un job resume porta l'id della richiesta a cui risponde. Il motore legge la richiesta corrente
del ticket al momento dell'esecuzione, e applica la risposta solo se l'id coincide. Una risposta
obsoleta è un no-op, quindi un resume duplicato non salta mai una pausa successiva.
Una richiesta che offre azioni esige una risposta esplicita tra quelle offerte. Un resume senza
risposta non può superarla. Ogni evento ticket_resumed registra l'id della richiesta.
Annullare e mettere in pausa
ticket.cancel ferma all'istante il turno dell'harness in corso, su qualunque worker lo esegua, e
in afe run --local. Il ticket diventa cancelled, gli artefatti e le righe di ledger scritti
fino a quel punto restano, e viene emesso un solo ticket_cancelled.
ticket.pause continua a fermarsi al confine del nodo successivo. Un turno interrotto e poi
ripreso pagherebbe altrimenti due volte la chiamata al modello.
Recupero dopo un crash
Un worker che riprende un ticket running lo continua dal suo ultimo checkpoint. All'avvio i
worker accodano anche un job di recupero per ogni ticket running che nessun lock tiene. I ticket
che aspettano una persona (paused_budget, paused_human, needs_human) restano come sono, con
la richiesta in sospeso leggibile.
Un turno crashato con pause già risposte le rigioca in ordine, e riprende l'harness dall'ultimo token. Nessuna chiamata al modello già pagata viene ripetuta.
afe run --local non recupera mai altri ticket.
Risoluzione dei problemi
Un ticket resta in needs_human con la motivazione "gave up after 3 attempts".
Tre worker diversi hanno reclamato il job e nessuno lo ha finito, di solito perché il nodo stesso
va in crash o si blocca. Correggi prima la causa, poi usa retry per rimetterlo in coda con un
contatore nuovo, oppure take_over per eseguire il nodo a mano.
/readyz segnala broker come non integro
Il broker è irraggiungibile. I worker restano vivi e continuano a riprovare, quindi nessun lavoro
si perde, ma i ticket nuovi non si possono accodare e i cancel sono ritardati finché Redis non
risponde di nuovo. Controlla Redis stesso, non il worker.