AI

AgentOps : diagnostiquer une saturation de contexte avant d'augmenter la limite de tokens

Un runbook de production pour reconstruire le contexte d'un agent IA, isoler historique, retrieval et sorties d'outils, puis valider une compaction ou un rollback.

28 août 2026 aiagentopsagentsmicrosoft-foundrycontext-windowtokensretrievaltoolsobservabilityevaluationguardrailsrunbookrollbackproduction

Un agent interne d’exploitation fonctionne correctement sur une conversation courte. Après plusieurs diagnostics, il oublie une contrainte posée au début, redemande une information déjà collectée ou sélectionne un outil trop large. En parallèle, la latence et les tokens d’entrée augmentent. Relever la limite de contexte ou changer de modèle peut repousser le symptôme, sans corriger l’historique répété, le retrieval trop volumineux ou les sorties d’outils réinjectées intégralement.

Ce runbook part d’un incident concret : un agent Microsoft Foundry assiste une équipe pendant un diagnostic de production, avec des sources documentaires et des outils en lecture ou en écriture soumis à approbation. L’objectif est de reconstruire le contexte réellement envoyé à chaque appel, de préserver les contraintes qui ne doivent pas disparaître et de décider entre compaction, réduction du retrieval, correction d’un outil ou rollback du bundle agent.

Figer un parcours dégradé et sa référence

Une conversation longue ne suffit pas à prouver une saturation. Commencez par choisir un parcours qui réussissait auparavant et un échec reproductible. L’unité de comparaison est le même objectif exécuté avec le même bundle de versions, puis avec un contexte court et un contexte proche de la zone de dégradation.

yaml agent-context-incident.yml
incident:
started_at_utc: 2026-08-28T13:20:00Z
journey: qualify_application_gateway_502
symptom:
  - approval constraint no longer applied
  - tool result requested twice
  - input tokens and latency increase by turn

release_bundle:
agent: ops-assistant-2026.08.28.2
prompt: prompt-42
model_deployment: <deployment-name>
tool_catalog: tools-18
retrieval_index: runbooks-2026.08.27
context_policy: context-policy-7

comparison:
healthy_trace_id: <trace-id>
degraded_trace_id: <trace-id>
same_user_goal: true
write_tools_disabled_for_replay: true

stop_conditions:
- a write is attempted without the expected approval
- the final answer cannot cite the evidence used
- context construction cannot be tied to a version
- compaction removes an unresolved production action

Conservez les identifiants de trace, de conversation, de réponse et d’appel d’outil. Ne copiez pas automatiquement le contenu complet dans un ticket : prompts, documents récupérés et résultats d’outils peuvent contenir des données sensibles. Les tailles, rôles, versions, empreintes et identifiants suffisent souvent pour localiser l’accumulation.

Reconstituer le contexte réellement envoyé

La fenêtre utile n’est pas seulement l’historique visible dans l’interface. Un appel peut contenir le prompt système, des consignes de l’application, un résumé, des tours précédents, des documents récupérés, des définitions d’outils et leurs résultats. Le runtime peut aussi ajouter des éléments que le client ne montre pas.

Microsoft Foundry permet d’observer entrées, sorties, appels d’outils, latence et consommation de tokens dans les traces. Utilisez cette preuve pour produire un manifeste par appel de modèle. Lorsque le runtime ne fournit pas la ventilation exacte en tokens, mesurez les octets ou caractères par segment et ajoutez une estimation hors production ; ne présentez pas une estimation comme la valeur facturée.

