Il gap tra demo e produzione
Un LLM in demo gira su dieci esempi accuratamente scelti, con latenza accettabile e connessione stabile. Un LLM in produzione deve gestire migliaia di richieste al giorno, provider che tornano 429 (rate limit) o 503, utenti che inviano prompt in lingue inaspettate, e output che cambiano senza preavviso quando il provider aggiorna il modello.
I pattern architetturali che seguono non sono ottimizzazioni: sono requisiti di base per qualsiasi sistema LLM che deve girare 24/7 con SLA definiti.
Fallback chain: mai un singolo punto di fallimento
Il principio è semplice: nessun provider LLM ha una SLA del 100%. La fallback chain definisce cosa fare quando il provider primario non risponde.
Schema tipico
Provider primario (es. GPT-4o) → se timeout/error → Provider secondario (es. Claude Haiku) → se timeout/error → Risposta cached → se non disponibile → Fallback statico con messaggio di errore trasparente.
La fallback chain deve essere configurabile, non hardcoded. I provider cambiano, i prezzi cambiano, le SLA cambiano. L'architettura deve permettere di cambiare l'ordine di priorità senza deploy.
Retry con exponential backoff e jitter
Una chiamata fallita non è necessariamente persa. I rate limit temporanei (429) e i timeout transitori (503) si risolvono ritentando — ma con logica.
- Backoff esponenziale: primo retry dopo 1s, secondo dopo 2s, terzo dopo 4s. Non inondare il provider già in difficoltà con retry immediati.
- Jitter: aggiungi una componente casuale al delay (es. +/- 20%) per evitare che tutti i client retry sincronizzati colpiscano il provider nello stesso momento.
- Max retry limit: 3 tentativi è il massimo sensato. Oltre si aspetta troppo e si crea code.
- Retry solo su errori transitori: 429 e 503 → retry. 400 (bad request) e 401 (auth) → fail immediatamente, il retry non cambia nulla.
Circuit breaker: proteggere il sistema quando il provider è giù
Se il provider LLM restituisce errori al 50% delle richieste, smetti di inviare richieste per 30-60 secondi. Questo è il pattern circuit breaker, e serve a tre cose: proteggere il rate limit residuo, non accumulare latenza sulle richieste utente, e dare al provider il tempo di recuperare.
Stati del circuit breaker
Closed (normale): tutte le richieste passano. Open (provider in difficoltà): tutte le richieste vanno al fallback, zero chiamate al provider. Half-open (dopo il timeout): una richiesta di test. Se passa, torna Closed. Se fallisce, torna Open.
Semantic caching: ridurre latenza e costi
Molte applicazioni LLM ricevono prompt simili in modo ricorrente — FAQ, query su documenti, trasformazioni di testo standard. Il semantic cache memorizza la coppia (embedding del prompt, risposta) e restituisce la risposta cached per prompt semanticamente simili.
Il funzionamento:
- Arriva un prompt → calcola l'embedding
- Cerca nel vector store i prompt cached con cosine similarity > threshold (es. 0.92)
- Se trovato e similarity alta → restituisci la risposta cached (latenza: 20-80ms)
- Se non trovato → chiama il provider LLM → salva in cache prima di rispondere
Su workload ripetitivi (chatbot su FAQ, risposta su catalogo prodotti), il semantic cache riduce i costi del 40-70% e la latenza da 2-5 secondi a meno di 100ms per le cache hit.
Output validation: non fidarsi ciecamente dell'LLM
Quando l'applicazione richiede output strutturato (JSON, lista, codice), l'LLM può produrre output malformati — JSON con trailing comma, HTML non chiuso, codice con syntax error. La validazione deve essere a livello applicativo, non solo UI.
- Schema Pydantic o JSON Schema per validare la struttura
- Se validazione fallisce: retry con prompt più esplicito sul formato atteso
- Se retry fallisce: log dell'errore + fallback (risposta generica o cached)
- Mai propagare output non validato al layer applicativo
Monitoring delle derive del modello
Un modello in produzione cambia senza che tu lo decida: il provider rilascia una nuova versione, il comportamento cambia sottilmente, i tuoi prompt producono output diversi. Il concept drift in un sistema LLM è insidioso perché spesso non rompe nulla — produce solo output degradati.
Cosa monitorare:
- Distribuzione lunghezza output: cambiamenti bruschi segnalano un cambio di modello
- Parse failure rate sugli output strutturati: aumento → il modello risponde in modo diverso
- Latenza p50/p95/p99: degrado di latenza indica problemi lato provider
- Token usage per richiesta: aumento inaspettato → prompt injection o cambio comportamento
- Feedback utente: il segnale più lento ma più affidabile
Definire un token budget per tipo di richiesta prima di andare in produzione. Task semplici (classificazione, estrazione) → modelli piccoli e economici. Task complessi (ragionamento, sintesi lunga) → modelli grandi solo quando necessario. Loggare ogni token consumato. Impostare hard limit per utente/sessione per evitare sorprese in fattura.
Come implementiamo in Blueware
I sistemi AI che costruiamo includono questi pattern come componenti di base, non come afterthought. La fallback chain, il circuit breaker e il semantic cache vengono definiti in fase di design — non aggiunti dopo il primo outage in produzione.
Il monitoring delle derive è integrato nella pipeline: ogni risposta viene loggata con metadati (provider, model version, token count, latenza, esito validazione) e disponibile in dashboard in tempo reale. Se il comportamento del modello cambia, lo sappiamo prima degli utenti.