Lección 7 — Fiabiliser et sécuriser

Le journal d'audit

4 min de lectura

Este contenido aún no está traducido. Se muestra la versión original.

Le journal d'audit

Pourquoi un journal dédié ?

Quand un assistant agit dans un SIRH, on vous demandera tôt ou tard : « Qui a annulé la demande D0001 ? Est-ce l'IA ? ». Les journaux techniques (logs HTTP) ne suffisent pas : ils ne disent ni quel utilisateur a parlé à l'assistant, ni ce que le modèle avait demandé, ni si un humain avait confirmé.

flowchart LR
  B["executer()"] --> V["refuse_validation"]
  B --> U["refuse_utilisateur"]
  B --> OK["ok"]
  B --> ER["erreur (4xx, timeout)"]
  V --> J[("audit.jsonl")]
  U --> J
  OK --> J
  ER --> J
  J --> Q["Requêtes : jq, pandas, BigQuery"]

Tous les chemins de executer() écrivent une ligne, y compris les refus : un refus est souvent l'événement le plus intéressant (tentative d'accès, injection de prompt).

Le format : JSON Lines

JSON Lines (.jsonl) = un objet JSON par ligne. On ajoute en fin de fichier sans relire le reste, et chaque outil (jq, pandas, BigQuery) sait le lire.

files/assistant.py (extrait)
def journaliser(**evenement):
    evenement = {"ts": datetime.now(timezone.utc).isoformat(timespec="seconds"), **evenement}
    with open(AUDIT_PATH, "a", encoding="utf-8") as f:
        f.write(json.dumps(evenement, ensure_ascii=False) + "\n")

Et dans executer(), l'appel après exécution :

files/assistant.py (extrait)
t0 = time.perf_counter()
resultat = outils_rh.OUTILS[fc.name](utilisateur, **args)
journaliser(**base, statut="erreur" if "erreur" in resultat else "ok",
            duree_ms=round((time.perf_counter() - t0) * 1000), resultat=resultat)
ChampExempleÀ quoi il sert
ts2026-10-15T14:35:19+00:00chronologie (en UTC, sans ambiguïté de fuseau)
utilisateurE001l'identité de la session, pas une affirmation du chat
outil, argscreer_demande_conges, datesce que le modèle a demandé
statutok, erreur, refuse_validation, refuse_utilisateurfiltrer les incidents
duree_ms4repérer une API lente
resultat{"demande_id": "D0001", ...}prouver ce qui a été fait

Pourquoi journaliser aussi les appels refusés à la validation ?

D'où doit venir le champ utilisateur du journal ?

Exercice : répondre à l'auditeur

Avec le fichier audit.jsonl, listez toutes les actions sensibles (création, annulation) avec leur statut, en une commande Python ou jq.

Voir la solution

Avec jq :

jq -c 'select(.outil=="creer_demande_conges" or .outil=="annuler_demande")
       | {ts, utilisateur, outil, statut, args}' audit.jsonl

En Python pur :

import json
for ligne in open("audit.jsonl", encoding="utf-8"):
    e = json.loads(ligne)
    if e["outil"] in {"creer_demande_conges", "annuler_demande"}:
        print(e["ts"], e["utilisateur"], e["outil"], e["statut"])

Résultat obtenu

Extrait du journal produit par python e2e.py le 2026-10-15 (gemini-3.5-flash), tel qu'affiché à la fin du script :

Journal d'audit :
  E001 consulter_solde       ok                  {"employe_id": "E001"}
  E004 chercher_employe      ok                  {"nom": "Camille Martin"}
  E004 chercher_employe      ok                  {"nom": "Karim Benali"}
  E004 consulter_solde       ok                  {"employe_id": "E001"}
  E004 consulter_solde       ok                  {"employe_id": "E002"}
  E001 creer_demande_conges  ok                  {"date_fin": "2026-11-06", "employe_id": "E001", "date_debut": "2026-11-04", "type_conge": "rtt"}
  E001 annuler_demande       refuse_utilisateur  {"demande_id": "D0001"}
  E004 chercher_employe      ok                  {"nom": "Lucie Moreau"}
  E004 consulter_solde       erreur              {"employe_id": "E003"}

La ligne brute de la création, avec la preuve de ce qui a été enregistré :

{"ts": "2026-10-15T14:35:19+00:00", "utilisateur": "E001", "outil": "creer_demande_conges", "args": {"date_fin": "2026-11-06", "employe_id": "E001", "date_debut": "2026-11-04", "type_conge": "rtt"}, "statut": "ok", "duree_ms": 4, "resultat": {"demande_id": "D0001", "statut": "en_attente", "jours": 3, "employe_id": "E001", "type_conge": "rtt", "date_debut": "2026-11-04", "date_fin": "2026-11-06"}}

Je sais faire

  • Écrire une ligne d'audit JSON Lines pour chaque appel d'outil, refus compris
  • Choisir les champs utiles : horodatage UTC, utilisateur de session, outil, arguments, statut, durée
  • Interroger le journal avec jq ou Python
  • Protéger le journal comme une donnée personnelle
Siguiente