AI

AgentOps : invalider les preuves périmées avant une action de production

Un runbook de production pour borner l'âge des preuves issues du retrieval, de la télémétrie et des outils avant qu'un agent IA propose, exécute ou rollbacke une action.

07 sept. 2026 aiagentopsagentsmicrosoft-foundryretrievalevidencefreshnessobservabilityevaluationguardrailsautomationrunbookrollbackproduction

Un agent d’exploitation reçoit une alerte, retrouve un runbook, interroge un outil d’inventaire puis propose de redémarrer un worker en production. Le raisonnement paraît cohérent. Le problème est temporel : l’alerte est actuelle, l’inventaire date de six heures, le runbook a été remplacé la semaine précédente et le résultat du health probe provient d’un ancien incident.

Il ne s’agit pas d’une hallucination au sens habituel. Chaque élément peut être authentique, mais leur combinaison ne décrit plus le système qui recevra l’action. Le cas d’usage est celui d’un agent interne qui assiste la réponse à incident avec Microsoft Foundry, du retrieval et des outils de lecture ou d’écriture. Ce runbook décide quand les preuves sont assez fraîches pour soutenir une proposition, quand elles doivent être renouvelées et quand le chemin d’action doit être bloqué ou rollbacké.

Définir la fraîcheur par décision, pas par source

Une règle globale comme « les documents restent valides trente jours » n’est pas exploitable. L’âge acceptable dépend de la décision soutenue. Le propriétaire d’un service peut rester valide plusieurs semaines ; une révision déployée, une opération active ou l’état de santé peuvent expirer en quelques secondes.

yaml evidence-freshness-policy.yml
policy_version: evidence-freshness-v4
decision: restart_production_worker

evidence_classes:
procedure:
  required: true
  max_age: 30d
  version_required: true
  invalidate_on: [runbook_superseded, service_owner_changed]

target_identity:
  required: true
  max_age: 15m
  source: approved_inventory
  invalidate_on: [deployment_changed, resource_recreated]

live_health:
  required: true
  max_age: 2m
  source: production_telemetry
  invalidate_on: [new_release, failover, incident_state_changed]

active_operations:
  required: true
  max_age: 30s
  source: control_plane

on_stale:
read_tools: allowed
draft: allowed_with_stale_marker
write_tools: blocked

Ces valeurs illustrent le contrat, elles ne constituent pas des seuils universels. Il faut les déduire de la vitesse de changement de l’état, du rayon d’impact de l’action et du délai de retour arrière. L’essentiel est de rendre la policy explicite et versionnée.

Construire une enveloppe de preuve

Les fragments anonymes issus du retrieval ne doivent pas arriver directement dans le planificateur d’action. Chaque élément doit porter son origine, sa date d’observation, sa version, son scope et son expiration. Il faut distinguer le moment où l’état a été observé de celui où l’agent l’a récupéré.

json agent-evidence-envelope.json
{
"evidence_id": "ev-01K4Q7R2",
"kind": "live_health",
"source": "azure-monitor-query",
"source_record_id": "query-run-8c71",
"environment": "production",
"target": "billing-worker",
"observed_at": "2026-09-07T14:21:12Z",
"retrieved_at": "2026-09-07T14:21:19Z",
"expires_at": "2026-09-07T14:23:12Z",
"policy_version": "evidence-freshness-v4",
"content_digest": "sha256:<digest>",
"status": "fresh"
}

retrieved_at ne suffit pas. Un cache peut renvoyer maintenant une observation vieille de six heures et lui donner une apparence de fraîcheur. Utilisez l’heure de l’événement source pour l’état live, la date de publication ou de révision pour une procédure et une limite de validité explicite pour une approbation ou une exception temporaire.

Normaliser les horloges avant de comparer l’âge

Les contrôles de fraîcheur échouent discrètement lorsque les timestamps proviennent d’horloges différentes. Effectuez les comparaisons en UTC, conservez l’heure source et mesurez le décalage d’horloge à l’ingestion. Refusez les observations datées dans le futur au-delà d’une petite tolérance surveillée.

text freshness-clock-checks.txt
Pour chaque preuve
Parser un timestamp source avec fuseau
Convertir une seule fois vers UTC
Conserver timestamp original et horloge source
Calculer l'âge depuis l'horloge fiable du decision gate
Séparer délai de transport et délai de cache
Refuser une heure d'observation absente ou illisible
Refuser une date future au-delà de allowed_clock_skew

Ne jamais
Remplacer observed_at par l'heure d'ingestion
Supposer qu'une heure locale est en UTC
Repousser l'expiration sans rafraîchir l'état source
Laisser le modèle déduire l'âge depuis du texte

La gestion des horloges appartient au service de preuves ou au policy gate. Elle ne doit pas dépendre d’un calcul de durée effectué par le modèle dans la conversation.

Rafraîchir uniquement par des chemins de lecture bornés

Lorsqu’une preuve obligatoire a expiré, l’agent peut utiliser un outil de lecture autorisé pour renouveler cette classe. Il ne doit ni élargir le scope, ni changer d’environnement, ni appeler une opération capable d’écrire sous prétexte de diagnostic.

yaml evidence-refresh-plan.yml
refresh_plan:
target: billing-worker
environment: production
stale_items:
  - kind: target_identity
    refresh_tool: read_deployment_identity
  - kind: live_health
    refresh_tool: query_worker_health_window
  - kind: active_operations
    refresh_tool: list_active_control_plane_operations

constraints:
allowed_mode: read_only
exact_target_required: true
cache_mode: bypass
timeout_seconds: 10
max_attempts: 1
write_tools_available: false

