Salvatore ClemenzaIngegnere full-stack + IoT
← Tutti i progetti

02 in sviluppo in corso

Tracciabilità alimentare

Un grafo dei lotti che, partendo da un lotto sospetto, trova i lotti collegati e i clienti esposti.

Architettura, backend, frontend

  • Python / Flask
  • Google Sheets API
  • Grafo DAG bidirezionale
  • HTML / Vanilla JS
  • fpdf2 (report PDF)
  • Costruito sopra il foglio Google che il magazzino aggiorna ogni giorno
  • Grafo bidirezionale dei lotti: trace-back e trace-forward
  • Bilancio di massa e report PDF per gli audit
01 Colpo d'occhio

Per un'azienda alimentare la tracciabilità non è facoltativa: è un obbligo regolamentare. Quando un lotto risulta contaminato — un batterio, un allergene non dichiarato, un residuo fuori soglia — bisogna rispondere in fretta a quattro domande: quali lotti padre l'hanno generato, quali lotti figli ne sono derivati, quali clienti hanno già ricevuto cosa, e come quarantenare prima che il prodotto arrivi sulla tavola.

Da noi queste risposte vivono dentro un foglio Google che il magazzino aggiorna ogni giorno: cresce di settimana in settimana, raccoglie anni di movimenti, è ricco di dati e di campi per descriverli. Ogni riga è un movimento di magazzino — un arrivo, una miscelazione, una lavorazione, una spedizione al cliente. Per rispondere a mano, sotto la pressione di un richiamo, bisogna seguire i codici lotto in avanti e all'indietro lungo il foglio, riga per riga. È lento, ed è esattamente il tipo di lavoro su cui un operatore umano stanco sbaglia.

Oggi le quattro domande del richiamo hanno una risposta dentro al sistema. Bastano il codice di un lotto e un click: il grafo bidirezionale lo segue all'indietro fino alle materie prime in ingresso e in avanti fino a ogni cliente che lo ha ricevuto. Una timeline cronologica rimette in ordine i movimenti. La lista dei clienti e dello stock esposto esce pronta per l'ufficio commerciale. Un piano d'azione prioritizzato indica cosa fare per primo — la priorità 1 è sempre comunicazione e quarantena. E un report PDF per l'audit ASL/HACCP si genera in un comando.

Sopra a tutto questo c'è una funzione che non era stata richiesta ma che è diventata la più utile in pratica: il bilancio di massa. Se da un lotto risulta uscito più prodotto di quanto ne sia entrato — un'impossibilità fisica — il sistema lo segnala. È spesso il primo indizio che un codice è stato riusato per errore, o che una riga di lavorazione è stata digitata male sul foglio.

02 Superficie

La difficoltà di un progetto come questo non è scrivere un'API Flask o costruire un grafo: quello è lavoro normale. La difficoltà è leggere correttamente un foglio scritto per anni dall'ufficio operativo. Sotto la superficie di un foglio Google fatto di tantissime righe e di tanti campi per descriverle c'è la storia di anni di operatività che fuori dall'azienda nessuno capisce: tipi di movimento con regole diverse, categorie di lotto che dicono se una riga è un padre o un figlio o una lavorazione intermedia, convenzioni di scrittura che separano un'entrata da un'uscita.

Il lavoro vero con l'IA è stato qui. Capire cosa significa in pratica ogni parte del foglio ha richiesto giri in magazzino: chiedere ai colleghi qualità e magazzino regola per regola, riportare quelle regole a Claude Code, trasformarle in modello. Da quelle conversazioni è uscito il flusso fisico — obbligo, arrivo, miscelazione, lavorazioni, distribuzione — che oggi guida il grafo. Per non perdere quel modello nel codice è nato un subagent dedicato, `@tracer-expert`: un assistente specializzato solo sulle query di questo grafo, che conosce le regole di dominio e non le riapplica al volo a ogni richiesta nuova.

Leggere dal foglio invece di sostituirlo con un database. È la decisione che ha tenuto in piedi il progetto.

La tentazione, all'inizio, era ovvia: il foglio Google fa fatica, ha race condition se in due lo modificano insieme, è limitato dall'API rate-limit di Google. Soluzione "pulita" da manuale: gli si affianca un Postgres, ci si scrive sopra una webapp gestionale con form e validazioni, e si dismette il foglio.

Non è la decisione giusta in un'azienda alimentare. Quel foglio è dove i colleghi qualità e magazzino registrano i movimenti da anni: ci sono le loro intestazioni, le loro abbreviazioni, le formule che si sono cuciti addosso. Sostituirlo con una webapp significa due cose insieme: chiedere a tutto il magazzino di cambiare strumento dall'oggi al domani, e assumere di aver capito al primo colpo cosa serve davvero registrare (cosa che non è quasi mai vera).