json agent-context-manifest.json
{
"traceId": "<trace-id>",
"responseId": "<response-id>",
"conversationId": "<conversation-id>",
"modelCallId": "<model-call-id>",
"turn": 12,
"versions": {
  "agent": "ops-assistant-2026.08.28.2",
  "prompt": "prompt-42",
  "toolCatalog": "tools-18",
  "retrievalIndex": "runbooks-2026.08.27",
  "contextPolicy": "context-policy-7"
},
"segments": [
  {"type": "system", "items": 2, "bytes": 14800, "digest": "<sha256>"},
  {"type": "summary", "items": 1, "bytes": 6200, "digest": "<sha256>"},
  {"type": "history", "items": 18, "bytes": 78200, "digest": "<sha256>"},
  {"type": "retrieval", "items": 9, "bytes": 112000, "digest": "<sha256>"},
  {"type": "tool_output", "items": 6, "bytes": 164000, "digest": "<sha256>"},
  {"type": "tool_schema", "items": 14, "bytes": 44800, "digest": "<sha256>"}
],
"usage": {"inputTokens": 0, "outputTokens": 0},
"result": "tool_repeated"
}

Comparez le premier appel sain, le premier appel dégradé et l’appel qui le précède. Cherchez le segment qui croît sans apporter de nouvelle décision. Une sortie d’outil identique présente à plusieurs tours est un défaut de construction de contexte. Des documents différents mais fortement redondants pointent vers le retrieval. Un historique qui conserve chaque détail brut sans état synthétique pointe vers la mémoire de conversation.

Mesurer la croissance par segment

Normalisez les manifestes dans une table dédiée ou dans des événements custom Application Insights. La requête suivante suppose une table AgentContextEvents créée par votre instrumentation ; adaptez les colonnes au schéma réellement émis.

kusto agent-context-growth.kql
let StartTime = datetime(2026-08-28T13:00:00Z);
let EndTime = datetime(2026-08-28T14:00:00Z);
AgentContextEvents
| where TimeGenerated between (StartTime .. EndTime)
| where Journey == "qualify_application_gateway_502"
| summarize
  InputTokens=max(InputTokens),
  SystemBytes=sumif(SegmentBytes, SegmentType == "system"),
  SummaryBytes=sumif(SegmentBytes, SegmentType == "summary"),
  HistoryBytes=sumif(SegmentBytes, SegmentType == "history"),
  RetrievalBytes=sumif(SegmentBytes, SegmentType == "retrieval"),
  ToolOutputBytes=sumif(SegmentBytes, SegmentType == "tool_output"),
  ToolSchemaBytes=sumif(SegmentBytes, SegmentType == "tool_schema"),
  DistinctDigests=dcount(SegmentDigest),
  DurationMs=max(DurationMs),
  Results=make_set(Result, 10)
by ConversationId, ModelCallId, Turn, AgentVersion, ContextPolicyVersion
| extend TotalBytes = SystemBytes + SummaryBytes + HistoryBytes
                  + RetrievalBytes + ToolOutputBytes + ToolSchemaBytes
| order by ConversationId asc, Turn asc

Une hausse simultanée des tokens, de la latence et d’un segment dominant renforce le diagnostic, mais ne prouve pas à elle seule la cause fonctionnelle. Rejouez le même cas avec ce segment borné. Si l’agent échoue aussi en contexte court, examinez plutôt le prompt, le modèle, les permissions ou l’outil lui-même.

Protéger les invariants avant de compacter

Une compaction agressive peut rendre l’agent moins cher tout en le rendant dangereux. Classez les informations avant de les réduire.

À conserver sous une forme stable et versionnée :

  • objectif courant, périmètre et critères d’arrêt ;
  • contraintes de sécurité et actions nécessitant une approbation ;
  • identité, environnement et ressource réellement ciblés ;
  • actions déjà exécutées avec leur résultat et leur identifiant d’idempotence ;
  • décisions encore ouvertes, hypothèses et preuves qui les supportent ;
  • références vers les sources, leur version et leur date de lecture.

À compacter ou remplacer par une référence : détails déjà résolus, contenu documentaire dupliqué, sorties d’outils brutes après extraction des champs utiles, messages d’interface et traces verbeuses. Ne laissez pas un résumé généré devenir l’unique preuve d’une action de production : gardez un pointeur immuable vers le résultat source.

Borner historique, retrieval et outils séparément

Une seule limite globale rend le comportement difficile à expliquer. Appliquez un budget par segment et définissez ce qui se passe quand il est atteint.

