piattaforma RAG per Ufficio Legale

piattaforma RAG per Ufficio Legale

La piattaforma RAG per Ufficio Legale nasce come sistema RAG (Retrieval-Augmented Generation) dedicato al settore legale e compliance. L’obiettivo è permettere agli operatori di interrogare documentazione tecnica e giuridica complessa — contratti, sentenze, PDF, manuali di conformità — tramite un’interfaccia conversazionale simile a ChatGPT o KimiK2, ONPREMISE

L’applicazione è stata progettata come infrastruttura full-stack con separazione netta tra frontend, backend applicativo, database relazionale e database vettoriale. Attualmente il backend è operativo, la pipeline di ingestion è configurata e integrata con Qdrant e PostgreSQL, mentre il frontend è in fase di debugging dell’interfaccia chat.

L’architettura generale del sistema è organizzata nel seguente modo:

┌────────────────────────────────────────────────────────┐
│                   FRONTEND (React)                     │
│  - @assistant-ui/react (Gestione Interfaccia Chat)     │
│  - Tailwind / Material-UI                              │
└──────────────────────────┬─────────────────────────────┘
                           │ HTTP / REST (Porta 5099)
┌──────────────────────────▼─────────────────────────────┐
│                   BACKEND (Bun / TS)                   │
│  - Express / Elysia (routing)                          │
│  - Zod (Validazione rigida della configurazione)       │
└──────────────┬───────────────────────────┬─────────────┘
               │                           │
  SQL Query    │                           │ Vector Search / Ingestion
(Porta 5432)   │                           │ (Porta 6333)
┌──────────────▼──────┐             ┌──────▼──────────────┐
│  PostgreSQL / DB    │             │   Qdrant Docker     │
│  - Sessioni utenti  │             │   - Collection:     │
│  - Login & Registr. │             │     manuali_..._v1  │
│  - Storico chat     │             │   - Dims: 3072      │
└─────────────────────┘             └─────────────────────┘

Il frontend è sviluppato in React con supporto a @assistant-ui/react, Tailwind e Material-UI per la gestione dell’interfaccia conversazionale. Tutta la comunicazione avviene tramite API HTTP REST esposte dal backend sulla porta 5099.

Sul lato server ho scelto Bun come runtime principale al posto di Node.js. La scelta è legata principalmente alle prestazioni di bundling, all’esecuzione nativa di TypeScript senza layer intermedi come tsc o esbuild e alla gestione integrata delle variabili d’ambiente tramite --env-file.

Per la persistenza relazionale viene utilizzato PostgreSQL, responsabile della gestione utenti, dell’autenticazione JWT e dello storico delle conversazioni. La ricerca semantica è invece demandata a Qdrant, eseguito in Docker locale sulla porta 6333. Qdrant rappresenta il cuore della componente RAG perché gestisce le collezioni vettoriali e le operazioni di similarity search tramite distanza coseno.

L’infrastruttura AI utilizza un approccio ibrido. Il modello gpt-4o-mini viene impiegato per la generazione delle risposte, privilegiando velocità ed economicità operativa, mentre gli embeddings sono generati tramite text-embedding-3-large a 3072 dimensioni, scelto per garantire maggiore granularità semantica nel dominio legale.

Dal punto di vista funzionale, il sistema implementa una separazione completa dei ruoli. Gli amministratori accedono a una dashboard privata che permette di collegare database vettoriali, mappare risorse locali e definire la collezione attiva per il sistema. Gli utenti finali operano invece tramite un’interfaccia minimale protetta da autenticazione JWT, senza accesso ai dettagli infrastrutturali del vector database.

Anche la persistenza delle conversazioni è stata progettata in ottica enterprise. Il frontend implementa una sidebar per il recupero delle sessioni precedenti, mentre PostgreSQL conserva lo stato delle chat evitando la perdita del contesto tra diverse sessioni utente.

La parte più importante dell’intera architettura è la pipeline RAG. Invece di interrogare direttamente l’LLM con richieste generiche, il sistema esegue una ricerca vettoriale su Qdrant per recuperare i frammenti documentali più pertinenti. Questi contenuti vengono poi inseriti nel prompt come contesto certificato, costringendo il modello linguistico a produrre risposte ancorate ai documenti realmente presenti nella knowledge base.


Il processo di ingestion e popolamento vettoriale

La fase di ingestion rappresenta il passaggio che trasforma documenti statici in dati semanticamente interrogabili. Senza questa pipeline, PDF e DOCX resterebbero semplici file testuali non utilizzabili da un motore RAG.

Il flusso operativo della pipeline è strutturato nel seguente modo:

