[EF-08] Service d'inférence : prévision servie et signal de dépassement #37

Closed
opened 2026-09-01 10:35:25 +00:00 by lenaic · 4 comments
Owner

Exigence couverte

EF-07, EF-08, ENF-04

Épreuve servie

EC06 · IA et automatisation

Charge estimée

2 j.h

Ce qu'on veut obtenir

Servir en ligne la prévision H+1 du modèle promu et signaler un dépassement
prévisionnel de la capacité d'un site avant l'heure concernée.
/!\ du nouveau a été ajouté au contenu du ticket cf https://10.105.200.41/g2/enervision/issues/37#issuecomment-3254

Critères d'acceptation

  • Le job horaire charge le modèle promu depuis MLflow et écrit une ligne par site dans public.prevision. L'API sert cette table sous 200 ms, sans toucher MLflow sur le chemin de requête. Une prévision plus vieille que l'heure qu'elle vise n'est pas servie telle quelle : l'API la refuse ou la marque périmée.
  • La prévision H+1 est servie par site, avec sa référence de comparaison (EF-07).
  • Chaque prédiction est journalisée avec son entrée, les retards t−1 à t−24, et l'identifiant du modèle qui l'a produite. L'entrée va au journal du job, comme le collecteur écrit le sien, et à l'exécution MLflow côté entraînement. Elle ne va pas dans public.prevision : la table est une hypertable comprimée à une ligne par site et par heure, et vingt-quatre flottants de plus y seraient payés sur chaque lecture du tableau de bord pour une donnée que personne n'affiche.
  • Un dépassement de la capacité déclarée d'un site déclenche un signal avant l'heure concernée. C'est le job qui porte l'antériorité : à l'émission en ligne, il refuse d'écrire une prévision dont l'instant visé est déjà passé et le dit dans son journal. Le contrat PrevisionH1 ne la vérifie volontairement pas, sinon aucun rejeu ne serait possible, et le #36 a besoin du rejeu pour constituer trois jours de comparaison sans attendre trois jours. Un rattrapage se reconnaît à ce que son emise_le suit son horodatage.

Comment on le vérifie

Tests tests/unit/inference/, tests/integration/inference/
Commande pytest tests/unit/inference tests/integration/inference
Preuve un cas de dépassement déclenché sur données réelles, avec l'horodatage du signal, et le relevé de latence sur la route de l'API, pas sur le job.

Hors périmètre

  • Le suivi dans le temps de l'écart prévu / réalisé et la détection de dérive : #117 (Portée/Post-jury).
  • L'écran qui affiche l'alerte : vue site de #24, écran dédié #118.
  • Le réentraînement automatique.
### Exigence couverte EF-07, EF-08, ENF-04 ### Épreuve servie EC06 · IA et automatisation ### Charge estimée 2 j.h ### Ce qu'on veut obtenir Servir en ligne la prévision H+1 du modèle promu et signaler un dépassement prévisionnel de la capacité d'un site avant l'heure concernée. /!\ du nouveau a été ajouté au contenu du ticket cf https://10.105.200.41/g2/enervision/issues/37#issuecomment-3254 ### Critères d'acceptation - [ ] Le job horaire charge le modèle promu depuis MLflow et écrit une ligne par site dans public.prevision. L'API sert cette table sous 200 ms, sans toucher MLflow sur le chemin de requête. Une prévision plus vieille que l'heure qu'elle vise n'est pas servie telle quelle : l'API la refuse ou la marque périmée. - [ ] La prévision H+1 est servie par site, avec sa référence de comparaison (EF-07). - [ ] Chaque prédiction est journalisée avec son entrée, les retards t−1 à t−24, et l'identifiant du modèle qui l'a produite. L'entrée va au journal du job, comme le collecteur écrit le sien, et à l'exécution MLflow côté entraînement. Elle ne va pas dans public.prevision : la table est une hypertable comprimée à une ligne par site et par heure, et vingt-quatre flottants de plus y seraient payés sur chaque lecture du tableau de bord pour une donnée que personne n'affiche. - [ ] Un dépassement de la capacité déclarée d'un site déclenche un signal avant l'heure concernée. C'est le job qui porte l'antériorité : à l'émission en ligne, il refuse d'écrire une prévision dont l'instant visé est déjà passé et le dit dans son journal. Le contrat PrevisionH1 ne la vérifie volontairement pas, sinon aucun rejeu ne serait possible, et le #36 a besoin du rejeu pour constituer trois jours de comparaison sans attendre trois jours. Un rattrapage se reconnaît à ce que son emise_le suit son horodatage. ### Comment on le vérifie Tests tests/unit/inference/, tests/integration/inference/ Commande pytest tests/unit/inference tests/integration/inference Preuve un cas de dépassement déclenché sur données réelles, avec l'horodatage du signal, et le relevé de latence sur la route de l'API, pas sur le job. ### Hors périmètre - Le suivi dans le temps de l'écart prévu / réalisé et la détection de dérive : #117 (Portée/Post-jury). - L'écran qui affiche l'alerte : vue site de #24, écran dédié #118. - Le réentraînement automatique.
florian added this to the EnerVision project 2026-09-01 12:11:53 +00:00
Member

Repriorisation du 03/09 : réduit dans la fenêtre à prévision H+1 + signal de dépassement (EF-07, EF-08). La détection de dérive est sortie et suivie dans #117, hors fenêtre (Portée/Post-jury). Voir docs/BACKLOG.md et docs/PRD.md §8.

Repriorisation du 03/09 : réduit dans la fenêtre à **prévision H+1 + signal de dépassement** (EF-07, EF-08). La détection de dérive est sortie et suivie dans #117, hors fenêtre (`Portée/Post-jury`). Voir `docs/BACKLOG.md` et `docs/PRD.md` §8.
gabriel added the due date 2026-09-11 2026-09-03 12:41:01 +00:00
gabriel changed title from [EF-08] Service d'inférence, dépassements et dérive to [EF-08] Service d'inférence : prévision servie et signal de dépassement 2026-09-03 13:56:49 +00:00
Member

Recadré sur la repriorisation du 03/09 : le titre et le périmètre ne couvrent plus la détection de dérive, sortie en #117 (Portée/Post-jury). Rattaché EF-07 (référence de prévision servie) en plus d'EF-08.

Recadré sur la repriorisation du 03/09 : le titre et le périmètre ne couvrent plus la détection de dérive, sortie en #117 (Portée/Post-jury). Rattaché EF-07 (référence de prévision servie) en plus d'EF-08.
Author
Owner

Justine, trois de tes six remarques sur la #161 tombent sur ce ticket et pas sur
la fiche. C'est le tien, donc je propose la rédaction plutôt que de l'éditer.
Tu appliques, tu réécris, ou tu dis non.

Le contexte est l'ADR 0012,
mise à jour ce matin après ta relecture.

CA1, aujourd'hui

Le service charge le modèle promu depuis MLflow et répond sous 200 ms.

CA1, proposé

Le job horaire charge le modèle promu depuis MLflow et écrit une ligne par
site dans public.prevision. L'API sert cette table sous 200 ms, sans
toucher MLflow sur le chemin de requête.

Tu l'as dit toi même : c'est probablement le seul montage qui tient les 200 ms.
La cible ne change pas, c'est le chargement du modèle qui sort du chemin de
requête. Écrit comme aujourd'hui, l'écart entre le ticket et ce qui sera montré
se lirait au jury.

CA3, aujourd'hui

Chaque prédiction est journalisée avec son entrée et l'identifiant du modèle
qui l'a produite.

CA3, proposé

Chaque prédiction est journalisée avec son entrée, les retards t−1 à t−24,
et l'identifiant du modèle qui l'a produite. L'entrée va au journal du job,
comme le collecteur écrit le sien, et à l'exécution MLflow côté
entraînement. Elle ne va pas dans public.prevision : la table est une
hypertable comprimée à une ligne par site et par heure, et vingt quatre
flottants de plus y seraient payés sur chaque lecture du tableau de bord pour
une donnée que personne n'affiche.

Ton constat était juste, le critère n'avait aucun emplacement écrit. C'est
l'emplacement que je propose de fixer, pas le critère.

CA4, aujourd'hui

Un dépassement de la capacité déclarée d'un site déclenche un signal avant
l'heure concernée.

CA4, proposé (une phrase ajoutée, le critère est inchangé)

