API Agents OpenAI + JSON Schema en pratique : un JSON stable, vérifiable, exécutable (2026)
Le texte générique sur Schema a déjà demandé pourquoi un contrat. Celui-ci ne tombe que sur l’API Agents : chaque JSON rempli par le modèle doit passer une porte avant toute exécution. Paramètres d’outil, entrée MCP et votre grand livre sont trois portes — pas la phrase « veuillez sortir du JSON ».
Avant de lire
| Porte | Ce qu’elle verrouille | Qui exécute |
|---|---|---|
| Function parameters | order_id, booléens, enums | additionalProperties: false |
| Contrôle hôte | arguments dans required_actions | Pas de passage, pas de fonction |
| Carte finale | L’objet montré à l’utilisateur | Second Schema ; pas text.format |
| MCP | inputSchema d’une autre table d’outils | Les subagents peuvent l’hériter |
Un Schema est une porte, pas une politesse. Un object large est une porte de plus laissée ouverte.
Stable, vérifiable, exécutable : trois procédures, pas un interrupteur
Sur l’API Agents, le modèle ne remplit toujours que du JSON. Une fonction officielle a besoin d’un Schema parameters : required, additionalProperties: false, et pattern / enum là où ça compte. Cela stabilise le remplissage. Vérifiable : relancer le même Schema sur arguments avant d’exécuter. Exécutable : l’hôte crée des effets de bord seulement après la porte.
- Stable : écrire un Schema d’outil serré. Un
objectlarge invite des clés inventées. - Vérifiable : valider l’appel dans
required_actionsen local. Un function_call dans l’historique ne suffit pas. - Exécutable : ne pas lancer
lookup_ordersi la porte échoue. Renvoyersuccess: falseet une raison. Ne pas toucher le magasin en silence.
Comment les outils sont appelés : Tour de l’API Agents OpenAI. Comment les quatre objets se pincent sur des ID : Structures JSON de l’API Agents. Le partage générique Schema / Function Calling / Structured Output : Pourquoi l’IA a besoin de JSON Schema. Ce texte ne redéfinit pas ces mots. Il les pose seulement sur une session Agents.
Première porte : parameters sur agent.tools
Une entrée function officielle est un nom, une description et un JSON Schema. L’écrire dans agent.tools à la création. Commande fictive — pas de secrets réels :
{
"type": "function",
"name": "lookup_order",
"description": "Look up an order by ID. Confirm before any refund.",
"parameters": {
"type": "object",
"properties": {
"order_id": { "type": "string", "pattern": "^[A-Z]-\\d{4}$" },
"include_lines": { "type": "boolean" }
},
"required": ["order_id", "include_lines"],
"additionalProperties": false
}
}
Peu de leviers comptent : chaque champ dans required ; les objets ferment les clés en trop ; les identifiants usent pattern, pas une string nue. Les fonctions se chargent d’avance par défaut. Pour en différer une : defer_loading: true et tool_search sur la table. La porte elle-même ne change pas.
Les arguments qui ratent la porte ne doivent pas tourner
Quand la session s’arrête sur agent.session.requires_action, required_actions tient un function_call. Celui-ci ressemble à une consultation et a déjà sauté la clôture : l’ID rate le pattern, une clé refund apparaît :
{
"type": "function_call",
"turn_id": "turn_2",
"call_id": "call_3c91",
"name": "lookup_order",
"arguments": {
"order_id": "1042",
"include_lines": true,
"refund": true
}
}
Même couple turn_id / call_id — une forme qui passe ressemble à ceci. Clé en trop, required manquant, pattern dérivé : aucun ne doit s’exécuter :
{
"type": "function_call",
"turn_id": "turn_2",
"call_id": "call_3c91",
"name": "lookup_order",
"arguments": {
"order_id": "A-1042",
"include_lines": true
}
}
Deuxième porte : l’hôte relance le même Schema
Le Schema officiel améliore les remplissages. Il n’assume pas vos effets de bord. Valider en local avant d’exécuter. Enregistrer un échec comme objet à relire :
{
"check": "lookup_order",
"schema": "parameters",
"pass": ["order_id", "include_lines"],
"fail": [
{ "path": "/order_id", "reason": "pattern" },
{ "path": "/refund", "reason": "additionalProperties" }
],
"execute": false
}
En échec, envoyer agent.session.input.tool_result avec success: false et un error lisible. En succès, sérialiser output en chaîne. Pas de nouvelle session comme « retry ».
{
"type": "agent.session.input.tool_result",
"turn_id": "turn_2",
"call_id": "call_3c91",
"success": false,
"error": "order_id failed schema: expected ^[A-Z]-\\d{4}$"
}
La carte finale est une troisième porte, pas text.format de Responses
La carte commande montrée à l’utilisateur n’est pas le Schema des paramètres d’outil. Une session Agents n’applique pas automatiquement text.format.json_schema de Responses. Tenir trois tâches à part :
- Paramètres d’outil : verrouiller la porte des effets de bord. Consultations et écritures ici.
- Carte finale : extraire un objet des items, le valider sur un second Schema, puis l’afficher.
- S’il faut verrouiller la forme finale avec Structured Outputs : c’est la couche Responses. Ne pas la mêler à la même ressource de session.
Qui doit posséder la boucle : Agents API vs Responses API. Une ligne MCP est une autre table d’outils ; son inputSchema veut encore additionalProperties: false. Frontière : MCP Tool Schema vs Function Calling. Les subagents héritent le Schema MCP, pas votre function.
Cinq pas vers un JSON exécutable
Ne pas partir de « veuillez sortir du JSON ». Marcher les portes :
- Écrire d’abord le Schema d’outil. Serrer pattern, enum, required, additionalProperties: false une fois.
- Charger le même Schema dans le validateur hôte. Pas de seconde copie qui dérive.
- Sur required_actions, valider puis exécuter. Porte ratée → success: false.
- Donner à la carte finale son propre Schema. Valider après extraction des items. La prose n’est pas une carte.
- Garder vous-même les champs du grand livre. Compact jette le détail. Montants et validations ne vivent pas seulement dans le chat.
Comment un flux devient Schema : JSON Schema et flux d’agents. Ce texte demande seulement que chaque porte d’une session Agents soit un objet.
Relire une sortie stable en étalant deux traces JSON
La dérive est souvent une porte mal alignée, pas un modèle plus bête.
- formater parameters à côté d’arguments. Chercher clés en trop et champs manquants.
- Avec extraire en JSONPath, extraire
order_idet$.parameters.required. - JSON Diff un appel qui échoue et un qui passe. Si le pattern a dérivé, lire le Schema d’abord.
- Relire le Schema de carte et celui d’outil séparément. Verrouiller la carte sans les paramètres, c’est de la façade.
- 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 contre le Schema, vous avez collecté du JSON. Sinon de la prose.
Pièges courants
- N’écrire « veuillez sortir du JSON » que dans instructions. Ce n’est pas une porte. La porte est parameters plus le validateur hôte.
- Ne lister qu’une partie des champs dans required. Les clés oubliées peuvent rester vides. Le saut suivant a des trous.
- Laisser additionalProperties absent ou true. Les exemples officiels ferment les clés en trop. Un refund de trop et l’effet de bord penche.
- Copier text.format de Responses dans une session Agents. Deux ressources. Valider la carte finale vous-même.
- Lancer la fonction après un échec de contrôle. Exécutable veut dire : la porte est passée. Écrire l’erreur d’abord.
- Envoyer votre function à un subagent. Les subagents n’ont pas d’outils function. Le Schema serré reste sur l’agent principal.
FAQ
L’API Agents a-t-elle Structured Outputs ?
Les paramètres d’outil se serrent avec JSON Schema. L’objet montré à l’utilisateur n’est pas auto-enveloppé dans text.format de Responses. Verrouiller la forme finale vous-même, ou poser ce saut sur Responses.
S’il existe un Schema officiel, dois-je encore valider ?
Oui. La porte officielle améliore les remplissages. Les effets de bord ont lieu dans votre processus. Passer arguments de required_actions dans le même Schema ; échouer avec success: false.
Faut-il pattern et enum ?
Oui. Une string nue laisse le modèle remplir 1042 au lieu de A-1042. Montants et statuts veulent un enum ou un format explicite. Les types larges sont le passage habituel par-dessus la clôture.
Les outils MCP ont-ils besoin de la même discipline ?
Oui. inputSchema doit fermer les clés en trop et lister chaque required. Les subagents héritent MCP, pas les functions. La porte de vos commandes reste sur l’agent principal.
Un échec de contrôle bloque-t-il la session ?
Renvoyer un tool_result sur le même couple turn_id / call_id. Un error clair laisse le modèle réessayer avec de nouveaux arguments. Pas de nouvelle session comme retry.
Conclusion
Un JSON stable sur l’API Agents n’est pas un prompt plus long. Ce sont trois portes : parameters d’outil, un contrôle hôte, et un Schema séparé pour la carte finale. L’exécution vient après la porte.
Écrivez quelle règle ferait échouer A-1042. Si vous pouvez pointer un chemin, les données sont vérifiables. Les modèles changent ; les portes doivent d’abord tenir.
Pour aller plus loin
- API Agents OpenAI, tour complet Comment Session, tool calling et Schema se lient
- Structures JSON de l’API Agents Agent / Tool Call / Tool Result / Handoff
- Agents API vs Responses API Qui doit faire tourner la boucle
- API Agents longue durée Session, compact, subagents
- Pourquoi l’IA a besoin de JSON Schema Schema / Function Calling / Structured Output
- Boîte à outils locale JSONSort Formater, valider, Diff
- Tous les articles Plus de notes JSON et IA
Journal : Première publication