Files
david 5112c2aa7c
Evals au push — seules celles dont un fichier sensible est touché / tri (push) Successful in 14s
Evals au push — seules celles dont un fichier sensible est touché / evals (push) Skipped
CI / ci (push) Skipped
Revue — preuve d'approbation / revue-ok (pull_request_review) Successful in 9s
Gate — evals et acceptance réelles / revue (push) Successful in 8s
Gate — evals et acceptance réelles / evaluation (push) Successful in 4m54s
CD — Staging / build (push) Successful in 32s
CD — Staging / deploy (push) Successful in 34s
docs(vigie): dedicated Gitea account for the vigie token
Mise en service de la vigie faite le 23/10 côté Gitea et serveur : compte
dédié `vigie` (restreint, Écriture sur le seul dépôt qualicheck), jeton à
la seule portée « tickets », secrets du dépôt, étiquette `simulation`,
entrée DNS et bloc Caddy du sous-domaine. La procédure de mise en service
le dit désormais.
2026-10-23 14:46:58 +02:00

321 lines
14 KiB
Markdown

# Exploitation : surveiller, réagir, revenir en arrière
Page d'entrée de l'exploitation de QualiCheck (staging). Elle dit où
regarder, quoi faire quand une alerte arrive et comment revenir en arrière.
Elle renvoie aux documents existants au lieu de les recopier. Conception de
la vigie : `conception/5_exploitation/vigie.md`.
## 1. Où regarder
- **La page « Santé du service »** : `https://vigie.qualicheck.koabana.fr`
(en local : `http://localhost:8884`). Mise à jour par la vigie toutes les
15 minutes, lisible sans compte. Elle donne l'état de chaque signal. Si
l'heure du dernier passage date de plus de 30 minutes, la vigie ne tourne
plus : voir `docker compose logs vigie` sur le serveur.
La pastille « i » de chaque ligne dit en une phrase ce que mesure le
signal (survol ou touche Tab).
- **Les tickets « incident »** du dépôt Gitea : un ticket par signal en
alerte, ouvert par la vigie, accompagné d'un e-mail.
- **Langfuse** (projet du staging) : les traces en temps réel, pour
comprendre une alerte.
- **`make traces-agent ARGS="--depuis AAAA-MM-JJ"`** : les mesures de
l'agent (tours, statuts, citations) lues dans les traces. Détail de la
chaîne : `docs/developpement/surveillance.md`.
- **Les journaux du serveur** : `docker compose logs <service>` sur le
serveur de staging.
## 2. Mise en service
La vigie est un service du docker-compose : elle démarre avec le premier
déploiement qui la contient. À faire **avant ce déploiement** :
1. Créer le compte Gitea dédié `vigie`, **restreint** (il ne voit que les
dépôts où on l'ajoute) et collaborateur du seul dépôt `qualicheck` avec
le droit **Écriture** (la Lecture ouvre un ticket mais n'y pose pas
d'étiquette). Connecté en `vigie`, créer son jeton à la seule portée
« tickets » (`write:issue`) : il ne peut pas pousser de code, et la
portée d'un jeton Gitea valant pour tous les dépôts de son propriétaire,
un jeton du compte principal aurait pu écrire partout. Puis, connecté en
administrateur du dépôt, les secrets du dépôt `VIGIE_GITEA_TOKEN` (ce
jeton) et `VIGIE_DESTINATAIRE` (les secrets `LANGFUSE_*`, `SMTP_*` et
`DEPLOY_*` existent déjà). Les tickets apparaissent signés `vigie`.
2. Vérifier que les étiquettes `incident` et `simulation` existent dans le
dépôt.
3. Sur le serveur, deux gestes : l'entrée DNS `vigie.qualicheck.koabana.fr`
chez Infomaniak, puis le bloc Caddy du sous-domaine (voir
`docs/developpement/deploiement_staging.md`, « Page de la vigie »). Sans
eux, la vigie tourne mais sa page n'est pas joignable de l'extérieur.
## 3. Déployer
Voir `docs/developpement/deploiement_staging.md` (déploiement, Caddy, page
de santé) et `make gate` puis `make staging` pour la mise en service.
## 4. Une fiche par signal
Le ticket, l'e-mail et la page renvoient ici, vers l'ancre du signal.
Chaque fiche dit ce que le signal veut dire, comment le vérifier, l'action à
mener et quand fermer le ticket. Les seuils sont dans
`docs/developpement/metriques_seuils.md` (section 6) et dans la section
`vigie` de `app/observability/config.yml`.
### Les pannes (alerte dès la première occurrence, sur 30 minutes)
<a id="citations-non-verifiees"></a>
#### Citations non vérifiées
- **Sens** : une réponse a cité une règle que la vérification n'a pas pu
confirmer. Le garde-fou de la réponse a laissé passer ou signalé un doute.
- **Vérifier** : dans Langfuse, chercher les traces `questions_libres` dont
l'attribut `citations_non_verifiees` est non vide (liste de numéros).
- **Action** : lire la réponse et les règles citées ; si la règle existe
bien, la vérification est trop stricte, sinon le modèle invente et le
prompt ou le garde-fou est à corriger.
- **Fermer** : quand la cause est comprise et que la page est revenue à
« normal » (30 minutes sans nouvelle occurrence).
<a id="reponse-503"></a>
#### Réponse 503 de `POST /questions`
- **Sens** : l'API a répondu « service indisponible » à une question
d'un auditeur.
- **Vérifier** : dans Langfuse, le span racine `api_business` avec
`http.status_code` égal à 503 ; ses spans enfants montrent l'étape en
cause. `docker compose logs api-business` donne l'erreur côté serveur.
- **Action** : si un fournisseur de modèle est en panne, attendre son
rétablissement ; sinon corriger le composant fautif.
- **Fermer** : service rétabli et page revenue à « normal ».
<a id="panne-authentification"></a>
#### Panne de l'authentification
- **Sens** : la vérification du compte a échoué pour une raison technique
(et non parce que le jeton est invalide).
- **Vérifier** : span `api_business.authentification` au niveau `ERROR`
dans Langfuse ; la base de données est le premier suspect
(`docker compose ps`, `docker compose logs`).
- **Action** : rétablir la base ; si elle est saine, lire l'erreur du span.
- **Fermer** : une connexion réussie est visible et la page est « normal ».
<a id="api-regles-erreur"></a>
#### API des règles en erreur ou jeton de l'agent refusé
- **Sens** : l'agent n'arrive plus à interroger l'API des règles ; ses
réponses perdent leur source.
- **Vérifier** : span `tools.appel_api_regles` au niveau `ERROR` ou
`api_regles.statut` supérieur ou égal à 500. Un jeton de l'agent refusé
apparaît en **502** avec le message « jeton de l'agent refusé ».
- **Action** : redémarrer ou corriger l'API des règles ; pour un jeton
refusé, vérifier celui configuré pour l'agent (voir
`docs/developpement/creation_cle_api_regles.md`).
- **Fermer** : un appel réussi est visible et la page est « normal ».
<a id="sujet-panne"></a>
#### Contrôle du sujet en panne
- **Sens** : le contrôle qui écarte les questions hors sujet n'a pas pu
répondre ; la question est traitée sans lui ou refusée.
- **Vérifier** : traces dont l'attribut `sujet.categorie` vaut `panne`.
- **Action** : vérifier le fournisseur du modèle de ce contrôle et sa
configuration.
- **Fermer** : plus de trace en panne sur 30 minutes.
<a id="intention-panne"></a>
#### Contrôle de l'intention en panne
- **Sens** : même chose pour le contrôle qui détecte les demandes
malveillantes.
- **Vérifier** : traces dont l'attribut `intention.categorie` vaut
`panne`.
- **Action** : comme pour le contrôle du sujet.
- **Fermer** : plus de trace en panne sur 30 minutes.
<a id="bypass"></a>
#### Composant court-circuité
- **Sens** : un composant de protection est désactivé par configuration
(l'attribut `bypass` du span racine est renseigné). Normal pour une
mesure en local, **jamais en staging**.
- **Vérifier** : lire l'attribut `bypass` sur la trace, puis les variables
d'environnement du staging.
- **Action** : retirer le réglage qui court-circuite et redéployer.
- **Fermer** : plus de trace avec `bypass` sur 30 minutes.
<a id="service-injoignable"></a>
#### Service injoignable
- **Sens** : la route `/health` de l'API a échoué deux fois de suite, à
30 secondes d'écart.
- **Vérifier** : ouvrir `https://api.qualicheck.koabana.fr/health` ;
`docker compose ps` sur le serveur.
- **Action** : relancer le conteneur ; si un déploiement vient d'avoir lieu,
envisager le retour arrière (section 5).
- **Fermer** : `/health` répond et la page est « normal ».
<a id="purge-arretee"></a>
#### Purge des comptes arrêtée
- **Sens** : la dernière ligne du journal de la purge date de plus de
26 heures ; la purge quotidienne des comptes ne tourne plus.
- **Vérifier** : lire la fin de `logs/purge_comptes.log` sur le serveur et
la tâche planifiée qui la lance.
- **Action** : relancer la purge (`make purge-comptes`) et corriger la
tâche planifiée.
- **Fermer** : une ligne récente apparaît dans le journal.
### Les dérives (alerte sur une fenêtre, au moins 20 questions)
Sous 20 questions dans la fenêtre, la page affiche « pas assez de
questions » et rien ne sonne (voir les limites, section 9).
<a id="limite-atteinte"></a>
#### Questions coupées par la limite de tours
- **Sens** : plus de 5 % des questions des dernières 24 heures n'ont pas
abouti parce que l'agent a atteint le nombre maximal de tours.
- **Vérifier** : `make traces-agent ARGS="--depuis <hier>"`, puis dans
Langfuse les traces `questions_libres` dont `limite_atteinte` est vrai.
- **Action** : regarder si ce sont des questions nouvelles (le jeu de
questions a changé) ou une dégradation (outil lent, modèle qui boucle).
- **Fermer** : la part repasse sous le seuil ou la cause est traitée.
<a id="tours-p90"></a>
#### 90e centile du nombre de tours
- **Sens** : 10 % des questions demandent plus de 4 tours à l'agent ; il
cherche plus longtemps qu'avant, souvent avant de dériver vers la limite.
- **Vérifier** : comme pour la limite de tours.
- **Action** : même analyse ; relire le prompt et les sorties d'outils.
- **Fermer** : le centile repasse à 4 ou moins.
<a id="citations-inventees"></a>
#### Citations inventées avant correction
- **Sens** : plus de 10 % des questions contenaient, avant correction par
le garde-fou, une citation de règle inexistante. Le garde-fou rattrape
l'erreur, mais le modèle se dégrade.
- **Vérifier** : `make traces-agent`, colonne des citations inventées.
- **Action** : vérifier si le modèle ou son prompt a changé chez le
fournisseur ou dans le dépôt.
- **Fermer** : la part repasse sous le seuil.
<a id="latence"></a>
#### Latence de `POST /questions` (sans page et avec page)
- **Sens** : le 95e centile du temps de réponse dépasse son seuil (13 s sans
page, 24 s avec page, carte #83). Deux signaux, deux tickets possibles :
les deux groupes n'ont pas la même durée normale.
- **Vérifier** : dans Langfuse, la durée du span racine `api_business` ;
`make mesure-latence` rejoue la mesure.
- **Action** : chercher l'étape lente (modèle, API des règles, base).
- **Fermer** : le centile repasse sous son seuil.
<a id="fidelite"></a>
#### Fidélité des réponses
- **Sens** : la moyenne des 20 dernières notations du juge de fidélité est
sous 0,90 : les réponses s'écartent des règles citées.
- **Vérifier** : attribut `fidelite` du span `questions_libres.juge`.
- **Action** : lire les réponses les moins bien notées ; vérifier si le
modèle de réponse a changé.
- **Fermer** : la moyenne remonte à 0,90 ou plus.
<a id="refus-authentification"></a>
#### Refus d'authentification
- **Sens** : plus de 10 refus d'authentification en une heure : jeton
expiré distribué par erreur, ou tentative de force brute.
- **Vérifier** : spans `api_business.authentification` avec un motif de
refus, sur la dernière heure (sans noter d'identité dans le ticket).
- **Action** : si c'est un client légitime, corriger son jeton ; sinon
vérifier les limites de débit.
- **Fermer** : 10 refus ou moins sur l'heure suivante.
### Les mesures sans seuil
<a id="mesures-sans-seuil"></a>
#### Part refusée par les contrôles, provenance, questions par compte
- **Sens** : mesures affichées, sans alerte. Elles restent « à mesurer
d'abord » (`docs/developpement/metriques_seuils.md`).
- **Vérifier** : la page de santé ; `make traces-agent` pour le détail.
- **Action** : aucune pour l'instant ; un seuil sera proposé après
plusieurs semaines de traces réelles.
- **Fermer** : sans objet, aucun ticket n'existe pour ces mesures.
## 5. Revenir en arrière
`make rollback-staging RUN=<numéro>` rejoue un déploiement réussi
précédent (64 secondes mesurées le 14/10). Aucune migration de base n'est
défaite. Procédure : `docs/developpement/rollback_staging.md`.
## 6. Le cycle d'un incident
1. **Ouvert** : la vigie ouvre un ticket `[vigie] <signal> : <libellé>`
(étiquette `incident`) et envoie un e-mail, une seule fois par incident.
2. **Rétabli** : quand le signal redevient normal, elle ajoute un seul
commentaire « rétabli à HH:MM » et un e-mail. Une panne n'est déclarée
rétablie qu'après 30 minutes sans nouvelle occurrence. Si le signal
repart avant la fermeture du ticket, un commentaire « revenu à HH:MM » et
un e-mail le signalent (rechute).
3. **Fermé** : à la main, après avoir mené l'action de la fiche. La vigie
ne ferme jamais un ticket.
Si Gitea ou le relais d'e-mails ne répond pas, la page est écrite quand même
et l'échec est consigné dans les journaux de la vigie.
## 7. Changer un seuil
Les valeurs sont dans la section `vigie` de `app/observability/config.yml`
(jamais dans le code). Modifier la valeur, passer les tests, puis fusionner
sur `dev` puis déployer : la vigie lit ses seuils dans l'image déployée. Reporter le changement
dans `docs/developpement/metriques_seuils.md`.
## 8. Rejouer la démo de la dérive simulée
Dans Gitea, onglet Actions, choisir « Vigie — démo » puis « Run workflow ».
Un fichier de 25 questions, dont 4 coupées par la limite de
tours, remplace Langfuse ; tout le reste est réel. Résultat attendu : deux
tickets `[simulation]` (limite de tours et 90e centile du nombre de tours),
un e-mail par ticket, la page de santé montrant les deux dérives en alerte.
Fermer ensuite les tickets de la démo.
La démo a besoin des secrets `VIGIE_*` (jeton Gitea, destinataire) du
staging. Sans le jeton, elle tourne à blanc : ni ticket ni e-mail, et le
workflow reste vert.
En local, sans ticket ni e-mail : `make vigie`.
## 9. Limites connues
- **Volume du staging** : environ 8 questions par jour (107 en 14 jours).
Avec un minimum de 20 sur 24 heures, les dérives affichent presque
toujours « pas assez de questions » : aujourd'hui, seules les pannes et la
simulation sonnent réellement. C'est voulu.
- **La vigie lance la version `dev` du code**, qui peut avoir un peu
d'avance sur le staging.
- **La fidélité se lit sur le span du juge** : le Langfuse du staging
refuse la lecture des scores par son API publique.
- **Pas d'e-mail de secours** : si la vigie s'arrête, rien n'écrit à
personne. Une page dont l'heure de passage a plus de 30 minutes signale
une vigie arrêtée ; la cause se lit avec `docker compose logs vigie`.
- **Langfuse injoignable** : le passage échoue avant d'écrire la page. Elle
n'est plus mise à jour (son heure vieillit) et aucune alerte ne part, pas
même pour une panne de l'API. Seule trace : `docker compose logs vigie`.