docs: compatta README — rimuovi sezioni verbose, mantieni l'essenziale

This commit is contained in:
2026-04-20 11:20:54 +02:00
parent 4f28358ec1
commit 5215f53ad0
2 changed files with 85 additions and 394 deletions
+84 -393
View File
@@ -1,468 +1,159 @@
# RAG from Scratch — Singolo PDF Generico
# RAG from Scratch
Sistema RAG (Retrieval-Augmented Generation) costruito da zero, senza framework di alto livello.
Funziona su qualsiasi PDF digitale. Gira interamente in locale, senza GPU, senza cloud.
**Stack:** Python · Ollama · ChromaDB · Qwen3-embedding · Qwen3.5
**Compatibile con:** Linux · macOS · Windows (WSL2) · CPU Only · ~8 GB RAM libera
**Stack:** Python · Ollama · ChromaDB
**Compatibile con:** Linux · macOS · Windows (WSL2) · CPU only · ~8 GB RAM
---
## Indice
- [Panoramica](#panoramica)
- [Struttura del progetto](#struttura-del-progetto)
- [Pipeline](#pipeline)
- [Conversione](#conversione)
- [Revisione Markdown](#revisione-markdown)
- [Chunking](#chunking)
- [Verifica chunk](#verifica-chunk)
- [Ambiente Ollama](#ambiente-ollama)
- [Vettorizzazione](#vettorizzazione)
- [Interrogazione](#interrogazione)
- [Principi di progettazione](#principi-di-progettazione)
---
## Panoramica
## Pipeline
```
PDF (sources/)
▼ conversione/pipeline.py
clean.md ← revisiona con /prepare-md
▼ step-5/chunker.py
chunks.json
▼ step-6/verify_chunks.py + fix_chunks.py
chunks.json verificato
▼ step-8/ingest.py
ChromaDB
▼ rag.py
risposta
PDF → conversione → chunking → verifica → vettorizzazione → retrieval
```
### Dove si concentra il rischio
| Fase | Rischio | Motivo |
|---|---|---|
| Conversione | 🟡 Medio | Automatica, ma il PDF deve essere digitale e non protetto |
| Revisione Markdown | 🔴 Alto | Manuale — la qualità del MD determina la qualità del RAG |
| Chunking | 🟡 Medio | Logica adattiva, dipende dalla qualità del MD |
| Verifica chunk | 🟢 Basso | Automatica, solo verifica |
| Ambiente Ollama | 🟢 Basso | Installazione standard |
| Revisione Markdown | 🔴 Alto | La qualità del MD determina la qualità del RAG |
| Chunking | 🟡 Medio | Adattivo, dipende dalla qualità del MD |
| Vettorizzazione | 🟢 Basso | Meccanica, lenta ma affidabile |
| Interrogazione | 🟡 Medio | Qualità del prompt e dei parametri in `config.py` |
| Retrieval | 🟡 Medio | Dipende dai parametri in `config.py` |
---
## Struttura del progetto
```
rag-from-scratch/
├── sources/ # PDF originali — non modificare mai
── documento.pdf
├── conversione/ # PDF → Markdown strutturato
│ ├── pipeline.py # Conversione PDF → clean.md
│ ├── validate.py # Validazione batch di tutti gli stem
rag/
├── sources/ # PDF originali — non modificare
├── conversione/ # PDF → Markdown strutturato
── pipeline.py
├── validate.py
│ └── <stem>/
│ ├── raw.md # MD grezzo (non toccare)
│ ├── clean.md # MD pulito — copia di lavoro
│ └── report.json # Metriche qualità conversione
├── step-5/ # Chunking adattivo
│ ├── raw.md # grezzo non modificare
│ ├── clean.md # copia di lavoro
│ └── report.json
├── step-5/ # Chunking
│ ├── chunker.py
│ └── <stem>/
│ └── chunks.json
├── step-6/ # Verifica e fix chunk
│ └── <stem>/chunks.json
├── step-6/ # Verifica e fix chunk
│ ├── verify_chunks.py
│ ├── fix_chunks.py
│ └── <stem>/
── chunks.json # Chunk verificati
├── step-8/ # Vettorizzazione → ChromaDB
── ingest.py
│ └── README.md
├── ollama/ # Ambiente Ollama
├── check_env.py # Verifica prerequisiti
│ ├── test_ollama.py # Test chat senza RAG
│ └── README.md
├── chroma_db/ # Vector store — generato da ingest.py
├── config.py # Configurazione pipeline RAG ← modifica qui
├── rag.py # Pipeline RAG interattiva
├── retrieve.py # Retrieval puro (senza LLM)
├── requirements.txt
├── .gitignore
└── README.md
── chunks.json
└── report.json
├── step-8/ # Vettorizzazione
── ingest.py
├── ollama/ # Setup ambiente
├── check_env.py
│ └── test_ollama.py
├── chroma_db/ # Vector store (generato)
├── config.py # Configurazione pipeline ← modifica qui
├── rag.py # Interrogazione RAG interattiva
└── retrieve.py # Retrieval puro (senza LLM)
```
---
## Pipeline
`--stem` = nome del PDF senza estensione = nome della collection ChromaDB.
---
### Conversione
**Tipo:** automatico
**Input:** `sources/<stem>.pdf`
**Output:** `conversione/<stem>/clean.md` + `report.json`
**Script:** `conversione/pipeline.py`
## Setup
```bash
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```
**Java 11+** richiesto per la conversione (`opendataloader-pdf`):
```bash
sudo apt install default-jdk # Ubuntu/Debian/WSL
java -version # verifica
```
Vedi [`ollama/README.md`](ollama/README.md) per l'installazione di Ollama e il download dei modelli.
---
## Workflow
### 1. Converti il PDF
```bash
# Singolo documento
python conversione/pipeline.py --stem <nome>
# Tutti i PDF in sources/
python conversione/pipeline.py
# Forza riesecuzione (sovrascrive output esistente)
python conversione/pipeline.py --stem <nome> --force
```
Converte il PDF in Markdown strutturato in quattro fasi automatiche: validazione, estrazione testo (algoritmo XY-Cut++ per layout multi-colonna), pulizia strutturale e analisi della struttura del documento.
Produce `conversione/<stem>/clean.md`. Vedi [`conversione/README.md`](conversione/README.md).
Produce tre file in `conversione/<stem>/`:
| File | Descrizione |
|---|---|
| `raw.md` | Markdown grezzo estratto dal PDF — **non modificare mai** |
| `clean.md` | Markdown pulito e strutturato — input per il chunker |
| `report.json` | Metriche qualità, anomalie, strategia di chunking suggerita |
**Requisiti aggiuntivi:** Java 11+ nel PATH (`opendataloader-pdf` lo richiede).
**Validazione batch:**
```bash
python conversione/validate.py
```
Mostra una tabella di stato per tutti gli stem convertiti. Vedi [`conversione/README.md`](conversione/README.md) per dettagli completi.
**PDF supportati:** digitali con testo selezionabile. Non supportati: scansionati (solo immagini) e protetti da password.
---
### Revisione Markdown
**Tipo:** semi-automatico
**Input:** `conversione/<stem>/clean.md`
**Output:** `conversione/<stem>/clean.md` corretto in-place
> Questo è il passaggio più importante dell'intera pipeline.
> La qualità del RAG dipende da questo passaggio più di qualsiasi
> parametro tecnico o scelta di modello.
### 2. Rivedi il Markdown
```
/prepare-md conversione/<stem>/clean.md
```
La skill analizza il `clean.md` e corregge automaticamente i problemi che compromettono il chunking: sillabazione, artefatti, header malformati, paragrafi spezzati, gerarchia incoerente, sezioni vuote.
Passaggio più importante: la qualità del RAG dipende da questo.
**Struttura target dopo la revisione:**
```markdown
# Titolo del documento
## Sezione principale
### Sottosezione o unità atomica
Testo fluente, frasi complete, nessun artefatto.
Ogni paragrafo è semanticamente autonomo.
Una riga vuota separa le sezioni.
```
**Criterio di qualità:** leggi ogni sezione ad alta voce. Se suona naturale è corretta. Se si interrompe c'è una riga spezzata. Se suona ripetitiva c'è un artefatto.
---
### Chunking
**Tipo:** automatico
**Input:** `conversione/<stem>/clean.md`
**Output:** `step-5/<stem>/chunks.json`
**Script:** `step-5/chunker.py`
### 3. Chunking
```bash
python step-5/chunker.py --stem <stem>
python step-5/chunker.py --stem <nome>
```
Divide il Markdown pulito in chunk. Usa il profilo strutturale da `report.json` per scegliere la strategia giusta. Non sa nulla del contenuto — si basa solo sulla struttura.
**Regole invarianti per qualsiasi documento:**
- Un chunk non attraversa mai il confine tra due sezioni diverse
- Un chunk non spezza mai una frase a metà
- Ogni chunk porta il suo contesto nel prefisso
- L'overlap tra chunk avviene solo su frasi intere, mai tra sezioni diverse
**Parametri (in `step-5/chunker.py`):**
| Parametro | Default | Significato |
|---|---|---|
| `MIN_CHARS` | 200 | Sotto questa soglia, accorpa al chunk successivo |
| `MAX_CHARS` | 800 | Sopra questa soglia, spezza su frasi |
| `OVERLAP_S` | 2 | Frasi di overlap tra sotto-chunk dello stesso boundary |
**Struttura di ogni chunk:**
```json
{
"chunk_id": "sezione_principale__sottosezione_3__s0",
"text": "[Sezione principale > Sottosezione 3]\nTesto del chunk...",
"sezione": "Sezione principale",
"titolo": "Sottosezione 3",
"sub_index": 0,
"n_chars": 412
}
```
Il prefisso `[Sezione > Titolo]` è fondamentale: permette all'embedding di catturare il contesto topico del chunk anche quando il testo da solo sarebbe ambiguo.
---
### Verifica chunk
**Tipo:** automatico
**Input:** `step-5/<stem>/chunks.json`
**Output:** `step-6/<stem>/chunks.json` verificato + `report.json`
**Script:** `step-6/verify_chunks.py`, `step-6/fix_chunks.py`
Questo passaggio si articola in un ciclo: verifica → fix automatico → ri-verifica. Non si procede alla vettorizzazione finché non ci sono 🔴.
**Workflow:**
```
1. Verifica
python step-6/verify_chunks.py --stem <stem>
2a. Se ✅ OK o solo 🟡 → vai alla vettorizzazione
2b. Se ci sono 🔴 → prova il fix automatico:
python step-6/fix_chunks.py --stem <stem> --dry-run # anteprima
python step-6/fix_chunks.py --stem <stem> # applica
3. Ri-verifica dopo il fix:
python step-6/verify_chunks.py --stem <stem>
4. Se rimangono 🔴 → torna alla revisione Markdown e correggi clean.md,
poi riesegui dall'inizio:
python step-5/chunker.py --stem <stem> --force
python step-6/verify_chunks.py --stem <stem>
```
> **Shortcut con Claude:** usa `/step6-fix <stem>` — esegue dry-run, spiega le operazioni, chiede conferma e ri-verifica automaticamente.
**Output di `verify_chunks.py` — tre condizioni finali:**
| Condizione | Significato | Cosa fare |
|---|---|---|
| `✅ N/N documenti senza problemi` | Nessun problema | Procedi |
| `🟡 Solo avvisi minori` | Chunk corti o lunghi, non bloccanti | Puoi procedere o ottimizzare con `fix_chunks.py` |
| `⚠️ 0/N documenti senza problemi` + 🔴 | Frasi spezzate o chunk vuoti | Esegui `fix_chunks.py`, poi ri-verifica |
**Cosa verifica:**
- Nessun chunk sotto `MIN_CHARS` 🟡
- Nessun chunk sopra `MAX_CHARS × 1.5` 🟡
- Ogni chunk finisce con punteggiatura di fine frase 🔴
**Cosa corregge `fix_chunks.py`:**
| Operazione | Quando |
|---|---|
| Rimuovi chunk vuoti | Chunk privi di testo |
| Fondi chunk incompleto col successivo | Chunk che finisce senza punteggiatura |
| Fondi chunk troppo corto col successivo | Chunk sotto `MIN_CHARS` |
| Spezza chunk troppo lungo | Chunk sopra `MAX_CHARS × 1.5` |
**Se i 🔴 persistono dopo il fix** — i casi tipici e la soluzione in `clean.md`:
| Sintomo nel report | Causa in `clean.md` | Correzione |
|---|---|---|
| Chunk finisce con `:` | Intro di un elenco separata dall'elenco da una riga vuota | Rimuovi la riga vuota tra l'intro e la lista |
| Chunk finisce a metà parola | Numero di pagina nel mezzo del testo | Trova e rimuovi il numero di pagina, unisci le righe |
| Chunk con testo artefatto | Artefatto non rimosso nella revisione | Elimina la sezione in `clean.md` |
| Chunk con frase enorme non spezzabile | Paragrafo >MAX_CHARS senza frasi intermedie | Spezza manualmente il paragrafo |
---
### Ambiente Ollama
**Tipo:** manuale (una volta sola)
**Input:** nessuno
**Output:** ambiente locale funzionante
**Script:** `ollama/check_env.py`
Installa Ollama, scarica i modelli e verifica l'ambiente. Si esegue una volta sola prima della vettorizzazione.
Vedi [`ollama/README.md`](ollama/README.md) per istruzioni dettagliate e scelta dei modelli.
### 4. Verifica e fix chunk
```bash
python ollama/check_env.py
python step-6/verify_chunks.py --stem <nome>
python step-6/fix_chunks.py --stem <nome> # se ci sono 🔴
python step-6/verify_chunks.py --stem <nome> # ri-verifica
```
---
Non procedere alla vettorizzazione se ci sono 🔴.
### Vettorizzazione
**Tipo:** automatico (lento)
**Input:** `step-6/<stem>/chunks.json`
**Output:** `chroma_db/<stem>` popolato
**Script:** `step-8/ingest.py`
### 5. Vettorizza
```bash
source .venv/bin/activate
python step-8/ingest.py --stem <nome>
```
Trasforma ogni chunk in un vettore numerico e lo salva in ChromaDB.
È il processo più lento — su CPU circa 1 secondo per chunk.
Per 900 chunk aspetta circa 15 minuti.
Vedi [`step-8/README.md`](step-8/README.md). Usa `--force` se hai cambiato `EMBED_MODEL` o i chunk.
**Argomenti:**
| Argomento | Descrizione |
|---|---|
| `--stem <nome>` | Processa un singolo documento. Senza questo argomento processa tutti gli stem trovati in `step-6/` |
| `--force` | Cancella e ricrea la collection se esiste già |
**Quando usare `--force`:**
Se hai modificato i chunk o cambiato `EMBED_MODEL` in `config.py`, la collection in ChromaDB contiene i vecchi vettori. `--force` la cancella e ricrea da zero.
**Cosa succede per ogni chunk:**
### 6. Interroga
```bash
python rag.py --stem <nome> # risposta LLM
python retrieve.py --stem <nome> # retrieval puro (debug)
```
testo del chunk
▼ Ollama (EMBED_MODEL)
vettore N-dim
▼ ChromaDB
salva: testo + vettore + metadati (sezione, titolo, sub_index)
```
Vedi [`step-8/README.md`](step-8/README.md) per la scelta del modello di embedding e le regole di coerenza con la fase di interrogazione.
---
### Interrogazione
**Tipo:** interattivo
**Input:** `chroma_db/<stem>` + domanda dell'utente
**Output:** risposta basata sul documento
Due modalità:
| Script | Modalità | Quando usarlo |
|---|---|---|
| `rag.py` | Retrieval + generazione LLM | Risposta in linguaggio naturale |
| `retrieve.py` | Solo retrieval (no LLM) | Debug, verifica chunk, ricerca semantica |
#### rag.py — Risposta in linguaggio naturale
```bash
source .venv/bin/activate
python rag.py --stem <nome>
```
| Sintassi | Comportamento |
|---|---|
| `<testo>` | Risposta basata sul documento |
| `<testo> -v` | Risposta + chunk recuperati con score di similarità |
| `exit` | Esce dal programma |
Flusso interno:
```
domanda
▼ embed (EMBED_MODEL, Ollama)
vettore N-dim
▼ query ChromaDB — similarità coseno, top-K
chunk rilevanti
▼ build_prompt (SYSTEM_PROMPT + contesti + domanda)
▼ generate (OLLAMA_MODEL, Ollama)
risposta
```
#### retrieve.py — Retrieval puro (senza LLM)
```bash
source .venv/bin/activate
python retrieve.py --stem <nome>
```
Vettorizza la query e restituisce i chunk più simili con score di similarità — senza chiamare Ollama per la generation. Utile per verificare la qualità del retrieval e diagnosticare risposte sbagliate.
| Sintassi | Comportamento |
|---|---|
| `<testo>` | Chunk più simili con score (testo troncato a 200 car.) |
| `<testo> -f` | Chunk più simili con testo completo |
| `exit` | Esce dal programma |
Accetta `--top-k N` per sovrascrivere il valore di `config.py` per quella sessione.
#### Configurazione (`config.py`)
## Configurazione (`config.py`)
| Parametro | Default | Descrizione |
|---|---|---|
| `TOP_K` | `6` | Chunk recuperati per ogni domanda. Valori consigliati: `3``10` |
| `TEMPERATURE` | `0.0` | Deterministico a `0.0`, creativo verso `1.0`. Per RAG consigliato `0.0` |
| `NO_THINK` | `True` | Disabilita il chain-of-thought interno dei modelli Qwen3/Qwen3.5. `True` = risposta diretta, più veloce |
| `EMBED_MODEL` | `"nomic-embed-text"` | Deve corrispondere al modello usato in `ingest.py`. Se cambiato, rieseguire con `--force` |
| `OLLAMA_URL` | `"http://localhost:11434"` | Modifica solo se Ollama gira su porta o host diversi |
| `OLLAMA_MODEL` | `"qwen3.5:0.8b"` | Modello LLM. Vedi [`ollama/README.md`](ollama/README.md) per la scelta |
| `SYSTEM_PROMPT` | *(vedi file)* | Istruzioni di comportamento inviate al LLM. Modifica per cambiare tono, lingua o condizione di fallback |
| `EMBED_MODEL` | `"nomic-embed-text"` | Modello embedding — deve corrispondere tra ingest e retrieval |
| `OLLAMA_MODEL` | `"qwen3.5:0.8b"` | Modello LLM |
| `OLLAMA_URL` | `"http://localhost:11434"` | Endpoint Ollama |
| `TOP_K` | `6` | Chunk recuperati per query |
| `TEMPERATURE` | `0.0` | Deterministico a `0.0` |
| `NO_THINK` | `True` | Disabilita chain-of-thought (Qwen3/Qwen3.5) |
| `SYSTEM_PROMPT` | *(vedi file)* | Istruzioni di comportamento per il LLM |
#### Test senza RAG
Per verificare che Ollama risponda correttamente prima di interrogare il documento:
```bash
python ollama/test_ollama.py
```
Chat diretta con il modello, senza ChromaDB. Usa gli stessi parametri di `config.py`.
> Se cambi `EMBED_MODEL`, riesegui `step-8/ingest.py --stem <nome> --force`.
---
## Principi di progettazione
## Principi
**Atomico**
Ogni fase fa una cosa sola. Il chunker non sa niente di Ollama.
L'ingestione non sa niente del MD originale.
Se un pezzo si rompe, sai esattamente dove.
**Atomico** — ogni fase fa una cosa sola; se si rompe qualcosa sai esattamente dove.
**Verificabile**
Ogni fase ha un criterio di completamento oggettivo.
Non si passa alla fase successiva finché la precedente non è verificata.
**Verificabile** — ogni fase ha un criterio di completamento oggettivo prima di procedere.
**Reversibile**
Puoi tornare indietro senza perdere il lavoro delle altre fasi.
Cambi il MD? Riesegui solo chunking e vettorizzazione.
Cambi i parametri del chunker? Riesegui solo chunking e vettorizzazione.
Non si riparte mai da zero.
**Reversibile** — puoi tornare indietro senza perdere il lavoro delle altre fasi.
**Senza assunzioni**
Il sistema non assume nulla sulla struttura del documento.
Rileva il livello strutturale e si adatta.
Funziona su libri, manuali, articoli, contratti, dispense.
**Adattivo** — nessuna assunzione sulla struttura del documento; si adatta automaticamente.
**Tutto locale**
Nessuna chiamata a API esterne.
Nessun dato trasmesso fuori dalla macchina.
Nessun costo di utilizzo.
**Locale** — nessuna API esterna, nessun dato trasmesso fuori dalla macchina.
+1 -1
View File
@@ -1,4 +1,4 @@
# Ollama — Step 7 (Verifica Ambiente)
# Ollama — Verifica Ambiente
Prima di procedere con la vettorizzazione (step 8) devi avere installato: