2026-05-11 15:46:52 +02:00
|
|
|
# PDF → Chunk RAG-ready
|
2026-04-12 23:53:13 +02:00
|
|
|
|
2026-05-11 15:46:52 +02:00
|
|
|
Converte PDF digitali in chunk semantici pronti per la vettorizzazione RAG,
|
|
|
|
|
senza LLM né OCR.
|
2026-04-12 23:53:13 +02:00
|
|
|
|
2026-05-11 16:05:23 +02:00
|
|
|
**Pipeline:** PDF → Markdown strutturato → chunk semantici → embedding ChromaDB
|
2026-05-11 15:46:52 +02:00
|
|
|
**Stack:** Python · PyMuPDF · pdfplumber
|
|
|
|
|
**Non supportati:** PDF scansionati (solo immagini), PDF protetti da password.
|
2026-04-12 23:53:13 +02:00
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
2026-04-20 11:20:54 +02:00
|
|
|
## Setup
|
2026-04-12 23:53:13 +02:00
|
|
|
|
|
|
|
|
```bash
|
2026-04-20 11:20:54 +02:00
|
|
|
python -m venv .venv
|
|
|
|
|
source .venv/bin/activate
|
|
|
|
|
pip install -r requirements.txt
|
2026-04-12 23:53:13 +02:00
|
|
|
```
|
|
|
|
|
|
2026-05-11 15:46:52 +02:00
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Flusso completo
|
|
|
|
|
|
|
|
|
|
### 1. Posiziona il PDF
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
sources/<nome>.pdf
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 2. Converti il PDF in Markdown
|
2026-04-12 23:53:13 +02:00
|
|
|
|
|
|
|
|
```bash
|
2026-05-11 15:46:52 +02:00
|
|
|
# Singolo documento
|
|
|
|
|
.venv/bin/python conversione/ --stem <nome>
|
|
|
|
|
|
|
|
|
|
# Tutti i PDF in sources/
|
|
|
|
|
.venv/bin/python conversione/
|
|
|
|
|
|
|
|
|
|
# Forza riesecuzione (sovrascrive output esistente)
|
|
|
|
|
.venv/bin/python conversione/ --stem <nome> --force
|
2026-04-12 23:53:13 +02:00
|
|
|
```
|
|
|
|
|
|
2026-05-11 15:46:52 +02:00
|
|
|
Output in `conversione/<nome>/`:
|
2026-04-12 23:53:13 +02:00
|
|
|
|
2026-05-11 15:46:52 +02:00
|
|
|
| File | Descrizione |
|
|
|
|
|
|------|-------------|
|
|
|
|
|
| `raw.md` | Markdown grezzo — **non modificare** |
|
|
|
|
|
| `clean.md` | Markdown pulito — input per il chunker |
|
|
|
|
|
| `structure_profile.json` | Struttura rilevata e strategia di chunking |
|
|
|
|
|
| `report.json` | Metriche di qualità della conversione |
|
|
|
|
|
|
|
|
|
|
### 3. Verifica la qualità del Markdown (opzionale)
|
2026-04-12 23:53:13 +02:00
|
|
|
|
|
|
|
|
```bash
|
2026-05-11 15:46:52 +02:00
|
|
|
.venv/bin/python conversione/ validate <nome> --detail
|
|
|
|
|
```
|
2026-04-12 23:53:13 +02:00
|
|
|
|
2026-05-11 15:46:52 +02:00
|
|
|
Se lo score è ≥ 80 e `valid=true`, procedi. Altrimenti usa `/prepare-md` per
|
|
|
|
|
correzioni manuali (sillabazione residua, header malformati, ecc.).
|
|
|
|
|
|
|
|
|
|
### 4. Genera i chunk
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
.venv/bin/python chunks/chunker.py --stem <nome>
|
2026-04-15 13:33:56 +02:00
|
|
|
|
2026-05-11 14:44:16 +02:00
|
|
|
# Forza riesecuzione
|
2026-05-11 15:46:52 +02:00
|
|
|
.venv/bin/python chunks/chunker.py --stem <nome> --force
|
2026-04-15 13:33:56 +02:00
|
|
|
```
|
2026-04-12 23:53:13 +02:00
|
|
|
|
2026-05-11 15:46:52 +02:00
|
|
|
La strategia di chunking (`h3_aware`, `h2_paragraph_split`, `paragraph`,
|
|
|
|
|
`sliding_window`) viene scelta automaticamente da `structure_profile.json`.
|
|
|
|
|
|
|
|
|
|
Output in `chunks/<nome>/`:
|
|
|
|
|
|
|
|
|
|
| File | Descrizione |
|
|
|
|
|
|------|-------------|
|
|
|
|
|
| `chunks.json` | Lista di chunk con testo, sezione, titolo e metadati |
|
|
|
|
|
| `report.json` | Statistiche e anomalie del chunking |
|
|
|
|
|
|
|
|
|
|
### 5. Verifica i chunk
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
.venv/bin/python chunks/verify_chunks.py --stem <nome>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Verdict possibili:
|
|
|
|
|
|
|
|
|
|
| Verdict | Significato | Cosa fare |
|
|
|
|
|
|---------|-------------|-----------|
|
|
|
|
|
| `ok` | Nessun problema | Procedi alla vettorizzazione |
|
|
|
|
|
| `warnings_only` | Solo avvisi minori | Puoi procedere o eseguire il fix |
|
|
|
|
|
| `blocked` | Problemi bloccanti (chunk incompleti) | Esegui il fix |
|
|
|
|
|
|
|
|
|
|
### 6. Correggi i problemi (se necessario)
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
# Anteprima delle correzioni senza applicarle
|
|
|
|
|
.venv/bin/python chunks/fix_chunks.py --stem <nome> --dry-run
|
|
|
|
|
|
|
|
|
|
# Applica le correzioni (ricorsivo, fino a 3 iterazioni)
|
|
|
|
|
.venv/bin/python chunks/fix_chunks.py --stem <nome>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Il fix gestisce automaticamente: chunk incompleti (frase spezzata), chunk
|
|
|
|
|
troppo corti (accorpa al successivo), chunk eccessivamente lunghi (spezza
|
|
|
|
|
su punteggiatura). Ogni chunk termina sempre su un confine di frase.
|
2026-04-12 23:53:13 +02:00
|
|
|
|
2026-05-11 16:05:23 +02:00
|
|
|
### 7. Esegui l'ingestion
|
|
|
|
|
|
|
|
|
|
Prima verifica che Ollama e i modelli siano pronti:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
.venv/bin/python ollama/check_env.py
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Poi genera gli embedding e salva in ChromaDB:
|
|
|
|
|
|
|
|
|
|
```bash
|
2026-05-12 11:21:24 +02:00
|
|
|
# Singolo documento → collection con lo stesso nome
|
2026-05-11 16:05:23 +02:00
|
|
|
.venv/bin/python ingestion/ingest.py --stem <nome>
|
|
|
|
|
|
2026-05-12 11:21:24 +02:00
|
|
|
# Più documenti → un'unica collection condivisa
|
|
|
|
|
.venv/bin/python ingestion/ingest.py --collection <nome-collection> --stems doc1 doc2 doc3
|
|
|
|
|
|
|
|
|
|
# Tutti i documenti in chunks/ → collection separate
|
2026-05-11 16:05:23 +02:00
|
|
|
.venv/bin/python ingestion/ingest.py
|
|
|
|
|
|
|
|
|
|
# Rigenera dopo aver cambiato modello o aggiornato i chunk
|
|
|
|
|
.venv/bin/python ingestion/ingest.py --stem <nome> --force
|
|
|
|
|
```
|
|
|
|
|
|
2026-05-12 11:21:24 +02:00
|
|
|
Con `--collection` i chunk di documenti diversi vengono uniti in una singola
|
|
|
|
|
collection. Il metadato `source` identifica il documento di provenienza di ogni chunk.
|
|
|
|
|
|
|
|
|
|
Output in `chroma_db/` (ignorata da git).
|
2026-05-11 16:05:23 +02:00
|
|
|
|
2026-05-11 14:44:16 +02:00
|
|
|
---
|
2026-04-12 23:53:13 +02:00
|
|
|
|
2026-05-11 15:46:52 +02:00
|
|
|
## Configurazione del chunking
|
2026-04-12 23:53:13 +02:00
|
|
|
|
2026-05-11 15:46:52 +02:00
|
|
|
Tutti i parametri sono in [`chunks/config.py`](chunks/config.py):
|
2026-04-17 18:51:09 +02:00
|
|
|
|
2026-05-11 15:46:52 +02:00
|
|
|
```python
|
|
|
|
|
TARGET_CHARS = 600 # dimensione target dei chunk
|
|
|
|
|
CHUNK_TOLERANCE = 0.25 # ±25% → range accettabile [450, 750]
|
|
|
|
|
OVERLAP_SENTENCES = 1 # frasi di overlap tra chunk consecutivi
|
|
|
|
|
PROTECT_TABLES = True # tabelle emesse come chunk atomici
|
|
|
|
|
FIX_MAX_ITERATIONS = 3 # iterazioni massime del fix ricorsivo
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Per ogni strategia è possibile definire valori diversi tramite `STRATEGY_OVERRIDES`.
|
|
|
|
|
Modificare solo questo file — chunker, verify e fix si aggiornano automaticamente.
|
2026-04-12 23:53:13 +02:00
|
|
|
|
2026-05-11 14:44:16 +02:00
|
|
|
---
|
2026-04-12 23:53:13 +02:00
|
|
|
|
2026-05-12 10:39:27 +02:00
|
|
|
## Configurazione modelli
|
|
|
|
|
|
|
|
|
|
Tutti i parametri LLM e embedding sono in [`config.py`](config.py):
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
OLLAMA_MODEL = "qwen3.5:4b" # modello LLM per la generazione
|
|
|
|
|
EMBED_MODEL = "nomic-embed-text" # modello embedding (deve coincidere con l'ingestion)
|
|
|
|
|
TEMPERATURE = 0.2 # 0.0 = deterministico, valori alti = più creativo
|
|
|
|
|
NO_THINK = True # True = risposta diretta (più veloce), False = con ragionamento
|
|
|
|
|
TOP_K = 6 # numero di chunk recuperati per ogni domanda
|
|
|
|
|
OLLAMA_URL = "http://localhost:11434"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
> Se cambi `EMBED_MODEL` devi rieseguire l'ingestion con `--force` — gli embedding
|
|
|
|
|
> devono essere prodotti dallo stesso modello usato nel retrieval.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Testare il modello (senza RAG)
|
|
|
|
|
|
|
|
|
|
Verifica che il modello LLM risponda correttamente prima di coinvolgere la pipeline:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
.venv/bin/python ollama/test_ollama.py
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Il modello usato è quello configurato in `config.py` (`OLLAMA_MODEL`).
|
|
|
|
|
Digita `exit` per uscire.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Retrieval puro (senza generazione)
|
|
|
|
|
|
|
|
|
|
Utile per verificare che i chunk giusti vengano recuperati prima di diagnosticare
|
|
|
|
|
risposte sbagliate:
|
|
|
|
|
|
|
|
|
|
```bash
|
2026-05-12 11:21:24 +02:00
|
|
|
# Singolo documento
|
2026-05-12 10:39:27 +02:00
|
|
|
.venv/bin/python retrieve.py --stem <nome>
|
|
|
|
|
|
2026-05-12 11:21:24 +02:00
|
|
|
# Collection multi-documento
|
|
|
|
|
.venv/bin/python retrieve.py --collection <nome-collection>
|
|
|
|
|
|
2026-05-12 10:39:27 +02:00
|
|
|
# Modifica il numero di chunk restituiti
|
|
|
|
|
.venv/bin/python retrieve.py --stem <nome> --top-k 10
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Nel loop interattivo:
|
|
|
|
|
|
|
|
|
|
| Comando | Effetto |
|
|
|
|
|
|---------|---------|
|
|
|
|
|
| `<query>` | Mostra i chunk più simili con score di similarità (testo troncato) |
|
|
|
|
|
| `<query> -f` | Testo completo dei chunk |
|
|
|
|
|
| `exit` | Termina |
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## RAG interattivo
|
|
|
|
|
|
|
|
|
|
Risponde a domande in linguaggio naturale usando i chunk indicizzati in ChromaDB:
|
|
|
|
|
|
|
|
|
|
```bash
|
2026-05-12 11:21:24 +02:00
|
|
|
# Singolo documento
|
2026-05-12 10:39:27 +02:00
|
|
|
.venv/bin/python rag.py --stem <nome>
|
2026-05-12 11:21:24 +02:00
|
|
|
|
|
|
|
|
# Collection multi-documento
|
|
|
|
|
.venv/bin/python rag.py --collection <nome-collection>
|
2026-05-12 10:39:27 +02:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Nel loop interattivo:
|
|
|
|
|
|
|
|
|
|
| Comando | Effetto |
|
|
|
|
|
|---------|---------|
|
|
|
|
|
| `<domanda>` | Risposta generata dal LLM con contesto dai chunk |
|
2026-05-12 11:21:24 +02:00
|
|
|
| `<domanda> -v` | Risposta + chunk recuperati con score di similarità e documento sorgente |
|
2026-05-12 10:39:27 +02:00
|
|
|
| `exit` | Termina |
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
2026-05-11 15:46:52 +02:00
|
|
|
## Test
|
2026-04-12 23:53:13 +02:00
|
|
|
|
|
|
|
|
```bash
|
2026-05-11 15:46:52 +02:00
|
|
|
.venv/bin/python -m pytest tests/
|
2026-04-12 23:53:13 +02:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
2026-05-11 15:46:52 +02:00
|
|
|
## Riferimenti
|
|
|
|
|
|
|
|
|
|
- [`conversione/README.md`](conversione/README.md) — dettagli sulla pipeline PDF→Markdown e sui tipi di documento supportati
|
2026-05-11 16:05:23 +02:00
|
|
|
- [`ingestion/README.md`](ingestion/README.md) — configurazione embedding, scelta modello, regole --force
|