Tutoriel

Qu’est-ce que l’AI Agent State ? Guide 2026 : gestion d’état, JSON State, Memory, Workflow et exécution de tâches

L’historique de chat n’est pas un état. Un agent ne peut pause, reprendre et passer le relais que s’il existe un instantané JSON vérifiable : objectif, étape, reçus d’outils, pointeurs mémoire et enveloppe de tâche.

En 2026, un agent en production bloque rarement parce que le modèle « n’est pas assez intelligent ». Il bloque après un crash, un réessai ou une validation humaine — quand le runtime ne sait plus à quel hop il en était. Le guide précédent a plié l’agent en boucle observer–décider–appeler ; voir Qu’est-ce qu’un AI Agent. Celui-ci ajoute la couche hors boucle : l’état. L’état n’est pas tout le transcript dans le contexte, ni un extrait vectoriel. C’est un instantané structuré de la position du runtime — presque toujours du JSON. Nous séparons JSON State, mémoire, workflows et exécution, puis renvoyons vers MCP sans état, A2A, Structured Output et le guide 1M tokens.

Qu’est-ce que l’Agent State : chat et sessions

Une phrase suffit : l’Agent State est l’instantané structuré qui rend un run pausable, reprenable et rejouable. Il répond à quatre questions : quel est l’objectif, à quel hop sommes-nous, quels résultats sont déjà commités, qui bloque la suite (outil, humain, maxSteps). Le modèle choisit une action d’après l’observation courante ; le runtime exécute et réécrit la position. Sans position vérifiable, la boucle ne peut que recoller tout le chat — c’est du chat, pas de la gestion d’état.

L’historique est la liste que voit le modèle : user / assistant / tool. Une session (Conversations OpenAI, previous_response_id, l’ancien Thread Assistants) est la poignée que voit le fournisseur : quels items côté modèle la requête suivante doit emporter. L’état est la position que voit votre runtime : où en est le rapprochement, le montant à confirmer, le nœud de graphe, le checkpoint id. Ils peuvent coexister ; ils ne doivent pas se faire passer l’un pour l’autre. Traiter messages[] comme seule vérité, et les réessais double-facturent ou double-envoient. Traiter un Conversation id comme état métier, et un changement de fournisseur fait tomber la position. Après Assistants → Responses, le primitif de session a changé — liez encore moins les instantanés métier aux objets plateforme. Voir migration Assistants → Responses.

La plupart des incidents 2026 mélangent les couches : arguments d’outil écrits dans l’état, blob d’état entier fourré dans le prompt suivant, mémoire longue rejouée comme checkpoint. Une fois séparées, le debug a une prise : le modèle a mal lu une observation ; le reducer / Schema a écrit une mauvaise position ; la mémoire a rappelé une préférence fausse. La carte :

Concept Qui lit Si c’est perdu
Chat / messagesLe modèle (contexte de ce tour)Réponses fausses ; souvent reprojectable depuis un checkpoint
Session / ConversationLe fournisseur de modèleLes items de raisonnement dérivent ; la position métier doit rester autonome
Agent State / checkpointVotre runtime et l’orchestrateurReprise dangereuse ; réessai susceptible de double-écrire
MemoryRecherche et préférences inter-runsHabitudes oubliées ; jamais la vérité de l’étape courante

JSON State : le checkpoint est le contrat

On écrit l’état en JSON pour valider, differ et rejouer. Les orchestrateurs de la famille LangGraph persistent un checkpoint à chaque super-step, les enfilent avec thread_id, et pointent une image avec checkpoint_id. En prod : saver Postgres ou SQLite, pas un MemorySaver en processus. Les noms changent ; la forme ne doit pas : un objet parseable avec schemaVersion, enum de status, step, working, memoryRefs. Le sérialiseur peut être JsonPlus ou du JSON étendu, mais la couche que vous journalisez, exposez et déboguez doit être un JSON object ordinaire — sinon ni Schema ni Diff navigateur.

L’instantané ci-dessous n’est que de la position. Pas de messages[], pas de HTTP aval brut. Les champs métier vivent dans working ; un appel d’outil garde name et callId ; la mémoire n’est que des pointeurs. Projetez le transcript depuis le checkpoint vers le contexte modèle — ne traitez pas le contexte comme état à l’envers.

{
  "schemaVersion": "1.0",
  "runId": "run_7c2a",
  "threadId": "thr_invoice_42",
  "goal": "Reconcile January 2026 paid invoices",
  "status": "awaiting_tool",
  "step": 3,
  "maxSteps": 12,
  "node": "call_tools",
  "plan": ["searchInvoices", "sumTotals", "askConfirm"],
  "working": {
    "invoiceCount": 2,
    "currency": "USD"
  },
  "pendingTool": {
    "name": "searchInvoices",
    "callId": "call_8f3a"
  },
  "memoryRefs": ["mem_user_prefs", "mem_last_reconcile"],
  "checkpointId": "ckpt_3"
}

