diff --git a/CLAUDE.md b/CLAUDE.md index d3ff47b..f9bcc51 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,9 +2,28 @@ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. +--- + +## Lingua e comportamento + +- **Lingua:** Rispondi sempre in italiano. +- **Prima di eseguire qualsiasi istruzione**, esponi: + 1. Come intendi procedere (approccio, file coinvolti, stadi modificati). + 2. Se l'istruzione ha problemi concettuali — e perché — con una proposta alternativa. + 3. Aspetta conferma o correzione prima di toccare il codice, salvo che l'istruzione sia banale (rinomina, formattazione). + +Esempio di risposta corretta a "aggiungi OCR al parser": +> "L'OCR contraddice il vincolo 'Niente LLM né OCR nella pipeline' di questo progetto, che richiede output deterministico e riproducibile. Se il problema sono i PDF scansionati, l'approccio corretto è rilevare il caso e restituire un errore esplicito. Procedo in questo senso?" + +--- + ## Missione -Ricostruire la struttura logica di PDF digitali e serializzarla in Markdown **stabile e valido per la vettorizzazione RAG**, senza LLM né OCR. Il Markdown è solo il formato di output finale — la pipeline deve operare su una rappresentazione intermedia strutturata. +Ricostruire la struttura logica di PDF digitali e serializzarla in Markdown **stabile e valido per la vettorizzazione RAG**, senza LLM né OCR. + +``` +PDF → Structured Document Tree → Markdown → Chunks → ChromaDB → RAG +``` **Non supportato:** PDF scansionati (immagini), PDF protetti da password. @@ -12,203 +31,120 @@ Ricostruire la struttura logica di PDF digitali e serializzarla in Markdown **st ## Regole invarianti -- **Lingua:** Rispondi sempre in italiano. -- **Venv:** Usa `.venv/bin/python` o `source .venv/bin/activate`. Mai `pip`/`python` di sistema. +- **Venv:** Usa `.venv/bin/python`. Mai `pip`/`python` di sistema. - **`raw.md` immutabile:** Non modificare mai `raw.md`. La copia di lavoro è sempre `clean.md`. - **Niente LLM nella pipeline:** tutta la logica deve essere rule-based e riproducibile. - ---- - -## Setup - -```bash -python -m venv .venv -source .venv/bin/activate -pip install -r requirements.txt -``` - -Dipendenze principali: - -- **PyMuPDF** (`fitz`) — estrazione primaria con metadati font e coordinate -- **pdfplumber** — ricostruzione tabelle (opzionale, non per parsing generico) +- **Markdown generato solo dall'albero:** Mai da `Block` direttamente — sempre da `Section`. --- ## Comandi ```bash +# Setup +python -m venv .venv && source .venv/bin/activate && pip install -r requirements.txt + # Converti un PDF (posizionalo prima in sources/.pdf) .venv/bin/python conversione/ --stem +.venv/bin/python conversione/ --stem --force # sovrascrive output -# Forza riesecuzione (sovrascrive clean.md esistente) -.venv/bin/python conversione/ --stem --force +# Chunking +.venv/bin/python chunks/chunker.py --stem -# Tutti i PDF in sources/ -.venv/bin/python conversione/ +# Vettorizzazione (richiede Ollama attivo) +.venv/bin/python ingestion/ingest.py --stem -# Validazione batch +# RAG interattivo +.venv/bin/python rag.py --stem +.venv/bin/python retrieve.py --stem # retrieval puro, senza LLM + +# Validazione .venv/bin/python conversione/ validate .venv/bin/python conversione/ validate --detail -# Rimuove l'output di uno stem -bash conversione/clear.sh - -# Test suite +# Test .venv/bin/python -m pytest tests/ -``` +.venv/bin/python -m pytest tests/unit/test_stage4.py # singolo file -`--stem` = nome file PDF senza estensione. +# Pulizia output +bash conversione/clear.sh +``` --- ## Architettura -### Principio fondamentale +### Pipeline di conversione — `conversione/_pipeline/` -La pipeline **non converte direttamente PDF → Markdown**. +9 stadi in sequenza, ognuno riceve l'output tipizzato del precedente: -``` -PDF → Structured Document Tree → Markdown -``` +| Stage | File | Responsabilità | +|-------|------|----------------| +| 1 | `stage1_metadata.py` | Estrazione span da PyMuPDF (`get_text("dict")`), TOC, dimensioni pagina | +| 2 | `stage2_layout.py` | Analisi layout, reading order, tipo blocco | +| 3 | `stage3_font.py` | Font profile per documento (body size, cluster, header sizes) | +| 4 | `stage4_headers.py` | Classificazione header_candidate (font+bold+numerazione+spacing) | +| 5 | `stage5_hierarchy.py` | Inferenza livello H1/H2/H3 (priorità: numerazione > TOC > font size) | +| 6 | `stage6_tree.py` | Costruzione albero `Section` con parent-child | +| 7 | `stage7_markdown.py` | Serializzazione albero → Markdown raw | +| 8 | `stage8_normalize.py` | Riparazione gerarchia (level jump, header vuoti, duplicati) | +| 9 | `stage9_validate.py` | Validazione struttura finale | -Il Markdown è generato solo dall'albero documentale. Non dal testo grezzo. +Orchestrazione: `runner.py`. Entry point: `conversione/__main__.py`. -### Modello dati intermedio +### Modello dati — `conversione/_pipeline/models.py` ```python -class Block: - text: str - page: int - bbox: tuple - font_size: float - font_name: str - is_bold: bool - block_type: str # "header" | "paragraph" | "list" | "table" | "code" - -class Section: - title: str - level: int # 1, 2, 3 - content: list[Block] - children: list[Section] +Block # span estratto: text, page, bbox, font_size, is_bold, block_type, level +Section # nodo albero: title, level, content: list[Block], children: list[Section] +FontProfile # body_size, cluster_map, header_sizes ``` -Il Markdown si genera **solo** da `Section`. Mai da `Block` direttamente. +### Pipeline RAG — file radice + +| File | Responsabilità | +|------|---------------| +| `chunks/chunker.py` | Chunking adattivo da `clean.md` + `structure_profile.json` | +| `chunks/config.py` | Parametri chunking (TARGET_CHARS, OVERLAP, STRATEGY_OVERRIDES) | +| `ingestion/ingest.py` | Embedding Ollama → ChromaDB | +| `retrieve.py` | Retrieval puro (debug retrieval senza LLM) | +| `rag.py` | Loop RAG interattivo (retrieval + generazione Ollama) | +| `config.py` | Parametri globali RAG (TOP_K, TEMPERATURE, OLLAMA_MODEL, EMBED_MODEL) | + +### Output per stem + +``` +conversione//raw.md # immutabile, output stage 7 +conversione//clean.md # con frontmatter YAML, output stage 8-9 +conversione//structure_profile.json +conversione//report.json +chunks//chunks.json +chroma_db// # collection ChromaDB +``` --- -### I 9 stadi della pipeline +## Linee guida per la pipeline -#### Stage 1 — Metadata Extraction +- Le regex per header numbering vanno in `_constants.py`, mai inline. +- PyMuPDF è il parser primario. pdfplumber solo per tabelle complesse. +- Ogni stage deve essere indipendentemente testabile. +- Prima di aggiungere un nuovo segnale a Stage 4, validarlo su almeno 3 PDF. -Usa `page.get_text("dict")` (o `"rawdict"`) di PyMuPDF. **Non usare estrazione plain text.** +### Test richiesti -Per ogni span estrai: testo, font size, font name, flags, bbox, numero di pagina. -Estrai anche: TOC del documento, bookmark, dimensioni pagina. - -#### Stage 2 — Layout Analysis - -Identifica i blocchi strutturali preservando l'ordine di lettura: -headers, paragrafi, liste, tabelle, code block, interruzioni di pagina. - -#### Stage 3 — Font Analysis - -Inferisce la gerarchia visiva **per documento** (non hardcoded): -- calcola il font size dominante del corpo -- raggruppa i font size in cluster -- identifica i candidati header per dimensione - -#### Stage 4 — Header Detection - -Segnali combinati (tutti richiesti): -- font size > corpo testo -- boldness / semibold -- numerazione (`^\d+(\.\d+)*\s+`) -- spaziatura verticale sopra/sotto -- lunghezza riga corta - -#### Stage 5 — Hierarchy Inference - -Priorità delle regole (in ordine): - -1. **Numerazione** — `1` → H1, `1.1` → H2, `1.1.1` → H3 (ha precedenza sul font size) -2. **TOC del PDF** — se presente, è autoritativo; allineare i header rilevati alla sua gerarchia -3. **Font size clustering** — fallback se né numerazione né TOC esistono - -#### Stage 6 — Document Tree Reconstruction - -Costruisce l'albero `Section` con relazioni parent-child, ordinamento e nesting. Ogni nodo contiene titolo, livello, contenuto e figli. - -#### Stage 7 — Markdown Generation - -Serializza l'albero in Markdown valido: -- Header: `#`/`##`/`###` senza salti di livello -- Liste: preserva nesting ordered/unordered -- Tabelle: GitHub-compatible; fallback testo strutturato -- Code block: fenced con language tag dove rilevabile - -#### Stage 8 — Hierarchy Normalization - -Ripara le inconsistenze strutturali: -- salti di livello invalidi (`# A` → `#### B` diventa `# A` → `## B`) -- header vuoti (rimuovi o mergia) -- header consecutivi duplicati (collassa) -- nesting rotto - -#### Stage 9 — Structural Validation - -Valida il Markdown finale: -- nessun salto di livello heading -- nessuna sezione vuota -- liste correttamente annidate -- tabelle con colonne consistenti -- ordine uguale al PDF sorgente - ---- - -## Cosa rende un Markdown perfetto per la vettorizzazione - -- **Struttura semantica:** ogni header è un confine naturale di chunk; ogni sezione è un'unità concettuale. -- **Gerarchia corretta:** h1/h2/h3 riflettono la struttura logica, non il layout tipografico. -- **Testo pulito:** nessun artefatto di encoding, footnote superscript, `
`, dot-leader, PUA. -- **Paragrafi interi:** nessuna frase troncata da salto pagina. -- **Output deterministico:** stessa pipeline su stesso PDF produce sempre lo stesso output. - ---- - -## Linee guida per sviluppare la pipeline - -- Ogni stage deve essere **indipendentemente testabile**. -- Le regex per header numbering e simili vanno compilate in `_constants.py`, mai inline. -- PyMuPDF è il parser primario. pdfplumber si usa solo per tabelle complesse. -- Ogni stage deve ricevere l'output del precedente come struttura tipizzata, non testo grezzo. -- Prima di aggiungere un nuovo segnale di detection (Stage 4), validarlo su almeno 3 PDF diversi. - -### Categorie di test richieste - -| Categoria | Input | Validazione attesa | -|-----------|-------|-------------------| -| Header reconstruction | PDF con H1/H2/H3 numerati | gerarchia corretta, no level skip | -| TOC alignment | PDF con bookmark/TOC | markdown allineato al TOC | -| Mixed font sizes | Font inconsistenti, bold nel corpo | body non classificato come header | -| Broken layout | Header multi-riga, spacing irregolare | header mergiati, markdown valido | -| Tables | Tabelle nel PDF | markdown table con colonne preservate | -| Lists | Liste ordered/unordered annidate | nesting corretto | -| Large documents | PDF tecnico voluminoso | output deterministico, memoria stabile | -| Invalid hierarchy repair | `# A` + `#### B` artificiale | riparazione automatica in `# A` + `## B` | - ---- - -## Pipeline attuale - -La pipeline in `conversione/_pipeline/` (basata su trasformazioni testo con `_apply.py`) è **deprecata** e deve essere sostituita dall'architettura a 9 stadi descritta sopra. Durante la migrazione: - -- separare estrazione da ricostruzione -- introdurre strutture intermedie esplicite (`Block`, `Section`) -- rimuovere l'architettura parser-centrica -- ogni stage deve essere indipendente e testabile +| Categoria | Validazione attesa | +|-----------|-------------------| +| Header numerati | gerarchia corretta, no level skip | +| TOC presente | markdown allineato al TOC del PDF | +| Font inconsistenti | body non classificato come header | +| Header multi-riga | header mergiati, markdown valido | +| Tabelle | markdown table con colonne preservate | +| Gerarchia rotta artificiale | riparazione automatica | --- ## Skills custom -- `/prepare-md ` — corregge `clean.md` quando la pipeline non basta: sillabazione, artefatti residui, header malformati, gerarchia incoerente. +- `/prepare-md ` — corregge `clean.md` quando la pipeline non basta: sillabazione, artefatti, header malformati, gerarchia incoerente. +- `/post-chunk` — verifica e perfeziona i chunk prima della vettorizzazione.