Blog

IA e Avvocati (9): creare un motore RAG base

IA e Avvocati (9): creare un motore RAG base

Intelligenza artificiale e Avvocati

9. Implementazione locale di un motore RAG base per studi legali

Questo nono post della guida pratica accompagna gli studi legali nell’adozione concreta dell’Intelligenza Artificiale generativa.

In particolare, ci concentriamo sull’implementazione in ambiente Windows di un semplice sistema RAG (Retrieval-Augmented Generation) locale di esempio, utilizzando LangChain, FAISS e Ollama.


🎯 Obiettivo del post

Fornire una guida chiara e commentata per costruire un semplice motore RAG locale d’esempio in grado di interrogare archivi interni di documenti legali e generare risposte pertinenti, con attenzione a riservatezza, semplicità e riutilizzabilità del codice.


🧩 Concetti chiave

  • RAG: sistema che unisce il recupero semantico da un archivio indicizzato con la generazione di testo tramite LLM.
  • LangChain: framework per creare pipeline tra LLM, archivi e altri strumenti NLP.
  • FAISS: libreria per l’indicizzazione e la ricerca veloce di vettori.
  • Ollama: ambiente locale per eseguire LLM su dispositivi propri.

💼 Prerequisiti

Per poter utilizzare correttamente lo script che crea un motore RAG base, è necessario predisporre l’ambiente nel modo seguente (vedi post 6):

  • Un computer con Windows 10 o superiore.
  • Python 3.10 o versione compatibile installato.
  • Visual Studio Code configurato con ambiente virtuale attivo.
  • Il software Ollama installato e funzionante.
  • I seguenti modelli scaricati con Ollama tramite terminale:
ollama pull llama3.1:8b
ollama pull nomic-embed-text
  • Una cartella locale che contenga i documenti PDF legali da interrogare. Questa cartella può essere:
    • denominata documenti_legali e posizionata nella stessa cartella del file python (.py) dello script;
    • oppure configurata usando un percorso assoluto in formato Windows, preferibilmente come raw string, ad esempio:
directory = r"C:\Users\NomeUtente\Documents\StudioLegale\documenti_legali"

🔍 Nota per utenti non esperti: per sapere qual è il percorso esatto della cartella, fai clic con il tasto destro su di essa in Esplora File e seleziona “Copia come percorso”. Ricordati di anteporre r alla stringa incollata nello script, oppure di raddoppiare ogni barra \ per evitare errori.


🛠️ Installazione dei pacchetti necessari

Se stai utilizzando Visual Studio Code e non hai familiarità con il terminale, segui questi semplici passaggi:

  1. Apri Visual Studio Code e assicurati di avere attivato l’ambiente virtuale come spiegato nel post 6.
  2. Clicca sul menu Visualizza > Terminale.
  3. Nella parte inferiore dello schermo si aprirà il terminale.
  4. Copia e incolla il comando seguente nel terminale e premi Invio:
pip install langchain langchain-community faiss-cpu langchain-ollama PyMuPDF langchain-text-splitters

Queste librerie servono per:

  • orchestrare il sistema (LangChain);
  • creare rappresentazioni numeriche dei testi (embedding);
  • effettuare ricerche semantiche (FAISS);
  • far funzionare i modelli in locale (Ollama);
  • leggere i contenuti dei PDF (PyMuPDF).

Una volta completata l’installazione, puoi passare direttamente allo script di esempio.


Script completo con spiegazioni per il motore RAG base

Esecuzione di esempio

Immagina di avere nella cartella ./documenti_legali un file PDF contenente il testo della Costituzione italiana. Se esegui lo script e scrivi nel terminale la domanda:

Quali sono i principi fondamentali della Costituzione italiana?

il sistema estrae il testo dal documento PDF, lo analizza semanticamente, individua i blocchi rilevanti e, tramite LLM, fornisce una risposta come questa:

Secondo l’articolo 1 della Costituzione italiana, i principi fondamentali sono:

* L’Italia è una Repubblica democratica, fondata sul lavoro.