Les deux styles tiennent : un instantané complet à chaque fois, ou un JSON Patch / reducer. Le complet se diff et se rejoue clairement ; le patch économise du stockage et doit lui-même être validable. Dans les deux cas, figez un Schema Draft 2020-12 : status en enum (running / awaiting_tool / awaiting_human / succeeded / failed / cancelled), step entier, clés de working métier, additionalProperties false pour que les déchets d’outils ne fuient pas. La réponse à l’utilisateur ou à un système aval est un fichier Structured Output séparé — ne le partagez pas avec le checkpoint. Voir AI Structured Output.

Memory : le souvenir n’est pas la position machine

La mémoire répond à « que faut-il encore savoir à travers le temps ». L’état répond à « où cette image s’est arrêtée ». Trois couches courantes en 2026 : mémoire de travail (messages de ce tour et tool_result récents), mémoire courte / fil (chaîne de checkpoints sur un thread_id — short-term memory LangGraph), mémoire longue (Store inter-fils : préférences, faits, procédures). Tout fusionner dans un gros JSON a l’air économique ; rejeu et politiques d’oubli cassent ensemble.

Par contenu : épisodique (ce qui s’est passé dans ce rapprochement), sémantique (l’utilisateur préfère des totaux USD), procédurale (étapes réutilisables : « chercher les factures, puis sommer »). Seules les mémoires citées par le run courant vont dans State.memoryRefs. Une fenêtre à un million de tokens ne retire pas ces pointeurs — une fenêtre est un budget, pas une source de vérité. Voir contexte 1M tokens.

Couche Support typique À ne pas mettre ici
Mémoire de travailmessages / tool_result de ce tourPréférences utilisateur inter-sessions en entier
Court terme / checkpointInstantanés d’état sur un thread_idTexte brut du store vectoriel, journaux non coupés
Store long termeItems clés par userId / namespacestep courant, pendingTool, clés d’idempotence
Session fournisseurConversation / previous_response_idVotre objet working métier

Donnez aux items longs une enveloppe stable : id, kind, scope, text ou data, source, updatedAt. source dit si l’utilisateur l’a énoncé, si un outil l’a renvoyé, ou si le modèle l’a résumé — les deux derniers doivent être révocables. Après un hit, n’écrivez que l’id dans l’état et injectez le corps dans le contexte à la demande. Exemple :

{
  "id": "mem_user_prefs",
  "kind": "semantic",
  "scope": "user",
  "userId": "u_1042",
  "text": "Prefers USD totals and weekday email summaries",
  "source": "explicit_setting",
  "updatedAt": "2026-09-01T09:00:00Z"
}

Workflow et agent : qui trace les arêtes, qui écrit l’état

Dans un workflow classique (n8n, Temporal, automate maison), les humains câblent le hop suivant. L’état, ce sont des variables : id commande, nombre de réessais, compensation déjà émise. Chez un agent, le modèle choisit le hop d’après l’observation JSON courante ; l’état note aussi plan, node, pendingTool. La prod 2026 est rarement de race pure : un graphe à nœuds agents — les arêtes sont du workflow, l’intérieur d’un nœud est une boucle d’outils. Un seul objet JSON sert deux lecteurs : l’orchestrateur lit status / node ; le modèle ne voit que le sous-ensemble d’observation que vous projetez.

MCP ne possède pas cette couche. Le MCP distant vers 2026-07-28 est du JSON-RPC sans état : chaque requête porte ses métadonnées ; le serveur n’oublie pas « à votre place » la position métier. C’est le bon choix de transport, pas « un agent ne peut pas avoir d’état ». L’état applicatif reste dans votre checkpoint. Détails : MCP sans état. Le Schema des arguments d’outil et le Schema d’état doivent être deux fichiers — entrées d’un hop vs position machine. Voir MCP et JSON Schema.

La délégation latérale passe par A2A : le pair est un agent opaque ; la tâche a son cycle (submitted / working / completed / failed) et des artifacts. C’est une autre machine d’état — ne la fusionnez pas dans le checkpoint local. L’orchestrateur les épingle avec parentRunId. Comparer A2A vs MCP. Une passerelle de modèles (un /v1 local, par ex.) ne change que l’approvisionnement d’inférence, pas la forme d’état. Un contrat stable rend le repli utile.

Exécution : run, step, idempotence, réessais

La couche d’exécution transforme l’instantané en machine récupérable. Chaque objectif utilisateur ouvre un runId ; chaque hop d’outil est un step ; toute écriture aval (facture, e-mail, ticket) porte une idempotencyKey. Après un crash, reprendre au dernier checkpoint et ne pas rejouer les effets des steps déjà réussis. Les pending writes (certains nœuds OK, d’autres non) appartiennent à l’instantané — pas à l’intuition d’un opérateur.

