Files
ThothII/docs/plans/2026-08-18-evidence-canonica-design.md
T
2026-08-18 09:52:53 +02:00

10 KiB
Raw Blame History

Evidence canonica — struttura tipizzata per disambiguazione, schema linking e SQL

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)

---
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.