* La sovranità appartiene al popolo, che la esercita nelle forme e nei limiti della Costituzione.

Inoltre, l’articolo 2 precisa che la Repubblica riconosce e garantisce i diritti inviolabili dell’uomo, sia come singolo sia nelle formazioni sociali ove si svolge la sua personalità, e richiede l’adempimento dei doveri inderogabili di solidarietà politica, economica e sociale.

Infine, l’articolo 3 afferma che tutti i cittadini hanno pari dignità sociale e sono eguali davanti alla legge, senza discriminazioni di sorta.

Confrontiamo questa risposta con quella data alla medesima domanda dallo stesso LLM “generalista” prima di “conoscere” il file contenente la Costituzione (post 7).

Ora il modello fornisce una risposta più precisa e pertinente.

Lo script

Lo script seguente costruisce un motore RAG locale, passo dopo passo. I documenti PDF vengono letti, segmentati in blocchi, trasformati in vettori semantici, indicizzati e interrogati tramite un LLM locale.

Lo script lavora con PDF nativi. Per l’estrazione del testo da file immagine o PDF scannerizzati, e per la pulizia preliminare del testo, si rimanda al post 8.

# -- a) Importazioni --
import os
import fitz  # PyMuPDF per leggere PDF
from langchain_core.documents import Document
from langchain_ollama import OllamaEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import FAISS
from langchain_core.prompts import ChatPromptTemplate
from langchain_ollama.llms import OllamaLLM

# -- b) Caricamento PDF --
directory = "./documenti_legali"
documents = []
for filename in os.listdir(directory):
    if filename.endswith(".pdf"):
        filepath = os.path.join(directory, filename)
        with fitz.open(filepath) as pdf:
            text = "\n".join(page.get_text() for page in pdf)
            documents.append(Document(page_content=text, metadata={"source": filename}))

# -- c) Segmentazione in chunk --
splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100)
split_docs = splitter.split_documents(documents)

# -- d) Embedding semantico --
embeddings = OllamaEmbeddings(model="nomic-embed-text")

# -- e) Vector store con FAISS --
vector_store = FAISS.from_documents(split_docs, embeddings)

# -- f) Query e recupero documenti simili --
query = input("Ciao, sono il tuo assistente legale. Inserisci la tua domanda: ")
docs = vector_store.similarity_search(query)

# Estrai il contenuto testuale dai documenti
context = "\n\n".join([f"FONTE: {doc.metadata['source']}\nCONTENUTO: {doc.page_content}" for doc in docs])

# -- g) Generazione risposta con LLM --
prompt = ChatPromptTemplate.from_template ("""
Sei un assistente giuridico specializzato in diritto italiano. 
Usa esclusivamente il testo fornito come contesto per elaborare le tue risposte.

CONTESTO:
{context}

DOMANDA: {question}
RISPOSTA:
""")

model = OllamaLLM(model="llama3.1:8b")
chain = prompt | model
response = chain.invoke({"question": query, "context": context})
print(response)

Glossario essenziale:

  • Chunk: un frammento di testo (es. 1000 caratteri) in cui viene suddiviso il documento per facilitare l’elaborazione da parte dell’IA. Serve per non superare i limiti del modello e per mantenere il contesto logico.
  • Embedding: la trasformazione di un testo in un vettore numerico che ne rappresenta il significato. Più due testi sono simili, più i loro vettori saranno vicini nello spazio matematico.
  • Vector store: un archivio indicizzato di vettori (testi trasformati in numeri) che permette di cercare i documenti più simili a una domanda.
  • LLM: acronimo di “Large Language Model”. È il cervello generativo che, dato un contesto (come i chunk trovati), formula una risposta in linguaggio naturale.
  • LangChain: un framework Python che consente di mettere insieme tutti questi strumenti in modo fluido e strutturato.

