Développement IA

Comment l’API Agents d’OpenAI fait-elle tenir un agent IA longtemps ? Context, Tools, Subagents et état JSON (2026)

Environ 16 min

La bêta publique du 10 septembre n’était pas « un autre chat ». OpenAI a livré le harness qui faisait déjà tourner Codex des jours : la Session continue, le contexte est compacté, chaque subagent garde sa fenêtre. Ce texte ne parle que de la survie d’un long travail : quel JSON vous stockez, ce qu’OpenAI comprime, quels items relire après une coupure.

Avant de lire

Objet Ce qu’il fait Ce que vous tenez
Session Instance durable sur des heures et des fenêtres session_id
Turn Travail asynchrone démarré par une entrée turn_id / événements terminaux
Items Messages et appels d’outils enregistrés Relecture après coupure
Compact Compression auto près de la limite Garder les champs clés
Subagent Contexte propre, travail parallèle Plafond de concurrence

Une session idle n’est pas un succès. Lisez les événements de turn et les items, pas la dernière phrase.

Un long travail vit dans une Session, pas dans un prompt plus long

La réponse de l’API Agents est concrète : créer une session durable et envoyer les entrées suivantes au même session_id. Près de la limite, le harness compacte le travail ancien. Des sous-tâches indépendantes peuvent aller à un subagent. Vous ne reconstruisez pas tout le journal.

  • Continuer : lier session_id à votre conversation. Pas de seconde session pour le même dossier.
  • Couper : les flux ne rejouent rien. Relire la session et ses items. Ne pas supposer que le client a encore le dernier saut.
  • Argent et identifiants : compact jette le détail. Numéros de commande, montants, validations dans votre JSON.

Comment Session rejoint le JSON d’outil : Tour de l’API Agents OpenAI. Qui doit posséder la boucle : Agents API vs Responses API. Si Agent et Function Calling ne font encore qu’un mot, commencer par Guide complet de l’agent IA.

La Session continue ; un Turn tourne en asynchrone

Créer une fois : modèle, outils, environnement, multi_agent. Officiel : POST /v1/agents/sessions avec OpenAI-Beta: agents=v1. Sandbox hébergée : openai_hosted :

{
  "agent": {
    "model": "gpt-6-astra",
    "instructions": "Look up orders. Confirm before any refund. Delegate independent lookups to subagents.",
    "tools": [
      {
        "type": "function",
        "name": "lookup_order",
        "parameters": {
          "type": "object",
          "properties": {
            "order_id": { "type": "string", "pattern": "^[A-Z]-\\d{4}$" }
          },
          "required": ["order_id"],
          "additionalProperties": false
        }
      },
      { "type": "web_search" }
    ],
    "multi_agent": { "enabled": true, "max_concurrent_subagents": 3 }
  },
  "environment": { "type": "openai_hosted", "workspace_directory": "/workspace" },
  "input": [
    {
      "role": "user",
      "content": [{ "type": "input_text", "text": "Look up A-1042 and keep the case open." }]
    }
  ]
}

Envoyer un autre agent.session.input.message sur la même session. Idle ouvre un nouveau turn ; actif oriente le turn en cours. Annuler avec agent.session.input.cancel — la session et le travail sauvé restent :

{
  "events": [
    {
      "type": "agent.session.input.message",
      "input": [
        {
          "role": "user",
          "content": [
            { "type": "input_text", "text": "Warehouse marked A-1042 packed. Continue." }
          ]
        }
      ]
    }
  ]
}

Lire l’issue, pas « est-ce idle ». Événements officiels : agent.session.turn.completed, failed, cancelled. Un turn achevé ne veut pas dire que chaque outil a réussi. S’il faut un résultat de fonction, lire required_actions.

La chaîne d’outils traverse des heures ; le retour reste du JSON

Sur un long travail, le modèle ne remplit toujours que les arguments. Quand votre fonction est due, le harness s’arrête ; vous renvoyez un résultat clé par turn_id / call_id. Quatre heures plus tard, la forme est la même :

{
  "type": "tool_result",
  "turn_id": "turn_4",
  "call_id": "call_3c91",
  "success": true,
  "output": "{\"order_id\":\"A-1042\",\"status\":\"packed\"}"
}

Les fonctions vous demandent. MCP et web_search peuvent être exécutés par le harness. Les subagents héritent MCP et la recherche web ; ils n’héritent pas des outils function. Les effets de bord sur vos commandes restent sur l’agent principal.

La porte reste le Schema. Un object large est plus dangereux sur un long run : après compact, le modèle invente plus facilement un identifiant. MCP est une autre table d’outils ; la frontière est MCP Tool Schema vs Function Calling.

Compact réduit la fenêtre, pas votre grand livre

Libellé officiel : à l’approche de la limite, l’API Agents compacte le contexte ancien pour que le travail traverse des fenêtres. Vous n’écrivez pas ce compresseur. Ce n’est pas la couche context_management / /responses/compact de Responses.

  • API Agents : la session se compacte elle-même. Relire les items et vérifier que le numéro de commande est encore là.
  • Responses : activer compact, ou appeler l’endpoint compact. Faire passer l’item de compaction chiffré ; ne pas le tailler à la main.
  • Les deux : la compression jette le détail. Montants, confirmations, mises en circulation restent chez l’hôte.

Si order_id ne vit que dans le chat, vous avez donné le grand livre à un résumé. Schema comme porte : Pourquoi l’IA a besoin de JSON Schema.

Subagents : parallèle, avec un contexte séparé

Avec multi_agent.enabled, le harness fournit create, message, wait et interrupt. Vous ne déclarez pas ces outils. La coordination apparaît en items :

