Findings layer: contenitori tipizzati e audit della risposta per /correlation #3

Merged
francesco merged 2 commits from findings-layer into develop 2026-08-14 17:54:18 +00:00

Findings layer — contenitori tipizzati e audit della risposta

Proposta, non un cambio di comportamento: additiva, stdlib pura, niente si
attiva da solo. Serve a chiudere il secondo fallimento, dopo quello aritmetico.

Il problema

La MR precedente ha tolto al modello l'ARITMETICA: il coefficiente lo calcola
il motore, il modello lo cita. Resta però l'INQUADRAMENTO. Con numeri veri, il
modello decide comunque cosa mettere in apertura, cosa seppellire e cosa
significhi un coefficiente — e decide male, perché giudicare l'ordine di
grandezza è esattamente ciò in cui i modelli sono peggiori, mentre scegliere e
ordinare fra alternative date è ciò in cui sono bravi.

Scrivere le frasi in Python non è la soluzione: la prosa preconfezionata è
per-caso, cresce senza limite e soprattutto non può sapere cosa è stato
CHIESTO. La rilevanza non è una proprietà del risultato, è una relazione fra
risultato e domanda.

Quindi: il motore calcola la SALIENZA (quanto è grande, quanto è preciso,
supera una soglia) — calcolabile e testabile; il modello inferisce la
RILEVANZA (quale di questi risponde a QUESTA domanda, in che ordine).

Come

Ogni verdetto diventa uno o più Finding: il plain del motore, i numeri che
autorizza, la salienza (dimensioni separate, mai mescolate in un punteggio
unico), e i due campi portanti — constraint (cosa NON può essere affermato)
e would_change (cosa cambierebbe la risposta). I contenitori viaggiano in
card["findings"], il campo instruction esistente è esteso, nulla è rimosso.

audit() verifica una risposta contro i suoi contenitori, in due zone:

CITATA — i plain dei motori, testuali. Possono dire tutto, comprese le
negazioni causali: le ha scritte il motore, sono già vere.
LIBERA — la prosa del modello. Qui l'intero vocabolario di attribuzione è
rifiutato, SENZA eccezione per la negazione.

