From b4e64ccedb6418593326121c17d33acbfc9c241e Mon Sep 17 00:00:00 2001 From: Davide Grilli Date: Tue, 9 Jun 2026 10:04:38 +0200 Subject: [PATCH] docs: riscrive README con struttura passo-passo Abstract, requisiti minimi, setup venv, MinerU (Colab + locale), chunking, ingest, interrogazione (terminale + GUI) con tabelle parametri per ogni config.py. Co-Authored-By: Claude Sonnet 4.6 --- README.md | 351 ++++++++++++++++++++++++------------------------------ 1 file changed, 154 insertions(+), 197 deletions(-) diff --git a/README.md b/README.md index 7a574a9..b776be4 100644 --- a/README.md +++ b/README.md @@ -1,245 +1,192 @@ -# RAG su documenti accademici +# RAG from scratch -Pipeline completa per trasformare PDF accademici in un sistema RAG interrogabile in linguaggio naturale. +Esercizio pratico per costruire una pipeline RAG (Retrieval-Augmented Generation) completa a partire da documenti PDF, interamente in locale senza dipendenze cloud. L'obiettivo è avere un assistente che risponde a domande sui tuoi documenti usando solo il contenuto di quei documenti, senza allucinazioni. **Stack:** Python · MinerU · ChromaDB · Ollama -**Chunking:** rule-based, deterministico, senza LLM -**Embedding + Generazione:** Ollama (locale) - ---- - -## Prerequisiti - -### 1. MinerU — conversione PDF → Markdown - -MinerU è il tool esterno che converte i PDF in Markdown strutturato. -Repository: [https://github.com/opendatalab/MinerU](https://github.com/opendatalab/MinerU) - -#### Requisiti di sistema - -| Risorsa | Minimo | Raccomandato | -|---------|--------|--------------| -| Python | 3.10–3.13 | 3.11 | -| RAM | 16 GB | 32 GB+ | -| Disco | 20 GB (modelli inclusi) | 40 GB+ | -| GPU | opzionale | 4–8 GB VRAM (CUDA) | -| OS | Linux, macOS, Windows | Linux / WSL2 | - -#### Installazione - -```bash -pip install "mineru[all]" -``` - -Al primo avvio scarica automaticamente i modelli (~15 GB). - -#### Uso — CLI - -```bash -mineru -p -o -b pipeline -``` - -| Flag | Descrizione | -|------|-------------| -| `-p` | Percorso PDF o cartella | -| `-o` | Cartella di output | -| `-b` | Backend: `pipeline` (CPU), `hybrid-auto-engine` (GPU raccomandato) | -| `-m` | Metodo: `auto`, `txt`, `ocr` | - -#### Output di MinerU +**Chunking:** AST-based, deterministico, senza LLM +**Embedding + Generazione:** Ollama (locale, nessun dato inviato a server esterni) ``` -_output/ -└── auto/ - ├── .md ← Markdown (input della pipeline) - ├── _content_list_v2.json - ├── _model.json - └── images/ -``` - -Il file usato dalla pipeline è **`.md`** nella cartella `auto/`. - -### 2. Ollama — embedding e generazione - -Scarica e avvia [Ollama](https://ollama.com), poi installa i modelli: - -```bash -ollama pull qwen3-embedding:0.6b # embedding (obbligatorio) -ollama pull qwen3.5:4b # generazione LLM (o altro modello) +PDF + └─ MinerU ──► sources/.md + └─ chunker.py ──► chunks//chunks.json + └─ ingest.py ──► chroma_db/ + └─ rag.py / GUI ``` --- -## Setup +## Requisiti minimi + +| Risorsa | Minimo | Note | +|---------|--------|------| +| Python | 3.10 – 3.13 | testato su 3.11 | +| RAM | 8 GB | 16 GB+ per modelli LLM grandi | +| Disco | 5 GB liberi | + spazio modelli Ollama | +| GPU | non necessaria | accelera MinerU e modelli LLM | +| OS | Linux, macOS, Windows (WSL2) | | + +**Dipendenze Python** — installate via `requirements.txt`: + +- `chromadb` — database vettoriale locale +- `markdown-it-py` + `mdit-py-plugins` — parser Markdown AST-based per il chunking +- `PySide6` — interfaccia grafica desktop (opzionale, solo per la GUI) + +**Dipendenze esterne:** + +- [Ollama](https://ollama.com) — server locale per embedding e generazione LLM +- [MinerU](https://github.com/opendatalab/MinerU) — conversione PDF → Markdown (opzionale se hai già i file `.md`) + +--- + +## Setup ambiente ```bash -git clone -cd rag - +# Crea e attiva il virtual environment python -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate + +# Installa le dipendenze pip install -r requirements.txt ``` --- -## Flusso completo +## Passo 1 — Converti i PDF con MinerU -``` -PDF - │ - ▼ MinerU (esterno — vedi sotto) -sources/_output/auto/.md - │ - ▼ python chunks/chunker.py --stem -chunks//chunks.json - │ - ▼ python chunks/verify_chunks.py --stem -(verifica qualità — opzionale ma consigliato) - │ - ▼ python ingestion/ingest.py --stem -chroma_db// - │ - ▼ python rag.py --stem -Interrogazione in linguaggio naturale -``` +MinerU estrae il testo dai PDF producendo un Markdown strutturato con tabelle, formule ed immagini. Ci sono due modalità: ---- +### Opzione A — Google Colab (consigliata, usa la GPU gratuita di Google) -## Passo 1 — Converti il PDF con MinerU +Il notebook `mineru.ipynb` esegue MinerU su Colab sfruttando la GPU messa a disposizione gratuitamente. Utile se non hai una GPU locale o se i PDF sono pesanti. -**Opzione A — Google Colab (nessuna installazione locale)** - -Usa il notebook incluso: - -``` -mineru.ipynb -``` - -Aprilo su Google Colab, carica il PDF e segui le celle. Al termine scarica automaticamente lo ZIP con l'output. Decomprimi la cartella in `sources/`: +1. Apri `mineru.ipynb` su Google Colab +2. Carica i PDF nella cartella `input/` indicata nelle celle +3. Esegui tutte le celle — al termine viene scaricato uno ZIP per ogni documento +4. Decomprimi gli ZIP nella cartella `sources/` del progetto: ``` sources/ └── _output/ └── auto/ - ├── .md ← usato dalla pipeline - ├── _content_list_v2.json - ├── _model.json + ├── .md ← questo è l'input della pipeline └── images/ ``` -**Opzione B — Installazione locale** +### Opzione B — Installazione locale ```bash +pip install "mineru[all]" # installa MinerU (scarica ~15 GB di modelli al primo avvio) + mineru -p documento.pdf -o sources/_output/ -b pipeline +# Con GPU: +mineru -p documento.pdf -o sources/_output/ -b hybrid-auto-engine ``` --- ## Passo 2 — Chunking +Il chunker analizza il Markdown con un parser AST, divide il testo in blocchi semantici e li impacchetta in chunk ottimizzati per il retrieval. + ```bash # Singolo documento -.venv/bin/python chunks/chunker.py --stem +python chunks/chunker.py --stem -# Tutti i documenti in sources/ -.venv/bin/python chunks/chunker.py +# Tutti i documenti presenti in sources/ in una volta +python chunks/chunker.py # Rigenera anche se chunks.json esiste già -.venv/bin/python chunks/chunker.py --stem --force +python chunks/chunker.py --stem --force ``` -Il chunker legge `sources/_output/auto/.md` e produce `chunks//chunks.json`. +Output in `chunks//`: `chunks.json`, `meta.json`, `report.json`. -**Regole applicate:** +### Configurazione — `chunks/config.py` -- 1 paragrafo = 1 chunk; paragrafi di sezioni diverse non si mescolano -- Split a confine di frase se il paragrafo supera `MAX_CHARS` (mai a metà frase) -- Frasi spezzate tra paragrafi consecutivi vengono ri-fuse automaticamente -- Paragrafi brevi (< `MIN_CHARS`) vengono accorpati al successivo (stesso contesto) -- Sezioni configurabili vengono saltate (indice, sommario, frontespizio) -- Tabelle, liste e code block sono blocchi atomici (non si spezzano) - -Output in `chunks//`: - -| File | Contenuto | -|------|-----------| -| `chunks.json` | Chunk con testo, sezione, titolo, n_chars | -| `meta.json` | Parametri usati | -| `report.json` | Statistiche e anomalie (generato da verify) | - ---- - -## Passo 3 — Verifica i chunk (opzionale) - -```bash -.venv/bin/python chunks/verify_chunks.py --stem -``` - -| Verdict | Significato | -|---------|-------------| -| `ok` | Nessun problema — procedi | -| `warnings_only` | Solo avvisi minori — puoi procedere | -| `blocked` | Problemi strutturali — rivedi il sorgente `.md` | - ---- - -## Passo 4 — Vettorizzazione - -```bash -# Singolo documento → collection omonima -.venv/bin/python ingestion/ingest.py --stem - -# Più documenti → collection condivisa (RAG cross-documento) -.venv/bin/python ingestion/ingest.py --collection --stems doc1 doc2 doc3 - -# Rigenera dopo aver aggiornato i chunk o cambiato modello embedding -.venv/bin/python ingestion/ingest.py --stem --force -``` - -> Se cambi `EMBED_MODEL` in `config.py` devi rieseguire l'ingestion con `--force`. - ---- - -## Passo 5 — Interrogazione - -```bash -# RAG interattivo (retrieval + generazione LLM) -.venv/bin/python rag.py --stem -.venv/bin/python rag.py --collection - -# Retrieval puro (debug, senza LLM) -.venv/bin/python retrieve.py --stem -.venv/bin/python retrieve.py --stem --top-k 10 -``` - ---- - -## Configurazione - -### Chunking — `chunks/config.py` +Modifica i parametri nella classe `ChunkerConfig` se necessario: | Parametro | Default | Descrizione | |-----------|---------|-------------| -| `MAX_CHARS` | 1200 | Lunghezza massima chunk (caratteri) | -| `MIN_CHARS` | 80 | Soglia minima (warning sotto questa soglia) | -| `CONTEXT_DEPTH` | 3 | Livelli heading inclusi nel prefisso chunk (1–3) | -| `SKIP_HEADINGS` | set italiano | Sezioni saltate completamente (indice, sommario…) | -| `SKIP_PRE_HEADING` | `True` | Salta contenuto prima del primo heading (frontespizio) | -| `MERGE_SHORT_PARAGRAPHS` | `True` | Accorpa paragrafi brevi fino a `MIN_CHARS` | -| `SENTENCE_SPLIT_RE` | regex | Confine di fine frase per lo split | -| `ATOMIC_TYPES` | `table, code, list` | Blocchi mai spezzati | +| `max_chars` | `1200` | Dimensione massima di un chunk (caratteri) | +| `min_chars` | `80` | Soglia minima; chunk piu' piccoli vengono accorpati | +| `target_chars` | `800` | Dimensione target per il packing greedy | +| `context_depth` | `3` | Livelli di heading inclusi come prefisso nel testo per l'embedding | +| `skip_headings` | vedi file | Sezioni saltate completamente (indice, sommario, ecc.) | +| `skip_pre_heading` | `True` | Salta il contenuto prima del primo heading (frontespizi, ecc.) | +| `atomic_types` | `table, code, list, math, html` | Tipi di blocco che non vengono mai spezzati | -### RAG — `config.py` +--- + +## Passo 3 — Vettorizzazione (ingest) + +Genera gli embedding dei chunk e li indicizza in ChromaDB. Richiede Ollama attivo. + +```bash +# Avvia Ollama (se non e' gia' in esecuzione) +ollama serve + +# Installa il modello di embedding (una volta sola) +ollama pull bge-m3 + +# Indicizza un singolo documento +python ingestion/ingest.py --stem + +# Indicizza piu' documenti in una collection condivisa (RAG cross-documento) +python ingestion/ingest.py --collection --stems doc1 doc2 doc3 + +# Rigenera dopo aver aggiornato i chunk o cambiato modello embedding +python ingestion/ingest.py --stem --force +``` + +### Configurazione — `ingestion/config.py` + +| Parametro | Default | Descrizione | +|-----------|---------|-------------| +| `EMBED_MODEL` | `bge-m3` | Modello Ollama per gli embedding. Se lo cambi, riesegui l'ingest con `--force` | +| `EMBED_MAX_CHARS` | `6000` | Caratteri massimi inviati al modello per ogni chunk | +| `OLLAMA_URL` | `http://localhost:11434` | URL del server Ollama | + +--- + +## Passo 4 — Interrogazione + +### Da terminale + +```bash +# Installa il modello LLM (una volta sola) +ollama pull qwen3.5:4b + +# RAG interattivo — retrieval + risposta generata dal modello +python rag/rag.py --collection +python rag/rag.py --stem # scorciatoia per collection a documento singolo + +# Retrieval puro senza LLM (utile per debug) +python rag/retrieve.py --collection +python rag/retrieve.py --stem --top-k 10 +``` + +Nel loop interattivo: digita la domanda e premi Invio. Aggiungi `-v` alla fine per vedere i chunk recuperati con i relativi score di similarita'. + +### Con la GUI desktop + +```bash +python gui/main.py +python gui/main.py --collection # apre direttamente la collection +``` + +La GUI mostra le risposte con Markdown e formule matematiche renderizzate. Il tema chiaro/scuro e' selezionabile dal pulsante in alto a destra. + +### Configurazione — `rag/config.py` | Parametro | Default | Descrizione | |-----------|---------|-------------| | `OLLAMA_MODEL` | `qwen3.5:4b` | Modello LLM per la generazione | -| `EMBED_MODEL` | `qwen3-embedding:0.6b` | Modello embedding | -| `EMBED_MAX_CHARS` | 6000 | Caratteri massimi inviati al modello embedding | -| `TOP_K` | 6 | Chunk recuperati per domanda | -| `TEMPERATURE` | 0.2 | Creatività del modello (0 = deterministico) | -| `OLLAMA_URL` | `localhost:11434` | URL server Ollama | +| `TOP_K` | `6` | Numero di chunk recuperati per ogni domanda | +| `TEMPERATURE` | `0.2` | 0 = deterministico, valori alti = piu' creativo | +| `NO_THINK` | `True` | Disabilita il reasoning interno (piu' veloce; solo modelli Qwen3) | +| `OLLAMA_URL` | `http://localhost:11434` | URL del server Ollama | +| `SYSTEM_PROMPT` | vedi file | Istruzioni di sistema inviate al modello prima del contesto | --- @@ -247,17 +194,27 @@ Output in `chunks//`: ``` rag/ -├── sources/ ← output MinerU (una cartella per documento) -│ └── _output/auto/ +├── sources/ ← output MinerU (una cartella per documento) ├── chunks/ -│ ├── chunker.py ← chunking da MD pulito -│ ├── config.py ← parametri di chunking -│ └── verify_chunks.py ← verifica qualità chunk +│ ├── chunker.py ← orchestrazione pipeline AST +│ ├── config.py ← parametri di chunking +│ ├── parser.py ← markdown-it-py + dollarmath +│ ├── segmenter.py ← token stream -> Block[] +│ ├── packer.py ← Block[] -> Chunk[] +│ └── validator.py ← invarianti e metriche ├── ingestion/ -│ └── ingest.py ← embedding → ChromaDB -├── chroma_db/ ← database vettoriale (ignorato da git) -├── config.py ← parametri RAG (modelli, TOP_K, prompt) -├── rag.py ← loop RAG interattivo -├── retrieve.py ← retrieval puro (debug) -└── mineru.ipynb ← notebook Colab per conversione PDF +│ ├── ingest.py ← embedding -> ChromaDB +│ └── config.py ← parametri embedding +├── rag/ +│ ├── rag.py ← loop RAG interattivo +│ ├── retrieve.py ← retrieval puro (debug) +│ └── config.py ← parametri RAG e system prompt +├── gui/ +│ ├── main.py ← entry point GUI +│ ├── chat_window.py ← finestra principale PySide6 +│ ├── worker.py ← thread per il pipeline RAG +│ └── chat.html ← template HTML (Markdown + KaTeX) +├── chroma_db/ ← database vettoriale (non tracciato da git) +├── mineru.ipynb ← notebook Colab per conversione PDF +└── requirements.txt ```