docs: publish MkDocs site on gh-pages
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
name: Publish documentation
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- gh-pages
|
||||
paths:
|
||||
- "docs/**"
|
||||
- "mkdocs.yml"
|
||||
- ".github/workflows/docs-pages.yml"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
concurrency:
|
||||
group: pages
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
|
||||
steps:
|
||||
- name: Checkout documentation source
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.x"
|
||||
cache: pip
|
||||
cache-dependency-path: docs/requirements.txt
|
||||
|
||||
- name: Install MkDocs dependencies
|
||||
run: python -m pip install -r docs/requirements.txt
|
||||
|
||||
- name: Build documentation
|
||||
# The repository intentionally links some docs to source files outside
|
||||
# docs/. MkDocs reports those as warnings, but they must not block Pages.
|
||||
run: mkdocs build
|
||||
|
||||
- name: Configure GitHub Pages
|
||||
uses: actions/configure-pages@v5
|
||||
|
||||
- name: Upload Pages artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: site
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
@@ -0,0 +1,235 @@
|
||||
# Gestione delle memory
|
||||
|
||||
Questo documento descrive l'organizzazione attuale delle memory nel workflow ThothII: modello concettuale, ciclo di vita, persistenza, ricerca semantica, gate di revisione, visualizzazione delle sessioni e principali limiti tecnici.
|
||||
|
||||
## Sintesi architetturale
|
||||
|
||||
Una memory è conoscenza di dominio riutilizzabile tra domande. Non è una copia dello schema-linking di una singola domanda.
|
||||
|
||||
```text
|
||||
F1: chiarimento di un concetto
|
||||
│
|
||||
▼
|
||||
decisione concept_clarified nel ledger della sessione
|
||||
│
|
||||
▼
|
||||
F8: il reviewer decide se promuoverla
|
||||
│
|
||||
├── registro globale registry.jsonl
|
||||
└── indice semantico pgvector
|
||||
│
|
||||
▼
|
||||
F2 di una sessione futura
|
||||
ricerca e proposta al reviewer
|
||||
```
|
||||
|
||||
L'invariante principale è `REUSABLE_TYPES = {"concept_clarified"}`: le sole memory generabili, salvabili, ricercabili e proponibili sono i concetti chiariti. Le decisioni `table_promoted`, `table_excluded`, `column_promoted` e analoghe restano decisioni locali alla domanda.
|
||||
|
||||
Implementazione principale: [harness/tht/memory.py](../harness/tht/memory.py:14) e [harness/tht/cli/memory_cmd.py](../harness/tht/cli/memory_cmd.py:368).
|
||||
|
||||
## I tre livelli della gestione
|
||||
|
||||
| Livello | Contenuto | Funzione |
|
||||
| --- | --- | --- |
|
||||
| Ledger della sessione | `concept_clarified`, `memory_promoted`, `memory_promotion_declined` | Audit e stato della singola sessione |
|
||||
| Registro globale | Record `mem-XXXX` in `registry.jsonl` | Archivio canonico attuale delle memory |
|
||||
| Indice pgvector | Embedding e metadati derivati dal registro | Ricerca semantica |
|
||||
|
||||
Il ledger contiene la provenienza e le decisioni umane. Il record globale contiene il testo riutilizzabile. L'indice vettoriale è una proiezione per la ricerca, non il posto in cui il workflow registra direttamente le decisioni.
|
||||
|
||||
## Che cosa può diventare una memory
|
||||
|
||||
Durante F1 il workflow registra i chiarimenti come decisioni `concept_clarified`. Un chiarimento può esprimere:
|
||||
|
||||
- definizioni di concetti clinici o organizzativi;
|
||||
- criteri di inclusione ed esclusione di una popolazione;
|
||||
- formule e metodi di calcolo;
|
||||
- interpretazioni temporali;
|
||||
- mapping verso tabelle e colonne specifiche;
|
||||
- significato di flag, codici o indicatori.
|
||||
|
||||
Il modello `MemoryRecord` contiene:
|
||||
|
||||
- `id`, ad esempio `mem-0001`;
|
||||
- timestamp, sessione e sequenza della decisione originale;
|
||||
- `type`;
|
||||
- `subject`;
|
||||
- `detail`;
|
||||
- `rationale`;
|
||||
- `question_context`;
|
||||
- `tables` e `concepts`.
|
||||
|
||||
Per le nuove memory il tipo è sempre `concept_clarified` e `tables` viene inizializzato vuoto. Una tabella o una colonna può essere citata dentro la spiegazione come mapping tecnico; non può essere il concetto autonomo della memory.
|
||||
|
||||
Esempio valido:
|
||||
|
||||
> Per ablazione si intende una procedura con `ablazione_transcatetere = TRUE`, conteggiata con `COUNT(DISTINCT cod_paz)` per anno.
|
||||
|
||||
Esempi non validi:
|
||||
|
||||
- `fact_cardioversione` come memory approvata;
|
||||
- `dim_time` come memory rifiutata;
|
||||
- una decisione “includi questa tabella” salvata per domande future.
|
||||
|
||||
La regola è documentata anche nella skill del workflow, in [harness/.pi/skills/tht-sessione/SKILL.md](../harness/.pi/skills/tht-sessione/SKILL.md:217).
|
||||
|
||||
## Promozione alla fine della sessione: F8
|
||||
|
||||
Alla fine del workflow il gate `reviewer_memory_promote` esegue una preview deterministica:
|
||||
|
||||
```text
|
||||
tht memory promote --session <id> --preview --json
|
||||
```
|
||||
|
||||
La preview:
|
||||
|
||||
1. legge le decisioni effettive della sessione;
|
||||
2. considera solo `concept_clarified`;
|
||||
3. scarta le decisioni già promosse;
|
||||
4. scarta le sequenze già rifiutate in F8;
|
||||
5. deduplica contenuti equivalenti;
|
||||
6. propone al massimo cinque candidati.
|
||||
|
||||
Il codice applica il filtro e la deduplica in [harness/tht/memory.py](../harness/tht/memory.py:199); il gate applica un ulteriore filtro difensivo in [harness/.pi/extensions/tht-gate.js](../harness/.pi/extensions/tht-gate.js:628).
|
||||
|
||||
Il reviewer vede un'unica checklist, preselezionata. Per ogni candidato:
|
||||
|
||||
- selezionato: viene eseguito `tht memory save-one` e poi viene registrato `memory_promoted`;
|
||||
- deselezionato: viene registrato `memory_promotion_declined`;
|
||||
- nessun candidato: F8 si chiude automaticamente.
|
||||
|
||||
Il marker `memory_promoted` usa `detail: seq:N`, cioè un riferimento alla decisione `concept_clarified` originale. Il flusso è in [harness/.pi/extensions/tht-gate.js](../harness/.pi/extensions/tht-gate.js:1691).
|
||||
|
||||
La promozione non viene eseguita in F2 e il modello non può inventare candidati F8. I comandi diretti di promozione sono inoltre protetti dal gate anti-bypass.
|
||||
|
||||
## Persistenza globale
|
||||
|
||||
### Registro JSONL
|
||||
|
||||
Il registro attuale è:
|
||||
|
||||
```text
|
||||
<artifacts>/memory/registry.jsonl
|
||||
```
|
||||
|
||||
La scrittura viene fatta tramite file temporaneo e `os.replace`, quindi la sostituzione del registro è atomica. L'idempotenza della promozione è basata sulla coppia `session_id + decision_seq`: la stessa decisione della stessa sessione non genera due record globali.
|
||||
|
||||
### pgvector
|
||||
|
||||
Dopo la promozione, `save-one` costruisce un solo `VectorRecord` e lo invia all'indice pgvector. Il testo indicizzato include:
|
||||
|
||||
- tipo e soggetto;
|
||||
- dettaglio;
|
||||
- motivazione;
|
||||
- domanda di contesto;
|
||||
- eventuali concetti e mapping.
|
||||
|
||||
Il record vettoriale usa l'id `memory:mem-XXXX`, mentre i metadati conservano `subject`, `detail`, `rationale`, `tables` e `concepts`. L'hash SHA-256 del contenuto impedisce di ricalcolare embedding e upsert quando il testo non è cambiato.
|
||||
|
||||
Il comportamento è implementato in [harness/tht/memory.py](../harness/tht/memory.py:253) e [harness/tht/memory.py](../harness/tht/memory.py:305).
|
||||
|
||||
### Fonte canonica attuale
|
||||
|
||||
Oggi il registro JSONL è ancora la fonte canonica applicativa e pgvector è l'indice derivato. Il commento iniziale di [memory_cmd.py](../harness/tht/cli/memory_cmd.py:1) segnala un debito tecnico: l'architettura futura prevista sarebbe usare direttamente il vector DB come archivio unico, ma questa migrazione non è ancora completata.
|
||||
|
||||
## Riutilizzo in F2
|
||||
|
||||
In una sessione futura F2 esegue:
|
||||
|
||||
```text
|
||||
tht memory search "<domanda>" --session <id> --json
|
||||
```
|
||||
|
||||
Il comando:
|
||||
|
||||
1. crea l'embedding della domanda;
|
||||
2. cerca nel vector store solo record `kind=memory`;
|
||||
3. risolve ogni hit nel registro JSONL tramite il suo `ref`;
|
||||
4. scarta record assenti dal registro;
|
||||
5. scarta qualsiasi tipo diverso da `concept_clarified`;
|
||||
6. esclude le memory già decise nella sessione corrente;
|
||||
7. restituisce i risultati ordinati per similarità.
|
||||
|
||||
L'implementazione è in [harness/tht/cli/memory_cmd.py](../harness/tht/cli/memory_cmd.py:368).
|
||||
|
||||
Le memory non vengono mai applicate automaticamente. Il modello deve presentarle in un'unica scelta `reviewer_decide`:
|
||||
|
||||
- una memory selezionata viene registrata come nuovo `concept_clarified` nella sessione corrente;
|
||||
- il rationale deve citare l'id originale `mem-XXXX`;
|
||||
- una memory deselezionata significa “non applicarla ora”, non “cancellarla globalmente”.
|
||||
|
||||
Se F2 viene riaperta, una memory deselezionata può quindi essere proposta di nuovo. `memory_rejected` resta supportato per decisioni e sessioni legacy, ma non rappresenta il normale comportamento della deselezione F2 attuale.
|
||||
|
||||
## Ledger effettivo, rollback e riaperture
|
||||
|
||||
Il ledger è append-only. Riaperture e ritrattazioni non cancellano le righe precedenti; cambiano però quali decisioni sono effettive.
|
||||
|
||||
Gli helper memory usano `effective_decisions()` per:
|
||||
|
||||
- escludere decisioni ritirate;
|
||||
- ignorare decisioni appartenenti a fasi diventate stale dopo un rollback;
|
||||
- impedire la promozione di chiarimenti non più validi.
|
||||
|
||||
La vista effettiva è definita in [harness/tht/phase.py](../harness/tht/phase.py:82).
|
||||
|
||||
## Visualizzazione nel riepilogo della sessione
|
||||
|
||||
La sezione “Memories” del riepilogo viene proiettata a runtime dal ledger della sessione; non è una copia diretta del registro globale.
|
||||
|
||||
La proiezione:
|
||||
|
||||
- mostra prima le memory approved;
|
||||
- mostra poi le memory declined;
|
||||
- risolve `seq:N` verso il `concept_clarified` originale;
|
||||
- nasconde marker il cui record originale non è `concept_clarified`;
|
||||
- nasconde soggetti che corrispondono a tabelle dello schema-linking;
|
||||
- nasconde soggetti autonomi con forma `fact_*` o `dim_*`;
|
||||
- mantiene invece i riferimenti a tabelle e campi quando fanno parte della spiegazione concettuale.
|
||||
|
||||
La logica è in [harness/tht/session/store.py](../harness/tht/session/store.py:237). Il rendering frontend usa una lista strutturata e tratta `subject`, `detail` e `rationale` come Markdown, evitando di mostrare il markdown grezzo.
|
||||
|
||||
La trasformazione avviene in lettura: anche le sessioni storiche vengono organizzate con il layout e i filtri correnti senza riscrivere gli artefatti originali.
|
||||
|
||||
## Invarianti applicate
|
||||
|
||||
Le protezioni sono distribuite su più confini:
|
||||
|
||||
1. `REUSABLE_TYPES` nel core Python;
|
||||
2. filtro del comando `memory search`;
|
||||
3. filtro della preview F8;
|
||||
4. filtro e deduplica nel gate Pi;
|
||||
5. esclusione delle table-memory nella proiezione UI.
|
||||
|
||||
Questo evita che una singola modifica al prompt o a un solo componente reintroduca le tabelle come memory.
|
||||
|
||||
## Limiti e rischi residui
|
||||
|
||||
### Registro e indice non sono una singola transazione
|
||||
|
||||
Il salvataggio segue sostanzialmente questa sequenza:
|
||||
|
||||
```text
|
||||
registro JSONL → pgvector → marker memory_promoted nel ledger
|
||||
```
|
||||
|
||||
Se pgvector non è disponibile, il registro può contenere una memory non ancora ricercabile; il comando segnala che sarà necessario reindicizzare.
|
||||
|
||||
Se il marker del ledger fallisce dopo il salvataggio nel vector DB, la memory può risultare globalmente presente ma senza audit completo nella sessione. Il gate restituisce un comando di recupero manuale.
|
||||
|
||||
### Limite di cinque candidati
|
||||
|
||||
F8 propone al massimo cinque memory. Se una sessione produce più di cinque concetti validi, gli elementi eccedenti non vengono mostrati e la sessione può essere finalizzata senza promuoverli.
|
||||
|
||||
### Deduplica non globale
|
||||
|
||||
La deduplica impedisce duplicati nella stessa proposta e l'idempotenza impedisce di ripromuovere la stessa decisione. Non esiste però una fusione globale di due memory semanticamente simili provenienti da sessioni diverse.
|
||||
|
||||
### Vecchi record fisici
|
||||
|
||||
Vecchi record `table_promoted` o `table_excluded` possono ancora esistere in artefatti o indici storici. Il codice attuale li rende non riutilizzabili filtrandoli per tipo e non li mostra nella proiezione delle sessioni. La loro eventuale rimozione fisica dal vector DB resta un'attività di bonifica separata.
|
||||
|
||||
## Valutazione finale
|
||||
|
||||
La gestione attuale è coerente con il requisito funzionale: una memory è una conoscenza concettuale riutilizzabile, non una scelta di schema-linking.
|
||||
|
||||
La parte più solida è la difesa multilivello del tipo `concept_clarified`. Il principale debito tecnico riguarda invece la convivenza del registro JSONL con pgvector e l'assenza di una transazione unica tra archivio globale, indice semantico e ledger della sessione.
|
||||
@@ -48,6 +48,7 @@ nav:
|
||||
- ThothII (Documentazione Tecnica):
|
||||
- Panoramica Architettura: architecture/overview.md
|
||||
- Installazione Docker (4 contesti): installazione-docker-4-contesti.md
|
||||
- Gestione delle memory: gestione-memory.md
|
||||
- Specifiche di Design:
|
||||
- Architettura ThothII: superpowers/specs/2026-06-25-thothii-architecture-design.md
|
||||
- Backend: superpowers/specs/2026-06-27-backend-design.md
|
||||
|
||||
Reference in New Issue
Block a user