LLM in produzione: pattern architetturali per sistemi affidabili

La demo funziona su input controllati. La produzione riceve tutto: prompt malformati, provider in outage, context length superato, latenza variabile e output inattesi. Questi sono i pattern che fanno la differenza tra un sistema AI che regge e uno che si rompe in silenzio.

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:

  1. Arriva un prompt → calcola l'embedding
  2. Cerca nel vector store i prompt cached con cosine similarity > threshold (es. 0.92)
  3. Se trovato e similarity alta → restituisci la risposta cached (latenza: 20-80ms)
  4. 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
Gestione dei costi

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.