Arcocat come piattaforma agentica: come funziona l'infrastruttura
Il funzionamento logico di arcocat dall'alto: il cervello e il tool-loop, i mattoncini deterministici, il layer dati come fonte di verita', la svolta a piattaforma con client separati, e la traiettoria di assorbimento agentico di un'app gestionale esistente.
Questo studio spiega come funziona arcocat dall'alto, in modo logico: cosa fa ogni pezzo, perche' sta dove sta, e come il sistema e' cambiato nell'ultima fase. E' la pagina che vorrei avere davanti quando riapro il progetto dopo mesi e devo ricostruire il quadro senza rileggere il codice.
Il punto di partenza e' un cambio di prospettiva. Arcocat non e' piu' "il chatbot del catalogo": e' diventato una piattaforma server agentica, e l'interfaccia con cui ci si parla e' solo la sua prima faccia. Le sezioni che seguono raccontano l'infrastruttura attuale e la direzione.
1. Cos'e' arcocat oggi
Arcocat e' un assistente tecnico interno per la consultazione dei cataloghi prodotto, sviluppato dentro una PMI manifatturiera italiana del Nordest. Nasce come successore di un chatbot mono-brand precedente (che restava su un solo marchio) e oggi copre sei brand reali, con alcune migliaia di codici prodotto in un database strutturato. Il backend e' un servizio Python (FastAPI) servito in un container Docker su un server di LAN interno.
Il salto di framing e' il cuore di questo studio: ho smesso di pensare ad arcocat come "un assistente conversazionale" e ho iniziato a pensarlo come una piattaforma server sopra cui girano uno o piu' client. Oggi il client e' una console agentica web. Domani potranno essercene altri (per esempio un'app verticale che indaga gli ordini di un cliente). Tutti parlano alla stessa parte server, nessuno duplica la logica.
2. Il cervello e il loop
Il cervello e' un modello Claude raggiunto via OpenRouter. Non uso un framework agent (LangChain, LangGraph, Microsoft Agent Framework): per un singolo sviluppatore in-house l'overhead di astrazione supera il beneficio. Ho scritto un tool-loop custom: il modello decide a runtime quali strumenti chiamare, il loop intercetta le chiamate, esegue, restituisce i risultati, finche' il modello non produce la risposta o chiede una scelta all'utente.
Il prompt di sistema non vive nel codice: arriva da un gestore di prompt versionati (Langfuse). Posso cambiarlo senza ridistribuire il container.
query utente
|
v
[routing brand] (regex deterministico, niente AI)
| espone solo gli strumenti del brand giusto
v
[modello tool-loop] <--> strumenti (discovery, grafo, RAG, validate)
| ogni filtro passa dal guardiano discovery
v
[guardrail deterministici] (regole fisiche, policy)
|
v
risposta in streaming + cita la pagina del catalogo
3. I mattoncini deterministici
L'idea di fondo: determinismo dove la verita' e' finita (fisica, schema, regole), AI dove c'e' ambiguita' (intent, prosa, scoping). I mattoncini che metto attorno al modello:
- Routing per brand: un classificatore a espressioni regolari legge la query prima del modello e capisce di che brand si parla. Cosi' il modello vede 7-9 strumenti invece di tutti. Misurato: prompt iniziale meno 57%, costo per turno meno 46%.
- Discovery enforcement: un guardiano tra modello e database. Se il modello prova a filtrare su un attributo che non esiste (il classico "pirolitico uguale vero" quando nel database l'attributo si chiama "autopulizia" con valore "pirolitica"), blocca e restituisce i valori reali disponibili. Il modello ritenta nel turno dopo. I filtri sbagliati silenziosi sono scesi dal 14% al 7%, gli attributi inventati a zero.
- Guardrail e regole fisiche: una cinquantina di regole eseguite fuori dal prompt, piu' alcune regole fisiche (per esempio la norma EN 60335 sulla distanza cappa-piano, i range di peso ammessi per i meccanismi di sollevamento anta). Dopo che il modello risponde, un motore verifica e, se viola, lo fa riformulare. La responsabilita' fisica e' deterministica, non delegata alla memoria dell'AI.
- Policy come fonte unica: una cartella di file YAML tiene brand, regole di routing, espressioni per riconoscere i codici di ogni brand, alias e sinonimi, regole fisiche, range, attributi nativi, tipi di legame del grafo. Onboardare un brand nuovo e' editare pochi file YAML piu' un check automatico, non toccare dieci file di codice.
4. Il layer dati: la verita'
Qui vive la fonte di verita', ed e' la parte che mi sta piu' a cuore tenere pulita.
- Database prodotti (SQLite): alcune migliaia di codici su sei brand. E' la verita' strutturata sui prodotti (codice, brand, categoria, attributi tipizzati).
- Wiki narrativo: file di testo curati a mano per brand, organizzati in regole, famiglie, distinte, schede di categoria. E' il "manuale di consultazione" editabile dall'esperto senza toccare codice. Da qui un programma offline costruisce un knowledge graph: quando l'utente chiede qualcosa il modello consulta il grafo, non il PDF grezzo. Risultato: codici sempre verificabili, zero codici inventati.
- RAG sui PDF voluminosi: per i cataloghi sopra le mille pagine un secondo indice permette al modello di citare la pagina esatta. I PDF sono serviti staticamente.
- Media prodotto: immagini servite staticamente, alimentate dai feed strutturati dei brand.
- Policy deterministiche: alias, invarianti, regole guardrail (la cartella YAML gia' citata).
5. Prestazioni e osservabilita'
- Streaming e prompt caching: la risposta arriva parola per parola (stream NDJSON: evento di avvio, delta di testo, evento di chiusura). Il prefisso del prompt (sistema piu' descrizioni strumenti) e' marcato come riusabile verso il provider, e c'e' un secondo punto di cache sulla conversazione per il tool-loop a piu' iterazioni. Tempo alla prima parola sceso da 8 secondi a circa 1.5; costo del prefisso in rilettura crollato (lo sconto sulla cache arriva al 96%).
- Prompt versionati e telemetria: costo e latenza per turno tracciati.
- Osservabilita' di servizio: un endpoint di salute (con modalita' profonda che esegue anche uno smoke del provider) e un endpoint di stato per il monitoraggio. Le trace dei turni vengono conservate.
Esempio concreto: una query "forni pirolitici" passata dal client produce uno stream completo, il loop chiama in sequenza gli strumenti di sinonimi, attributi, valori distinti e ricerca prodotti, trova i modelli pertinenti, risponde raggruppando per linea e chiede quale serie interessa. Costo dell'ordine del centesimo di dollaro, latenza di alcune decine di secondi per il giro discovery completo. Il path completo e' vivo end-to-end, non solo la configurazione.
6. Sicurezza dei rilasci
La domanda operativa: come faccio a sapere che un cambiamento non rompe altro? Risposta: un orchestratore di refresh piu' una suite di regressione.
- Refresh orchestrator: un comando che rilancia in sequenza indicizzatore del wiki, ingest dei PDF, audit dei codici contro i range, coerenza wiki contro database, allineamento dei sinonimi, cross-validate delle policy.
- Golden queries: circa 27 conversazioni di test con assertion non solo sul "strumento chiamato" ma sull'effettivo match degli attributi sui prodotti ritornati.
- Health check su drift degli attributi e freshness dei sample.
Severity aggregata, exit code 0/1/2. Ogni intervento si chiude con una run, non con un "secondo me funziona". E' cio' che mi permette di toccare piu' punti del sistema in due settimane senza paura.
7. La svolta a piattaforma: server e client
Fino a poco fa la UI era una pagina tecnica monolitica. Ora una console agentica (sviluppata in SvelteKit) la sostituisce come interfaccia di produzione: chat-first, canvas adattivo, voce, mobile-first.
Il punto chiave non e' "ho fatto una UI nuova", e' aver formalizzato il confine client/server:
- Il client tocca solo se stesso: componenti, UX, stato UI, rendering, voce, chiamate HTTP.
- Il server tocca tutto il resto: modello, prompt, strumenti, grafo, database, guardrail, dati, integrazioni.
- L'unico accoppiamento e' il contratto HTTP: un endpoint di streaming (NDJSON), piu' gli endpoint per il turno sincrono e il reset; un payload prodotto tipizzato; uno snapshot del preventivo passato a ogni turno; i mount statici di PDF e immagini. Gli endpoint di salute e stato sono osservabilita', non li consuma il client.
- Il backend non ha CORS abilitato: in sviluppo il client passa da un reverse proxy che inoltra le chiamate al backend stesso-origin; in produzione si mette un proxy davanti o si serve same-origin.
8. La traiettoria: assorbire l'app gestionale agenticamente
Qui sta la parte ambiziosa. L'azienda ha un'app gestionale tradizionale (Next.js, database PostgreSQL sincronizzato dall'ERP, integrazione con l'ERP, invio email, un assistente per le statistiche, e i concetti centrali del commerce: clienti, carrelli, ordini, accordi, listini personalizzati).
La decisione: non riscrivere l'app gestionale dentro il client. Le sue procedure si ripensano una alla volta come agenti, e la logica nuova si costruisce lato piattaforma (il server), non nel client. E' uno strangler-fig: l'app tradizionale resta in produzione in parallelo e si spegne modulo per modulo man mano che gli agenti la sostituiscono. Dove serve solo data-entry puro, l'app tradizionale puo' restare.
Conseguenza importante: assorbendo le sue procedure, la piattaforma eredita anche il ruolo di integrazione dati che oggi ha l'app gestionale. Le entita' (clienti, carrelli, ordini) restano la verita' dell'ERP: la piattaforma le espone agli agenti, non le riscrive da zero. Come le raggiunge (riuso delle API dell'app esistente oppure accesso diretto al database) e' un dettaglio implementativo lato server, ancora da decidere.
9. Cosa NON ho fatto e perche'
- Niente framework agent pesante: pezzi minimi necessari (strumenti standalone, supervisor leggero, contratti tipizzati), con la consapevolezza che un giorno potrei migrare senza pagarne il prezzo oggi.
- Niente database industriale al day-1: SQLite basta finche' resto sotto soglie ragionevoli. Il database di scala entra quando arriva un PIM cross-brand reale.
- Safety non delegata al modello: il guardrail deterministico copre l'intera classe di problemi, non dipende dal fatto che l'AI "si ricordi" di chiamare lo strumento giusto.
- Niente riscrittura uno-a-uno dell'app gestionale: le procedure si ripensano come agenti, non si ricopiano come schermate CRUD.
- Niente logica di dominio nei client: se una feature richiede logica server, si fa nella piattaforma e il client la consuma.
Per le sette scelte tecniche distintive raccontate per un pubblico non sviluppatore, vedi Arcocat in sintesi. Per la mappa architetturale completa vedi Agentizzare una PMI, e per il pattern del wiki curato Wiki narrativo AI-maintained.
Registro aggiornamenti
- v1.0
Prima stesura. Quadro infrastrutturale di arcocat dopo la svolta a piattaforma: cervello e tool-loop, mattoncini deterministici, layer dati, prestazioni e osservabilita', sicurezza dei rilasci, confine client/server, traiettoria di assorbimento agentico dell'app gestionale esistente.