Il bollettino valanghe descrive una zona, per un giorno, a certe quote ed esposizioni. Chi va fuoripista lo legge come un'informazione con una scadenza: che ieri il pendio abbia tenuto dice poco su oggi. Scio, e questo paragone mi serve per spiegare un errore che vedo spesso nei sistemi AI in produzione. Il paragone ha dei limiti: un'eval non ha la copertura, l'autorità né le conseguenze di un bollettino. Le storie qui sotto invece sono vere e vengono dai miei progetti.
La tesi: un check stabilisce qualcosa su uno stato preciso. Nel software un test passato, una verifica riuscita o un health check verde diventano facilmente verità permanenti. Il lavoro di produzione è decidere quando quella prova scade, come ti accorgi che lo stato è cambiato e cosa succede dopo.
Un check superato scade quando cambiano le condizioni che lo giustificavano.
Per CVing, una HR tech di Milano, ho costruito un agente WhatsApp che permette di candidarsi a un'offerta in conversazione. Per le azioni delicate il candidato conferma la sua email con un codice. In produzione un candidato è rimasto bloccato: il codice era scaduto a metà conversazione e il sistema che lo gestisce lo rifiutava, mentre l'agente ricordava ancora «verificato» e non ne chiedeva uno nuovo. Continuava a dirgli di controllare la posta, e nessuna email partiva.
Ogni pezzo riportava correttamente quello che sapeva. Messi insieme, intrappolavano una persona. Ho deciso che una verifica ricordata ha una durata, che quando il sistema con l'autorità dice no il ricordo locale si cancella subito, e che la verifica si usa nello stesso passo dell'azione che protegge, così non resta in giro a invecchiare.
Una regola che stava nelle istruzioni del modello, riconoscere un codice dalla sua forma, è diventata un check nel codice. Quello che deve reggere sempre va scritto dove il modello non può interpretarlo.
Prima del retry, chiediti cosa può essere già successo.
Nella stessa analisi ho guardato ogni scrittura chiedendomi cosa garantisce il sistema a monte quando fallisce. Se fallisce la richiesta di un nuovo codice, ci si ferma e si fa retry. Se fallisce il salvataggio di una verifica già riuscita, la verifica resta valida e il problema finisce nell'audit log, descritto onestamente. Se il database non risponde, retry. Se risponde che non c'è niente, quella è una risposta e niente retry.
«Retry se c'è un errore» schiaccia in una sola tre situazioni diverse: non è successo niente, è successo tutto, non lo sai. Dai a ogni operazione una di queste etichette e conserva l'incertezza quando c'è, nello stato e nell'audit.
Una conversazione ordinata per la persona può arrivare in disordine al server.
Il candidato vive una conversazione sola. Il server invece riceve i messaggi uno per uno e può lavorarne due insieme, così due messaggi ravvicinati dallo stesso numero si contendevano gli stessi dati. Ho fatto in modo che i messaggi di una stessa persona si mettano in fila, mentre gli altri candidati non aspettano nessuno.
C'era un secondo pezzo, meno ovvio. Quando più messaggi ravvicinati vengono raccolti in un turno solo, il modello rischiava di vederli fusi in un unico testo e di perdere quale messaggio diceva cosa. Ora ognuno arriva al modello con il suo posto e la sua ora. Trova la cosa il cui stato deve avanzare in ordine, metti in fila lì, e quando unisci più input conserva i confini tra l'uno e l'altro.
Una regola nuova deve valere anche per i dati vecchi.
Nello stesso agente i nomi dei file dei CV caricati finiscono davanti al modello, quindi vanno trattati come testo non fidato. Ho aggiunto un filtro all'ingresso e ho separato chiaramente quel nome dal resto delle istruzioni. Poi ho applicato lo stesso filtro ai nomi già salvati, ogni volta che vengono riletti.
Quest'ultimo passo è quello che si dimentica. I dati salvati prima della regola sono ancora lì, e correggere l'ingresso non li tocca. Per ogni regola nuova guarda dove rileggi dati vecchi e decidi se migrarli, ricontrollarli, isolarli o rifiutarli.
Il primo passo riuscito non prova che sia avvenuto il secondo.
Nel company brain di Yempik l'approvazione di una scheda funzionava. Il passo successivo, quello che la rende trovabile, falliva in silenzio, e la ricerca tornava vuota senza un errore. Il successo del primo passo faceva da prova falsa del secondo. Abbiamo aggiunto un check che confronta di continuo le schede approvate con quelle trovabili, una ricerca che dice quando è indietro e un test che percorre tutta la strada, dalla scrittura alla ricerca.
Tra le regole con cui lavoro c'è questa: ogni scorciatoia ha una strada di riparazione. Scrivi in modo durevole cosa deve succedere, e fai controllare a qualcosa, di continuo, che sia successo.
Testa dall'ambiente che farà il lavoro.
Una routine commerciale di Yempik contava su file presenti sul mio computer. In una sessione locale quei file ci sono, i run programmati in cloud non li vedono. Ogni test fatto dalla sessione sbagliata aumentava la fiducia senza toccare il punto debole. Il classico «sul mio computer funziona». Adesso ogni automazione dichiara cosa legge, cosa scrive e dove, e il test si fa con i permessi e l'ambiente di chi la eseguirà.
Vale anche per gli health check. Una homepage che risponde dice che la homepage risponde. Se il lavoro dell'utente passa da un database o da un fornitore esterno, l'health check deve fare quella strada, e un servizio che non può fare il suo lavoro deve smettere di dichiararsi disponibile.
L'obiezione più comoda: con il prossimo modello questi problemi spariscono.
I modelli migliorano davvero. Guarda però la lista: una verifica ricordata troppo a lungo, una ricerca vuota, due messaggi in gara, dati vecchi mai ricontrollati, un run in cloud senza i file giusti. Spoiler: nessuno di questi dipende dall'intelligenza del modello. Un modello più bravo avrebbe detto al candidato di controllare la posta con più garbo. Le domande che contano stanno fuori dal modello: quando scade una prova, chi si accorge del lavoro non finito, chi esegue e con quali permessi.
Da lunedì
- Dai a ogni check importante uno scope e una durata. Quando l'autorità dice no, dimentica il sì che avevi in memoria.
- Usa una verifica nello stesso passo dell'azione che protegge.
- Sposta nel codice le regole che devono reggere sempre.
- Prima di fare retry su un'operazione, decidi se può essere già successa.
- Metti in fila il lavoro per la persona o la cosa il cui stato deve avanzare in ordine, e conserva i confini quando unisci i messaggi.
- Per ogni regola nuova, controlla anche dove rileggi i dati vecchi.
- Scrivi in modo durevole cosa deve succedere e controlla di continuo che sia successo.
- Fai i test con l'identità, i permessi e l'ambiente che faranno davvero il lavoro.