Questo script è composto da sette fasi principali:

  1. Importazioni: in questa fase vengono caricate le librerie necessarie. Queste librerie permettono di:
    • leggere i PDF;
    • dividere il testo in parti più gestibili (chunk);
    • trasformare i testi in vettori numerici comprensibili dal sistema;
    • costruire una base dati semantica interrogabile;
    • generare una risposta coerente tramite un modello di linguaggio.
  2. Caricamento PDF: lo script accede a una cartella locale contenente i PDF e li apre uno a uno. Utilizza PyMuPDF per leggere il testo da ogni pagina. Ogni file viene trasformato in un oggetto strutturato che conserva sia il contenuto testuale che informazioni utili (es. il nome del file).
  3. Segmentazione in chunk: i testi estratti vengono suddivisi in blocchi da 1000 caratteri con 100 caratteri di sovrapposizione tra un blocco e l’altro. Questo consente di preservare la continuità delle informazioni e ottenere risposte più precise. È come dividere un lungo paragrafo in sezioni leggibili senza perdere il filo logico.
  4. Embedding semantico: ogni blocco di testo viene convertito in un vettore numerico grazie al modello nomic-embed-text. Questo passaggio è essenziale per consentire al sistema di confrontare testi in modo “intelligente”, basandosi sul significato e non solo sulle parole.
  5. Creazione del vector store: i vettori ottenuti vengono inseriti in un archivio indicizzato chiamato vector store, grazie alla libreria FAISS. Questo archivio consente di effettuare ricerche veloci e accurate, trovando i blocchi di testo più simili a una domanda posta in linguaggio naturale.
  6. Ricerca: quando l’utente formula una domanda, il sistema cerca nel vector store i blocchi di testo più pertinenti. Non si tratta di una ricerca per parole chiave, ma di una ricerca semantica: il sistema capisce il significato della domanda e recupera ciò che realmente serve.
  7. Generazione della risposta: i risultati recuperati vengono passati a un LLM locale (llama3.1:8b), che genera una risposta strutturata e in linguaggio naturale alla domanda posta. In questa fase, i documenti trovati non vengono semplicemente copiati, ma vengono usati come “contesto” per istruire il modello. Il modello prende la domanda dell’utente e costruisce una risposta sfruttando le informazioni recuperate. Questo è il cuore del sistema RAG: il recupero (retrieval) trova ciò che serve, e la generazione (generation) riformula in modo chiaro e coerente. È come se un assistente legale umano, leggendo i documenti più rilevanti, producesse una sintesi precisa su misura per la domanda ricevuta. L’intelligenza artificiale, grazie a questo meccanismo, diventa realmente utile e affidabile, offrendo risposte basate sui contenuti reali dell’archivio legale interno dello studio.

Questa sequenza consente di costruire un semplice assistente giuridico che consulta archivi interni e fornisce risposte basate su fonti testuali reali.

Si tratta ovviamente di un semplice esempio, un punto di partenza.


🔧 Come usare lo script per il motore RAG base in Visual Studio Code

Questo script può essere copiato in un file motore_rag.py ed eseguito in Visual Studio Code (VS Code).

Nel post 6 abbiamo già spiegato:

  • come installare VS Code,
  • come creare un progetto python,
  • e come attivare un ambiente virtuale.

Una volta completati quei passaggi:

  1. Crea un nuovo file motore_rag.py
  2. Incolla il codice dello script e salva il file
  3. Installa i pacchetti nel terminale virtuale
  4. Esegui lo script con il comando python motore_rag.py

⚖️ Vantaggi e limiti di un sistema RAG locale per lo studio legale

Un sistema RAG locale costruito su misura per uno studio legale italiano presenta numerosi vantaggi, ma va considerato nel contesto di altre soluzioni disponibili, come quelle ibride (parzialmente su cloud) o verticali (pensate specificamente per il settore legale).

