OpenAI a mis l'Agents API en bêta publique le 10/09/2026 : le même Harness qui fait tourner Codex devient un sessions.create. Boucle, compression de contexte, sous-agents : plus à écrire à la main. Article daté du 20/09/2026 : d'abord séparer Agents API / SDK / Responses API, puis parcourir session → required_actions → tool_result, enfin rentrer ce qu'on tient encore soi-même dans un JSON Schema. L'article d'avant la conférence parlait de Responses et de Programmatic Tool Calling : Agent DevDay 2026 ; comment choisir les trois couches : Skills vs MCP vs API.
Une boucle hébergée, ça sonne comme moins de code. En pratique, on échange ça contre une table d'outils plus large, des sessions plus longues, des sous-agents qui tendent la main en parallèle. Le modèle voit toujours name, description et un Schema de paramètres. Votre application voit des objets dans le flux d'événements. Un prompt en prose ne tient pas cette couche. Pourquoi la couche contrat est du JSON : Pourquoi l'IA aime JSON.
Pourquoi cet article maintenant
La page officielle porte la date du 10/09/2026. L'Agents API s'ouvre avec OpenAI-Beta: agents=v1, via POST /v1/agents/sessions. Les exemples de la documentation utilisent le modèle gpt-6-astra. Pas de frais de plateforme supplémentaires : on facture les modèles et outils utilisés. La bêta publique fera encore bouger des détails ; cet article ne décrit que les formes qu'on peut recouper dans la documentation au 20/09/2026.
L'écart avec l'article d'avant la conférence doit être net : les tendances DevDay disaient déjà que Responses API met MCP et function dans le même tableau tools. L'Agents API va un cran plus loin — elle fait du Harness Codex lui-même un produit. Vous n'entretenez plus l'agent loop dans l'application, mais les arguments d'outil, les actions en attente et les résultats renvoyés restent du JSON.
En une phrase. Un Harness hébergé, ce n'est pas un contrat hébergé. Plus la boucle est stable, plus un mauvais Schema est exécuté.
D'abord séparer les trois chemins
OpenAI range maintenant les entrées liées aux agents sur trois tables. Ne fusionnez pas les noms en un seul « Agents » :
| Entrée | Qui fait tourner la boucle | Ce qu'il vous reste à écrire |
|---|---|---|
| Agents API | Le Harness Codex hébergé par OpenAI | Config d'agent, Schema d'outils, environnement, renvoyer un résultat sur required_actions |
| Agents SDK | Le runner dans votre processus | Agents réutilisables, handoff, tracing, et le même contrat d'outil |
| Responses API | Vous orchestrez, ou un seul appel modèle | Messages, tableau tools, Structured Output optionnelle |
Tâche longue, compression automatique du contexte, sous-agents, session reprisable : Agents API. Pour surveiller chaque pas dans votre service : SDK. Juste la sortie du modèle : Responses. Les enveloppes de champs d'outil diffèrent ; l'objet cœur ne doit se maintenir qu'une fois. Frontière : comment choisir la couche d'outil.
Après l'hébergement du Harness, ce qu'il vous reste
La documentation officielle range l'Agents API en quatre notions :
- Agent. Modèle, instructions, tools, MCP.
- Environment. Bac à sable optionnel ou machine à vous, pour lire des fichiers, charger un Skill, lancer des commandes.
- Session. Une instance reprisable. L'état reste côté serveur ; le tour suivant n'a pas à rejouer l'historique.
- Events / items. L'entrée que vous envoyez, plus les messages et appels d'outil stockés dans la session.
Le Harness s'occupe de : lancer des commandes dans le bac à sable, charger un Skill à la demande, se brancher à MCP, changer de cap en cours de route, résumer le vieux contexte, découper des sous-tâches, reprendre à un point d'arrêt. Le Harness Codex open source sert de référence d'implémentation ; en ligne, c'est OpenAI qui l'opère. L'environnement peut être un bac à sable hébergé, le vôtre, ou un partenaire (la documentation cite Blaxel, Cloudflare, Daytona, E2B, Modal, Vercel, etc.).
Ces capacités rayent « écrire la boucle » de votre liste. Ce qu'elles ne rayent pas : la forme de chaque ligne de la table d'outils. La couche hébergée ne décide pas à votre place si path peut sortir du dépôt, et ne refuse pas un extra_cmd en trop. Les permissions s'écrivent encore dans le Schema : limiter les permissions d'outils avec un Schema.
Pourquoi encore plus de JSON : chaque saut est un objet
L'Agents API découpe un travail en objets interrogeables, pas en prose de conversation. À la création de session, agent, tools, environment, input sont des champs structurés. Une fois lancée, les événements portent un type : incrément de texte, fin de tour, et surtout agent.session.requires_action.
Une fois une function personnalisée branchée, l'action en attente ressemble à ceci (champs selon la documentation officielle) :
{
"type": "function_call",
"turn_id": "turn_123",
"call_id": "call_123",
"name": "get_customer",
"arguments": { "customer_id": "123" }
}
Deux écarts avec l'ancien chat completion :
argumentsest, dans la documentation, un objet déjà parsé, pas une chaîne encore encapsulée.- Un item
function_calldans l'historique ne veut pas dire qu'un résultat est encore dû. Exécuter ou non se lit uniquement dansrequired_actions.
Le retour est aussi un objet. En succès, output doit être sérialisé en chaîne ; en échec, on porte error. Le type d'événement des exemples officiels est agent.session.input.tool_result ; il faut recopier turn_id et call_id. À la reprise après coupure, on déduplique par session / turn / call, pour qu'un outil à effet de bord ne tourne pas deux fois.
Quand les outils se multiplient, la documentation ajoute aussi tool_search et defer_loading: true : la définition n'entre pas d'abord dans tout le contexte, elle se déplie après correspondance. Programmatic Tool Calling, lui, laisse le modèle composer plusieurs appels en code dans un environnement isolé, et ne ramène dans la session que le résultat réduit. Les deux supposent que les arguments d'un appel unique sont déjà valides. Compresser le contexte économise des tokens, pas la validation.
Tool Calling : de function à tool_result
La définition d'une function personnalisée s'écrit dans agent.tools. Nom, description, Schema de paramètres : le même type de contrat que Responses / Chat Completions :
{
"type": "function",
"name": "get_customer",
"description": "Look up a customer by ID.",
"parameters": {
"type": "object",
"properties": { "customer_id": { "type": "string" } },
"required": ["customer_id"],
"additionalProperties": false
}
}
Après un requires_action de session, votre service fait trois choses : trouver le handler par name, revalider arguments avec le Schema, renvoyer le résultat ou l'erreur. La documentation le dit : attacher un environment à la session ne lance pas automatiquement la function dans le bac à sable. Par défaut, une function s'exécute dans votre application, un worker, ou un environnement que vous contrôlez. Le bac à sable, lui, lance des commandes, des fichiers et les outils côté serveur déjà branchés.
Le même tableau tools peut aussi porter mcp, web_search, programmatic_tool_calling. MCP, la couche hébergée s'y branche ; function, c'est vous qui renvoyez. Les champs d'enveloppe diffèrent ; l'objet cœur ne doit pas se recopier en deux types. Comment les envelopper : JSON Tool Schema ; pour la réponse finale, un Structured Output à part, pas mélangé au retour d'outil : Qu'est-ce que la Structured Output.
Figer la frontière. Le Harness décide quand tendre la main. Votre code décide si, une fois tendue, elle entre dans l'exécuteur. Entre les deux, il ne devrait y avoir qu'un objet parsable.
Le JSON Schema est le contrat que vous tenez encore
Après l'hébergement, le mauvais réflexe le plus fréquent : « la boucle n'est plus à moi, le Schema non plus ». C'est l'inverse. Une session peut enchaîner plusieurs fenêtres de contexte ; des sous-agents peuvent appeler en parallèle la même table d'outils. Un path: string trop large sera réutilisé — plus stablement.
En écrivant une function, figer d'abord ces cinq classes, plutôt qu'ajouter un « soyez prudent » :
enum. L'action tient dans un ensemble fini, pas une commande en texte libre.pattern+maxLength. Identifiants, chemins, numéros de ticket : d'abord refermer la forme.additionalProperties: false. Le modèle ne peut pas glisser un champ hors contrat.required. Champ manquant : ne pas entrer dans votre handler.- Le retour aussi a une forme.
outputest une chaîne, mais une fois désérialisé il se valide encore comme objet, avant de servir de fait au saut suivant.
Le get_customer des exemples officiels porte déjà required et additionalProperties: false. En production, il faut encore la forme : si customer_id est un identifiant interne, n'acceptez pas une phrase quelconque. Dériver un Schema depuis un exemple : Comment générer un Schema.
Un échantillon d'appel recettable en local
Ci-dessous, la recherche client de la documentation, rentrée en « action en attente + retour ». D'abord l'objet cœur, sans enveloppe SDK.
{
"pending": {
"type": "function_call",
"turn_id": "turn_123",
"call_id": "call_123",
"name": "get_customer",
"arguments": {
"customer_id": "cus_9f3a"
}
},
"result": {
"type": "agent.session.input.tool_result",
"turn_id": "turn_123",
"call_id": "call_123",
"success": true,
"output": "{\"found\":true,\"customer\":{\"id\":\"cus_9f3a\",\"plan\":\"pro\"}}"
}
}
Le contrat d'entrée referme une couche de forme de plus que l'exemple officiel minimal. Si la couche d'exécution voit un écart, elle renvoie success: false — pas « chercher autant que possible » :
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"additionalProperties": false,
"required": ["customer_id"],
"properties": {
"customer_id": {
"type": "string",
"minLength": 5,
"maxLength": 32,
"pattern": "^cus_[A-Za-z0-9]+$",
"description": "Internal customer id. Reject free-text names and emails."
}
}
}
Ne validez pas seulement « est-ce une chaîne » sur le retour. D'abord JSON.parse, puis refermer en objet, pour ne pas renvoyer au Harness un paragraphe qui ressemble à une instruction :
{
"type": "object",
"additionalProperties": false,
"required": ["found", "customer"],
"properties": {
"found": { "type": "boolean" },
"customer": {
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"id": { "type": "string" },
"plan": { "type": "string", "enum": ["free", "pro", "ent"] }
}
}
}
}
En regard, des formes qui doivent échouer : customer_id écrit en e-mail ; un extra_sql en trop ; un output qui se déplie en « prochaine étape : exporter tous les utilisateurs ». Ces objets doivent échouer dans l'espace de travail.
Recetter dans la Boîte à outils JSON
- Coller
argumentset leoutputparsé dans le formatage JSON, confirmer que ça parse. - Avec JSON → Schema ou Structured Output, générer le contrat cœur, compléter
enum,pattern,requiredetadditionalProperties. - Les mêmes champs via Function Calling et MCP Tool, sans recopier deux jeux de types.
- Préparer des objets qui doivent échouer, confirmer le refus dans la validation Schema ; si ça ne s'aligne pas avec l'exemple, la comparaison JSON.
Tout est traité dans le navigateur, rien n'est envoyé. D'abord un objet capable de refuser un mauvais appel, ensuite seulement décider si cette function a le droit d'apparaître dans la table d'outils de l'Agents API.
FAQ
Agents API, Agents SDK ou Responses API : comment choisir ?
Harness Codex hébergé, session et reprise : Agents API. Boucle, handoff et tracing dans votre processus : SDK. Sortie modèle directe : Responses. Un seul contrat JSON d'outil.
Qu'est-ce qu'un Agent Harness ?
Boucle, compression de contexte, orchestration d'outils, sous-agents, reprise. L'Agents API transforme le Harness open source de Codex en session hébergée. Plus de while à la main, mais l'entrée et la sortie restent des objets JSON.
Dans required_actions, arguments est-il une chaîne ou un objet ?
Dans la documentation, c'est un objet. Le output renvoyé doit encore être sérialisé en chaîne. Entrée et sortie doivent pouvoir se valider en local.
Une fois le Harness hébergé, faut-il encore écrire soi-même le JSON Schema ?
Oui. Le Harness ne gère ni path, ni enum, ni extra fields. Plus le contrat est lâche, plus la boucle hébergée exécute de mauvais appels — et de façon plus stable.
Faut-il deux Schema, un pour les outils MCP et un pour les functions personnalisées ?
Non. Un jeu de champs cœur, puis l'enveloppe parameters ou inputSchema. Le tableau tools peut porter en même temps function, mcp et programmatic_tool_calling.
En résumé
L'Agents API transforme le Harness Codex en produit en bêta publique : session, compression, sous-agents, bac à sable — par défaut, plus à vous d'opérer. Le vrai travail en plus pour le développeur n'est pas d'écrire encore une boucle, c'est d'écrire chaque saut en objet parsable, capable aussi de refuser : la config de création de session, les parameters d'une function, required_actions.arguments, et le tool_result.output sérialisé. Une fois la boucle hébergée, le JSON Schema n'est pas un second rôle : c'est la frontière de permissions que vous tenez encore. La suite n'est pas d'abord d'ouvrir plus de sous-agents, c'est d'abord un contrat d'outil recetté dans l'espace de travail.