runbook: guardrails flotte hermes (hard_stop, tirith, verificateur croise) + incidents decouverts

This commit is contained in:
Nabil Derouiche
2026-08-12 23:55:17 +01:00
parent abf58b7567
commit 75cbce7c50
2 changed files with 37 additions and 0 deletions
+2
View File
@@ -200,3 +200,5 @@
- [hermes-tt/nyora-notes-tt-phase2-embedding-relance-post-sweep-08-08-2026.md](hermes-tt/nyora-notes-tt-phase2-embedding-relance-post-sweep-08-08-2026.md) -- Sweep Phase1 toutes annees termine seul via son plafond MAX_BATCHES=15 (3 notes restantes, cas OWA permanents deja connus, aucune intervention necessaire). Phase 2 + embedding relances sur le volume complet via nouveau script run_phase2_embed_full.sh (boucle jusqu'a epuisement + embedding + audit) : 24/27 PJ telechargees extraites avec succes, 24/24 embeddees (100%), content_hash 27/27 (100% des telechargees), ao_reference 23/27. JOIN exact content_hash avec onedrive_documents : 3 correspondances (1 seul le 08/08 matin, confirme la resorption progressive). Piege : docker exec -i obligatoire pour un heredoc Python en stdin ; sqlite_vec.load(conn) obligatoire avant toute requete sur *_vec, y compris en audit ponctuel (08/08/2026) - [hermes-tt/nyora-notes-tt-phase2-embedding-relance-post-sweep-08-08-2026.md](hermes-tt/nyora-notes-tt-phase2-embedding-relance-post-sweep-08-08-2026.md) -- Sweep Phase1 toutes annees termine seul via son plafond MAX_BATCHES=15 (3 notes restantes, cas OWA permanents deja connus, aucune intervention necessaire). Phase 2 + embedding relances sur le volume complet via nouveau script run_phase2_embed_full.sh (boucle jusqu'a epuisement + embedding + audit) : 24/27 PJ telechargees extraites avec succes, 24/24 embeddees (100%), content_hash 27/27 (100% des telechargees), ao_reference 23/27. JOIN exact content_hash avec onedrive_documents : 3 correspondances (1 seul le 08/08 matin, confirme la resorption progressive). Piege : docker exec -i obligatoire pour un heredoc Python en stdin ; sqlite_vec.load(conn) obligatoire avant toute requete sur *_vec, y compris en audit ponctuel (08/08/2026)
- [hermes-tt/download-attachments-phase1-bug-timeout.md](hermes-tt/download-attachments-phase1-bug-timeout.md) -- Sweep PJ 2026 degradait la session OWA jusqu'a marquer des notes attachments_scanned=1 sans verification reelle : deux endpoints (search_folder par dossier, list_attachments par note) catchaient un timeout exactement comme un 404 et le mettaient en cache comme vide. Fix : contrat None (inconnu, a retenter) vs liste vide (verifie vide/404) strict + une retentative + circuit-breaker 3 echecs consecutifs. Filtre year_bucket ajoute (absent avant, bug distinct meme session), parametrable ATTACH_YEAR_BUCKET pour reprendre sur 2024/2025 apres 2026. Teste 2x5 notes (07/08/2026) - [hermes-tt/download-attachments-phase1-bug-timeout.md](hermes-tt/download-attachments-phase1-bug-timeout.md) -- Sweep PJ 2026 degradait la session OWA jusqu'a marquer des notes attachments_scanned=1 sans verification reelle : deux endpoints (search_folder par dossier, list_attachments par note) catchaient un timeout exactement comme un 404 et le mettaient en cache comme vide. Fix : contrat None (inconnu, a retenter) vs liste vide (verifie vide/404) strict + une retentative + circuit-breaker 3 echecs consecutifs. Filtre year_bucket ajoute (absent avant, bug distinct meme session), parametrable ATTACH_YEAR_BUCKET pour reprendre sur 2024/2025 apres 2026. Teste 2x5 notes (07/08/2026)
- [hermes-tt/nyora-notes-tt-message-id-elem-id-volatile-08-08-2026.md](hermes-tt/nyora-notes-tt-message-id-elem-id-volatile-08-08-2026.md) -- Piste "message_id natif via elem_id OWA" FERMEE : elem_id (attribut id de ligne) s'est revele VOLATILE (GUID de rendu regenere a chaque appel -- test rigoureux 0/19 messages stables, ensembles disjoints, meme index -> id different). Un premier test a 1 seul message en position 0 sur appels rapproches l'avait faussement valide (DOM re-utilise). Decision : message_id reste le hash SHA256(subject|date|sender) STABLE ; conversation_id (data-convid, niveau THREAD, regex ciblee jamais split sur |) et fix retry /search count:0 transitoire CONSERVES. Garde-fou dedup verifie (message_id = cle de dedup ; 399 notes toutes en dossiers 'done' jamais re-scannes ; 0 note degradee inseree pendant la fenetre elem_id). Lecon SPA : tester un id DOM sur >=2 rendus reels et plusieurs messages (08/08/2026) - [hermes-tt/nyora-notes-tt-message-id-elem-id-volatile-08-08-2026.md](hermes-tt/nyora-notes-tt-message-id-elem-id-volatile-08-08-2026.md) -- Piste "message_id natif via elem_id OWA" FERMEE : elem_id (attribut id de ligne) s'est revele VOLATILE (GUID de rendu regenere a chaque appel -- test rigoureux 0/19 messages stables, ensembles disjoints, meme index -> id different). Un premier test a 1 seul message en position 0 sur appels rapproches l'avait faussement valide (DOM re-utilise). Decision : message_id reste le hash SHA256(subject|date|sender) STABLE ; conversation_id (data-convid, niveau THREAD, regex ciblee jamais split sur |) et fix retry /search count:0 transitoire CONSERVES. Garde-fou dedup verifie (message_id = cle de dedup ; 399 notes toutes en dossiers 'done' jamais re-scannes ; 0 note degradee inseree pendant la fenetre elem_id). Lecon SPA : tester un id DOM sur >=2 rendus reels et plusieurs messages (08/08/2026)
- [common/guardrails-flotte-hermes-hard-stop-tirith-verificateur-12-08-2026.md](common/guardrails-flotte-hermes-hard-stop-tirith-verificateur-12-08-2026.md) -- hard_stop_enabled et tirith_enabled actives sur les 4 instances (etaient false), verificateur delegate_task croise (NAS genere DeepSeek/verifie Mimo V2.5, hermes-nabil inverse) ; faille provenance mail->RAG confirmee par lecture code mais pas corrigee (chantier a part) ; hermes-agent-tt trouve down 8h (mort non-propre, cause inconnue), hermes-workspace-perso bloque par port 3030 orphelin (12/08/2026)
@@ -0,0 +1,35 @@
# Guardrails flotte Hermes (NAS + VPS) — hard_stop, tirith, verificateur croise
## Contexte
Suite au succes du RAG hermes-tt (rapports d'evaluation, Excel, DCO livres sans retouche), demande explicite d'un Hermes avec garde-fous auto-actives + auto-contre independant, sur les 4 instances (hermes-tt, hermes-nyora, hermes-perso sur NAS + hermes-nabil sur VPS Contabo).
## Failles confirmees (lecture directe des config.yaml, pas une supposition)
- `tool_loop_guardrails.hard_stop_enabled: false` sur les 3 instances NAS et absent (donc false par defaut) sur hermes-nabil : l'agent ne fait qu'avertir sur boucle d'echec repetee, ne s'arrete jamais reellement. Recommandation officielle Nous Research pour deploiement gateway/non-surveille : l'activer.
- `security.tirith_enabled: false` explicitement sur les 3 instances NAS (le binaire tirith — scanner anti-prompt-injection, homograph URL, exfiltration credentials — n'etait pas actif) alors qu'il est actif par defaut (et le binaire present) sur hermes-nabil.
- Lecture code `nyora-notes-tt/pipeline_processor.py` + `mcp_server.py` : aucun marquage provenance/confiance sur le contenu mail avant ingestion RAG. Risque "sleeper channel" (cf. litterature academique sur les agents always-on) confirme reel, PAS corrige ce tour — chantier a part entiere (schema + formatage contexte), voir section "reste a faire".
## Changements appliques (12/08/2026, backups config.yaml.bak-guardrails-<timestamp> avant chaque edit)
| Instance | hard_stop_enabled | tirith_enabled | delegation (verificateur) |
|---|---|---|---|
| hermes-tt | false -> true | false -> true | deepseek-v4-pro -> **mimo-v2.5** (opencode-go, cle deja en place) |
| hermes-nyora | false -> true | false -> true | deja mimo-v2.5 (personne ne s'en souvenait) |
| hermes-perso | false -> true | false -> true | '' -> **mimo-v2.5** (opencode-go) |
| hermes-nabil (VPS) | absent -> true (bloc ajoute) | deja true par defaut, explicite maintenant | absent -> **deepseek/deepseek-v4-flash** via openrouter (cle OPENROUTER_API_KEY deja en .env) |
## Principe retenu
Le sous-agent verificateur (`delegate_task`) ne doit jamais tourner sur le meme modele que le generateur principal, pour eviter les angles morts partages (doc officielle Hermes : "the agent that does the work should not be its sole verifier"). NAS = generateur DeepSeek V4 Flash -> verificateur Mimo V2.5. hermes-nabil = generateur Mimo V2.5 (modele principal) -> verificateur DeepSeek V4 Flash. C'est l'inverse exact entre NAS et VPS, deliberement.
## Piege / a savoir
- `docker compose up -d` ne recharge PAS un `config.yaml` modifie si le container tourne deja (le fichier est un bind-mount, pas une image) — il faut un `docker restart <container>` explicite. `up -d` seul affiche "Running" sans rien faire.
- `data/` est gitignore sur les repos par-instance (hermes-tt, hermes-nyora, hermes-perso, hermes-nabil) — **ne jamais force-add `config.yaml`**, il contient des cles API en clair (Bifrost, OpenCode Zen). C'est un choix delibere du `.gitignore` existant, pas un oubli. Capitalisation via runbook + NyoraNotes uniquement pour ce type de fichier, pas de commit.
- Clone Gitea depuis le NAS : `gitea.bolbol.tn` (domaine externe) a timeout HTTP 504 en cours de session ; `192.168.100.33:3232` (LAN direct) fonctionne. A garder en tete si un futur clone echoue bizarrement.
## Incidents decouverts en cours de route (non lies au chantier, decouverts par effet de bord)
- hermes-agent-tt etait **down depuis 8h** avant intervention (Exited(0) cote Docker mais lifecycle_ledger interne Hermes indique mort non-propre, SIGKILL/VM death, vers 12/08 14h13, `suspected_oom=False`). Cause non identifiee. Relance faite, healthy.
- hermes-workspace-perso : Exited(128) depuis 24h, blocage au restart par un binding orphelin sur le port 3030 (host-level, PID invisible sans sudo — non resolu, necessite intervention avec privileges eleves ou redemarrage du daemon Docker).
## Reste a faire (session dediee)
- Provenance/tag "non fiable" sur le contenu mail avant ecriture RAG (nyora-notes-tt)
- Debloquer port 3030 (hermes-workspace-perso)
- Investiguer la cause de la mort hermes-agent-tt du 12/08 14h13
- Envisager `tirith_fail_open: false` une fois confirme en usage reel que tirith tourne correctement (actuellement `true` = mode conservateur partout)