yaml agent-context-policy.yml
version: context-policy-8

budgets:
recent_turns: <derived-from-evaluation>
retrieved_documents: <derived-from-evaluation>
bytes_per_tool_output: <derived-from-tool-contract>
total_input_tokens: <below-model-and-runtime-limit>

history:
keep_recent_turns_verbatim: true
compact_into_state_ledger: true
preserve_open_decisions: true
preserve_approval_and_action_ids: true

retrieval:
deduplicate_by_source_and_section: true
require_source_version: true
reject_unbounded_results: true

tools:
prefer_structured_summary: true
retain_full_result_by_reference: true
paginate_large_collections: true
never_repeat_unchanged_payload: true

on_budget_exhausted:
disable_write_tools: true
return_bounded_diagnostic: true
emit_trace_and_policy_version: true

Le budget doit être appliqué par le runtime, le composant de retrieval ou la passerelle d’outils, pas seulement formulé dans le prompt. Le modèle ne peut pas garantir qu’un payload volumineux ne lui sera jamais transmis.

Évaluer la correction sur le processus et le résultat

Une baisse de tokens n’est pas un critère de promotion suffisant. Construisez un petit jeu de cas à partir de traces expurgées : conversation courte, historique long, documents redondants, sortie d’outil volumineuse, ancienne instruction contradictoire et action nécessitant une approbation.

Mesurez au minimum : réussite de la tâche, respect des consignes, sélection et succès des outils, utilisation de leur résultat, groundedness, latence, tokens d’entrée et nombre de compactions. Les évaluateurs Foundry peuvent couvrir le respect de la tâche, la sélection d’outils, l’utilisation des sorties et la qualité de la réponse ; les contrôles déterministes doivent vérifier séparément qu’aucune écriture ne contourne l’approbation.

yaml agent-context-evaluation.yml
cases:
- id: short_baseline
  expected: {task_completed: true, context_compactions: 0}

- id: long_history_with_open_decision
  expected: {open_decision_preserved: true, repeated_tool_call: false}

- id: redundant_retrieval
  expected: {sources_deduplicated: true, grounded_response: true}

- id: oversized_tool_output
  expected: {bounded_summary_used: true, full_result_reference_kept: true}

- id: approval_required_after_compaction
  expected: {write_before_approval: false, approval_context_preserved: true}

promotion_gates:
- no regression on task completion and task adherence
- no unauthorized write attempt
- no repeated side effect
- bounded input distribution on long cases
- latency remains inside the accepted journey SLO

Exécutez plusieurs fois les évaluations non déterministes et conservez la version du modèle évaluateur. Comparez la policy candidate à la précédente sur le même corpus ; une note isolée sans baseline ne permet pas de décider.

Déployer en canari et préparer le rollback

Versionnez ensemble prompt, modèle, catalogue d’outils, index de retrieval, stratégie de résumé et budgets. Activez la nouvelle policy d’abord sur des parcours en lecture seule, puis sur une faible part de conversations. Surveillez la distribution des tokens par intention, les appels répétés, les refus de budget, la réussite des tâches, la latence et les demandes d’approbation.

Conservez la policy si la zone de dégradation disparaît sans perte d’invariant ni régression métier. Revenez au bundle précédent si les décisions ouvertes disparaissent, si les appels d’outils deviennent moins fiables ou si la compaction augmente les réponses non fondées. Maintenez les outils d’écriture désactivés si les traces ne permettent pas de prouver l’état des actions déjà lancées.

Conclusion

Une fenêtre de contexte saturée est un problème de construction d’état, pas seulement de capacité du modèle. Il faut reconstituer ce qui est réellement envoyé, attribuer la croissance à l’historique, au retrieval, aux schémas ou aux sorties d’outils, puis protéger les invariants avant toute réduction.

La décision de production est alors claire : promouvoir une policy bornée après évaluation et canari, corriger le composant qui injecte trop de données, ou rollbacker le bundle complet. Augmenter la limite n’est acceptable que si le besoin est démontré et que la croissance reste elle-même contrôlée.