Hai una cartella piena di PDF — manuali, contratti, dispense, verbali, una tesi — e vorresti poterli interrogare a voce come faresti con ChatGPT, ma senza caricarli su un server altrui? Questa guida ti mostra come costruire un sistema RAG (Retrieval-Augmented Generation) che gira interamente sul tuo computer, gratis e offline, usando Ollama e poche righe di Python. Alla fine avrai un piccolo programma che indicizza i tuoi documenti e risponde alle domande citando le fonti.
A chi serve e cosa otterrai
E' una guida di livello intermedio, pensata per chi sa muoversi un minimo con il terminale e non si spaventa davanti a uno script Python. E' utile a professionisti che trattano documenti riservati (avvocati, commercialisti, medici, consulenti), a studenti e ricercatori, a chiunque voglia un assistente sui propri testi senza rinunciare alla privacy. Otterrai due script: uno che indicizza i PDF di una cartella e uno che ti permette di fare domande ottenendo risposte basate solo su quei documenti.
Prerequisiti reali
- Un computer con Windows, macOS o Linux e almeno 8 GB di RAM (16 GB consigliati per modelli piu' grandi).
- Python 3.10 o superiore installato.
- Circa 10 GB di spazio libero su disco per Ollama e i modelli.
- Nessun account e nessuna carta di credito: e' tutto gratuito e open source.
Cos'e' il RAG, in due parole
Un modello linguistico non conosce i tuoi documenti privati. Il RAG risolve il problema cosi': quando fai una domanda, il sistema cerca nei tuoi testi i frammenti piu' pertinenti e li passa al modello come contesto, chiedendogli di rispondere basandosi su quelli. Per farlo servono tre ingredienti: un modello di embedding che trasforma il testo in vettori numerici, un database vettoriale che li conserva e li cerca per similarita', e un modello linguistico che genera la risposta finale.
Quali strumenti useremo (e le alternative)
Per ognuno dei tre ruoli ci sono piu' opzioni. Ecco le scelte di questa guida e il perche'.
- Motore IA locale: Ollama (prima scelta). Gratuito, semplice, gestisce con un comando sia il modello linguistico sia quello di embedding. Alternative: LM Studio (ottimo con interfaccia grafica, comodo per chi non ama il terminale) e llama.cpp (massimo controllo, piu' tecnico). Per automatizzare con Python, Ollama e' il piu' lineare.
- Modello linguistico:
llama3.2(3B, leggerissimo) oqwen2.5:7bse hai 16 GB di RAM e vuoi risposte migliori in italiano. - Modello di embedding:
nomic-embed-text, piccolo, veloce e adatto a documenti multilingue. - Database vettoriale: ChromaDB (prima scelta). Si installa con pip, salva tutto in una cartella locale e non richiede server. Alternativa: FAISS (di Meta, velocissimo su grandi volumi ma piu' spartano).
- Orchestrazione: qui usiamo codice Python puro invece di framework come LangChain o LlamaIndex. Sono ottimi, ma per imparare conviene vedere ogni passaggio in chiaro; potrai sempre passare a loro in seguito.
Passo 1 - Installa Ollama
Scarica Ollama dal sito ufficiale ollama.com. Su macOS e Windows e' un normale installer. Su Linux puoi usare lo script ufficiale:
curl -fsSL https://ollama.com/install.sh | sh
Verifica che funzioni con:
ollama --version
Passo 2 - Scarica i modelli
Servono due modelli: quello linguistico e quello per gli embedding. Scaricali cosi':
ollama pull llama3.2
ollama pull nomic-embed-text
Il primo occupa circa 2 GB, il secondo poche centinaia di MB. Se hai 16 GB di RAM e vuoi risposte piu' curate in italiano, scarica anche qwen2.5:7b (circa 4,7 GB) e usalo al posto di llama3.2 nel codice.
Passo 3 - Prepara l'ambiente Python
Crea una cartella per il progetto, al suo interno una sottocartella documenti dove metterai i PDF, poi crea un ambiente virtuale e installa le librerie:
python -m venv venv
# macOS / Linux:
source venv/bin/activate
# Windows:
venv\Scripts\activate
pip install chromadb pypdf ollama
Passo 4 - Lo script di indicizzazione
Crea un file indicizza.py. Questo script legge tutti i PDF nella cartella documenti, li spezza in frammenti, calcola gli embedding con Ollama e li salva in ChromaDB.
import os, glob, chromadb, ollama
from pypdf import PdfReader
CARTELLA = "documenti"
client = chromadb.PersistentClient(path="db")
coll = client.get_or_create_collection("pdf")
def spezza(testo, dim=800, sovrapp=100):
parole = testo.split()
pezzi, i = [], 0
while i < len(parole):
pezzi.append(" ".join(parole[i:i+dim]))
i += dim - sovrapp
return pezzi
idx = 0
for pdf in glob.glob(os.path.join(CARTELLA, "*.pdf")):
lettore = PdfReader(pdf)
testo = "\n".join((p.extract_text() or "") for p in lettore.pages)
for pezzo in spezza(testo):
if not pezzo.strip():
continue
emb = ollama.embeddings(model="nomic-embed-text", prompt=pezzo)["embedding"]
coll.add(ids=[str(idx)], embeddings=[emb], documents=[pezzo],
metadatas=[{"fonte": os.path.basename(pdf)}])
idx += 1
print("Indicizzato:", os.path.basename(pdf))
print(f"Totale frammenti indicizzati: {idx}")
Lancialo con python indicizza.py. Spezziamo il testo in blocchi di circa 800 parole con 100 di sovrapposizione: la sovrapposizione evita di tagliare a meta' un concetto importante tra un frammento e l'altro.
Passo 5 - Lo script per fare domande
Crea ora chiedi.py. Trasforma la tua domanda in un embedding, recupera i 4 frammenti piu' simili e li passa al modello linguistico chiedendogli di rispondere solo in base a quelli.
import sys, chromadb, ollama
client = chromadb.PersistentClient(path="db")
coll = client.get_collection("pdf")
domanda = sys.argv[1] if len(sys.argv) > 1 else input("Domanda: ")
q_emb = ollama.embeddings(model="nomic-embed-text", prompt=domanda)["embedding"]
res = coll.query(query_embeddings=[q_emb], n_results=4)
contesto = "\n\n".join(res["documents"][0])
fonti = ", ".join(sorted({m["fonte"] for m in res["metadatas"][0]}))
prompt = f"""Sei un assistente che risponde SOLO usando il contesto qui sotto.
Se la risposta non e' presente nel contesto, scrivi: "Non e' nei documenti".
Rispondi in italiano, in modo chiaro.
CONTESTO:
{contesto}
DOMANDA: {domanda}
RISPOSTA:"""
out = ollama.generate(model="llama3.2", prompt=prompt)
print(out["response"])
print("\nFonti consultate:", fonti)
Passo 6 - Provalo
Metti qualche PDF nella cartella documenti, esegui python indicizza.py e poi fai una domanda:
python chiedi.py "Qual e' la durata del contratto e come si rinnova?"
Il risultato atteso e' una risposta in italiano che riassume cio' che dicono i tuoi documenti sul tema, seguita dall'elenco dei file da cui sono stati presi i frammenti. Se l'informazione non c'e', il modello risponde "Non e' nei documenti": e' il comportamento corretto di un buon sistema RAG, che non deve inventare.
Varianti e miglioramenti
- Risposte migliori: sostituisci
llama3.2conqwen2.5:7bnello script delle domande. - Piu' contesto: alza
n_resultsda 4 a 6-8 per domande complesse. - Interfaccia grafica: se preferisci non usare il terminale, strumenti come AnythingLLM o Open WebUI offrono un RAG locale con interfaccia, sempre basati su Ollama.
- Altri formati: aggiungi il supporto a Word o testo semplice adattando la lettura dei file.
Errori comuni e come risolverli
- "connection refused" o errori di connessione a Ollama: il servizio non e' attivo. Avvialo con
ollama servein un altro terminale (su macOS/Windows parte da solo con l'app). - Il PC si impalla o va in "out of memory": stai usando un modello troppo grande per la tua RAM. Torna a
llama3.2o prova varianti quantizzate piu' leggere. - Risposte vuote o senza senso da un PDF: probabilmente e' una scansione di immagini senza testo. Serve un passaggio di OCR (per esempio con Tesseract) prima di indicizzarlo.
- Risposte troppo generiche: riduci la dimensione dei frammenti (per esempio 500 parole) per indicizzazioni piu' precise.
Quando NON usare questo approccio
Il RAG locale e' perfetto per qualche centinaio o migliaio di documenti su un PC personale. Se devi gestire milioni di file, ti servira' un'infrastruttura dedicata con database vettoriali professionali (Qdrant, Weaviate, pgvector) e piu' potenza di calcolo. E in contesti dove un errore ha conseguenze legali o sanitarie, ricorda che il modello e' un assistente, non una fonte di verita': la risposta va sempre verificata sul documento originale, che il nostro script per fortuna ti indica.
Come proseguire
Da qui puoi crescere in piu' direzioni: aggiungere una memoria della conversazione, mostrare l'estratto esatto da cui proviene la risposta, o impacchettare tutto in una piccola interfaccia web. Il bello di aver scritto il codice a mano e' che ora sai cosa succede in ogni passaggio: quando passerai a framework come LlamaIndex o a soluzioni cloud, non saranno piu' una scatola nera. E soprattutto, i tuoi documenti non avranno mai lasciato il tuo computer.