{
  "data": [
    {
      "type": "function_call",
      "turn_id": "turn_4",
      "call_id": "call_3c91",
      "name": "lookup_order",
      "arguments": "{\"order_id\":\"A-1042\"}"
    },
    {
      "type": "create_subagent_call",
      "agent_id": "agent_main",
      "task": "Compare A-1042 packing notes with the invoice."
    }
  ]
}
  • Contexte propre : chaque subagent en a un. L’agent principal referme avec create / message / wait / interrupt.
  • Plafonner la concurrence : défaut 6, hors coordinateur. En production, figer un nombre plus petit.
  • Un item terminé ≠ une sous-tâche finie : un create_subagent_call achevé dit seulement qu’il a été envoyé. Lire agent.session.subagent.created et les items de ce subagent.

Un handoff doit être du JSON auditable, pas « passez ça à la facturation ». Comment le saisir en Schema : JSON Schema et flux d’agents.

Tenez votre état ; n’attendez pas que compact s’en souvienne

OpenAI garde la config de session, la conversation et le travail sauvé. Il vous faut encore un objet métier reconstructible après une coupure :

{
  "session_id": "sess_8f21",
  "order_id": "A-1042",
  "last_turn_id": "turn_4",
  "last_call_id": "call_3c91",
  "awaiting": "tool_result",
  "compact_checkpoint": "after_turn_3"
}

Cet objet n’est ni une Session, ni une session SDK, ni une conversation Responses, ni un sandbox. En supprimer un ne vide pas les autres. La résidence des données n’est pour l’instant que les États-Unis ; un sandbox auto-hébergé ne rend pas l’API ZDR.

Cinq pas pour finir un travail qui traverse les fenêtres

Ne pas reconstruire une démo à l’envers. Marcher les objets :

  1. Créer une session et écrire session_id dans votre conversation. Serrer la table d’outils et le Schema une fois.
  2. S’abonner, puis envoyer plus tard. Ouvrir le flux avant agent.session.input.message, sinon les premiers événements disparaissent.
  3. Réécrire sur required_actions. Réutiliser le même turn_id / call_id. Pas de nouvelle session pour réessayer.
  4. Après une coupure, lister les items. Les flux ne rejouent rien. order=asc et aligner le numéro de commande et tout appel ouvert.
  5. Après compact, revérifier le grand livre. Si le résumé a perdu A-1042, le saut suivant en inventera un.

Les cinq reposent sur des objets. En sauter un, c’est parier que la fenêtre n’est pas encore pleine.

Relire un long travail en étalant deux traces JSON

La plupart des échecs sont des objets décalés, pas un modèle plus bête.

  • formater les items de session à côté de votre état local. Confirmer que session_id / call_id s’alignent.
  • Avec extraire en JSONPath, extraire order_id, turn_id et un agent_id de subagent.
  • JSON Diff les items avant et après compact. Si l’identifiant a dérivé, regarder la compression d’abord.
  • Lister séparément les items de l’agent principal et ceux d’un subagent. Mélangés sur une page, la coordination s’accroche au mauvais acteur.
  • Ne pas coller commandes, sessions ou MCP intranet dans un formateur public. JSONSort tourne dans le navigateur, en local.

Si les deux traces se reconstruisent, vous avez continué le travail. Sinon vous l’avez redeviné.

Pièges courants

  • Prendre idle pour un succès. Lire turn.completed / failed / cancelled, puis les résultats d’outils.
  • Attendre que le flux rejoue après une coupure. Docs officielles : non. Relire session et items.
  • Laisser le numéro de commande dans le seul chat. Après compact, le résumé peut perdre A-1042.
  • Activer multi_agent sans plafond. Le défaut 6 éventaille encore jetons et effets de bord.
  • Envoyer votre fonction à un subagent. Les subagents ne prennent pas les outils function. Les consultations restent sur l’agent principal.
  • Traiter Session et Conversation comme une seule ressource. En supprimer une ne vide pas l’autre.

FAQ

Un long run n’est-il qu’un prompt plus long ?

Non. C’est une session durable, un compact automatique, et le JSON métier que vous gardez. Un prompt système plus long ne lève pas la limite de fenêtre.

Puis-je mêler le compact Agents et le compact Responses ?

Non. Une session Agents se compacte elle-même. Responses utilise context_management ou /responses/compact. Deux ressources, deux prochaines entrées de fenêtre.

Comment récupérer après une coupure ?

Charger la session et les items avec le session_id stocké. Le flux ne rejoue pas les événements manqués. Aligner last_turn_id et tout call_id ouvert, puis réécrire ou envoyer le message suivant.

Un subagent peut-il appeler mon lookup_order ?

Non. Docs officielles : pas d’outils function. Ils héritent MCP, la recherche web, et les fichiers / commandes de l’environnement. Les consultations de commandes restent sur l’agent principal.

Un sandbox auto-hébergé donne-t-il Zero Data Retention ?

Non. Docs officielles : l’API Agents ne prend aujourd’hui que la résidence américaine et pas le ZDR. Un sandbox self-hosted ne change rien.

Conclusion

Un long run n’est pas un modèle plus intelligent. C’est une Session durable, un compact automatique, et un état JSON que vous tenez. Arguments d’outils, événements de turn, items de subagent : des objets sur cette chaîne.

Écrivez si vous pouvez reconstruire A-1042 après une coupure. Si oui, le travail est vivant. Les modèles changent ; session_id et Schema doivent d’abord tenir.

Pour aller plus loin

Journal : Première publication