docs(claude-md): riscrittura con modalità collaborativa e architettura compatta
Aggiunge sezione comportamento: Claude espone approccio e problemi concettuali prima di toccare il codice. Ristruttura architettura in tabelle, completa i comandi con chunking/ingestion/retrieve, rimuove note sulla pipeline deprecata. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,9 +2,28 @@
|
|||||||
|
|
||||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
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
|
## 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.
|
**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
|
## Regole invarianti
|
||||||
|
|
||||||
- **Lingua:** Rispondi sempre in italiano.
|
- **Venv:** Usa `.venv/bin/python`. Mai `pip`/`python` di sistema.
|
||||||
- **Venv:** Usa `.venv/bin/python` o `source .venv/bin/activate`. Mai `pip`/`python` di sistema.
|
|
||||||
- **`raw.md` immutabile:** Non modificare mai `raw.md`. La copia di lavoro è sempre `clean.md`.
|
- **`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.
|
- **Niente LLM nella pipeline:** tutta la logica deve essere rule-based e riproducibile.
|
||||||
|
- **Markdown generato solo dall'albero:** Mai da `Block` direttamente — sempre da `Section`.
|
||||||
---
|
|
||||||
|
|
||||||
## 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)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Comandi
|
## Comandi
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# Setup
|
||||||
|
python -m venv .venv && source .venv/bin/activate && pip install -r requirements.txt
|
||||||
|
|
||||||
# Converti un PDF (posizionalo prima in sources/<nome>.pdf)
|
# Converti un PDF (posizionalo prima in sources/<nome>.pdf)
|
||||||
.venv/bin/python conversione/ --stem <nome>
|
.venv/bin/python conversione/ --stem <nome>
|
||||||
|
.venv/bin/python conversione/ --stem <nome> --force # sovrascrive output
|
||||||
|
|
||||||
# Forza riesecuzione (sovrascrive clean.md esistente)
|
# Chunking
|
||||||
.venv/bin/python conversione/ --stem <nome> --force
|
.venv/bin/python chunks/chunker.py --stem <nome>
|
||||||
|
|
||||||
# Tutti i PDF in sources/
|
# Vettorizzazione (richiede Ollama attivo)
|
||||||
.venv/bin/python conversione/
|
.venv/bin/python ingestion/ingest.py --stem <nome>
|
||||||
|
|
||||||
# Validazione batch
|
# RAG interattivo
|
||||||
|
.venv/bin/python rag.py --stem <nome>
|
||||||
|
.venv/bin/python retrieve.py --stem <nome> # retrieval puro, senza LLM
|
||||||
|
|
||||||
|
# Validazione
|
||||||
.venv/bin/python conversione/ validate
|
.venv/bin/python conversione/ validate
|
||||||
.venv/bin/python conversione/ validate <stem> --detail
|
.venv/bin/python conversione/ validate <stem> --detail
|
||||||
|
|
||||||
# Rimuove l'output di uno stem
|
# Test
|
||||||
bash conversione/clear.sh <nome>
|
|
||||||
|
|
||||||
# Test suite
|
|
||||||
.venv/bin/python -m pytest tests/
|
.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 <nome>
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Architettura
|
## 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:
|
||||||
|
|
||||||
```
|
| Stage | File | Responsabilità |
|
||||||
PDF → Structured Document Tree → Markdown
|
|-------|------|----------------|
|
||||||
```
|
| 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
|
```python
|
||||||
class Block:
|
Block # span estratto: text, page, bbox, font_size, is_bold, block_type, level
|
||||||
text: str
|
Section # nodo albero: title, level, content: list[Block], children: list[Section]
|
||||||
page: int
|
FontProfile # body_size, cluster_map, header_sizes
|
||||||
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]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
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/<stem>/raw.md # immutabile, output stage 7
|
||||||
|
conversione/<stem>/clean.md # con frontmatter YAML, output stage 8-9
|
||||||
|
conversione/<stem>/structure_profile.json
|
||||||
|
conversione/<stem>/report.json
|
||||||
|
chunks/<stem>/chunks.json
|
||||||
|
chroma_db/<stem>/ # 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.
|
| Categoria | Validazione attesa |
|
||||||
Estrai anche: TOC del documento, bookmark, dimensioni pagina.
|
|-----------|-------------------|
|
||||||
|
| Header numerati | gerarchia corretta, no level skip |
|
||||||
#### Stage 2 — Layout Analysis
|
| TOC presente | markdown allineato al TOC del PDF |
|
||||||
|
| Font inconsistenti | body non classificato come header |
|
||||||
Identifica i blocchi strutturali preservando l'ordine di lettura:
|
| Header multi-riga | header mergiati, markdown valido |
|
||||||
headers, paragrafi, liste, tabelle, code block, interruzioni di pagina.
|
| Tabelle | markdown table con colonne preservate |
|
||||||
|
| Gerarchia rotta artificiale | riparazione automatica |
|
||||||
#### 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, `<br>`, 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
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Skills custom
|
## Skills custom
|
||||||
|
|
||||||
- `/prepare-md <path|stem>` — corregge `clean.md` quando la pipeline non basta: sillabazione, artefatti residui, header malformati, gerarchia incoerente.
|
- `/prepare-md <path|stem>` — corregge `clean.md` quando la pipeline non basta: sillabazione, artefatti, header malformati, gerarchia incoerente.
|
||||||
|
- `/post-chunk` — verifica e perfeziona i chunk prima della vettorizzazione.
|
||||||
|
|||||||
Reference in New Issue
Block a user