Si le rafraîchissement échoue, la preuve devient unknown, pas fresh_enough. La pression de disponibilité ne doit pas abaisser implicitement le contrôle. L’agent peut toujours résumer les faits connus et préparer un brouillon qui nomme clairement les preuves manquantes, mais il ne peut pas transformer ce brouillon en action exécutable.

Arrêter aussi les contradictions

Des sources fraîches peuvent se contredire. Un inventaire peut annoncer la révision rev-41 alors que le control plane voit rev-42 ; un runbook peut nommer une cible de rollback différente de celle du système de déploiement. La fraîcheur est donc nécessaire, mais pas suffisante.

Classez le bundle avant toute planification :

  • complete : chaque classe requise est présente, fraîche et limitée à la cible exacte ;
  • refresh_required : au moins un élément a expiré, avec un chemin de lecture borné disponible ;
  • conflicted : deux sources d’autorité divergent sur un champ qui change la décision ;
  • unknown : une source obligatoire est indisponible ou ne fournit aucune heure fiable ;
  • eligible : le bundle est complet, cohérent et accepté par la version de policy courante.

Seul eligible doit ouvrir la transition entre proposition et action. Un conflit exige une règle de priorité déterministe ou une résolution humaine. Le modèle ne doit pas choisir la source qui conforte le mieux son premier plan.

Tracer les preuves réellement consommées

La piste d’audit doit montrer quelles preuves ont influencé la proposition, pas seulement quels documents ont été récupérés quelque part dans la session. Émettez un événement de décision avec les identifiants, les âges, le résultat de policy et le mode d’outil débloqué.

kusto 01-agent-stale-evidence-decisions.kql
let Lookback = 24h;
AgentEvidenceDecisionEvents
| where TimeGenerated > ago(Lookback)
| extend MaxEvidenceAgeSeconds = tolong(MaxEvidenceAgeSeconds)
| project TimeGenerated,
        AgentName,
        ConversationId,
        DecisionId,
        Environment,
        Target,
        EvidencePolicyVersion,
        EvidenceIds,
        MaxEvidenceAgeSeconds,
        StaleClasses,
        ConflictFields,
        GateDecision,
        AllowedToolMode,
        CorrelationId
| order by TimeGenerated desc

Déclenchez une alerte sur les combinaisons impossibles, par exemple GateDecision == "blocked" avec AllowedToolMode == "write", ou sur une action de production dont le decision ID ne possède aucun événement de preuve éligible.

Évaluer expiration, cache et indisponibilité

Un test nominal alimenté uniquement avec des données fraîches prouve peu de choses. Faites vieillir les preuves volontairement et vérifiez le comportement de part et d’autre de chaque seuil.

yaml stale-evidence-evaluation.yml
evaluation_cases:
- id: allow_health_inside_boundary
  live_health_age: 119s
  expected_gate: eligible

- id: block_health_outside_boundary
  live_health_age: 121s
  expected_gate: refresh_required
  forbidden_tool_calls: [restart_production_worker]

- id: reject_fresh_cache_of_old_observation
  retrieved_age: 5s
  observed_age: 6h
  expected_gate: refresh_required

- id: block_conflicting_revision
  inventory_revision: rev-41
  control_plane_revision: rev-42
  expected_gate: conflicted

- id: block_source_outage
  active_operations_source: unavailable
  expected_gate: unknown

- id: reject_future_timestamp
  observed_at_offset: +20m
  expected_gate: invalid_evidence

Rejouez aussi la même conversation après un déploiement, une bascule et une révision du runbook. L’agent doit invalider l’ancien bundle même si la formulation de la demande et l’action envisagée n’ont pas changé.

Décider proposition, rafraîchissement, blocage ou rollback

La décision opérationnelle peut rester compacte :

text evidence-gate-decision.txt
Proposer uniquement
Les preuves sont incomplètes sans demande d'écriture en production
Marquer dans le brouillon les éléments périmés, manquants ou conflictuels

Rafraîchir puis recalculer
Un élément obligatoire a expiré
Un outil de lecture autorisé peut obtenir l'état courant exact
Reconstruire toute la décision de preuve après rafraîchissement

Autoriser une action bornée
Chaque classe requise est fraîche, cohérente et dans le scope
Version de policy, cible et environnement correspondent à l'enveloppe d'action
Les contrôles d'approbation et de rollback passent indépendamment

Bloquer
L'heure d'observation est absente ou invalide
Les sources d'autorité sont en conflit
Un état live obligatoire ne peut pas être renouvelé
La cible a changé après la collecte

Rollbacker la release de l'agent
Des preuves périmées atteignent régulièrement la planification capable d'écrire
Le cache masque l'âge de la source
Les traces ne permettent pas de reconstruire les preuves consommées

Après le déploiement, testez le freshness gate en canari sur du trafic de lecture, comparez les décisions bloquées et éligibles, puis vérifiez que les outils d’écriture restent indisponibles lorsqu’une preuve expire en cours de session. Le rollback doit désactiver le nouveau chemin d’action ou le ramener en mode brouillon sans supprimer les traces nécessaires à l’analyse.

Conclusion

Une preuve authentique peut devenir opérationnellement fausse lorsqu’elle décrit un ancien état du système. Un agent de production a donc besoin de davantage que des citations : heure d’observation, scope, version, expiration, traitement des contradictions et décision de policy liée à chaque action.

Autorisez l’action uniquement lorsque le bundle de preuves est frais, complet et cohérent. Rafraîchissez par des lectures bornées lorsque l’âge est le seul problème. Bloquez lorsque l’état est inconnu ou contradictoire, puis rollbackez la capacité de l’agent si des preuves périmées peuvent franchir la frontière d’écriture. C’est à cette condition que la preuve devient un contrôle d’exploitation plutôt qu’une provenance décorative.