Una versione precedente provava a verificare il SIGNIFICATO con il match di
stringhe e falliva in entrambe le direzioni sui transcript reali: una negazione
contiene le parole di ciò che nega («non è evidenza di una sorgente comune»
veniva segnalata), mentre una parafrasi passava indisturbata («la cokeria è
all'origine di entrambi»). Nessuna lista di frasi risolve. Si fa quindi con il
linguaggio causale ciò che i motori hanno fatto con l'aritmetica: si toglie la
possibilità invece di controllare il risultato.

Conseguenza voluta: a volte il controllo vieta una frase vera. È la direzione
giusta in cui sbagliare — un verificatore che fallisce verso il silenzio è
difendibile, uno che fallisce verso l'affermazione è inutile.

Cosa è verificato, e cosa no

Verificato (21 invarianti offline, examples/findings_test.py):

  • nessun numero non autorizzato passa — compreso il caso di luglio 2026:
    «Pearson r ≈ 0,72 … indica una sorgente comune» viene ora intercettato su
    entrambi gli assi, senza che nessuno legga;
  • i numeri delle fonti recuperate sono autorizzati (sources=), altrimenti
    il controllo segnalerebbe ogni cifra vera citata da E-PRTR;
  • gli adapter sollevano un errore se una chiave del motore cambia nome:
    meglio rumorosi che silenziosi. In produzione l'errore finisce in
    findings_error nel payload, mai in un tool rotto per il lettore;
  • novelty resta None ovunque: richiede una linea di base storica, e
    inventare un punteggio sarebbe esattamente il peccato che questo modulo
    esiste per impedire.

NON verificato: che i contenitori migliorino davvero l'inquadramento. Nessun
modello è ancora stato eseguito contro questo payload. È un'ipotesi, e va
misurata prima di essere creduta.

Proposta: shadow mode

audit() non può girare qui: il server MCP passa i dati AL modello e non vede
mai la prosa che torna. Il punto di innesto è l'assistant, dopo la generazione.

Primo passo suggerito, rischio zero: chiamare audit(), scrivere
audit_log_line() nel log, non cambiare nulla di ciò che il lettore vede. Il
backend è pensato per la redazione, quindi un umano è già nel ciclo. Dopo
qualche decina di domande reali il log dice il tasso di segnalazione e quale
asse scatta — e la decisione se applicarlo (rigenerare / bloccare / segnalare
in redazione) si prende su dati invece che su fiducia.

Cosa NON c'è in questa MR

Il collegamento di audit() nell'assistant. È una scelta tua e sta in un altro
repo: qui c'è la libreria pronta e i contenitori nel payload.

File

findings.py nuovo, radice, accanto ai motori. stdlib pura.
domains/correlation.py import, helper _attach_findings, due chiamate,
_QUOTE_RULE esteso (nulla rimosso)
examples/findings_test.py nuovo, 21 invarianti, offline
CLAUDE.md / README.md una riga ciascuno

examples/correlation_endpoints_test.py continua a passare tutte le invarianti,
live comprese.

## Findings layer — contenitori tipizzati e audit della risposta Proposta, non un cambio di comportamento: additiva, stdlib pura, niente si attiva da solo. Serve a chiudere il secondo fallimento, dopo quello aritmetico. ### Il problema La MR precedente ha tolto al modello l'ARITMETICA: il coefficiente lo calcola il motore, il modello lo cita. Resta però l'INQUADRAMENTO. Con numeri veri, il modello decide comunque cosa mettere in apertura, cosa seppellire e cosa significhi un coefficiente — e decide male, perché giudicare l'ordine di grandezza è esattamente ciò in cui i modelli sono peggiori, mentre scegliere e ordinare fra alternative date è ciò in cui sono bravi. Scrivere le frasi in Python non è la soluzione: la prosa preconfezionata è per-caso, cresce senza limite e soprattutto non può sapere cosa è stato CHIESTO. La rilevanza non è una proprietà del risultato, è una relazione fra risultato e domanda. Quindi: il motore calcola la SALIENZA (quanto è grande, quanto è preciso, supera una soglia) — calcolabile e testabile; il modello inferisce la RILEVANZA (quale di questi risponde a QUESTA domanda, in che ordine). ### Come Ogni verdetto diventa uno o più `Finding`: il `plain` del motore, i numeri che autorizza, la salienza (dimensioni separate, mai mescolate in un punteggio unico), e i due campi portanti — `constraint` (cosa NON può essere affermato) e `would_change` (cosa cambierebbe la risposta). I contenitori viaggiano in `card["findings"]`, il campo `instruction` esistente è esteso, nulla è rimosso. `audit()` verifica una risposta contro i suoi contenitori, in due zone: CITATA — i `plain` dei motori, testuali. Possono dire tutto, comprese le negazioni causali: le ha scritte il motore, sono già vere. LIBERA — la prosa del modello. Qui l'intero vocabolario di attribuzione è rifiutato, SENZA eccezione per la negazione. Una versione precedente provava a verificare il SIGNIFICATO con il match di stringhe e falliva in entrambe le direzioni sui transcript reali: una negazione contiene le parole di ciò che nega («non è evidenza di una sorgente comune» veniva segnalata), mentre una parafrasi passava indisturbata («la cokeria è all'origine di entrambi»). Nessuna lista di frasi risolve. Si fa quindi con il linguaggio causale ciò che i motori hanno fatto con l'aritmetica: si toglie la possibilità invece di controllare il risultato. Conseguenza voluta: a volte il controllo vieta una frase vera. È la direzione giusta in cui sbagliare — un verificatore che fallisce verso il silenzio è difendibile, uno che fallisce verso l'affermazione è inutile. ### Cosa è verificato, e cosa no Verificato (21 invarianti offline, examples/findings_test.py): - nessun numero non autorizzato passa — compreso il caso di luglio 2026: «Pearson r ≈ 0,72 … indica una sorgente comune» viene ora intercettato su entrambi gli assi, senza che nessuno legga; - i numeri delle fonti recuperate sono autorizzati (`sources=`), altrimenti il controllo segnalerebbe ogni cifra vera citata da E-PRTR; - gli adapter sollevano un errore se una chiave del motore cambia nome: meglio rumorosi che silenziosi. In produzione l'errore finisce in `findings_error` nel payload, mai in un tool rotto per il lettore; - `novelty` resta None ovunque: richiede una linea di base storica, e inventare un punteggio sarebbe esattamente il peccato che questo modulo esiste per impedire. NON verificato: che i contenitori migliorino davvero l'inquadramento. Nessun modello è ancora stato eseguito contro questo payload. È un'ipotesi, e va misurata prima di essere creduta. ### Proposta: shadow mode `audit()` non può girare qui: il server MCP passa i dati AL modello e non vede mai la prosa che torna. Il punto di innesto è l'assistant, dopo la generazione. Primo passo suggerito, rischio zero: chiamare `audit()`, scrivere `audit_log_line()` nel log, non cambiare nulla di ciò che il lettore vede. Il backend è pensato per la redazione, quindi un umano è già nel ciclo. Dopo qualche decina di domande reali il log dice il tasso di segnalazione e quale asse scatta — e la decisione se applicarlo (rigenerare / bloccare / segnalare in redazione) si prende su dati invece che su fiducia. ### Cosa NON c'è in questa MR Il collegamento di `audit()` nell'assistant. È una scelta tua e sta in un altro repo: qui c'è la libreria pronta e i contenitori nel payload. ### File findings.py nuovo, radice, accanto ai motori. stdlib pura. domains/correlation.py import, helper _attach_findings, due chiamate, _QUOTE_RULE esteso (nulla rimosso) examples/findings_test.py nuovo, 21 invarianti, offline CLAUDE.md / README.md una riga ciascuno examples/correlation_endpoints_test.py continua a passare tutte le invarianti, live comprese.
francesco merged commit ff4e630504 into develop 2026-08-14 17:54:18 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
peacelink/mcp-peacedata!3
No description provided.