supervision : la dérive du modèle se regarde, pas seulement s'alerte (#117) #253

Merged
lenaic merged 2 commits from gabriel/117-tableau-derive into develop 2026-09-09 20:44:54 +00:00
Member

Ce que ça change

Le #117 a livré l'alerte de dérive et ses sept métriques, mais aucun panneau : aucun des huit tableaux de bord ne référençait ev_ia_*. La dérive était binaire et muette — soit une issue s'ouvrait, soit on ne voyait rien. Cette demande ajoute le tableau de bord modele, et un contrôle de banc pour que l'oubli ne se reproduise pas.

Relevé en préparant la mise en production.

Preuve

1. Le trou, avant. Aucun tableau de bord ne traçait la moindre métrique de dérive, sur origin/develop :

$ for f in $(git ls-tree --name-only origin/develop infra/compose/supervision/grafana/dashboards/); do
    echo "$(git show origin/develop:$f | grep -c ev_ia)  $f"; done
0  chaine-donnee.json
0  conteneurs.json
0  disponibilite.json
0  etl.json
0  hote.json
0  postgresql.json
0  socle.json
0  sorties-produit.json

2. Le banc refuse maintenant ce trou. Les quatre séries contrôlées sont celles dont un exploitant a besoin pour trancher une fois l'issue ouverte — le verdict, les deux écarts qui le déclenchent, l'âge de la mesure :

$ bash tests/ci/test-supervision.sh
...
ok     JSON valide : modele.json
ok     un tableau de bord trace ev_ia_derive_verdict
ok     un tableau de bord trace ev_ia_derive_entrees_deplacement_ecarts_types
ok     un tableau de bord trace ev_ia_derive_erreur_ratio
ok     un tableau de bord trace ev_ia_derive_derniere_mesure_timestamp_seconds

Supervision : tous les cas d'essai passent.   (87 cas)

Le contrôle ne nomme aucun fichier : un tableau de bord peut être renommé ou fusionné dans un autre, ce qui compte est que la série soit tracée quelque part.

3. Les bornes des panneaux sont celles du code, orange compris. L'orange est l'étage « surveillance » tel que derive._verdict le calcule : la moitié de la borne pour le déplacement et l'erreur, qui partent de zéro ; la moitié du CHEMIN jusqu'à la borne pour la dispersion, dont le repos vaut 1. Un orange posé à la moitié de la valeur (×1.0) serait tombé sur le repos et se serait allumé en permanence.

Écart Orange Rouge Source
Déplacement des entrées 1.5 σ 3.0 σ _DEPLACEMENT_MAX_DEFAUT
Dispersion des entrées ×1.5 ×2.0 _DISPERSION_MAX_DEFAUT
Erreur / promotion ×1.25 ×1.5 _ERREUR_MAX_DEFAUT
Couples évalués plancher 14 _COUPLES_MINIMUM_DEFAUT

Ce qui suit le code

  • docs/runbooks/ mis à jour : le runbook annonçait « dix règles » depuis le #228, sa vérification d'après déploiement attendait dix titres et sept tableaux de bord, et sa table du § 7 n'avait pas la ligne de la dérive.

Où regarder en priorité

Le sens des trous. Un trou dans une courbe veut dire « pas mesuré », jamais « stable » : spanNulls est à false partout et chaque description de panneau le redit. C'est la lecture qui coûte le plus cher de se tromper — c'est exactement le défaut que les deux derniers correctifs du #117 ont corrigé côté job.

Le groupement. Toutes les requêtes sont en max by (modele_version) : la référence est figée à la promotion, une nouvelle version est une nouvelle série et non la suite de la précédente.

Ce que cette demande ne fait PAS, et qui reste à faire avant le jury