✅ Vantaggi principali

  • Privacy garantita: tutti i documenti, le domande e le risposte restano all’interno della macchina locale dello studio. Questo è fondamentale per rispettare il segreto professionale, il GDPR e l’etica forense. Nessun dato viene inviato a server esterni.
  • Velocità di consultazione: il sistema lavora su archivi interni indicizzati semanticamente, consentendo interrogazioni rapide anche su grandi quantità di testi. Le domande possono essere formulate in linguaggio naturale e ottenere risposte centrate.
  • Flessibilità e controllo: il sistema è completamente personalizzabile. Si può adattare a vari ambiti del diritto (es. immigrazione, civile, penale, lavoro) semplicemente caricando i documenti relativi. Inoltre, può essere esteso con nuove funzionalità, prompt specifici, o interfacce utente.
  • Indipendenza tecnologica: l’uso di strumenti open-source e l’esecuzione locale svincolano lo studio da licenze costose o soluzioni chiuse. Lo strumento resta pienamente controllabile e modificabile.

⚠️ Limiti da considerare

  • Maggiori responsabilità di gestione: serve un minimo di competenza tecnica per mantenere aggiornato il sistema e garantire il buon funzionamento (es. aggiornamento dei modelli, indicizzazione documenti).
  • Assenza di ottimizzazioni legali verticali: rispetto a piattaforme commerciali verticali, un sistema generico RAG locale non include per default modelli addestrati sul diritto italiano o su giurisprudenza specifica (ma si può arrivare a quel livello con estensioni mirate).
  • Accessibilità limitata: un sistema locale funziona solo nel contesto dove è stato installato. Le soluzioni ibride offrono invece l’accesso multi-dispositivo o da remoto.

In sintesi, un RAG locale è una scelta ottima per studi legali che vogliono autonomia, privacy e personalizzazione, ma va valutato attentamente rispetto a bisogni specifici, risorse disponibili e alternative sul mercato.


🚀 Come migliorare e potenziare l’assistente legale AI di base

Una volta compreso e testato il sistema RAG d’esempio presentato in questo post, è possibile ampliarlo gradualmente per ottenere un assistente legale ancora più completo, efficiente e utile nello studio. Ecco i principali miglioramenti che consigliamo, spiegati in modo semplice:

  1. Espandere la base documentale: puoi aggiungere nuove categorie di documenti (atti, pareri, giurisprudenza, normativa), anche suddividendole in sottocartelle tematiche. Il motore RAG potrà essere istruito a distinguere tra le fonti e a usare quella più adatta alla domanda.
  2. Interfaccia utente: puoi costruire una semplice interfaccia grafica (GUI) con Streamlit o Gradio, così da non dover usare il terminale.
  3. Controllo della pertinenza: puoi configurare il sistema per restituire, oltre alla risposta, anche i documenti effettivamente consultati, così da garantire la massima trasparenza e verificabilità delle risposte.
  4. Aggiungere sempre un livello di verifica umana: prima che una risposta venga utilizzata per finalità professionali occorre proporla sempre a un avvocato dello studio per la validazione. Questo garantisce rigore e tutela da errori.
  5. Prompt personalizzati: è possibile costruire prompt su misura per ogni tipo di richiesta: redazione di pareri, confronto normativo, sintesi di sentenze, ecc. Questo rende il sistema ancora più flessibile.
  6. Salvataggio e riutilizzo delle sessioni: con opportune modifiche, si può tenere traccia delle domande e delle risposte fornite, così da creare un archivio consultabile delle ricerche effettuate in studio.

Questi miglioramenti possono essere introdotti anche uno per volta, a seconda delle competenze disponibili e del tempo che si vuole investire. L’importante è partire da una base solida e locale come quella presentata qui: ogni estensione diventerà poi un naturale passo successivo.


🧠 Come migliorare la qualità delle risposte generate dal motore RAG base