Un dépassement de la capacité déclarée d'un site déclenche un signal avant
l'heure concernée. C'est le job qui porte l'antériorité : à l'émission en
ligne, il refuse d'écrire une prévision dont l'instant visé est déjà passé et
le dit dans son journal. Le contrat PrevisionH1 ne la vérifie
volontairement pas, sinon aucun rejeu ne serait possible, et le #36 a besoin
du rejeu pour constituer trois jours de comparaison sans attendre trois jours.
Un rattrapage se reconnaît à ce que son emise_le suit son horodatage.

Et la ligne de preuve

Preuve : un cas de dépassement déclenché sur données réelles, avec
l'horodatage du signal, et le relevé de latence sur la route de l'API, pas
sur le job.

Sans cette précision, quelqu'un chronométrera le job et trouvera bien plus que
200 ms, alors que ce n'est pas ce que le critère mesure.

Rien de tout ça ne touche au périmètre ni à la charge. Si tu es d'accord, tu
peux appliquer tel quel ; si un point te gêne, dis lequel et je réécris.

Justine, trois de tes six remarques sur la #161 tombent sur ce ticket et pas sur la fiche. C'est le tien, donc je propose la rédaction plutôt que de l'éditer. Tu appliques, tu réécris, ou tu dis non. Le contexte est l'[ADR 0012](../src/branch/develop/docs/adr/0012-prevision-h1-avec-reference-publiee.md), mise à jour ce matin après ta relecture. **CA1, aujourd'hui** > Le service charge le modèle promu depuis MLflow et répond sous 200 ms. **CA1, proposé** > Le **job horaire** charge le modèle promu depuis MLflow et écrit une ligne par > site dans `public.prevision`. L'API sert cette table **sous 200 ms**, sans > toucher MLflow sur le chemin de requête. Tu l'as dit toi même : c'est probablement le seul montage qui tient les 200 ms. La cible ne change pas, c'est le chargement du modèle qui sort du chemin de requête. Écrit comme aujourd'hui, l'écart entre le ticket et ce qui sera montré se lirait au jury. **CA3, aujourd'hui** > Chaque prédiction est journalisée avec son entrée et l'identifiant du modèle > qui l'a produite. **CA3, proposé** > Chaque prédiction est journalisée avec son entrée, les retards `t−1` à `t−24`, > et l'identifiant du modèle qui l'a produite. L'entrée va au **journal du job**, > comme le collecteur écrit le sien, et à l'**exécution MLflow** côté > entraînement. Elle ne va **pas** dans `public.prevision` : la table est une > hypertable comprimée à une ligne par site et par heure, et vingt quatre > flottants de plus y seraient payés sur chaque lecture du tableau de bord pour > une donnée que personne n'affiche. Ton constat était juste, le critère n'avait aucun emplacement écrit. C'est l'emplacement que je propose de fixer, pas le critère. **CA4, aujourd'hui** > Un dépassement de la capacité déclarée d'un site déclenche un signal avant > l'heure concernée. **CA4, proposé** (une phrase ajoutée, le critère est inchangé) > Un dépassement de la capacité déclarée d'un site déclenche un signal avant > l'heure concernée. **C'est le job qui porte l'antériorité** : à l'émission en > ligne, il refuse d'écrire une prévision dont l'instant visé est déjà passé et > le dit dans son journal. Le contrat `PrevisionH1` ne la vérifie > volontairement pas, sinon aucun rejeu ne serait possible, et le #36 a besoin > du rejeu pour constituer trois jours de comparaison sans attendre trois jours. > Un rattrapage se reconnaît à ce que son `emise_le` suit son `horodatage`. **Et la ligne de preuve** > Preuve : un cas de dépassement déclenché sur données réelles, avec > l'horodatage du signal, **et le relevé de latence sur la route de l'API**, pas > sur le job. Sans cette précision, quelqu'un chronométrera le job et trouvera bien plus que 200 ms, alors que ce n'est pas ce que le critère mesure. Rien de tout ça ne touche au périmètre ni à la charge. Si tu es d'accord, tu peux appliquer tel quel ; si un point te gêne, dis lequel et je réécris.
Member

Fait en plus du périmètre initial, et pourquoi

Le #38 a été fermé sans que la convergence de l'API soit faite : develop ne
porte aucun commit qui la contienne, et les quatre sorties du tableau de bord
servaient toujours une prévision inventée dans fixtures.py. Ce travail n'ayant
plus de ticket, il est repris ici.