Vérifié sur le serveur en préparant la mise en production, les deux points bloquants sont ailleurs :

  1. Le serveur tourne sur main (415af1a, PR #233), antérieur au merge du #238 : la passe horaire ne mesure aucune dérive et Grafana n'a chargé que 10 règles sur 11.
  2. Aucune version du registre MLflow ne porte la référence de dérive. Les versions 6, 7 et 8 n'ont ni entrees_moyenne_kw, ni entrees_ecart_type_kw, ni entrees_n ; l'alias production pointe la v6. Sans référence, aucun verdict n'est publié, et noDataState: OK rend l'alerte silencieuse — le filet « plus de mesure depuis 3 h » ne rattrape pas ce cas, puisqu'il faut qu'une première mesure réussie ait existé pour que la série vieillisse. Il faut donc réentraîner avec le nouveau code et promouvoir cette version.

Il reste aussi à supprimer /var/lib/node_exporter/textfile/ev_ia_derive.prom sur le serveur : un reliquat d'essai manuel du 09/09 à 14 h 59, à l'ancien schéma (ev_ia_derive_mesuree), que Prometheus sert encore.

## Ce que ça change Le #117 a livré l'alerte de dérive et ses sept métriques, mais aucun panneau : aucun des huit tableaux de bord ne référençait `ev_ia_*`. La dérive était binaire et muette — soit une issue s'ouvrait, soit on ne voyait rien. Cette demande ajoute le tableau de bord `modele`, et un contrôle de banc pour que l'oubli ne se reproduise pas. Relevé en préparant la mise en production. ## Preuve **1. Le trou, avant.** Aucun tableau de bord ne traçait la moindre métrique de dérive, sur `origin/develop` : ``` $ for f in $(git ls-tree --name-only origin/develop infra/compose/supervision/grafana/dashboards/); do echo "$(git show origin/develop:$f | grep -c ev_ia) $f"; done 0 chaine-donnee.json 0 conteneurs.json 0 disponibilite.json 0 etl.json 0 hote.json 0 postgresql.json 0 socle.json 0 sorties-produit.json ``` **2. Le banc refuse maintenant ce trou.** Les quatre séries contrôlées sont celles dont un exploitant a besoin pour trancher une fois l'issue ouverte — le verdict, les deux écarts qui le déclenchent, l'âge de la mesure : ``` $ bash tests/ci/test-supervision.sh ... ok JSON valide : modele.json ok un tableau de bord trace ev_ia_derive_verdict ok un tableau de bord trace ev_ia_derive_entrees_deplacement_ecarts_types ok un tableau de bord trace ev_ia_derive_erreur_ratio ok un tableau de bord trace ev_ia_derive_derniere_mesure_timestamp_seconds Supervision : tous les cas d'essai passent. (87 cas) ``` Le contrôle ne nomme aucun fichier : un tableau de bord peut être renommé ou fusionné dans un autre, ce qui compte est que la série soit tracée quelque part. **3. Les bornes des panneaux sont celles du code, orange compris.** L'orange est l'étage « surveillance » tel que `derive._verdict` le calcule : la moitié de la borne pour le déplacement et l'erreur, qui partent de zéro ; la moitié du CHEMIN jusqu'à la borne pour la dispersion, dont le repos vaut 1. Un orange posé à la moitié de la valeur (×1.0) serait tombé sur le repos et se serait allumé en permanence. | Écart | Orange | Rouge | Source | |---|---|---|---| | Déplacement des entrées | 1.5 σ | 3.0 σ | `_DEPLACEMENT_MAX_DEFAUT` | | Dispersion des entrées | ×1.5 | ×2.0 | `_DISPERSION_MAX_DEFAUT` | | Erreur / promotion | ×1.25 | ×1.5 | `_ERREUR_MAX_DEFAUT` | | Couples évalués | — | plancher 14 | `_COUPLES_MINIMUM_DEFAUT` | ## Ce qui suit le code - [x] `docs/runbooks/` mis à jour : le runbook annonçait « dix règles » depuis le #228, sa vérification d'après déploiement attendait dix titres et sept tableaux de bord, et sa table du § 7 n'avait pas la ligne de la dérive. ## Où regarder en priorité **Le sens des trous.** Un trou dans une courbe veut dire « pas mesuré », jamais « stable » : `spanNulls` est à `false` partout et chaque description de panneau le redit. C'est la lecture qui coûte le plus cher de se tromper — c'est exactement le défaut que les deux derniers correctifs du #117 ont corrigé côté job. **Le groupement.** Toutes les requêtes sont en `max by (modele_version)` : la référence est figée à la promotion, une nouvelle version est une nouvelle série et non la suite de la précédente. ## Ce que cette demande ne fait PAS, et qui reste à faire avant le jury Vérifié sur le serveur en préparant la mise en production, les deux points bloquants sont ailleurs : 1. **Le serveur tourne sur `main` (`415af1a`, PR #233)**, antérieur au merge du #238 : la passe horaire ne mesure aucune dérive et Grafana n'a chargé que 10 règles sur 11. 2. **Aucune version du registre MLflow ne porte la référence de dérive.** Les versions 6, 7 et 8 n'ont ni `entrees_moyenne_kw`, ni `entrees_ecart_type_kw`, ni `entrees_n` ; l'alias `production` pointe la v6. Sans référence, aucun verdict n'est publié, et `noDataState: OK` rend l'alerte silencieuse — le filet « plus de mesure depuis 3 h » ne rattrape pas ce cas, puisqu'il faut qu'une première mesure réussie ait existé pour que la série vieillisse. Il faut donc réentraîner avec le nouveau code **et** promouvoir cette version. Il reste aussi à supprimer `/var/lib/node_exporter/textfile/ev_ia_derive.prom` sur le serveur : un reliquat d'essai manuel du 09/09 à 14 h 59, à l'ancien schéma (`ev_ia_derive_mesuree`), que Prometheus sert encore.
supervision: la dérive du modèle s'alerte mais ne se regarde nulle part (#117)
All checks were successful
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 10s
Intégration / Terraform — format, validité et lint (pull_request) Successful in 16s
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 44s
Intégration / Checkov — audit de la configuration (pull_request) Successful in 19s
Intégration / Workflows — lint et audit de sécurité (pull_request) Successful in 17s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m49s
2660e51ea3
Le #117 a livré la sixième alerte d'exploitation et les sept métriques qui la
nourrissent, mais AUCUN panneau : aucun des huit tableaux de bord ne
référençait `ev_ia_*`. La dérive était donc binaire et muette — soit une issue
s'ouvrait dans la forge, soit on ne voyait rien.

Trois conséquences, et la deuxième est la plus gênante.

1. L'étage « surveillance » ne s'affichait nulle part. C'est pourtant lui qui
   justifie un verdict à trois valeurs plutôt que deux : il existe pour qu'on
   VOIE VENIR une dérive sans être réveillé. Sans graphe, il ne servait à rien.

2. L'annotation `action` de la règle demandait de comparer
   `entrees_deplacement_ecarts_types` et `erreur_ratio` — c'est le geste qui
   sépare « le parc a changé de régime, le modèle tient » de « le modèle
   décroche, il faut réentraîner ». Elle nommait deux séries qu'il fallait
   aller chercher à la main dans Explore, au moment précis où l'on vient
   d'être réveillé.

3. Rien ne montrait la surveillance elle-même s'arrêter. L'âge de la dernière
   mesure réussie déclenche l'alerte à 3 h, mais sa montée n'était visible
   qu'après coup.

Nouveau tableau de bord `modele` (uid `enervision-modele`), treize panneaux :
six indicateurs de tête (verdict, âges des deux horodatages, les trois
écarts), puis les courbes — verdict en marches, MAE récente contre MAE de
référence en kW, un graphe par écart avec sa borne en ligne tiretée, les
couples (prévu, réalisé) avec leur plancher de 14, et l'âge de la mesure.

LES BORNES DES PANNEAUX SONT CELLES DE `inference/config.py`, orange compris :
l'orange est l'étage « surveillance » tel que `derive._verdict` le calcule —
la moitié de la borne pour ce qui part de zéro, la moitié du CHEMIN jusqu'à la
borne pour la dispersion, dont le repos vaut 1. Un orange posé à la moitié de
la valeur serait tombé sous le repos et se serait allumé en permanence.

Les séries se lisent par `max by (modele_version)` : la référence est figée à
la promotion, une nouvelle version est une nouvelle série et non la suite de
la précédente. Un TROU dans une courbe veut dire « pas mesuré », jamais
« stable » — chaque description de panneau le dit, parce que c'est la lecture
qui coûte cher de se tromper.

Le banc de supervision exige désormais qu'un tableau de bord — n'importe
lequel, le contrôle ne nomme pas de fichier — trace les quatre séries dont on
a besoin pour trancher une fois l'issue ouverte. C'est ce qui empêche l'oubli
de se reproduire : il ne comptait jusqu'ici que les règles et leurs
destinataires.

Le runbook n'avait pas suivi non plus : il annonçait « dix règles » depuis le
#228, sa vérification d'après déploiement attendait dix titres et sept
tableaux de bord, et sa table du § 7 n'avait pas la ligne de la dérive.
gabriel self-assigned this 2026-09-09 19:18:03 +00:00
gabriel requested review from lenaic 2026-09-09 19:18:16 +00:00
lenaic approved these changes 2026-09-09 20:44:06 +00:00
Dismissed
Merge branch 'develop' into gabriel/117-tableau-derive
All checks were successful
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 10s
Intégration / Terraform — format, validité et lint (pull_request) Successful in 17s
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 45s
Intégration / Checkov — audit de la configuration (pull_request) Successful in 17s
Intégration / Workflows — lint et audit de sécurité (pull_request) Successful in 22s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 6m27s
6af9f5a039
lenaic approved these changes 2026-09-09 20:44:51 +00:00
lenaic merged commit 2491a73205 into develop 2026-09-09 20:44:54 +00:00
lenaic deleted branch gabriel/117-tableau-derive 2026-09-09 20:44:54 +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
g2/enervision!253
No description provided.