Quindi: il foglio resta. Il sistema di tracciabilità si affianca al foglio, lo legge a intervalli regolari, costruisce il grafo, risponde alle domande del richiamo. Il compromesso accettato è esplicito: la trace non è "live al millisecondo", e in scrittura il sistema non c'è (il foglio resta single source of truth). In cambio, lo strumento entra in produzione senza disturbare il lavoro di nessuno — ed è uno strumento usato, non un cruscotto abbandonato.

Il compromesso ha una scadenza: il prossimo passo, già pianificato, è ristrutturare il processo aziendale uscendo da Google Sheets — verso un database e una webapp gestionali pensati come strumento primario, non più come affiancamento. Quel cambio si farà quando le regole di dominio del foglio saranno chiare e codificate. Il sistema di oggi è ciò che permette di farlo domani in sicurezza: il modello è già stato letto, validato in produzione, e si porta dietro le anomalie scoperte durante l'uso.

03 Profondità
Il dettaglio tecnico Hardware, scelte di architettura, compromessi — per chi legge il codice

Pipeline dei dati. Tre componenti Python, ciascuno con responsabilità singola. `reader.py` parla con la CLI gws (Google Workspace) e fa sync del foglio Google a batch con qualche centinaio di millisecondi di pausa fra una chiamata e l'altra per restare sotto al rate-limit dell'API Sheets. Il risultato si normalizza riga per riga e si serializza in `cache.json`. `tracer.py` consuma la cache e in memoria costruisce tre indici: `forward_edges`, `backward_edges` e `by_token` (chiave→righe). `server.py` è un Flask con una decina di endpoint REST: `/search`, `/lot/{id}/trace`, `/timeline`, `/balance`, `/impact`, `/action-plan`, `/report.pdf`, `/sync`. Frontend HTML5 + JavaScript puro, dark theme, con grafo visuale e timeline cronologica.

La regola di costruzione del grafo. Solo le righe `AR`, `MAC` e `MAC-T` generano un arco in avanti. Le righe `DS` non lo fanno: sono consumatori, non generatori, e trattarle come tali aggancerebbe ogni cliente al grafo come se fosse un nodo intermedio della lavorazione — falsi positivi a cascata sulla trace, bilancio massa rotto. La regola sembra ovvia messa così, ma sul foglio reale, sotto pressione, è proprio il tipo di dettaglio che si confonde. Codificarla nel `tracer` ha eliminato l'intera classe di errori.

Ricerca a quattro livelli. Il codice lotto digitato a mano in un richiamo è spesso parziale, sporco, con suffissi. `/search` prova in cascata: exact match (codice esatto), substring (contiene), token AND (tutti i token presenti in qualsiasi ordine), majority (almeno il 60% dei token presenti). Il primo livello che restituisce risultati vince. Risolve il caso reale in cui l'operatore ricorda "lotto IT24-13..." ma non l'intero suffisso, senza inondarlo di falsi positivi.

Bilancio massa come check di sanità. Per ogni lotto, somma le quantità entrate e somma quelle uscite. Se l'uscita supera l'entrata di una soglia significativa (>110% sul lotto condiviso), il sistema lo segnala come anomalia. Non è un valore di soglia arbitrario: è la tolleranza di buon senso per piccoli arrotondamenti di pesata, oltre la quale c'è quasi sempre un codice riusato per errore. Il report PDF lo evidenzia in cima.

Il sistema oggi è in uso. La prova vera è arrivata con un richiamo reale: per la prima volta la risposta — chi è il padre, quali sono i figli, quali clienti hanno già ricevuto cosa, cosa va quarantenato — è uscita dalla visualizzazione a grafo invece che da una ricerca riga per riga sul foglio. Più veloce, più ripetibile, e con un audit trail leggibile per chi lo deve verificare dopo. Ai richiami simulati che facciamo per allenarci alla procedura, i tempi di ricerca dei prodotti — quanti ne sono ancora in magazzino, quanti sono già al cliente — sono dell'ordine dei minuti, dove prima erano ore di lavoro a mano sotto stress.

Il punto del progetto, però, non è la velocità: è la ripetibilità. Lo stesso codice lotto, in mano a due persone diverse, restituisce la stessa trace e lo stesso elenco di clienti esposti. È l'obiettivo dichiarato del lavoro di sviluppo qui in azienda — ridurre gli errori umani — applicato al dominio dove l'errore costa di più: un cliente non avvisato di un lotto contaminato.

E c'è un effetto collaterale che vale la pena dire: ogni mese che il sistema gira, le regole del foglio diventano più chiare. Le anomalie che il bilancio massa segnala sono al tempo stesso un check di sanità sui dati di oggi e materiale per la migrazione di domani, quando il foglio sarà sostituito da un gestionale strutturato.

← Tutti i progetti