218 lines
11 KiB
Markdown
218 lines
11 KiB
Markdown
# Evidence canonica — struttura tipizzata per disambiguazione, schema linking e SQL
|
||
|
||
> **Superseded (2026-08-24).** Questo documento conserva la storia della prima
|
||
> proposta. Il disegno approvato è
|
||
> [`2026-08-24-evidence-restructuring-design.md`](2026-08-24-evidence-restructuring-design.md)
|
||
> e il relativo piano esecutivo è
|
||
> [`2026-08-24-evidence-restructuring.md`](2026-08-24-evidence-restructuring.md).
|
||
|
||
## Contesto e decisioni prese
|
||
|
||
ThothII ha già due livelli separati che non si parlano:
|
||
|
||
- **`EvidenceDoc`** (`harness/tht/evidence/model.py`) — runtime: frontmatter piatto
|
||
(`id/title/tier/status/tables/concepts/sources`) + `body` markdown libero. `tier`
|
||
distingue solo `structural|concept`, insufficiente rispetto ai 6 tipi reali del corpus.
|
||
- **Pipeline corpus** (`harness/tht/corpus/`) — canonizzazione *tecnica* (hash,
|
||
provenienza, chunking, vettorizzazione), agnostica rispetto al tipo di evidenza.
|
||
|
||
Il corpus reale (`ChironeWp3/artifacts/evidence/`) ha una tassonomia implicita in 6
|
||
directory (`00-glossario`, `10-domini-clinici`, `20-valori-enum`, `30-esempi-nlq`,
|
||
`40-mapping-semantico`, `50-metadati-normalizzazione`) ma:
|
||
|
||
- frontmatter piatto, `tier` inadeguato;
|
||
- file già rotti (`--` invece di `---`, bullet `• ` invece di `-`) che `EvidenceDoc.parse`
|
||
rifiuterebbe;
|
||
- al retrieval la struttura si perde: `tht search pack` proietta solo `title` + 400 char.
|
||
|
||
**Decisioni (confermate nel brainstorming):**
|
||
|
||
1. **Obiettivo**: strutturare il *contenuto* runtime, non toccare la pipeline corpus.
|
||
2. **Forma**: ibrido — frontmatter tipizzato + sezioni canoniche per `kind`.
|
||
3. **Tassonomia**: doppia — `kind` (contenuto) + `applies_to` (destinazioni:
|
||
`disambiguation`, `rewriting`, `schema_linking`, `sql_generation`, `memory`).
|
||
4. **Anchors come fonte primaria** per il value-grounding in F4; LSH solo fallback.
|
||
5. **Approccio**: contratto in `tht` + authoring sottile (niente app separata, niente
|
||
client LLM diretto in `tht`).
|
||
6. **Modello sorgente→canonizzato** (non sovrascrittura in place): file umani in
|
||
`source_root/evidence/`, canonizzati derivati in `artifacts/evidence/`, coerente con
|
||
l'esistente `tht evidence extract`.
|
||
7. **Review**: batch con diff aggregato (approvazione per file).
|
||
8. **Rielaborazione**: LLM-assistita via Pi (sessione di manutenzione + gate), con parte
|
||
deterministica in `tht`.
|
||
|
||
## Modello canonico v2
|
||
|
||
### `CanonicalEvidence` (frontmatter tipizzato)
|
||
|
||
```yaml
|
||
---
|
||
schema_version: 2
|
||
id: ev-dom-ablazione-see
|
||
title: Dominio Ablazione e SEE
|
||
kind: domain # glossario | domain | enum | example | mapping | normalization
|
||
applies_to: # destinazioni d'uso
|
||
- disambiguation # F1
|
||
- schema_linking # F4
|
||
- sql_generation # F6/F7
|
||
status: reviewed
|
||
language: it
|
||
concepts:
|
||
- term: ablazione
|
||
synonyms: [ablazione transcatetere, SEE, studio elettrofisiologico]
|
||
- term: fibrillazione atriale
|
||
synonyms: [FA]
|
||
tables:
|
||
- name: datawarehouse.fact_studio_elettrofisiologico_endocavitario_ablazione
|
||
role: fact
|
||
columns: [ablazione_transcatetere, cod_paz, num]
|
||
anchors:
|
||
- column: datawarehouse.fact_see_ablazione_procedura_patologia.patologia
|
||
value: ablazione
|
||
match: exact
|
||
sources: [...]
|
||
# provenienza del derivato (aggiunta dal canonicalize, non dall'umano):
|
||
source_file: 10-domini-clinici/ablazione.md
|
||
source_fingerprint: sha256:...
|
||
canonicalized_at: 2026-08-18T...
|
||
---
|
||
```
|
||
|
||
Il `body` resta markdown, ma con **titoli di sezione canonici per `kind`**:
|
||
- `glossario`: `## Definizione`
|
||
- `domain`: `## Cosa rappresenta`, `## Schema a stella`, `## Granularità`, `## Domande di business tipiche`
|
||
- `enum`: `## Valori ammessi`
|
||
- `example`: `## Domanda → SQL` + `## Nota clinica`
|
||
- `mapping`: `## Trasformazioni disponibili`
|
||
- `normalization`: `## Regole di normalizzazione`
|
||
|
||
Le sezioni canoniche permettono al retrieval di estrarre solo la sezione rilevante per
|
||
fase invece di 400 caratteri generici.
|
||
|
||
### `kind` vs `applies_to`
|
||
|
||
- `kind` descrive il *contenuto* (i 6 tipi già impliciti).
|
||
- `applies_to` descrive *quando usarlo*. Esempi: `enum-tipo-intervento` è `kind: enum` ma
|
||
`applies_to: [disambiguation, sql_generation]`; un `example` è
|
||
`applies_to: [sql_generation, memory]`.
|
||
|
||
## Modello a 3 livelli
|
||
|
||
```
|
||
source_root/evidence/*.md (umano, libero, può essere sporco)
|
||
│ tht evidence canonicalize (deterministico + LLM via Pi + review batch)
|
||
▼
|
||
artifacts/evidence/*.md (canonico, validato, derivato, rigenerabile)
|
||
│ tht evidence index (esistente)
|
||
▼
|
||
corpus / vector store (vettorializzato dal canonico, mai dal sorgente)
|
||
```
|
||
|
||
Il canonizzato porta `source_file` + `source_fingerprint`: se il sorgente cambia, la
|
||
canonizzazione ripropone il diff; se invariato, no-op.
|
||
|
||
## Componenti da creare/modificare
|
||
|
||
### 1. `harness/tht/evidence/model.py` (modifica)
|
||
- Aggiungere `CanonicalEvidence` (pydantic, v2) con `schema_version`, `kind`,
|
||
`applies_to`, `concepts[]` (`{term, synonyms[]}`), `tables[]`
|
||
(`{name, role, columns[]}`), `anchors[]` (`{column, value, match}`), `source_file`,
|
||
`source_fingerprint`, `canonicalized_at`.
|
||
- Validatore strict: `kind` ammesso, `applies_to` ammesso, `anchors[].column` deve
|
||
essere `schema.colonna` ben formato, `match` in `{exact, contains, regex, substring}`.
|
||
- `EvidenceDoc` v1 resta per leggere il sorgente e per retro-compatibilità dei vecchi
|
||
canonizzati; `CanonicalEvidence` è un modello separato, non una sottoclasse.
|
||
- Validatore che garantisce `source_fingerprint` = sha256 del contenuto sorgente.
|
||
|
||
### 2. `harness/tht/evidence/lint.py` (nuovo, deterministico)
|
||
- `lint_source(path) -> list[Diagnostic]`: frontmatter rotto, `tier`/`status` non validi,
|
||
bullet Unicode, campi mancanti, `tables` non qualificate con schema, concetti senza
|
||
sinonimi, sezioni non canoniche per il `kind` atteso.
|
||
- Exit code 0 se nessun errore, 1 se warning, 2 se errori. Nessun LLM.
|
||
|
||
### 3. `harness/tht/evidence/canonicalize.py` (nuovo)
|
||
- `plan(sources, artifacts) -> CanonicalizePlan`: confronta fingerprint dei sorgenti con
|
||
i canonizzati esistenti, produce la lista dei file da (ri)canonizzare.
|
||
- `render_proposal(source, llm_completions) -> CanonicalEvidence`: assembla il
|
||
canonizzato da parsing deterministico + campi semantici forniti da Pi.
|
||
- `apply(plan, approved_ids) -> None`: scrive solo i canonizzati approvati in
|
||
`artifacts/evidence/`, atomico per file, mantiene la gerarchia per dominio.
|
||
- `diff(source, candidate) -> str`: diff markdown per il gate.
|
||
|
||
### 4. `harness/tht/vectorstore/records.py` (modifica)
|
||
- `evidence_records()`: metadata ora include `kind`, `applies_to`, `anchors` (non solo
|
||
`status/tier/tables/concepts`).
|
||
- Il `content` di ogni record resta title+body, ma per file con sezioni canoniche si
|
||
indicizza anche un record per sezione (id `evidence:<id>:<sezione>`) così il retrieval
|
||
può restringere per sezione oltre che per documento.
|
||
|
||
### 5. `harness/tht/search/__init__.py` + `cli/search_cmd.py` (modifica)
|
||
- `SearchResult` e `combined_search` accettano `applies_to` come filtro metadata.
|
||
- `tht search pack`: la sezione "Evidence rilevanti" ora proietta `kind` + sezione
|
||
canonica pertinente (non solo excerpt generico); usa `applies_to` per non mischiare
|
||
destinazioni.
|
||
- `tht search find --kind evidence --applies-to <dest>` per il retrieval mirato.
|
||
|
||
### 6. `harness/tht/cli/evidence_cmd.py` (modifica)
|
||
- Nuovi comandi:
|
||
- `tht evidence lint --source <path|dir>` — diagnostica deterministica.
|
||
- `tht evidence canonicalize plan --source-root <dir> --json` — piano di
|
||
(ri)canonizzazione.
|
||
- `tht evidence canonicalize apply --plan <file> --approved <ids> --json` — applica.
|
||
- `--json` sempre pristine (solo JSON su stdout), come da contratto di progetto.
|
||
|
||
### 7. `harness/.pi/extensions/tht-gate.js` (modifica)
|
||
- Nuovo tool `reviewer_evidence_batch`: riceve il piano + i diff aggregati e presenta un
|
||
multiselect per file (approva/rifiuta/richiedi modifica). Le scelte approvate vengono
|
||
persistite tramite `tht evidence canonicalize apply`.
|
||
- Aggiungere `tht evidence canonicalize apply` alla FORBIDDEN anti-bypass list (il
|
||
modello non può applicare canonizzazioni senza review).
|
||
|
||
### 8. Sessione di manutenzione Pi (nuova skill o sezione SKILL)
|
||
- Una skill dedicata `tht-evidence-canonicalize` (o un comando slash nel gate) descrive
|
||
la sessione di manutenzione: il modello legge i sorgenti marcati dal piano, propone i
|
||
campi semantici (`kind`, `applies_to`, `concepts` con sinonimi, `tables` con ruolo,
|
||
`anchors`) e li invia al `reviewer_evidence_batch`.
|
||
- Il gate è l'unico canale di review; il modello non scrive mai direttamente
|
||
`artifacts/evidence/`.
|
||
|
||
### 9. `harness/.pi/skills/tht-sessione/SKILL.md` (modifica)
|
||
- Aggiornare i punti F1/F4/F6-F7 dove il modello legge evidence: spiegare che l'evidence
|
||
canonica espone `kind`/`applies_to`/`anchors`, che gli `anchors` sono la fonte primaria
|
||
per il value-grounding in F4, e che le sezioni canoniche vanno citate per destinazione.
|
||
- Aggiungere il nuovo tool `reviewer_evidence_batch` alla lista dei tool disponibili.
|
||
|
||
## Migrazione del corpus (incrementale, v1+v2 convivono)
|
||
|
||
- `CanonicalEvidence` (v2) convive con `EvidenceDoc` (v1): `lint` segnala i non-canonici
|
||
ma nulla si rompe; `load_evidence_dir` carica entrambi.
|
||
- Convertire a lotti, partendo da `20-valori-enum` e `10-domini-clinici` (impatto
|
||
maggiore su F1/F4, anchors più ricchi).
|
||
- Il canonizzato derivato in `artifacts/evidence/` sostituisce progressivamente il file
|
||
sorgente mirrorato; finché un sorgente non è canonizzato, `extract` continua a
|
||
rispecchiare il v1 come oggi.
|
||
|
||
## Ordine di implementazione
|
||
|
||
1. `CanonicalEvidence` + `lint` (TDD, nessun LLM).
|
||
2. `canonicalize.py` (plan/apply/diff) + comandi CLI (TDD).
|
||
3. `records.py` + filtro `applies_to` in `search` + proiezione pack (TDD).
|
||
4. Gate `reviewer_evidence_batch` + skill manutenzione Pi.
|
||
5. Aggiornamento `SKILL.md` tht-sessione.
|
||
6. Conversione primo lotto (`enum` + `domain`) con review batch.
|
||
|
||
## Verifica
|
||
|
||
- `cd harness && .venv/bin/pytest -q` — test nuovi per model/lint/canonicalize/records/search.
|
||
- `node --test` sul gate per `reviewer_evidence_batch` e anti-bypass.
|
||
- Su un campione del corpus: `tht evidence lint` riporta i file rotti (frontmatter `--`,
|
||
bullet Unicode) senza crash; `tht evidence canonicalize plan --json` produce piano
|
||
corretto; `apply` con fingerprint invariato è no-op.
|
||
- `tht search pack "<domanda ablazione>"` mostra evidence con `kind`/sezione pertinente e
|
||
gli anchor presenti; `tht search find --kind evidence --applies-to schema_linking`
|
||
restituisce solo evidence pertinenti.
|
||
- Live: una sessione F4 su psd usa un anchor canonico per il value-grounding invece del
|
||
solo LSH (verificabile dal `reviewer_decide` con opzioni `value_grounded` provenienti
|
||
dall'evidence, non dal ranking LSH).
|
||
- `git diff --check` e typecheck/ruff puliti.
|