Anche partendo da uno script di base, è possibile ottenere risposte più pertinenti, affidabili e formalmente corrette. Ecco alcune strategie semplici ma efficaci per migliorare la qualità delle risposte fornite dal sistema:

  1. Curare la qualità del testo nei PDF: il sistema RAG può restituire buone risposte solo se i documenti da cui apprende sono scritti chiaramente. Evita PDF con testo malformattato o OCR impreciso. È utile usare strumenti come Tesseract (vedi post 8) per pulire e standardizzare i contenuti.
  2. Ottimizzare i prompt: istruzioni chiare nel prompt (es. “Usa un linguaggio giuridico formale”, oppure “Cita gli articoli pertinenti”) orientano il modello verso uno stile più preciso e contestuale. Il prompt attuale può essere ulteriormente raffinato in base al tipo di richiesta (pareri, analisi normativa, sintesi giurisprudenziale). Si affronterà in seguito nel dettaglio il tema del legal prompting.
  3. Limitare il contesto: invece di passare tutti i chunk trovati, si possono limitare a quelli con maggiore similarità (es. top 3). Questo aiuta il modello a concentrarsi sulle informazioni più rilevanti, evitando risposte vaghe.
  4. Impostare il modello con un tono specifico: tramite parametri del modello (come system in OllamaLLM) si può chiedere di mantenere sempre un tono formale, con terminologia legale e senza generalizzazioni.
  5. Controllare i limiti di token: se il contesto è troppo lungo, può essere tagliato. È importante monitorare quanto testo si fornisce al modello per rimanere entro i limiti e garantire risposte complete.
  6. Testare più modelli: LLaMA è un buon modello generale, ma si può valutare l’uso di LLM giuridici addestrati su corpora legali (es. modelli basati su sentenze, diritto italiano o europeo).

Applicando anche solo alcune di queste strategie, la qualità delle risposte migliora sensibilmente, rendendo il sistema più utile e affidabile nello studio.


📊 Come migliorare l’indicizzazione con FAISS e ottenere recuperi più efficaci

Per rendere il sistema ancora più potente ed efficiente, è possibile ottimizzare l’indicizzazione dei documenti e le ricerche nel vector store FAISS. Ecco alcune strategie, spiegate in modo semplice:

  1. Pre-elaborazione del testo: prima di generare i chunk e gli embeddings, è utile rimuovere elementi superflui come caratteri speciali, numerazioni inutili o note a piè di pagina (si veda il post 8). Standardizzare punteggiatura e maiuscole/minuscole migliora la consistenza e quindi anche la qualità dell’indice vettoriale.
  2. Aggiunta di metadati rilevanti: ogni documento indicizzato può includere metadati (come fonte normativa, anno, ambito tematico), che aiutano a filtrare e contestualizzare meglio i risultati. Ad esempio, potresti recuperare solo i documenti di diritto civile o solo le versioni più aggiornate.
  3. Salvataggio e aggiornamento dell’indice FAISS: salvare l’indice su disco consente di riutilizzarlo senza doverlo ricostruire ogni volta. È anche possibile aggiornarlo periodicamente con nuovi documenti, mantenendo il sistema sempre aggiornato.
  4. Regolare i parametri di ricerca: limitare il numero di risultati restituiti (ad esempio i 3 più simili) aiuta il modello generativo a concentrarsi sul contesto davvero utile, migliorando la precisione della risposta.
  5. Sovrapposizione tra chunk ben calibrata: una buona sovrapposizione evita che concetti importanti vengano tagliati tra due segmenti. Può essere utile aumentarla per testi normativi complessi e ridurla per testi più narrativi.
  6. Ricerca ibrida (semantica + keyword): per una copertura più completa, si può combinare la ricerca semantica con una ricerca per parole chiave. Questo approccio è particolarmente utile in ambito giuridico, dove il significato e il lessico specifico hanno entrambi valore. LangChain supporta la combinazione di più meccanismi di retrieval in modo coordinato.

Con questi accorgimenti, il tuo vector store diventa più efficiente, le ricerche più intelligenti e il sistema complessivamente più reattivo e affidabile.


📌 Cosa puoi fare ora con il motore RAG base

✅ Puoi caricare copia di documenti di prova nella cartella ./documenti_legali

✅ Esegui lo script di base in VS Code con una domanda reale

✅ Verifica la qualità della risposta

