RAG per la documentazione InterSystems IRIS
Backend di Retrieval-Augmented Generation per la documentazione IRIS: ingestione, retrieval ibrido con reranking e risposte con citazioni verificate. Costruito nel tempo libero a lavoro per colmare un vuoto reale degli LLM su ObjectScript.
Contesto e problema
In Healthy Reply lavoriamo moltissimo con InterSystems IRIS e il suo linguaggio proprietario ObjectScript. È un linguaggio di nicchia: online ci sono pochi esempi e di conseguenza gli LLM generici sono stati addestrati su una quantità ridotta di dati. Quando chiedevo a un modello come si definisce una classe, un metodo o una pratica specifica, ottenevo risposte spesso sbagliate o inventate.
Il problema non era la mancanza di documentazione: InterSystems pubblica una documentazione PDF/Markdown completa. Il problema era che nessuno la teneva aggiornata nel contesto del modello.
Così, nel tempo libero a lavoro (intraprendenza, più che un task assegnato), ho progettato e costruito un sistema RAG completo:
un agente che, data una domanda su IRIS/ObjectScript, recupera i passaggi pertinenti della documentazione e risponde in modo grounded, con citazioni che puntano alle sezioni esatte.
Architettura
Il sistema è composto da due parti integrate:
- Agente “Neurons” — gestisce l’interazione con l’utente ed esegue function calls verso il server RAG. È l’interfaccia che i colleghi usano davvero.
- Server RAG (questo progetto) — riceve le query via API, esegue retrieval e generazione. Espone endpoint retrieval-only (per l’agente) e un endpoint
/answerend-to-end.
flowchart LR
A["Documentazione IRIS<br/>(PDF / Markdown)"] --> B["Parsing & pulizia<br/>Markdown"]
B --> C["Parent-child chunking"]
C --> D["Embedding<br/>BAAI/bge-m3"]
D --> E[("Qdrant<br/>vector DB")]
C --> F[("Indice BM25")]
Q["Query utente<br/>(anche in italiano)"] --> G["Query expansion<br/>LLM"]
G --> H["Multi-query<br/>RAG-Fusion"]
E --> I["Fusione ibrida<br/>Reciprocal Rank Fusion"]
F --> I
I --> J["Reranking<br/>cross-encoder"]
J --> K["Contesti parent<br/>classificati"]
K --> L["LCEL answer chain<br/>(LangChain)"]
L --> M["Risposta grounded<br/>+ citazioni validate"]
Pipeline RAG: dalla documentazione alla risposta con citazioni
Componenti chiave
Ingestione. I PDF vengono convertiti in Markdown, puliti e splittati con una strategia parent-child: i child chunk piccoli vengono indicizzati per un retrieval preciso, i parent chunk più grandi vengono restituiti al chiamante per avere contesto ricco.
Retrieval ibrido. Dense retrieval con Qdrant e il modello di embedding BAAI/bge-m3; sparse retrieval con BM25. I due ranking vengono fusi con Reciprocal Rank Fusion (RRF), così il sistema non dipende da un solo metodo.
Query expansion. Un LLM trasforma la domanda dell’utente (spesso in italiano) in più query tecniche in inglese — varianti che catturano sinonimi, nomi di classi e API. RAG-Fusion le esegue tutte e fonde i risultati.
Reranking. Il cross-encoder ms-marco-MiniLM-L-6-v2 confronta query-documento in modo più accurato (ma più costoso), quindi viene applicato solo sui candidati già filtrati: massimizza recall in fase di retrieval, precision in fase finale.
Risposta grounded. La pipeline LCEL genera una risposta con citations strutturate (ID sorgente deterministici) e una validazione post-generazione: se una citazione non corrisponde a una sorgente recuperata, viene segnalata.
Protezione. API protetta con X-API-Key, guardrail sui limiti di contesto (caratteri/token), timing per stage e tracing opzionale LangSmith.
Cosa ho fatto io
Ho progettato e implementato tutto il server RAG da zero: ingestione, indicizzazione, engine di retrieval, pipeline di risposta, evaluation e Docker. L’ho integrato con l’agente Neurons tramite API REST. È un progetto nato per iniziativa mia, non assegnato: ho identificato il dolore (LLM che non conoscono ObjectScript), proposto la soluzione e costruito l’intero sistema.
Scelte progettuali (e trade-off)
- Perché BM25 + dense? BM25 è forte su keyword, nomi di classi/metodi e termini esatti; il dense retrieval cattura semantica e parafrasi. La fusione RRF riduce la dipendenza da un singolo metodo.
- Perché il reranker? Il retriever iniziale massimizza recall; il reranker migliora la precision sui top-k usando un confronto più costoso ma più accurato. Il modello scelto è volutamente leggero perché gira su CPU.
- Embedded Qdrant a worker singolo: semplice da gestire localmente; per il multi-worker in produzione si passerebbe a un server Qdrant standalone. Scelta documentata.
- Evaluation come regression test: il dataset sintetico (708 query generate via LLM con ground truth) serve a intercettare regressioni quando cambio chunking, embedding, query expansion o reranking. Metriche: Recall@K, MRR, Precision@K, NDCG@K.
Risultati
Baseline su 708 query di evaluation (sintetiche, in inglese):
| Metrica | Valore |
|---|---|
| MRR parent | 0.756 |
| Recall parent@5 | 0.904 |
| Recall file@5 | 0.980 |
| NDCG@5 | 0.834 |
| Latenza media | ~1.17 s |
Il retrieval a livello di file è molto forte; a livello di parent chunk è buono per un RAG tecnico. Le metriche operative più utili sono recall_parent@5, recall_parent@10 e mrr_parent.
Note
- Il dataset di eval è sintetico e in inglese: va trattato come baseline di regressione, non come gold set umano.
- Il codice è privato (progetto legato al lavoro): posso mostrarlo e discuterlo in un colloquio. Repo GitHub: profilo FilippoGalli001.
- Paper correlato: SEUPD@CLEF 2024 — stesso tema di query expansion e re-ranking, in contesto di Information Retrieval classico.