From 5215f53ad0fa59a76b46864d4e4af70d0df3a79f Mon Sep 17 00:00:00 2001 From: Davide Grilli Date: Mon, 20 Apr 2026 11:20:54 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20compatta=20README=20=E2=80=94=20rimuovi?= =?UTF-8?q?=20sezioni=20verbose,=20mantieni=20l'essenziale?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 477 +++++++++-------------------------------------- ollama/README.md | 2 +- 2 files changed, 85 insertions(+), 394 deletions(-) diff --git a/README.md b/README.md index 32465cc..262debf 100644 --- a/README.md +++ b/README.md @@ -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 │ └── / -│ ├── 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 -│ └── / -│ └── chunks.json -│ -├── step-6/ # Verifica e fix chunk +│ └── /chunks.json +├── step-6/ # Verifica e fix chunk │ ├── verify_chunks.py │ ├── fix_chunks.py │ └── / -│ └── 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/.pdf` -**Output:** `conversione//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 - -# Tutti i PDF in sources/ -python conversione/pipeline.py - -# Forza riesecuzione (sovrascrive output esistente) -python conversione/pipeline.py --stem --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//clean.md`. Vedi [`conversione/README.md`](conversione/README.md). -Produce tre file in `conversione//`: - -| 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//clean.md` -**Output:** `conversione//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//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//clean.md` -**Output:** `step-5//chunks.json` -**Script:** `step-5/chunker.py` +### 3. Chunking ```bash -python step-5/chunker.py --stem +python step-5/chunker.py --stem ``` -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//chunks.json` -**Output:** `step-6//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 - -2a. Se ✅ OK o solo 🟡 → vai alla vettorizzazione - -2b. Se ci sono 🔴 → prova il fix automatico: - python step-6/fix_chunks.py --stem --dry-run # anteprima - python step-6/fix_chunks.py --stem # applica - -3. Ri-verifica dopo il fix: - python step-6/verify_chunks.py --stem - -4. Se rimangono 🔴 → torna alla revisione Markdown e correggi clean.md, - poi riesegui dall'inizio: - python step-5/chunker.py --stem --force - python step-6/verify_chunks.py --stem -``` - -> **Shortcut con Claude:** usa `/step6-fix ` — 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 +python step-6/fix_chunks.py --stem # se ci sono 🔴 +python step-6/verify_chunks.py --stem # ri-verifica ``` ---- +Non procedere alla vettorizzazione se ci sono 🔴. -### Vettorizzazione - -**Tipo:** automatico (lento) -**Input:** `step-6//chunks.json` -**Output:** `chroma_db/` popolato -**Script:** `step-8/ingest.py` +### 5. Vettorizza ```bash -source .venv/bin/activate python step-8/ingest.py --stem ``` -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 ` | 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 # risposta LLM +python retrieve.py --stem # 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/` + 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 -``` - -| Sintassi | Comportamento | -|---|---| -| `` | Risposta basata sul documento | -| ` -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 -``` - -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 | -|---|---| -| `` | Chunk più simili con score (testo troncato a 200 car.) | -| ` -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 --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. diff --git a/ollama/README.md b/ollama/README.md index 79012ce..6faea9d 100644 --- a/ollama/README.md +++ b/ollama/README.md @@ -1,4 +1,4 @@ -# Ollama — Step 7 (Verifica Ambiente) +# Ollama — Verifica Ambiente Prima di procedere con la vettorizzazione (step 8) devi avere installato: