Dominio correlation: motori deterministici di correlazione e intervento #2

Open
NicolasMoerynck wants to merge 2 commits from nicolasmoerynck/correlation-engine into develop AGit

Cosa fa

Sposta il calcolo statistico fuori dal modello linguistico. Chiesto «benzene e NO₂ sono
correlati?», l'assistente dichiarava un coefficiente che non può calcolare (r=0.72
dichiarato; ricalcolato sugli stessi dati ISPRA: 0.569 grezzo, 0.334 tolto il ciclo
giornaliero, N efficace 53.5 — dettagli nel docstring di domains/correlation.py).
Questo dominio dà al modello due tool che i numeri li calcolano davvero e che, quando i
dati non permettono una risposta, restituiscono un rifiuto esplicito invece di un numero.

Principio di progetto: i tool scaricano da sé i dati (ISPRA WFS / EEA parquet) — il
modello passa solo selettori (stazione, inquinante, finestra), mai valori. Un errore del
modello può produrre una domanda sbagliata, mai un dato sbagliato.

Tool esposti su /correlation

  • correlate_pollutants — Spearman come stima primaria, N efficace corretto per
    l'autocorrelazione (Bretherton 1999), correlazione ricontrollata dopo la rimozione del
    ciclo giornaliero e di un trend comune. Verdetti: reportable, confounded_by_cycle,
    confounded_by_trend, identical_source_trivial, insufficient_overlap,
    cannot_compute — ciascuno con una frase plain in italiano che l'assistente deve
    citare testualmente.
  • intervention_effect — «X è cambiato alla data D?»: regressione segmentata con serie
    di controllo opzionale (difference-in-differences) ed errori Newey-West. Senza
    controllo il risultato è marcato descrittivo, mai causale.

File

File Ruolo
correlation_engine.py motore correlazione — puro, solo stdlib
intervention.py motore intervento — puro, solo stdlib
domains/correlation.py dominio FastMCP montato su /correlation
examples/correlation_endpoints_test.py 16 invarianti (7 offline, 9 contro i servizi vivi)
server.py, README.md, CLAUDE.md montaggio e documentazione

Primo commit, separato — due indurimenti piccoli ai domini esistenti:

  • Guardia sentinella -9999 in domains/ispra_air_quality.py (3 righe). Oggi quel
    canale omette le ore invalidate invece di segnalarle, quindi la guardia non scatta
    mai — ma la sorgente ha già cambiato comportamento senza preavviso (aprile 2026):
    meglio fallire rumorosi che in silenzio.
  • Diagnostica di fetch ASCII-safe ( diventa -> nelle print di ispra ed eea).
    Trovato rilanciando i test il 6 agosto su Windows: con stdout non-UTF-8 (pipe,
    cp1252) la print sollevava UnicodeEncodeError dentro il tool e l'errore riemergeva
    come un finto cannot_compute. Sul server Linux non può manifestarsi — ma meglio
    togliere la mina.

Test e auto-audit

  • 16/16 invarianti verdi al 2026-08-06 in ambiente pulito (fastmcp 3.4.4, httpx 0.28.1,
    pyarrow 25.0.0; su Windows serve anche tzdata per ZoneInfo("Europe/Rome")).
  • Il reporter del test ora regge i verdetti di rifiuto senza crashare: prima un rifiuto
    legittimo su dati vivi produceva un KeyError nel report invece di un [FAIL] pulito.
  • Il modulo di intervento è stato fatto girare su rumore puro, senza effetti da trovare:
    l'inferenza Newey-West sovra-dichiarava (falsi positivi 8–32% a seconda della
    persistenza). Mitigazione inclusa: verdetto ristretto a p<0.01 più rifiuto esplicito
    inference_unreliable quando i residui restano troppo autocorrelati. Sul nullo, con
    finestre ≥1 anno, i falsi positivi tornano all'1.8–5%. La simulazione è nel test,
    eseguibile.

Fuori da questa MR (lato assistant)

  1. Espansione del sub-path /correlation in _load_mcp_servers
  2. Intent + research profile (UI «Gestione intenti» o postgres/10-intents.sql)
  3. Regola di sistema: se si chiede una correlazione o un nesso causale, chiamare i tool
    di correlation e riportare verdict e plain testualmente — mai r, p o nessi
    causali riformulati dal modello.