Une seule source. SiteOut, SyntheseParcOut, SerieSiteOut et
SerieParcOut lisent désormais public.prevision, par repository.sites qui
interroge la table une fois par requête et pose la ligne sur SiteEtat.
Sans ça, la route /v1/previsions servait le vrai chiffre pendant que les
écrans en affichaient un autre : deux vérités sur la même notion, sans que
l'écran puisse dire laquelle il montre.

Une prévision absente vaut null, jamais zéro. C'est la règle que le
glossaire §2 pose déjà pour la consommation, appliquée à la prévision : null
dit que la passe horaire n'a rien écrit, pas que le site ne consommera rien.
D'où prevision_kw, reference_kw et marge_kw nullables, depassement_prevu
à false sans prévision — on ne signale que ce qu'on a prévu —, et
sites_prevus sur la synthèse, pour qu'une somme partielle dise qu'elle l'est.

Deux corrections d'affichage que le branchement a rendues nécessaires :
formaterKw rend pour une valeur absente au lieu de 0, et la courbe d'un
site ne lit plus prevision.valeur_kw sans garde — sur un null elle levait et
vidait le composant entier.

instant_depassement passe à l'heure ronde. Le jeu portait 17 h 22, une
minute que rien ne fonde : le modèle prévoit une moyenne horaire et
public.prevision n'a qu'une ligne par site et par heure.

Ce que l'API n'importe pas, et délibérément : PrevisionH1. Le contrat rend
reference_kw obligatoire pour arrêter un producteur ; l'opposer à une ligne
lue ferait refuser de servir toute ligne antérieure à ce ticket. L'API a son
modèle de lecture, PrevisionServie. Le préambule de packages/contracts
affirmait l'inverse : il est corrigé.

## Fait en plus du périmètre initial, et pourquoi Le #38 a été fermé sans que la convergence de l'API soit faite : `develop` ne porte aucun commit qui la contienne, et les quatre sorties du tableau de bord servaient toujours une prévision inventée dans `fixtures.py`. Ce travail n'ayant plus de ticket, il est repris ici. **Une seule source.** `SiteOut`, `SyntheseParcOut`, `SerieSiteOut` et `SerieParcOut` lisent désormais `public.prevision`, par `repository.sites` qui interroge la table **une fois par requête** et pose la ligne sur `SiteEtat`. Sans ça, la route `/v1/previsions` servait le vrai chiffre pendant que les écrans en affichaient un autre : deux vérités sur la même notion, sans que l'écran puisse dire laquelle il montre. **Une prévision absente vaut `null`, jamais zéro.** C'est la règle que le glossaire §2 pose déjà pour la consommation, appliquée à la prévision : `null` dit que la passe horaire n'a rien écrit, pas que le site ne consommera rien. D'où `prevision_kw`, `reference_kw` et `marge_kw` nullables, `depassement_prevu` à `false` sans prévision — on ne signale que ce qu'on a prévu —, et `sites_prevus` sur la synthèse, pour qu'une somme partielle dise qu'elle l'est. **Deux corrections d'affichage** que le branchement a rendues nécessaires : `formaterKw` rend `—` pour une valeur absente au lieu de `0`, et la courbe d'un site ne lit plus `prevision.valeur_kw` sans garde — sur un `null` elle levait et vidait le composant entier. **`instant_depassement` passe à l'heure ronde.** Le jeu portait 17 h 22, une minute que rien ne fonde : le modèle prévoit une moyenne horaire et `public.prevision` n'a qu'une ligne par site et par heure. **Ce que l'API n'importe pas, et délibérément :** `PrevisionH1`. Le contrat rend `reference_kw` obligatoire pour arrêter un *producteur* ; l'opposer à une ligne lue ferait refuser de servir toute ligne antérieure à ce ticket. L'API a son modèle de lecture, `PrevisionServie`. Le préambule de `packages/contracts` affirmait l'inverse : il est corrigé.
justine added reference justine/37-inference-prevision-servie 2026-09-08 08:45:06 +00:00
justine added spent time 2026-09-08 08:55:34 +00:00
8 hours
Sign in to join this conversation.
No project
No assignees
3 participants
Notifications
Total time spent: 8 hours
justine
8 hours
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".
2026-09-11
Reference
g2/enervision#37
No description provided.