✅ Applica uno o più miglioramenti suggeriti nei paragrafi precedenti (es. prompt personalizzati, aggiunta metadati, ricerca ibrida)

✅ Considera di salvare l’indice FAISS per riutilizzarlo e migliorare la velocità


✅ Conclusione

Il sistema RAG locale presentato in questo post rappresenta un semplice e mero esempio, un punto di partenza per comprendere l’IA applicata al settore legale e sperimentare l’implementazione di un assistente legale basato sull’intelligenza artificiale.

È semplice, estendibile, e rispettoso delle esigenze di privacy e controllo dei dati.

Ogni parte del codice è documentata per consentire agli Avvocati, anche senza esperienza tecnica, di comprendere e personalizzare lo strumento.

Non è ovviamente idoneo a un uso professionale.

Nel prossimo post vedremo come migliorare le performance del RAG costruendo prompt intelligenti e su misura per lo studio legale.


Per un aiuto e per approfondire

Per un aiuto e per approfondire i temi dell’intelligenza artificiale nello studio legale, del legal tech e del legal design è online il GPT Iusreporter.tech (link esterno, richiede ChatGPT)

Per gli altri articoli pubblicati su questo blog sul tema:
Intelligenza artificiale e Avvocati


Scritto con l’aiuto di Iusreporter, il tuo assistente per la ricerca giuridica online 

Studio legale Avvocato Giuseppe Briganti

Pesaro – Urbino

Post aggiornato alla data di pubblicazione

📄 Disclaimer

Gli script Python e i contenuti di questo testo sono forniti esclusivamente a scopo informativo, didattico e sperimentale in ambienti di test controllati. Non costituiscono consulenza legale o tecnica e non sostituiscono il parere di un professionista qualificato.

L’autore declina ogni responsabilità per eventuali errori, omissioni, malfunzionamenti, danni diretti o indiretti, incidentali, consequenziali o di altro tipo derivanti dall’uso, dall’uso improprio o dall’impossibilità di utilizzo degli script o delle informazioni contenute nel presente testo. Gli script sono forniti “così come sono” (AS IS), senza garanzie esplicite o implicite, incluse, ma non limitate a, garanzie di commerciabilità, idoneità a uno scopo particolare o assenza di violazioni.

L’utilizzo degli strumenti è sotto la piena responsabilità dell’utente, che è tenuto a verificarne, in particolare:

  • la correttezza tecnica e funzionale,
  • la conformità alle normative vigenti, incluse, a titolo esemplificativo, l’AI Act, il GDPR, il Codice Deontologico Forense,
  • il rispetto delle licenze delle librerie e dei componenti e software di terze parti eventualmente utilizzati, inclusi quelli distribuiti con licenze open source.

Gli script non sono progettati né validati per l’uso in ambienti produttivi o per l’elaborazione di dati personali. Qualsiasi utilizzo in tali contesti è esclusiva responsabilità dell’utente, che deve adottare le opportune misure di sicurezza e valutare l’impatto normativo.

Gli script sono stati testati su Python 3.10 in ambiente Windows. Non è garantita la compatibilità con versioni diverse del linguaggio o con altri sistemi operativi. L’autore non fornisce assistenza tecnica, aggiornamenti periodici o correzione dei malfunzionamenti.

Tutti i contenuti di questo ebook, inclusi i testi e il codice sorgente, sono protetti dal diritto d’autore ai sensi della Legge 22 aprile 1941, n. 633 e successive modificazioni. È vietata la riproduzione, distribuzione, pubblicazione, comunicazione o modifica, totale o parziale, in qualsiasi forma, dei contenuti senza autorizzazione scritta dell’autore, salvo espressa indicazione contraria.

L’uso di questo testo e degli script in esso contenuti implica l’accettazione integrale del presente disclaimer. Se non si accettano tali condizioni, si prega di non utilizzare il materiale fornito.

Eventuali errori, osservazioni o segnalazioni possono essere comunicati all’autore, che si riserva di valutarli senza alcun obbligo di risposta o correzione.

Condividi

Leave a comment

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