┌───────────┐      ┌───────────┐      ┌───────────────┐      ┌─────────────┐      ┌─────────────┐
│ File PDF  ├─────►│ Chunking  ├─────►│ Vectorization ├─────►│   Payload   ├─────►│   Qdrant    │
│  Legali   │      │ (Frammenti│      │ (OpenAI 3072) │      │ Metadati    │      │  Upserting  │
└───────────┘      └───────────┘      └───────────────┘      └─────────────┘      └─────────────┘

La prima fase consiste nel parsing dei documenti. Il testo viene estratto dai file sorgente e normalizzato. Quando il documento contiene strutture tabellari complesse, il sistema può appoggiarsi a LlamaCloud o LlamaParse per preservare la struttura logica del contenuto.

Successivamente entra in gioco il chunking. I documenti vengono suddivisi in frammenti più piccoli e parzialmente sovrapposti. Una configurazione tipica utilizza blocchi da 500 caratteri con overlap di 50 caratteri. Questa sovrapposizione evita la perdita di contesto tra sezioni semanticamente collegate.

Ogni frammento viene poi inviato al modello text-embedding-3-large, che restituisce un vettore numerico di 3072 dimensioni. Questo array rappresenta la posizione semantica del contenuto all’interno dello spazio vettoriale. Frammenti semanticamente simili finiranno quindi vicini nello spazio multidimensionale, permettendo a Qdrant di recuperarli tramite similarity search.

A ciascun embedding vengono associati metadati JSON contenenti informazioni come nome file, pagina originale e testo sorgente. Il vettore arricchito viene infine salvato nella collezione target tramite operazioni di upsert.


Problematiche tecniche affrontate durante lo sviluppo

Durante lo sviluppo del prototipo sono emersi diversi problemi infrastrutturali legati alla configurazione runtime, alla gestione HTTP e alla compatibilità matematica degli embeddings.

Una delle prime criticità ha riguardato la validazione delle variabili d’ambiente tramite Zod. Il backend implementa un controllo runtime che blocca l’avvio dell’applicazione se mancano parametri critici di configurazione.

const configSchema = z.object({
  QDRANT_URL: z.string().url(),
  QDRANT_API_KEY: z.string().min(1, "QDRANT_API_KEY is required"),
  OPENAI_API_KEY: z.string().startsWith("sk-"),
});

Il problema nasceva dal fatto che Qdrant, in ambiente Docker locale, non richiedeva autenticazione API. Tuttavia il validatore imponeva comunque la presenza di QDRANT_API_KEY, impedendo l’avvio del backend. La soluzione adottata è stata l’introduzione di una chiave placeholder alfanumerica all’interno del file .env, sufficiente per soddisfare la validazione senza alterare il comportamento del database locale.

Una seconda criticità ha coinvolto gli header HTTP. Durante l’avvio del backend veniva generata l’eccezione:

TypeError: Header 'api-key' has invalid value: '...clòdask...'

L’errore era causato dalla presenza di caratteri non ASCII all’interno della chiave API fittizia. Bun e Node.js applicano una validazione conforme alla RFC 7230 e rifiutano header contenenti caratteri speciali. La sostituzione della chiave con una stringa esclusivamente alfanumerica ha eliminato il problema a livello di serializzazione HTTP.

La problematica più delicata ha però riguardato la compatibilità dimensionale degli embeddings. La collezione manuali_fotocopiati_v1 era stata inizialmente creata utilizzando vettori da 3072 dimensioni compatibili con text-embedding-3-large. Se il backend avesse utilizzato text-embedding-3-small, che produce vettori da 1536 dimensioni, qualsiasi ricerca vettoriale avrebbe generato un errore irreversibile su Qdrant.

Per evitare il mismatch è stata introdotta una configurazione esplicita nel file .env:

OPENAI_MODEL=gpt-4o-mini
OPENAI_EMBEDDING_MODEL=text-embedding-3-large
OPENAI_EMBEDDING_DIM=3072

Questa configurazione garantisce la compatibilità matematica tra embeddings generati e struttura della collezione vettoriale.


Stato attuale del frontend e debugging React

Attualmente il backend è completamente funzionante ed espone correttamente gli endpoint REST sulla porta 5099, inclusa la connessione simultanea a PostgreSQL e Qdrant.

La fase di lavoro corrente riguarda invece il frontend React e l’integrazione di @assistant-ui/react. Durante il rendering dell’applicazione viene generata l’eccezione:

frontend TypeError: Cannot read properties of null (reading 'useRemoteThreadListRuntime')
    at .../@assistant-ui/react/dist/legacy-runtime/runtime-cores/remote-thread-list/index.js:1:10

L’analisi preliminare indica che alcuni componenti dell’interfaccia stanno tentando di accedere al runtime context al di fuori dell’albero React gestito da AssistantRuntimeProvider. In pratica, componenti come la cronologia delle chat o la finestra dei messaggi vengono renderizzati senza essere correttamente associati all’istanza runtime inizializzata dal client.

Dal punto di vista infrastrutturale il motore RAG è già pronto per processare query documentali complesse. Il prossimo step riguarda il riallineamento del wrapper React nel file App.tsx, assicurando che tutti i componenti grafici vengano montati all’interno del provider corretto e possano quindi accedere al runtime condiviso dell’assistente.

AI Disclosure

Questo contenuto è stato sviluppato con il supporto di strumenti di intelligenza artificiale generativa utilizzati come assistenza editoriale durante le fasi di revisione, organizzazione e ottimizzazione del testo.

L’analisi tecnica, l’architettura del sistema, le configurazioni infrastrutturali e le valutazioni progettuali descritte nel documento derivano da attività reali di sviluppo, debugging e implementazione svolte sul progetto sono del sottoscritto.

Tutti i contenuti sono stati revisionati e validati manualmente prima della pubblicazione.

Di R. Andrea Belvedere

Mi occupo di AI applicata, automazione, LLM, RAG e blockchain, con esperienza in contesti enterprise e industriali. Sono formatore tecnico, scrittore SEO e divulgatore scientifico: mi piace spiegare tecnologie complesse in modo semplice, pratico e orientato all’uso reale. Tutti gli Articoli sono elaborati dal sottoscritto sulla base di un progetto sviluppato direttamente dall’autore. Strumenti di intelligenza artificiale sono stati utilizzati come supporto all’analisi della documentazione e alla redazione. Il contenuto è stato verificato e revisionato dall’autore, che ne assume la responsabilità editoriale.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

error: Content is protected !!