La validation humaine est un status de premier rang, pas une branche spéciale : status=awaiting_human, l’objet à confirmer dans working, et la reprise n’autorise que des transitions légales (approuver → continuer, refuser → échouer ou replanifier). Ne « redemandez pas au modèle » à la place d’une transition — le modèle ne voit pas un clic que vous n’avez pas écrit dans l’observation. maxSteps, annulation et échec Schema sont aussi des arrêts : écrivez-les dans status, pas seulement dans les logs.

Choisissez un maître d’historique entre la session fournisseur et votre journal d’exécution. Responses peut enchaîner des items de raisonnement via Conversation ou previous_response_id ; cette bande est côté modèle, pas la position de rapprochement. Recommandé : vous possédez le checkpoint et l’enveloppe de tâche ; la plateforme ne possède que les items de raisonnement qu’elle exige de rejouer (certains vendeurs demandent reasoning_content intact dès qu’il y a tool_calls). Enveloppe :

{
  "taskId": "task_a2a_91",
  "parentRunId": "run_7c2a",
  "kind": "delegate",
  "status": "working",
  "idempotencyKey": "inv-jan-2026-reconcile",
  "steps": [
    { "id": "s1", "name": "searchInvoices", "ok": true },
    { "id": "s2", "name": "sumTotals", "ok": null }
  ],
  "artifacts": []
}

En déléguant à un agent enfant, stockez le taskId distant dans working ou steps locaux — ne aplatissez pas leurs artifacts dans le même checkpoint. Les produits intermédiaires sont une nouvelle famille JSON ; tenez-les hors du Structured Output final et des arguments MCP. Tamponnez dès maintenant correlationId / runId sur chaque journal d’outil. L’observabilité type DevDay doit joindre sur la même clé. Voir prédictions DevDay 2026.

Validation terrain et JSONVue

Avant qu’un checkpoint atterrisse : parse → Schema → règles métier. Un modèle habile ne remplace pas ces trois pas. Un mauvais état est plus grave que de mauvais arguments : les arguments sont un hop ; l’état est la vérité du run.

  1. JSON.parse du checkpoint ; en échec, refuser l’écriture et garder le ckpt_id précédent.
  2. Valider status / step / working contre le Schema d’état (Draft 2020-12) ; émettre path et keyword.
  3. Porte métier : step monotone, clé d’idempotence stable, chaque memoryRef existe, transitions de status illégales en échec fermé.

Alignez trois blobs : ckpt_n, ckpt_n+1, et l’observation projetée vers le modèle. Un saut de forme est presque toujours le reducer. Dans le navigateur : formateur JSON pour lire l’arbre ; Validateur JSON Schema pour figer les enveloppes State et Memory ; JSON Diff pour les checkpoints adjacents. Partagez les fixtures valid / step manquant / status illégal en CI et en debug manuel.

Pour aller plus loin : Qu’est-ce qu’un AI Agent, Structured Output, MCP sans état, A2A vs MCP, contexte 1M tokens.

FAQ

État et mémoire sont-ils la même chose ?

Non. L’état est la position du run courant (peut-on reprendre sans danger ?). La mémoire est le souvenir dans le temps (préférences, faits, vieux épisodes). Une chaîne de checkpoints peut servir de mémoire courte ; un store long ne doit pas porter step / pendingTool. Le rejeu utilise l’état ; la recherche utilise la mémoire.

Les fenêtres font 1M — faut-il encore des checkpoints ?

Oui. Une fenêtre décide combien d’observation tient ce tour. Elle ne décide pas à quel hop reprendre après un crash, ni si un réessai double-écrit. Traiter tout l’historique comme état aggrave la facture et la surface de panne. 1M est un outil de budget ; un checkpoint est un outil d’exécution.

Nous utilisons Conversations OpenAI — faut-il encore stocker du JSON State ?

Oui. Conversation / previous_response_id continue des items côté modèle, pas votre position métier. Après un quota, une région ou une passerelle, la session fournisseur peut ne plus s’aligner. working, clés d’idempotence et status d’approbation humaine appartiennent à du JSON que vous contrôlez.

Un MCP sans état interdit-il un agent à état ?

Non. « Sans état » au protocole signifie seulement que chaque tools/call porte ses arguments ; le serveur ne stocke pas votre avancement de rapprochement. L’état applicatif vit dans votre checkpoint ; MCP reste découverte et transport. Gardez inputSchema et le Schema d’état dans deux fichiers.

Synthèse et suite

L’Agent State 2026 en une ligne : le runtime retient la position dans un instantané JSON validable, la mémoire ne fournit que le souvenir cité, un workflow trace les arêtes ou les remet au modèle, et l’exécution transforme l’instantané en machine récupérable avec run / step / clés d’idempotence. L’historique de chat et les sessions fournisseur ne remplacent pas cet instantané.

Ensuite : écrivez votre Schema d’état et un checkpoint valid ; Différez les instantanés adjacents dans JSONVue ; ajoutez les fixtures champ manquant et status illégal. Définition de boucle dans l’article Agent ; forme de réponse finale dans Structured Output ; protocoles dans MCP / A2A.