Pipeline RAG + Agenti LLM per automazione documenti aziendali
Il problema di partenza: un'azienda con centinaia di documenti interni (policy, manuali, contratti, report) e zero capacità di interrogarli in modo strutturato. La ricerca per parola chiave non funziona su testi lunghi e non cattura il significato. Il sistema doveva rispondere a domande in linguaggio naturale, citare le fonti e non inventarsi nulla.
Architettura
Il sistema è diviso in tre blocchi distinti: ingestion, retrieval e inference.
Ingestion pipeline
I documenti entrano nel sistema attraverso una pipeline di preprocessing che gestisce PDF, DOCX e testo plain. Ogni documento viene:
- Estratto e pulito (rimozione header/footer, normalizzazione encoding)
- Suddiviso in chunk con overlap configurabile (512 token, overlap 64)
- Embeddato con
text-embedding-ada-002di OpenAI - Salvato in PostgreSQL con estensione
pgvector
Il chunking con overlap è critico: senza di esso, una risposta che attraversa il confine tra due chunk viene tagliata e il contesto si perde.

Retrieval con reranking
La query dell'utente viene embeddatat e confrontata con i vettori in pgvector tramite cosine similarity. I top-20 risultati vengono poi passati a un reranker cross-encoder (BGE-reranker-base) che riduce il set a 5 chunk con il punteggio di rilevanza più alto.
Il reranking risolve un problema reale: i modelli di embedding bi-encoder sono veloci ma imprecisi sul ranking. Il cross-encoder è lento ma sa effettivamente confrontare query e chunk insieme. Usarli in sequenza bilancia velocità e qualità.
Agenti LLM
Sopra il retrieval è stato costruito un layer di agenti con LangChain. L'agente principale ha accesso a tre tool:
- document_search: interroga pgvector con la query riformulata
- calculator: per domande che richiedono calcoli su dati estratti
- date_context: inietta la data corrente per domande temporali
L'agente decide autonomamente quale tool usare, in quale ordine, e quando ha abbastanza contesto per rispondere. Il prompt di sistema vincola il modello a citare le fonti e a rispondere "non so" quando il contesto non è sufficiente.

API e deployment
L'interfaccia è una REST API FastAPI con:
- Endpoint
/query(POST): riceve la domanda, restituisce risposta + fonti + latenza - Endpoint
/ingest(POST): accetta file, avvia la pipeline asincrona - Autenticazione Bearer token
- Rate limiting per tenant
- Logging strutturato su ogni chiamata (latenza, token usati, chunk recuperati)
Il tutto containerizzato con Docker Compose: un container FastAPI, uno PostgreSQL+pgvector, uno per il worker asincrono (Celery + Redis).
Risultati
- Latenza media per query: 1.8s (embedding + retrieval + inference)
- Accuracy su un test set di 50 domande interne: 87% di risposte corrette con fonte citata
- Hallucination rate: <5% (misurato manualmente su campione)
- Zero risposte inventate su domande fuori dominio — il sistema risponde correttamente "non trovato"