## Cosa fa Sposta il calcolo statistico fuori dal modello linguistico. Chiesto «benzene e NO₂ sono correlati?», l'assistente dichiarava un coefficiente che non può calcolare (r=0.72 dichiarato; ricalcolato sugli stessi dati ISPRA: 0.569 grezzo, 0.334 tolto il ciclo giornaliero, N efficace 53.5 — dettagli nel docstring di `domains/correlation.py`). Questo dominio dà al modello due tool che i numeri li calcolano davvero e che, quando i dati non permettono una risposta, restituiscono un rifiuto esplicito invece di un numero. Principio di progetto: **i tool scaricano da sé i dati** (ISPRA WFS / EEA parquet) — il modello passa solo selettori (stazione, inquinante, finestra), mai valori. Un errore del modello può produrre una domanda sbagliata, mai un dato sbagliato. ## Tool esposti su `/correlation` - `correlate_pollutants` — Spearman come stima primaria, N efficace corretto per l'autocorrelazione (Bretherton 1999), correlazione ricontrollata dopo la rimozione del ciclo giornaliero e di un trend comune. Verdetti: `reportable`, `confounded_by_cycle`, `confounded_by_trend`, `identical_source_trivial`, `insufficient_overlap`, `cannot_compute` — ciascuno con una frase `plain` in italiano che l'assistente deve citare testualmente. - `intervention_effect` — «X è cambiato alla data D?»: regressione segmentata con serie di controllo opzionale (difference-in-differences) ed errori Newey-West. Senza controllo il risultato è marcato descrittivo, mai causale. ## File | File | Ruolo | |---|---| | `correlation_engine.py` | motore correlazione — puro, solo stdlib | | `intervention.py` | motore intervento — puro, solo stdlib | | `domains/correlation.py` | dominio FastMCP montato su `/correlation` | | `examples/correlation_endpoints_test.py` | 16 invarianti (7 offline, 9 contro i servizi vivi) | | `server.py`, `README.md`, `CLAUDE.md` | montaggio e documentazione | Primo commit, separato — due indurimenti piccoli ai domini esistenti: - **Guardia sentinella -9999** in `domains/ispra_air_quality.py` (3 righe). Oggi quel canale omette le ore invalidate invece di segnalarle, quindi la guardia non scatta mai — ma la sorgente ha già cambiato comportamento senza preavviso (aprile 2026): meglio fallire rumorosi che in silenzio. - **Diagnostica di fetch ASCII-safe** (`→` diventa `->` nelle print di ispra ed eea). Trovato rilanciando i test il 6 agosto su Windows: con stdout non-UTF-8 (pipe, cp1252) la print sollevava UnicodeEncodeError dentro il tool e l'errore riemergeva come un finto `cannot_compute`. Sul server Linux non può manifestarsi — ma meglio togliere la mina. ## Test e auto-audit - 16/16 invarianti verdi al 2026-08-06 in ambiente pulito (fastmcp 3.4.4, httpx 0.28.1, pyarrow 25.0.0; su Windows serve anche `tzdata` per `ZoneInfo("Europe/Rome")`). - Il reporter del test ora regge i verdetti di rifiuto senza crashare: prima un rifiuto legittimo su dati vivi produceva un KeyError nel report invece di un `[FAIL]` pulito. - Il modulo di intervento è stato fatto girare su rumore puro, senza effetti da trovare: l'inferenza Newey-West sovra-dichiarava (falsi positivi 8–32% a seconda della persistenza). Mitigazione inclusa: verdetto ristretto a p<0.01 più rifiuto esplicito `inference_unreliable` quando i residui restano troppo autocorrelati. Sul nullo, con finestre ≥1 anno, i falsi positivi tornano all'1.8–5%. La simulazione è nel test, eseguibile. ## Fuori da questa MR (lato assistant) 1. Espansione del sub-path `/correlation` in `_load_mcp_servers` 2. Intent + research profile (UI «Gestione intenti» o `postgres/10-intents.sql`) 3. Regola di sistema: se si chiede una correlazione o un nesso causale, chiamare i tool di `correlation` e riportare `verdict` e `plain` testualmente — mai r, p o nessi causali riformulati dal modello.
Sentinel: the ISPRA channel omits invalidated hours instead of flagging them, so -9999 never fires today - but the source has changed behaviour without notice before (April 2026): fail loud if it ever leaks. Diagnostics: print() with a U+2192 arrow raises UnicodeEncodeError on non-UTF-8 stdout (Windows pipes) and surfaced as a bogus cannot_compute refusal; ASCII '->' behaves identically everywhere.
Two tools (correlate_pollutants, intervention_effect) that fetch their own data - the model passes only selectors, never values. Refusals are first-class verdicts with a plain-language sentence to quote verbatim. Engines are stdlib-only. 16 invariants in examples/correlation_endpoints_test.py (7 offline, 9 live), green 2026-08-06.
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin +refs/pull/2/head:nicolasmoerynck/correlation-engine
git switch nicolasmoerynck/correlation-engine

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch develop
git merge --no-ff nicolasmoerynck/correlation-engine
git switch nicolasmoerynck/correlation-engine
git rebase develop
git switch develop
git merge --ff-only nicolasmoerynck/correlation-engine
git switch nicolasmoerynck/correlation-engine
git rebase develop
git switch develop
git merge --no-ff nicolasmoerynck/correlation-engine
git switch develop
git merge --squash nicolasmoerynck/correlation-engine
git switch develop
git merge --ff-only nicolasmoerynck/correlation-engine
git switch develop
git merge nicolasmoerynck/correlation-engine
git push origin develop
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
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!2
No description provided.