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

This commit is contained in:
2026-04-20 11:20:54 +02:00
parent df6e580907
commit 722489baff
2 changed files with 85 additions and 394 deletions
+82 -391
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. 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. Funziona su qualsiasi PDF digitale. Gira interamente in locale, senza GPU, senza cloud.
**Stack:** Python · Ollama · ChromaDB · Qwen3-embedding · Qwen3.5 **Stack:** Python · Ollama · ChromaDB
**Compatibile con:** Linux · macOS · Windows (WSL2) · CPU Only · ~8 GB RAM libera **Compatibile con:** Linux · macOS · Windows (WSL2) · CPU only · ~8 GB RAM
--- ---
## Indice ## Pipeline
- [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
``` ```
PDF (sources/) PDF → conversione → chunking → verifica → vettorizzazione → retrieval
▼ 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
``` ```
### Dove si concentra il rischio
| Fase | Rischio | Motivo | | Fase | Rischio | Motivo |
|---|---|---| |---|---|---|
| Conversione | 🟡 Medio | Automatica, ma il PDF deve essere digitale e non protetto | | 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 | | Revisione Markdown | 🔴 Alto | La qualità del MD determina la qualità del RAG |
| Chunking | 🟡 Medio | Logica adattiva, dipende dalla qualità del MD | | Chunking | 🟡 Medio | Adattivo, dipende dalla qualità del MD |
| Verifica chunk | 🟢 Basso | Automatica, solo verifica |
| Ambiente Ollama | 🟢 Basso | Installazione standard |
| Vettorizzazione | 🟢 Basso | Meccanica, lenta ma affidabile | | 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 ## Struttura del progetto
``` ```
rag-from-scratch/ rag/
├── sources/ # PDF originali — non modificare
├── sources/ # PDF originali — non modificare mai
│ └── documento.pdf
├── conversione/ # PDF → Markdown strutturato ├── conversione/ # PDF → Markdown strutturato
│ ├── pipeline.py # Conversione PDF → clean.md │ ├── pipeline.py
│ ├── validate.py # Validazione batch di tutti gli stem │ ├── validate.py
│ └── <stem>/ │ └── <stem>/
│ ├── raw.md # MD grezzo (non toccare) │ ├── raw.md # grezzo non modificare
│ ├── clean.md # MD pulito — copia di lavoro │ ├── clean.md # copia di lavoro
│ └── report.json # Metriche qualità conversione │ └── report.json
├── step-5/ # Chunking
├── step-5/ # Chunking adattivo
│ ├── chunker.py │ ├── chunker.py
│ └── <stem>/ │ └── <stem>/chunks.json
│ └── chunks.json
├── step-6/ # Verifica e fix chunk ├── step-6/ # Verifica e fix chunk
│ ├── verify_chunks.py │ ├── verify_chunks.py
│ ├── fix_chunks.py │ ├── fix_chunks.py
│ └── <stem>/ │ └── <stem>/
── chunks.json # Chunk verificati ── chunks.json
└── report.json
├── step-8/ # Vettorizzazione → ChromaDB ├── step-8/ # Vettorizzazione
── ingest.py ── ingest.py
│ └── README.md ├── ollama/ # Setup ambiente
├── check_env.py
├── ollama/ # Ambiente Ollama │ └── test_ollama.py
├── check_env.py # Verifica prerequisiti ├── chroma_db/ # Vector store (generato)
│ ├── test_ollama.py # Test chat senza RAG ├── config.py # Configurazione pipeline ← modifica qui
│ └── README.md ├── rag.py # Interrogazione RAG interattiva
└── retrieve.py # Retrieval puro (senza LLM)
├── 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
``` ```
--- `--stem` = nome del PDF senza estensione = nome della collection ChromaDB.
## Pipeline
--- ---
### Conversione ## Setup
**Tipo:** automatico ```bash
**Input:** `sources/<stem>.pdf` python -m venv .venv
**Output:** `conversione/<stem>/clean.md` + `report.json` source .venv/bin/activate
**Script:** `conversione/pipeline.py` 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 ```bash
# Singolo documento
python conversione/pipeline.py --stem <nome> 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>/`: ### 2. Rivedi il Markdown
| 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.
``` ```
/prepare-md conversione/<stem>/clean.md /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:** ### 3. Chunking
```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`
```bash ```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. ### 4. Verifica e fix chunk
**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.
```bash ```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 ### 5. Vettorizza
**Tipo:** automatico (lento)
**Input:** `step-6/<stem>/chunks.json`
**Output:** `chroma_db/<stem>` popolato
**Script:** `step-8/ingest.py`
```bash ```bash
source .venv/bin/activate
python step-8/ingest.py --stem <nome> python step-8/ingest.py --stem <nome>
``` ```
Trasforma ogni chunk in un vettore numerico e lo salva in ChromaDB. Vedi [`step-8/README.md`](step-8/README.md). Usa `--force` se hai cambiato `EMBED_MODEL` o i chunk.
È il processo più lento — su CPU circa 1 secondo per chunk.
Per 900 chunk aspetta circa 15 minuti.
**Argomenti:** ### 6. Interroga
| 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:**
```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 ## Configurazione (`config.py`)
**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`)
| Parametro | Default | Descrizione | | Parametro | Default | Descrizione |
|---|---|---| |---|---|---|
| `TOP_K` | `6` | Chunk recuperati per ogni domanda. Valori consigliati: `3``10` | | `EMBED_MODEL` | `"nomic-embed-text"` | Modello embedding — deve corrispondere tra ingest e retrieval |
| `TEMPERATURE` | `0.0` | Deterministico a `0.0`, creativo verso `1.0`. Per RAG consigliato `0.0` | | `OLLAMA_MODEL` | `"qwen3.5:0.8b"` | Modello LLM |
| `NO_THINK` | `True` | Disabilita il chain-of-thought interno dei modelli Qwen3/Qwen3.5. `True` = risposta diretta, più veloce | | `OLLAMA_URL` | `"http://localhost:11434"` | Endpoint Ollama |
| `EMBED_MODEL` | `"nomic-embed-text"` | Deve corrispondere al modello usato in `ingest.py`. Se cambiato, rieseguire con `--force` | | `TOP_K` | `6` | Chunk recuperati per query |
| `OLLAMA_URL` | `"http://localhost:11434"` | Modifica solo se Ollama gira su porta o host diversi | | `TEMPERATURE` | `0.0` | Deterministico a `0.0` |
| `OLLAMA_MODEL` | `"qwen3.5:0.8b"` | Modello LLM. Vedi [`ollama/README.md`](ollama/README.md) per la scelta | | `NO_THINK` | `True` | Disabilita chain-of-thought (Qwen3/Qwen3.5) |
| `SYSTEM_PROMPT` | *(vedi file)* | Istruzioni di comportamento inviate al LLM. Modifica per cambiare tono, lingua o condizione di fallback | | `SYSTEM_PROMPT` | *(vedi file)* | Istruzioni di comportamento per il LLM |
#### Test senza RAG > Se cambi `EMBED_MODEL`, riesegui `step-8/ingest.py --stem <nome> --force`.
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`.
--- ---
## Principi di progettazione ## Principi
**Atomico** **Atomico** — ogni fase fa una cosa sola; se si rompe qualcosa sai esattamente dove.
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.
**Verificabile** **Verificabile** — ogni fase ha un criterio di completamento oggettivo prima di procedere.
Ogni fase ha un criterio di completamento oggettivo.
Non si passa alla fase successiva finché la precedente non è verificata.
**Reversibile** **Reversibile** — puoi tornare indietro senza perdere il lavoro delle altre fasi.
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.
**Senza assunzioni** **Adattivo** — nessuna assunzione sulla struttura del documento; si adatta automaticamente.
Il sistema non assume nulla sulla struttura del documento.
Rileva il livello strutturale e si adatta.
Funziona su libri, manuali, articoli, contratti, dispense.
**Tutto locale** **Locale** — nessuna API esterna, nessun dato trasmesso fuori dalla macchina.
Nessuna chiamata a API esterne.
Nessun dato trasmesso fuori dalla macchina.
Nessun costo di utilizzo.
+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: Prima di procedere con la vettorizzazione (step 8) devi avere installato: