Template architetturale Production-ready

Architettura RAG production

Lo stack che uso sui clienti reali per sistemi RAG che reggono il production load — con il perché di ogni scelta.

Premessa: il 70% dei sistemi RAG che vedo nelle aziende sono "PoC che hanno funzionato abbastanza da finire in produzione". Reggono finché ci sono 10 utenti, poi cominciano a dare risposte sbagliate, lente, o entrambe.

Questa è l'architettura che uso dal terzo progetto in poi, dopo aver capito cosa rompe quando la scala aumenta. È un template — vanno adattati nomi e parametri al tuo caso — ma le decisioni chiave sono le stesse.

Diagramma architettura RAG production Loader PDF / HTML / DB Chunker Semantic + overlap Embedder bge-m3 multilingual Vector DB Qdrant / pgvector User query (con sessione) Query rewriter LLM piccolo: HyDE / step-back Hybrid retriever vector + BM25 sparso Re-ranker bge-reranker → top 5 LLM Generator + Citation Layer Llama 3 70B Q4 / Mistral Large / GPT-4o Validator + Citation Check Risposta deve citare almeno 1 chunk dal contesto Eval + Tracing LangSmith / Phoenix / custom

1. Ingest pipeline (offline)

Loader

Estrae testo da PDF, HTML, database, file Office. Errore comune: usare un loader generico per tutti i tipi. PDF complessi (tabelle, layout multicolonna) richiedono loader dedicati come unstructured.io o docling di IBM.

Chunker

Splitta i documenti in pezzi indicizzabili. Non usare chunk fissi di 512 token: rompi le frasi a metà. Usa chunking semantic (basato su similarity tra frasi consecutive) o almeno recursive character splitter con overlap del 10-15%.

Embedder

Trasforma testo in vettori. Per italiano + inglese consiglio BAAI/bge-m3 (multilingual, 1024 dim) o nomic-embed-text-v1.5. Evita text-embedding-ada-002 di OpenAI: debole sull'italiano e costoso a scala.

Vector DB

Per < 10M chunk: pgvector su PostgreSQL — meno componenti, meno problemi. Per > 10M o se serve filtering complesso: Qdrant (production-ready, gestione filter potente). Pinecone va bene solo se vuoi managed e accetti il lock-in.

2. Query pipeline (online)

Query rewriter — il pezzo che il 90% degli sviluppatori salta

La query dell'utente ("come si imposta il timeout?") è quasi sempre peggiore della query ottimale per la ricerca. Usa un LLM piccolo (Llama 3.1 8B, Mistral 7B) per:

  • HyDE: genera una finta risposta plausibile, embedda quella invece della query → recall +15-30%
  • Step-back: chiedi al LLM "qual è la domanda più generale dietro questa?" e cerca quella
  • Query expansion: aggiungi sinonimi e termini correlati

Hybrid retrieval

Combinare vector search (semantica) con BM25 (keyword exact-match) batte sempre il solo vector search. Implementabile in pgvector con un join, in Qdrant via API. Peso tipico: 0.7 vector + 0.3 BM25.

Re-ranker

Recupera 30-50 candidati dal retriever, poi riordinali con un re-ranker cross-encoder (BAAI/bge-reranker-v2-m3). Prendi i top 5. Costo: +200ms di latenza, +20% di accuracy. Senza questo step, le risposte sono mediocri.

3. Generation

LLM Generator

Qui pesa relativamente poco la scelta del modello rispetto al lavoro fatto nelle fasi precedenti. Tre opzioni:

  • Llama 3.3 70B Q4 su A100 80GB → privacy garantita, costo predicibile, italiano ottimo
  • Mistral Large 2 via API mistral.ai → ospitato in UE (GDPR-friendly)
  • GPT-4o / Claude 3.5 via API → migliore qualità ma dati lasciano l'UE

Citation Layer (obbligatorio per produzione)

Il prompt deve forzare il modello a citare quale chunk ha usato per ogni affermazione. Pattern: ogni chunk passato al modello ha un ID, il modello deve scrivere [1], [2] nelle citazioni. Permette validazione, debugging, trust dell'utente.

4. Validator (il safety net)

Risposta deve passare almeno 2 check prima di andare all'utente:

  1. Citation check: ogni affermazione fattuale ha una citazione? Se no → "non ho informazioni sufficienti"
  2. Hallucination check: usa un altro LLM (più piccolo, più economico) per verificare che la risposta sia supportata dal contesto. Pattern NLI (Natural Language Inference): contesto + risposta → entails/contradicts/neutral.

5. Observability

Devi loggare ogni: query, query riscritta, chunk recuperati, score di re-ranking, risposta, feedback utente. Setup minimo:

  • Tracing: LangSmith, Phoenix (Arize), o stack custom (OpenTelemetry → Tempo)
  • Eval automatica: dataset di 50-200 query con risposte attese, regression test ad ogni deploy
  • Eval umana: campione settimanale di 20 risposte → rating 1-5 → trend nel tempo

Checklist pre-deploy

Non andare in produzione senza aver risposto sì a tutte queste:

  • ☐ Recall @ 5 sul retriever > 80% sul dataset di test?
  • ☐ Latency p95 < 3 secondi end-to-end?
  • ☐ Citation rate > 95% (quasi ogni affermazione cita una fonte)?
  • ☐ Hallucination rate < 5% sul test set?
  • ☐ Fallback configurato per ogni componente (LLM down, DB down, embedder down)?
  • ☐ Rate limiting per utente?
  • ☐ Cost monitoring con alert se > 150% del budget mensile?
  • ☐ PII detection sui log (non loggare dati personali)?
  • ☐ Re-indexing job schedulato (i documenti cambiano)?
  • ☐ User feedback loop (thumbs up/down → dataset di re-training)?

Cosa non c'è in questo template (e perché)

Agentic RAG / ReAct. Sono pattern utili ma aumentano la latenza 3-5× e la complessità di debugging 10×. Vai diretto solo se il caso d'uso lo richiede davvero (es. multi-step reasoning con tool use).

Knowledge graph. Promette molto ma in pratica fa fatica a superare un buon hybrid retrieval + re-ranker su domini generici. Vale la pena solo su domini molto strutturati (es. legale, medico).

Multi-modal RAG. Se devi gestire immagini, schemi, tabelle complesse, il setup raddoppia. Parti text-only, aggiungi multi-modal solo se necessario.