From 4c021023636805df2442de8987e059886d550423 Mon Sep 17 00:00:00 2001 From: Davide Grilli Date: Fri, 17 Apr 2026 18:51:09 +0200 Subject: [PATCH] docs(README): riscrittura per struttura reale del progetto MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sostituisce la struttura step-0…step-10 con la pipeline effettiva: conversione/, revisione /prepare-md, chunking, verifica, ollama/, vettorizzazione, interrogazione --- README.md | 719 +++++++++++++++--------------------------------------- 1 file changed, 201 insertions(+), 518 deletions(-) diff --git a/README.md b/README.md index c35ffd5..32465cc 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ 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 · nomic-embed-text · Qwen3.5 · ChromaDB +**Stack:** Python · Ollama · ChromaDB · Qwen3-embedding · Qwen3.5 **Compatibile con:** Linux · macOS · Windows (WSL2) · CPU Only · ~8 GB RAM libera --- @@ -12,18 +12,14 @@ Funziona su qualsiasi PDF digitale. Gira interamente in locale, senza GPU, senza - [Panoramica](#panoramica) - [Struttura del progetto](#struttura-del-progetto) -- [Gli step](#gli-step) - - [Step 0 — Scegli il PDF](#step-0--scegli-il-pdf) - - [Step 1 — Ispezione automatica](#step-1--ispezione-automatica) - - [Step 2 — Conversione in Markdown grezzo](#step-2--conversione-in-markdown-grezzo) - - [Step 3 — Rilevamento struttura](#step-3--rilevamento-struttura) - - [Step 4 — Revisione manuale](#step-4--revisione-manuale) - - [Step 5 — Chunking adattivo](#step-5--chunking-adattivo) - - [Step 6 — Verifica chunk](#step-6--verifica-chunk) - - [Step 7 — Installazione ambiente](#step-7--installazione-ambiente) - - [Step 8 — Vettorizzazione](#step-8--vettorizzazione) - - [Step 9 — Pipeline RAG](#step-9--pipeline-rag) - - [Step 10 — Test automatici](#step-10--test-automatici) +- [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) --- @@ -31,36 +27,35 @@ Funziona su qualsiasi PDF digitale. Gira interamente in locale, senza GPU, senza ## Panoramica ``` -PDF - └─► STEP 1 Ispezione automatica - └─► STEP 2 Conversione in Markdown grezzo - └─► STEP 3 Rilevamento struttura - └─► STEP 4 Revisione manuale ← step più importante - └─► STEP 5 Chunking adattivo - └─► STEP 6 Verifica chunk - └─► STEP 8 Vettorizzazione - └─► STEP 9 Pipeline RAG - └─► STEP 10 Test automatici - -STEP 0 Prerequisito iniziale (PDF adatto) -STEP 7 Prerequisito tecnico (ambiente locale) +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 ``` ### Dove si concentra il rischio -| Step | Rischio | Motivo | +| Fase | Rischio | Motivo | |---|---|---| -| Step 0 | 🔴 Alto | Un PDF inadatto invalida tutto il lavoro successivo | -| Step 1 | 🟢 Basso | Automatico, solo osservazione | -| Step 2 | 🟢 Basso | Automatico, tool maturo | -| Step 3 | 🟢 Basso | Automatico, solo analisi | -| Step 4 | 🔴 Alto | Manuale — la qualità del MD determina la qualità del RAG | -| Step 5 | 🟡 Medio | Logica adattiva, dipende dalla qualità del MD | -| Step 6 | 🟢 Basso | Automatico, solo verifica | -| Step 7 | 🟢 Basso | Installazione standard | -| Step 8 | 🟢 Basso | Meccanico, lento ma affidabile | -| Step 9 | 🟡 Medio | Qualità del prompt | -| Step 10 | 🟢 Basso | Test automatici | +| 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 | +| Vettorizzazione | 🟢 Basso | Meccanica, lenta ma affidabile | +| Interrogazione | 🟡 Medio | Qualità del prompt e dei parametri in `config.py` | --- @@ -72,54 +67,38 @@ rag-from-scratch/ ├── sources/ # PDF originali — non modificare mai │ └── documento.pdf │ -├── step-0/ -│ └── check_pdf.py # Verifica requisiti del PDF -│ -├── step-1/ -│ └── inspect_pdf.py # Ispezione automatica del PDF -│ -├── step-2/ -│ ├── convert_pdf.py # Conversione PDF → Markdown grezzo +├── conversione/ # PDF → Markdown strutturato +│ ├── pipeline.py # Conversione PDF → clean.md +│ ├── validate.py # Validazione batch di tutti gli stem │ └── / -│ └── raw.md # MD grezzo (non toccare) +│ ├── raw.md # MD grezzo (non toccare) +│ ├── clean.md # MD pulito — copia di lavoro +│ └── report.json # Metriche qualità conversione │ -├── step-3/ -│ ├── detect_structure.py # Rilevamento struttura MD +├── step-5/ # Chunking adattivo +│ ├── chunker.py │ └── / -│ └── structure_profile.json # Profilo struttura +│ └── chunks.json │ -├── step-4/ -│ ├── revise.py # Pre-processing automatico MD -│ ├── revision_log.md # Log modifiche manuali -│ └── / -│ ├── clean.md # MD revisionato -│ └── structure_profile.json # Profilo aggiornato -│ -├── step-5/ -│ ├── chunker.py # Chunking adattivo -│ └── / -│ └── chunks.json # Chunk pronti per la vettorizzazione -│ -├── step-6/ -│ ├── verify_chunks.py # Verifica chunk -│ ├── fix_chunks.py # Fix chunk problematici +├── step-6/ # Verifica e fix chunk +│ ├── verify_chunks.py +│ ├── fix_chunks.py │ └── / │ └── chunks.json # Chunk verificati │ -├── step-7/ -│ ├── check_env.py # Verifica ambiente locale -│ └── README.md # Guida installazione Ollama e dipendenze -│ -├── step-8/ -│ └── ingest.py # Vettorizzazione → ChromaDB -│ -├── step-9/ -│ ├── config.py # Configurazione pipeline RAG ← modifica qui -│ ├── rag.py # Pipeline RAG interattiva -│ ├── test_ollama.py # Test chat Ollama senza RAG +├── step-8/ # Vettorizzazione → ChromaDB +│ ├── ingest.py │ └── README.md │ -├── chroma_db/ # Vector store — generato da step-8 +├── 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 @@ -127,305 +106,67 @@ rag-from-scratch/ --- -## Gli step +## Pipeline --- -### Step 0 — Scegli il PDF - -**Tipo:** prerequisito manuale -**Input:** nessuno -**Output:** un PDF adatto al sistema - -Il PDF deve soddisfare requisiti minimi prima di qualsiasi elaborazione. -Un PDF inadatto rende tutto il lavoro successivo inutile. - -**Criteri obbligatori:** - -- Il testo è selezionabile nel PDF reader — se non riesci a copiare una parola, - pdfplumber non la leggerà -- Non è protetto da password -- È generato digitalmente, non scansionato — una foto di un libro non è un PDF di testo -- Il contenuto importante è nel testo, non nelle immagini - -**Criteri desiderabili:** - -- Ha una struttura logica riconoscibile: capitoli, sezioni, paragrafi -- Le sezioni hanno titoli espliciti -- Non ha layout a colonne multiple -- È in una lingua sola o prevalentemente una - -**Come verificarlo:** -Apri il PDF nel tuo reader, seleziona del testo da pagine diverse e copialo. -Se il testo copiato è leggibile e nell'ordine giusto, il PDF è adatto. -Se ottieni caratteri strani o testo nell'ordine sbagliato, il PDF ha problemi. - ---- - -### Step 1 — Ispezione automatica +### Conversione **Tipo:** automatico -**Input:** tutti i PDF in `sources/` -**Output:** `step-1/_step1_report.txt` -**Script:** `step-1/inspect_pdf.py` +**Input:** `sources/.pdf` +**Output:** `conversione//clean.md` + `report.json` +**Script:** `conversione/pipeline.py` ```bash -python step-1/inspect_pdf.py +# 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 ``` -Lo script scansiona automaticamente tutti i PDF in `sources/`, analizza ogni documento pagina per pagina e produce un report. -Serve per capire la qualità del documento e mappare i problemi -prima di affrontare la revisione manuale. +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. -**Cosa rileva:** +Produce tre file in `conversione//`: -- Testo non estraibile (pagine con sole immagini) -- Sillabazioni a fine riga -- Layout a colonne (righe molto corte e numerose) -- Intestazioni e piè di pagina ripetitivi -- Caratteri Unicode anomali -- Pagine vuote - -**Output del report:** - -``` -Score: 87/100 -Pagine totali: 243 -Pagine con problemi: 12 - - Pagina 14: sillabazione rilevata (3 occorrenze) - Pagina 67: possibile layout a colonne - Pagina 201: caratteri Unicode anomali - -PROSSIMI PASSI: - → conversione con marker funzionerà bene - → attenzione alle pagine 14 e 67 nella revisione manuale -``` - -**Decisione:** - -| Score | Azione | +| File | Descrizione | |---|---| -| ≥ 70 | Procedi allo step 2 | -| 40–70 | Procedi con cautela, revisione estesa necessaria | -| < 40 | Valuta una fonte PDF migliore | +| `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). -### Step 2 — Conversione in Markdown grezzo - -**Tipo:** automatico -**Input:** tutti i PDF in `sources/` (o uno solo con `--pdf`) -**Output:** `step-2//raw.md` + `step-2//clean.md` -**Script:** `step-2/convert_pdf.py` +**Validazione batch:** ```bash -python step-2/convert_pdf.py # tutti i PDF in sources/ -python step-2/convert_pdf.py --pdf sources/doc.pdf # un solo PDF +python conversione/validate.py ``` -Converte il PDF in Markdown usando `pymupdf4llm`. Il risultato non è perfetto — è la base -su cui lavorerai nello step 4. +Mostra una tabella di stato per tutti gli stem convertiti. Vedi [`conversione/README.md`](conversione/README.md) per dettagli completi. -Lo script crea due file: -- `raw.md` — conversione grezza, **non modificare mai**. È il punto di partenza di riferimento. -- `clean.md` — copia di lavoro che verrà modificata negli step successivi. - -**Cosa produce la conversione:** - -- Titoli riconosciuti e convertiti in `#` `##` `###` -- Paragrafi separati da righe vuote -- Sillabazione parzialmente risolta - -**Cosa non produce:** - -- Rimozione intestazioni e piè di pagina -- Correzione completa del layout a colonne -- Descrizione del contenuto delle immagini +**PDF supportati:** digitali con testo selezionabile. Non supportati: scansionati (solo immagini) e protetti da password. --- -### Step 3 — Rilevamento struttura +### Revisione Markdown -**Tipo:** automatico -**Input:** `step-2//` -**Output:** `step-3//structure_profile.json` -**Script:** `step-3/detect_structure.py` +**Tipo:** semi-automatico +**Input:** `conversione//clean.md` +**Output:** `conversione//clean.md` corretto in-place -```bash -python step-3/detect_structure.py # tutti i documenti in step-2/ -python step-3/detect_structure.py --stem # un solo documento -python step-3/detect_structure.py --force # riesegui anche se già presente -``` - -Copia `raw.md` e `clean.md` da `step-2//` e analizza la struttura del Markdown senza modificarla. -Il profilo prodotto guida sia la revisione manuale che il chunker. - -**I quattro livelli strutturali:** - -``` -Livello 3 — struttura ricca - Il documento ha ### con regolarità. - Ogni ### è un'unità semantica chiara. - Esempi: opere filosofiche, manuali tecnici, leggi. - Strategia chunking: boundary su ### - -Livello 2 — struttura parziale - Il documento ha ## ma pochi o nessun ###. - Le sezioni sono i capitoli, non le sottosezioni. - Esempi: articoli scientifici, report, saggi. - Strategia chunking: boundary su ##, split interno su paragrafi - -Livello 1 — solo paragrafi - Il documento non ha titoli significativi. - La struttura è data dalle righe vuote. - Esempi: testi narrativi, lettere, trascrizioni. - Strategia chunking: boundary su paragrafo - -Livello 0 — testo piatto - Un blocco continuo senza struttura riconoscibile. - Esempi: PDF mal convertiti, testi antichi. - Strategia chunking: sliding window su frasi -``` - -**Profilo prodotto:** - -```json -{ - "livello_struttura": 3, - "n_h1": 1, - "n_h2": 9, - "n_h3": 296, - "n_paragrafi": 312, - "boundary_primario": "h3", - "lingua_rilevata": "it", - "lunghezza_media_sezione": 420, - "strategia_chunking": "h3_aware", - "avvertenze": [ - "14 sezioni sotto i 200 caratteri — verranno accorpate", - "8 sezioni sopra i 800 caratteri — verranno divise" - ] -} -``` - ---- - -### Step 4 — Revisione manuale - -**Tipo:** manuale (con pre-processing automatico) -**Input:** `step-3//clean.md` + `step-3//structure_profile.json` -**Output:** `step-4//clean.md` — MD revisionato -**Script:** `step-4/revise.py` - -> Questo è lo step più importante dell'intera pipeline. -> La qualità del RAG dipende da questo step più di qualsiasi +> 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. -#### Pre-processing automatico - -Prima di qualsiasi revisione manuale, esegui lo script di revisione automatica: - -```bash -python step-4/revise.py --stem documento +``` +/prepare-md conversione//clean.md ``` -Lo script applica le seguenti trasformazioni euristiche, valide per qualsiasi documento: - -| Trasformazione | Descrizione | -|---|---| -| Rimozione TOC | Righe che iniziano con `INDICE`, `INDEX`, `CONTENTS`, ecc. | -| ALL-CAPS → `##` | Righe standalone in maiuscolo convertite in header section-case | -| `N. testo` → `### N.` | Sezioni numerate (con 1+ spazio dopo il punto) convertite in h3 | -| Unione paragrafi | Blocchi spezzati da salti pagina PDF uniti automaticamente | -| Whitespace | Spazi multipli normalizzati, righe vuote ridotte | - -Il profilo strutturale aggiornato viene salvato in `step-4//structure_profile.json`. - -#### Revisione assistita da Claude Code - -Dopo il pre-processing, usa la skill integrata per una revisione qualitativa: - -``` -/step4-review documento -``` - -La skill analizza `step-4//clean.md` e produce un report strutturato: - -``` -🔴 BLOCCANTI — problemi che compromettono il chunking -🟡 MINORI — artefatti visibili ma non bloccanti -🟢 OK — categorie senza problemi -``` - -Poi propone le correzioni e le applica solo su tua approvazione. - -#### Revisione manuale (senza Claude Code) - -Se non usi Claude Code, esegui questi 6 check dal terminale. -In tutti i comandi sostituisci `` con il nome reale del documento. - -**Check 1 — Sillabazione residua** -Parole spezzate a fine riga con trattino (artefatto da PDF non risolto): -```bash -grep -n "\-$" step-4//clean.md | head -20 -``` -Se trovi risultati: unisci la riga con la successiva eliminando il trattino -e il ritorno a capo. - -**Check 2 — Righe orfane** -Righe brevi (<60 char) isolate che sembrano numeri di pagina, autori, intestazioni: -```bash -grep -n "^[^#\-\*\|].\{1,59\}$" step-4//clean.md | grep -v "^\s*$" | head -30 -``` -Per ogni riga: valuta se è testo legittimo (frase breve) o artefatto -(numero di pagina, nome autore ripetuto, intestazione PDF). Gli artefatti vanno eliminati. - -**Check 3 — Frasi spezzate** -Paragrafi che terminano senza punteggiatura di fine frase: -```bash -grep -n "[^.!?»)\]\'\"]$" step-4//clean.md \ - | grep -v "^[0-9]*:#" \ - | grep -v "^[0-9]*:\s*$" \ - | grep -v "^\s*[-\*]" \ - | head -20 -``` -Segnala le righe brevi che finiscono a metà concetto. Uniscile alla riga successiva. - -**Check 4 — Header sospetti** -```bash -grep -n "^##\? " step-4//clean.md | head -40 -``` -Verifica: -- Header con testo >80 caratteri → probabilmente è testo normale, non un header -- Header in MAIUSCOLO non convertito → cambia in formato sentence-case -- Header duplicati (stesso testo due volte) → valuta se unire o rinominare -- `###` senza un `##` padre → salto di gerarchia anomalo - -**Check 5 — Sezioni quasi vuote** -```bash -python3 -c " -import re -text = open('step-4//clean.md').read() -sections = re.split(r'^(#{1,3} .+)$', text, flags=re.MULTILINE) -for i in range(1, len(sections)-1, 2): - header = sections[i].strip() - body = sections[i+1].strip() if i+1 < len(sections) else '' - if not body: - print(f'VUOTA: {header!r}') - elif len(body) < 80: - print(f'CORTA ({len(body)} char): {header!r} → {body[:60]!r}') -" -``` -Le sezioni vuote generano chunk inutili. Eliminale o accorpale alla sezione precedente. - -**Check 6 — Gerarchia strutturale** -```bash -grep -n "^#\{1,3\} " step-4//clean.md | head -50 -``` -Deve esserci un solo `# h1` all'inizio. Poi `## h2` e opzionalmente `### h3`. -Segnala `###` prima del primo `##`, o più di un `#`. - ---- +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. **Struttura target dopo la revisione:** @@ -441,36 +182,31 @@ 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. +**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. --- -### Step 5 — Chunking adattivo +### Chunking **Tipo:** automatico -**Input:** `step-4//clean.md` + `step-4//structure_profile.json` +**Input:** `conversione//clean.md` **Output:** `step-5//chunks.json` **Script:** `step-5/chunker.py` ```bash -python step-5/chunker.py --stem documento +python step-5/chunker.py --stem ``` -Divide il Markdown pulito in chunk. Usa il profilo strutturale -per scegliere la strategia giusta. Non sa nulla del contenuto — -si basa solo sulla struttura. +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 +- L'overlap tra chunk avviene solo su frasi intere, mai tra sezioni diverse -**Parametri:** +**Parametri (in `step-5/chunker.py`):** | Parametro | Default | Significato | |---|---|---| @@ -491,95 +227,54 @@ si basa solo sulla struttura. } ``` -Il prefisso `[Sezione > Titolo]` è fondamentale: permette all'embedding -di catturare il contesto topico del chunk anche quando il testo -da solo sarebbe ambiguo. +Il prefisso `[Sezione > Titolo]` è fondamentale: permette all'embedding di catturare il contesto topico del chunk anche quando il testo da solo sarebbe ambiguo. --- -### Step 6 — Verifica e fix chunk +### 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 step si articola in un ciclo: verifica → fix automatico → ri-verifica. Non si va allo step 8 finché non ci sono 🔴. +Questo passaggio si articola in un ciclo: verifica → fix automatico → ri-verifica. Non si procede alla vettorizzazione finché non ci sono 🔴. -**Workflow completo:** +**Workflow:** ``` 1. Verifica - python step-6/verify_chunks.py --stem documento + python step-6/verify_chunks.py --stem -2a. Se ✅ OK o solo 🟡 → vai allo step 8 +2a. Se ✅ OK o solo 🟡 → vai alla vettorizzazione 2b. Se ci sono 🔴 → prova il fix automatico: - python step-6/fix_chunks.py --stem documento --dry-run # anteprima - python step-6/fix_chunks.py --stem documento # applica + 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 documento + python step-6/verify_chunks.py --stem -4. Se rimangono 🔴 → torna allo step 4 e correggi clean.md, +4. Se rimangono 🔴 → torna alla revisione Markdown e correggi clean.md, poi riesegui dall'inizio: - python step-5/chunker.py --stem documento --force - python step-6/verify_chunks.py --stem documento + 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. -#### Senza Claude Code — come leggere l'output e decidere - -**1. Leggi l'output di `verify_chunks.py`** - -L'output termina con una delle tre condizioni: +**Output di `verify_chunks.py` — tre condizioni finali:** | Condizione | Significato | Cosa fare | |---|---|---| -| `✅ N/N documenti senza problemi` | Nessun problema | Vai allo step 8 | -| `🟡 Solo avvisi minori` | Chunk corti o lunghi, non bloccanti | Puoi andare allo step 8 oppure ottimizzare con `fix_chunks.py` | +| `✅ 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 | -**2. Prima di applicare il fix: leggi il dry-run** +**Cosa verifica:** -```bash -python step-6/fix_chunks.py --stem --dry-run -``` - -L'output elenca le operazioni pianificate. Significato: - -| Operazione | Cosa fa | Sicurezza | -|---|---|---| -| `fondi N chunk incompleti` | Unisce il chunk troncato col successivo | Sempre sicura | -| `fondi N chunk troppo corti` | Unisce chunk <200 char col successivo | Sicura se il risultato non supera MAX×1.5 | -| `spezza N chunk troppo lunghi` | Divide chunk >1200 char su frasi | Sicura solo se esistono frasi naturali dove spezzare | -| `rimuovi N chunk vuoti` | Elimina chunk senza testo | Sempre sicura | - -**3. Se i 🔴 persistono dopo il fix** - -`fix_chunks.py` non riesce ad autocorreggersi quando il problema -è nella struttura del testo sorgente. 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 | Salto di pagina PDF con numero di pagina nel mezzo | Trova e rimuovi il numero di pagina, unisci le righe | -| Chunk con testo artefatto (URL, watermark) | Artefatto non rimosso allo step 4 | Elimina la sezione in `clean.md` | -| Chunk con frase enorme non spezzabile | Singolo paragrafo >MAX_CHARS senza frasi intermedie | Spezza manualmente il paragrafo su frasi logiche | - -Dopo ogni correzione in `clean.md` riesegui dall'inizio dello step 5: - -```bash -python step-5/chunker.py --stem --force -rm -f step-6//chunks.json # forza la rilettura da step-5 -python step-6/verify_chunks.py --stem -``` - -**Cosa verifica `verify_chunks.py`:** - -- Nessun chunk è sotto `MIN_CHARS` 🟡 -- Nessun chunk è sopra `MAX_CHARS × 1.5` 🟡 +- 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`:** @@ -591,52 +286,39 @@ python step-6/verify_chunks.py --stem | Fondi chunk troppo corto col successivo | Chunk sotto `MIN_CHARS` | | Spezza chunk troppo lungo | Chunk sopra `MAX_CHARS × 1.5` | -**Tabella diagnosi — problemi non risolvibili con fix_chunks:** +**Se i 🔴 persistono dopo il fix** — i casi tipici e la soluzione in `clean.md`: -| Sintomo | Causa probabile | Soluzione | +| Sintomo nel report | Causa in `clean.md` | Correzione | |---|---|---| -| Molti chunk corti dopo il fix | `MIN_CHARS` troppo alto o testo frammentato nel MD | Abbassa `MIN_CHARS` o correggi step 4 | -| Chunk spezzato creato dal fix stesso | Frase singola > `MAX_CHARS` non spezzabile | Spezza manualmente il paragrafo in step 4 | -| Chunk che finisce a metà frase non risolvibile | Salto di pagina PDF non sanato nel MD | Correggi la riga spezzata in `clean.md` | - -**Output se tutto ok:** - -``` -Totale chunk: 301 -✅ OK: 301 - -Distribuzione lunghezze: - Min: 187 char - Max: 923 char - Media: 401 char - -✅ 1/1 documenti senza problemi -``` +| 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 | --- -### Step 7 — Installazione ambiente +### Ambiente Ollama **Tipo:** manuale (una volta sola) **Input:** nessuno **Output:** ambiente locale funzionante -**Script:** `step-7/check_env.py` +**Script:** `ollama/check_env.py` -Installa Ollama, scarica i modelli e verifica l'ambiente. Si esegue una volta sola. +Installa Ollama, scarica i modelli e verifica l'ambiente. Si esegue una volta sola prima della vettorizzazione. -Vedi [`step-7/README.md`](step-7/README.md) per istruzioni dettagliate e scelta dei modelli. +Vedi [`ollama/README.md`](ollama/README.md) per istruzioni dettagliate e scelta dei modelli. ```bash -python step-7/check_env.py +python ollama/check_env.py ``` --- -### Step 8 — Vettorizzazione +### Vettorizzazione **Tipo:** automatico (lento) **Input:** `step-6//chunks.json` -**Output:** `chroma_db/` popolato +**Output:** `chroma_db/` popolato **Script:** `step-8/ingest.py` ```bash @@ -653,125 +335,126 @@ Per 900 chunk aspetta circa 15 minuti. | 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à. Senza `--force`, se la collection è presente lo step viene saltato | +| `--force` | Cancella e ricrea la collection se esiste già | **Quando usare `--force`:** -Se hai modificato i chunk (es. hai rieseguito step-6 dopo correzioni), la collection in ChromaDB -contiene ancora i vecchi vettori. `--force` la cancella e la ricrea da zero con i chunk aggiornati. +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:** ``` testo del chunk │ - ▼ Ollama (nomic-embed-text) -vettore di 768 numeri -[0.23, -0.41, 0.87, 0.12, ...] + ▼ Ollama (EMBED_MODEL) +vettore N-dim │ ▼ ChromaDB salva: testo + vettore + metadati (sezione, titolo, sub_index) ``` -**Perché 768 numeri:** -Ogni numero rappresenta una dimensione semantica. -Testi con significato simile producono vettori simili — -i loro numeri sono vicini nello spazio a 768 dimensioni. -Questo è ciò che permette il retrieval semantico. - -**Output durante l'esecuzione:** - -``` -✅ Ollama OK — nomic-embed-text disponibile - -📦 872 chunk da ingestire - - [ 1/872] ✓ sezione_1__sotto_1__s0 ETA: 870s - [ 2/872] ✓ sezione_1__sotto_2__s0 ETA: 867s - ... - [872/872] ✓ sezione_9__sotto_42__s0 ETA: 0s - -✅ Ingestione completata in 718s — 872/872 chunk salvati - Collection 'nietzsche' in chroma_db/ -``` - -`chroma_db/` contiene ora tutti i vettori su disco. -Non è necessario ripetere questo step a meno che il documento cambi. +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. --- -### Step 9 — Pipeline RAG +### Interrogazione **Tipo:** interattivo -**Input:** `chroma_db/` + domanda dell'utente -**Output:** risposta basata sul documento -**Script:** `step-9/rag.py` +**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 step-9/rag.py --stem +python rag.py --stem ``` -Loop interattivo che risponde a domande sul documento. Configura i parametri in `step-9/config.py` prima di avviare. +| Sintassi | Comportamento | +|---|---| +| `` | Risposta basata sul documento | +| ` -v` | Risposta + chunk recuperati con score di similarità | +| `exit` | Esce dal programma | -Vedi [`step-9/README.md`](step-9/README.md) per la configurazione completa. +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 +``` -### Step 10 — Test automatici - -**Tipo:** automatico -**Input:** sistema completo -**Output:** tutti i test verdi -**Script:** `step-10/test_pipeline.py` *(da implementare)* +#### retrieve.py — Retrieval puro (senza LLM) ```bash -python step-10/test_pipeline.py --stem +source .venv/bin/activate +python retrieve.py --stem ``` -Verifica ogni componente in isolamento e poi nel sistema completo. -I test non dipendono dal contenuto del documento — usano dati -fittizi creati e distrutti in memoria. +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. -**Struttura dei test:** +| 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`) + +| 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 | + +#### Test senza RAG + +Per verificare che Ollama risponda correttamente prima di interrogare il documento: + +```bash +python ollama/test_ollama.py ``` -Test unitari — ogni componente isolato - ✓ split_sentences non spezza le frasi - ✓ parse_markdown rileva la struttura corretta - ✓ chunk_sezione rispetta i boundary - ✓ il prefisso è sempre presente in ogni chunk -Test integrazione — i componenti parlano tra loro - ✓ Ollama è raggiungibile - ✓ i modelli sono disponibili - ✓ l'embedding produce 768 dimensioni - ✓ testi diversi producono vettori diversi - ✓ ChromaDB scrive e legge correttamente - -Test qualità — il sistema si comporta bene - ✓ il retrieval trova il chunk pertinente - ✓ il retrieval non trova il chunk non pertinente - ✓ il LLM usa il contesto fornito - ✓ il LLM ammette quando la risposta non è nel contesto -``` +Chat diretta con il modello, senza ChromaDB. Usa gli stessi parametri di `config.py`. --- ## Principi di progettazione **Atomico** -Ogni step fa una cosa sola. Il chunker non sa niente di Ollama. +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** -Ogni step ha un criterio di completamento oggettivo. -Non si passa allo step successivo finché il precedente non è verificato. +Ogni fase ha un criterio di completamento oggettivo. +Non si passa alla fase successiva finché la precedente non è verificata. **Reversibile** -Puoi tornare indietro senza perdere il lavoro degli altri step. -Cambi il MD? Riesegui solo step 5 e 8. -Cambi i parametri del chunker? Riesegui solo step 5 e 8. +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**