Immagina di poter fare domande in linguaggio naturale a una cartella di PDF — manuali, contratti, dispense, bilanci — e ricevere risposte precise con tanto di riferimento alla fonte, tutto sul tuo computer, senza mandare un solo documento su internet. È quello che permette la tecnica RAG (Retrieval-Augmented Generation): un modello linguistico che, prima di rispondere, recupera i passaggi rilevanti dai tuoi documenti. In questa guida costruiamo un sistema RAG gratuito e completamente locale con Ollama e Python.
A chi serve, cosa otterrai e i prerequisiti
Questa guida è pensata per chi ha una minima dimestichezza con il terminale e vuole un assistente che risponda sui propri documenti senza usare servizi cloud — utile per motivi di riservatezza (dati aziendali, sanitari, legali) o semplicemente per non pagare abbonamenti. Alla fine avrai uno script Python che carica i PDF di una cartella, li indicizza e risponde alle tue domande citando i brani usati.
Prerequisiti reali:
- Un computer con Windows, macOS o Linux e almeno 8 GB di RAM (16 GB consigliati per modelli più grandi).
- Python 3.10 o superiore installato.
- Circa 5-6 GB di spazio libero per i modelli.
- Nessun account e nessuna carta di credito: tutto gira in locale.
Quali strumenti IA useremo e perché
Il cuore del sistema è Ollama, il programma gratuito e open source che scarica ed esegue modelli linguistici in locale con un comando. Ci servono due modelli:
- Un modello di generazione, che scrive le risposte. Consiglio come prima scelta
llama3.1:8b(ottimo equilibrio qualità/risorse) oqwen2.5:7b; su macchine potenti si può salire di dimensione. - Un modello di embedding, che trasforma il testo in vettori numerici per la ricerca semantica:
nomic-embed-textè leggero, veloce e gratuito.
Per la parte di codice useremo LangChain (che orchestra caricamento, suddivisione e interrogazione) e Chroma come database vettoriale locale. Perché questa combinazione e non un servizio cloud? Pro: gratis, privato, nessun limite d'uso. Contro: la qualità dipende dall'hardware e dal modello, e i modelli locali sono meno brillanti dei migliori modelli chiusi. Se ti serve la massima qualità e la privacy non è un vincolo, valuta in alternativa le API di OpenAI, Anthropic o Google; ma per iniziare, e per i dati sensibili, il locale è la scelta giusta.
Passo 1: installare Ollama e i modelli
Scarica Ollama dal sito ufficiale (ollama.com) e installalo. Poi, da terminale, scarica i due modelli:
ollama pull llama3.1:8b
ollama pull nomic-embed-text
Verifica che Ollama risponda:
ollama run llama3.1:8b "Dimmi ciao in italiano"
Se ricevi un saluto, sei pronto. Ollama resta attivo in background ed espone un servizio locale sulla porta 11434, che il nostro script userà.
Passo 2: installare le librerie Python
Crea una cartella per il progetto, mettici dentro i tuoi PDF in una sottocartella documenti/ e installa le dipendenze:
pip install langchain langchain-community langchain-ollama chromadb pypdf
Passo 3: indicizzare i PDF
Questo script legge tutti i PDF nella cartella, li divide in blocchi (chunk), calcola gli embedding e li salva in un database Chroma su disco. Salvalo come indicizza.py:
from langchain_community.document_loaders import PyPDFDirectoryLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_ollama import OllamaEmbeddings
from langchain_community.vectorstores import Chroma
# 1. Carica tutti i PDF della cartella
loader = PyPDFDirectoryLoader("documenti/")
pagine = loader.load()
print(f"Caricate {len(pagine)} pagine")
# 2. Dividi in blocchi con un po' di sovrapposizione
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000, chunk_overlap=150
)
blocchi = splitter.split_documents(pagine)
print(f"Creati {len(blocchi)} blocchi")
# 3. Calcola gli embedding e salvali in Chroma
embeddings = OllamaEmbeddings(model="nomic-embed-text")
db = Chroma.from_documents(
blocchi, embeddings, persist_directory="./chroma_db"
)
print("Indice creato in ./chroma_db")
Esegui con python indicizza.py. Alla fine avrai una cartella chroma_db con l'indice: va rifatto solo quando aggiungi o cambi i documenti.
Passo 4: fare domande ai documenti
Ora lo script che risponde. Recupera i blocchi più pertinenti, li passa al modello e stampa la risposta con le fonti. Salvalo come chiedi.py:
from langchain_ollama import OllamaEmbeddings, ChatOllama
from langchain_community.vectorstores import Chroma
embeddings = OllamaEmbeddings(model="nomic-embed-text")
db = Chroma(persist_directory="./chroma_db", embedding_function=embeddings)
llm = ChatOllama(model="llama3.1:8b", temperature=0.1)
domanda = input("Domanda: ")
# Recupera i 4 blocchi piu' rilevanti
risultati = db.similarity_search(domanda, k=4)
contesto = "\n\n".join(d.page_content for d in risultati)
prompt = f"""Rispondi alla domanda usando SOLO il contesto qui sotto.
Se la risposta non e' nel contesto, di' che non e' presente nei documenti.
Rispondi in italiano e in modo conciso.
CONTESTO:
{contesto}
DOMANDA: {domanda}
"""
risposta = llm.invoke(prompt)
print("\nRISPOSTA:\n", risposta.content)
print("\nFONTI:")
for d in risultati:
print("-", d.metadata.get("source"), "pag.", d.metadata.get("page"))
Esegui con python chiedi.py, digita una domanda e otterrai la risposta più il riferimento ai file e alle pagine usate. Il vincolo "usa solo il contesto" riduce le allucinazioni: se l'informazione non c'è, il modello lo dichiara invece di inventare.
Prompt di prova e risultato atteso
Supponiamo di aver caricato il manuale di un elettrodomestico. Una domanda tipo:
Qual è la temperatura massima consigliata per il lavaggio dei capi delicati e ogni quanto va pulito il filtro?
Il sistema recupererà i paragrafi pertinenti del manuale e risponderà con i valori esatti citati nel testo, indicando le pagine. Se chiedi qualcosa che il manuale non copre (per esempio "quanto consuma in standby?" quando il dato non c'è), risponderà che l'informazione non è presente nei documenti: esattamente il comportamento che vogliamo.
Varianti e casi avanzati
Alcuni miglioramenti utili quando prendi confidenza:
- Modello più capace: se hai una buona GPU, sostituisci
llama3.1:8bcon un modello più grande (per esempioqwen2.5:14b) per risposte più accurate. - Interfaccia grafica: collega Open WebUI a Ollama per avere un'esperienza simile a ChatGPT con caricamento documenti dal browser, senza scrivere codice.
- Citazioni più precise: aumenta
kper recuperare più contesto, o riducichunk_sizeper blocchi più mirati. - Molti documenti: con migliaia di pagine valuta un database vettoriale più robusto come Qdrant.
Errori comuni e soluzioni
ConnectionErroro "connection refused": Ollama non è in esecuzione. Avvialo (l'app o il comandoollama serve) e riprova.- Risposte lente: normale su CPU; usa un modello più piccolo o abilita la GPU. La prima domanda è sempre più lenta perché il modello viene caricato in memoria.
- Risposte "vuote" o fuori tema: probabilmente i PDF sono scansioni-immagine senza testo selezionabile. In quel caso serve un passaggio OCR (per esempio con Tesseract) prima dell'indicizzazione.
Chromache non trova l'indice: assicurati di aver eseguitoindicizza.pye che la cartellachroma_dbesista.
Quando non usare questo approccio e come proseguire
Il RAG locale è ideale per documenti riservati e per un uso personale o di piccolo team. Non è la scelta migliore se hai bisogno della massima qualità di ragionamento su testi molto complessi, o se devi servire tanti utenti contemporaneamente: in quei casi conviene passare a modelli via API (OpenAI, Anthropic, Google) o a soluzioni gestite, accettando però che i dati escano dalla tua macchina. Come passo successivo puoi trasformare questi script in una piccola app web con Streamlit, aggiungere la cronologia della conversazione, o integrare un modello aperto più potente come GLM-5.2 via API. Il concetto resta lo stesso: recuperare prima, generare poi — è questa la ricetta che rende un modello davvero utile sui tuoi dati.
Comandi e nomi dei modelli sono aggiornati a luglio 2026; consulta la documentazione ufficiale di Ollama e LangChain, che vengono aggiornate di frequente.




