Questo progetto implementa un sistema RAG (Retrieval-Augmented Generation) che permette di indicizzare documenti PDF e interrogarli in linguaggio naturale tramite Azure OpenAI.
Il flusso è composto da due fasi:
[PDF] → vettorizza.py → [ChromaDB] → interroga.py (risultati grezzi)
→ interroga_llm.py (risposta generata da LLM)
- Python 3.10 - 3.13 (vedi nota sotto su Python 3.14)
- Un endpoint Azure OpenAI con accesso ai modelli:
text-embedding-3-large(embedding)gpt-4o(chat)
Python 3.14 NON e' supportato. La libreria
chromadbdipende internamente dapydantic.v1.BaseSettings, che non e' compatibile con Python 3.14+. L'import dichromadbfallisce con l'errore:pydantic.v1.errors.ConfigError: unable to infer type for attribute "chroma_server_nofile"Il problema e' tracciato nella issue chroma-core/chroma#5996. Al momento (marzo 2026) nessuna versione di
chromadbpubblicata su PyPI risolve il bug, inclusa la 1.5.2.Versione consigliata: Python 3.13.x
# macOS con Homebrew brew install python@3.13 /opt/homebrew/bin/python3.13 -m venv .venv source .venv/bin/activate pip install -r requirements.txt
pip install -r requirements.txtPacchetti utilizzati:
| Pacchetto | Scopo |
|---|---|
langchain-openai |
Integrazione con Azure OpenAI (embedding + chat) |
langchain-chroma |
Integrazione con ChromaDB come vector store |
langchain-community |
Loader per documenti (PyPDFLoader) |
langchain-text-splitters |
Splitting dei documenti in chunk |
chromadb |
Database vettoriale locale |
pypdf |
Parsing dei file PDF |
python-dotenv |
Caricamento variabili d'ambiente da file .env |
La configurazione avviene tramite variabili d'ambiente nel file .env. Copia il template e inserisci i tuoi valori:
cp .env.example .envContenuto di .env.example:
AZURE_OPENAI_API_KEY=your_api_key_here
AZURE_OPENAI_ENDPOINT=https://your-endpoint.openai.azure.com
AZURE_OPENAI_EMBEDDING_DEPLOYMENT=your_embedding_deployment_name
AZURE_OPENAI_CHAT_DEPLOYMENT=your_chat_deployment_name
AZURE_OPENAI_API_VERSION=2024-06-01| Variabile | Descrizione |
|---|---|
AZURE_OPENAI_API_KEY |
Chiave di autenticazione per l'API Azure OpenAI |
AZURE_OPENAI_ENDPOINT |
URL dell'endpoint Azure OpenAI |
AZURE_OPENAI_EMBEDDING_DEPLOYMENT |
Nome del deployment per generare gli embedding |
AZURE_OPENAI_CHAT_DEPLOYMENT |
Nome del deployment per la generazione di risposte (LLM) |
AZURE_OPENAI_API_VERSION |
Versione dell'API Azure OpenAI |
Nota: il file
.enve' incluso nel.gitignoree non viene tracciato da git. Solo.env.example(con placeholder generici) viene committato.
Nota SSL: gli script disabilitano la verifica SSL (
urllib3.disable_warnings,CURL_CA_BUNDLE=''). Questo e' necessario in ambienti corporate con proxy/firewall che intercettano il traffico HTTPS.
Questo script va eseguito una sola volta per ogni nuovo documento (o quando il documento cambia).
python vettorizza.py- Caricamento PDF: usa
PyPDFLoaderper leggere il file PDF pagina per pagina - Splitting in chunk: il testo viene suddiviso in frammenti da 1000 caratteri con un overlap di 50 caratteri, usando
RecursiveCharacterTextSplitter. I separatori usati in ordine di priorita' sono:\n\n(doppio a capo)\n(singolo a capo)(spazio)""(carattere per carattere, come ultima risorsa)
- Generazione embedding: ogni chunk viene trasformato in un vettore numerico tramite il modello
text-embedding-3-largedi Azure OpenAI - Persistenza: i vettori e i metadati vengono salvati in un database ChromaDB locale nella cartella
./chroma_db
Vettorizzati 42 chunk da documento.pdf
Salvati in ./chroma_db
chroma_db/
├── chroma.sqlite3 # Database SQLite con metadati e mapping
└── <uuid>/
├── data_level0.bin # Vettori di embedding (HNSW index)
├── header.bin # Header dell'indice
├── length.bin # Lunghezze dei vettori
└── link_lists.bin # Struttura del grafo HNSW per la ricerca
Questa modalita' restituisce i chunk grezzi piu' simili alla domanda, senza rielaborazione da parte di un LLM.
python interroga.py- Apre il database ChromaDB esistente
- Avvia un loop interattivo: scrivi una domanda e premi Invio
- Per ogni domanda:
- La domanda viene trasformata in un vettore (embedding)
- Viene eseguita una similarity search contro i chunk indicizzati
- Vengono restituiti i 5 chunk piu' rilevanti (
k=5) con fonte e numero di pagina
- Digita
esci,exitoquitper uscire
Database caricato da: ./chroma_db
Documenti totali: 42
Domanda (o 'esci'): qual e' l'importo totale?
Trovati 5 risultati:
--- Risultato 1 ---
[contenuto del chunk piu' rilevante]
Fonte: documento.pdf
Pagina: 2
- Per verificare cosa il sistema ha effettivamente indicizzato
- Per debug: controllare se i chunk recuperati contengono l'informazione cercata
- Quando non serve una risposta elaborata ma solo i dati grezzi
Questa modalita' implementa il RAG completo: recupera i chunk rilevanti e li passa a GPT-4o per generare una risposta in linguaggio naturale.
python interroga_llm.py- Apre il database ChromaDB esistente
- Inizializza il modello di chat
gpt-4ovia Azure (temperature=0per risposte deterministiche,max_tokens=500) - Avvia un loop interattivo
- Per ogni domanda:
- Recupera i 5 chunk piu' rilevanti (similarity search)
- Concatena i chunk in un unico blocco di contesto
- Costruisce un messaggio con:
- System prompt: istruisce il modello a rispondere solo in base ai documenti forniti
- Human message: contesto (i chunk) + domanda dell'utente
- Invia il tutto a GPT-4o e stampa la risposta generata
- Digita
esci,exitoquitper uscire
Il modello riceve queste istruzioni:
Sei un assistente che analizza documenti.
- Analizza attentamente i documenti forniti
- Rispondi basandoti SOLO sulle informazioni presenti nei documenti
- Se l'informazione non e' presente, dillo chiaramente
- Sii preciso e cita i dati esatti quando possibile
- Non fare assunzioni sul tipo di documento
Questo prompt e' fondamentale per evitare che il modello "inventi" informazioni non presenti nel PDF (problema noto come hallucination).
Sistema pronto. Documenti: 42
Domanda (o 'esci'): qual e' l'importo totale?
L'importo totale risulta essere di 1.850,00 euro, come indicato
nella sezione riepilogativa del documento.
| File | Ruolo | Quando eseguirlo |
|---|---|---|
.env.example |
Template configurazione | Copiare in .env e compilare |
.env |
Configurazione con segreti (non tracciato da git) | Modificare prima di iniziare |
vettorizza.py |
Indicizza il PDF in ChromaDB | Una volta per documento |
interroga.py |
Ricerca semantica (chunk grezzi) | Per debug o consultazione rapida |
interroga_llm.py |
RAG completo con risposta LLM | Per interrogare il documento |
chroma_db/ |
Database vettoriale generato | Generato automaticamente |
- Aggiungere piu' PDF: modificare
vettorizza.pyper iterare su una lista di file - Cambiare chunk size: regolare
chunk_sizeechunk_overlapinvettorizza.pyper bilanciare precisione e contesto - Aumentare/diminuire i risultati: modificare il parametro
knellasimilarity_search - Cambiare modello LLM: modificare
AZURE_OPENAI_CHAT_DEPLOYMENTnel.env(es.gpt-4o-miniper risposte piu' economiche) - Aggiungere memoria conversazionale: integrare
ConversationBufferMemorydi LangChain per mantenere il contesto tra domande successive