Function calling et structured outputs
Le moyen le plus rapide de transformertransformerUn Transformer est une architecture de réseau de neurones qui utilise le self-attention pour traiter des séquences en parallèle. Elle est au cœur de la plupart des modèles de langage et d'IA générative actuels.Voir la définition complète → un chatbot en système consiste à donner au modèle un ensemble de fonctions qu'il peut appeler et à exiger que ses réponses reviennent sous forme de JSON strict auquel votre code peut faire confiance. Cette leçon creuse les deux mécanismes de l'APIAPIApplication Programming Interface : une interface standardisée qui permet aux applications de communiquer et d'échanger des données sans connaître leur fonctionnement interne respectif.Voir la définition complète → OpenAI : le tool calling (la boucle où le modèle demande à votre code d'exécuter quelque chose) et les Structured Outputs (la garantie que la réponse du modèle correspond à votre schémamaUtiliser un logiciel pour automatiser les tâches et campagnes marketing répétitives, afin de personnaliser à grande échelle sur des canaux comme l'email, le web et le social.Voir la définition complète →). Ils résolvent des problèmes différents et se combinent très bien.
Nous utiliserons la Responses API, l'interface principale actuelle d'OpenAI. Chat Completions fonctionne toujours et repose sur des concepts quasi identiques, mais c'est vers Responses que va l'écosystème.
Deux mécanismes, deux rôles
Gardez-les bien distincts :
- Function (tool) calling : le modèle décide qu'il a besoin de données externes ou d'une action, et émet une requête structurée pour appeler l'une de *vos* fonctions. Votre code l'exécute, renvoie le résultat, et le modèle continue. C'est ainsi que le modèle atteint ce qui est hors de son contexte : bases de données, votre API, une calculatrice, la météo.
- Structured Outputs : vous forcez la *réponse finale* du modèle à se conformer exactement à un JSON SchemaSchemaUn schema est le plan formel qui définit comment les données sont structurées, nommées, typées et reliées entre elles au sein d'une base de données, d'un fichier ou d'un message.Voir la définition complète →. Pas de prose, pas de balises markdown, pas de « Bien sûr, voici votre JSON ». Juste des données valides et parsables, à chaque fois.
On utilise souvent les deux : les tools pour collecter les faits, les Structured Outputs pour emballer le résultat.
Définir un tool
Un tool est une description JSON d'une fonction : un nom, une description et un schéma de paramètres. La description n'est pas décorative. **Le modèle la lit pour décider *quand* et *comment* appeler la fonction**, alors écrivez-la comme une documentation destinée à un développeur junior.
from openai import OpenAI
client = OpenAI()
tools = [{
"type": "function",
"name": "get_weather",
"description": "Get the current temperature for a city in Celsius.",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name, e.g. 'Lisbon'"}
},
"required": ["city"],
"additionalProperties": False
}
}]Deux détails comptent. required liste les arguments que le modèle doit fournir, et additionalProperties: False empêche le modèle d'inventer des champs supplémentaires. Les deux poussent vers des appels prévisibles.
La boucle de tool calling
Voici le point sur lequel les gens se trompent : le modèle n'exécute pas votre fonction. Il renvoie une *demande* de l'appeler. Vous exécutez la fonction, renvoyez le résultat, et rappelez le modèle. Cet aller-retour est la boucle, et elle vous appartient.
Le déroulé :
- Envoyez le message utilisateur avec vos définitions de tools.
- Le modèle répond avec un ou plusieurs éléments
function_call(ou une réponse normale si aucun tool n'est nécessaire). - Votre code exécute chaque appel et ajoute un
function_call_outputavec le résultat. - Vous rappelez le modèle avec l'input mis à jour. Il rédige alors la réponse finale.
import json
def get_weather(city):
# Imaginons que cela appelle une vraie API.
return {"city": city, "temp_c": 19}
input_list = [{"role": "user", "content": "What's the weather in Lisbon?"}]
response = client.responses.create(
model="gpt-4.1",
tools=tools,
input=input_list,
)
# On reporte la sortie du modèle dans la conversation.
input_list += response.output
for item in response.output:
if item.type == "function_call":
args = json.loads(item.arguments)
result = get_weather(**args)
input_list.append({
"type": "function_call_output",
"call_id": item.call_id,
"output": json.dumps(result),
})
final = client.responses.create(
model="gpt-4.1",
tools=tools,
input=input_list,
)
print(final.output_text)Notez le call_id. Le modèle peut demander plusieurs appels de tools dans un même tour (parallel tool calling), et chaque sortie doit être rattachée à son appel par id. Ajoutez tous les résultats avant de faire la requête suivante.
Règles pratiques pour la boucle
- Bouclez, ne supposez pas un seul tour. Après avoir renvoyé les sorties des tools, le modèle peut appeler un autre tool. Encapsulez l'étape requête/exécution dans une boucle
whilequi continue jusqu'à ce qu'aucun élémentfunction_callne revienne. Plafonnez-la (disons 8 itérations) pour qu'un modèle désorienté ne tourne pas indéfiniment. - Validez les arguments avant d'exécuter. Le schéma contraint le modèle, mais traitez les arguments de tools comme n'importe quelle entrée non fiable. Ne les passez jamais directement dans un shell, une chaîne SQLSQLSales Qualified Lead : un prospect que l'équipe commerciale a validé comme prêt pour une prise de contact directe et une proposition, après avoir passé des critères de qualification explicites.Voir la définition complète → ou un chemin de fichier.
- Gardez les fonctions étroites.
get_order_status(order_id)vaut mieux qu'une méga-fonction avec un flagmode. Les tools étroits sont plus faciles à choisir correctement pour le modèle et plus faciles à sécuriser pour vous. - Contrôlez le choix si nécessaire. Utilisez
tool_choice="auto"(par défaut),"required"pour forcer l'usage d'*un* tool, ou nommez un tool précis pour forcer exactement celui-là. Mettezparallel_tool_calls=Falsesi vos tools doivent s'exécuter en séquence.
Structured Outputs : arrarrL'Annual Recurring Revenue (ARR) est le revenu normalisé et prévisible qu'une entreprise par abonnement attend de ses contrats actifs sur une année.Voir la définition complète →êtez de parser de la prose
Le tool calling gère les *actions*. Les Structured Outputs gèrent la *forme de la réponse*. Quand vous mettez strict: true et fournissez un schéma, l'API contraint la génération de sorte que la sortie corresponde de façon prouvée à votre schéma. C'est plus solide que la vieille astuce de prompt « réponds en JSON », qui produisait du JSON valide la plupart du temps et cassait à 2 h du matin.
En Python, la voie la plus propre consiste à définir votre schéma comme un modèle Pydantic et à laisser le SDK le parser pour vous.
from pydantic import BaseModel
class CalendarEvent(BaseModel):
name: str
date: str
participants: list[str]
response = client.responses.parse(
model="gpt-4.1",
input=[
{"role": "system", "content": "Extract the event details."},
{"role": "user", "content": "Standup with Ana and Rui on Friday."},
],
text_format=CalendarEvent,
)
event = response.output_parsed
print(event.participants) # ['Ana', 'Rui']output_parsed vous rend un objet typé, pas une chaîne sur laquelle il faut faire un json.loads en priant. Si vous n'êtes pas en Python, vous passez un JSON Schema brut dans text.format avec "type": "json_schema" et "strict": true, et vous récupérez un texte JSON garanti conforme.
Concevoir des schémas qui fonctionnent vraiment
- Chaque propriété est de fait obligatoire. Le mode strict traite toutes les clés comme requises. Pour rendre un champ « optionnel », donnez-lui une union avec null, par exemple
Optional[str]en Pydantic, puis vérifiez la valeur null dans votre code. - Utilisez des enums pour contraindre les choix. Si
statusne peut valoir queopen,pendingouclosed, définissez-le comme un enum. Le modèle ne pourra pas renvoyerin-progresset vous surprendre en aval. - Les descriptions guident les valeurs. Les descriptions de champs dans le schéma orientent *ce qui* va dans chaque champ, pas seulement les types. Servez-vous-en.
- Attention au sous-ensemble supporté. Les Structured Outputs prennent en charge une portion définie de JSON Schema. Des motifs comme
minimum,maximumet certaines contraintesformatpeuvent ne pas être appliqués. Consultez le guide Structured Outputs avant de vous appuyer sur un mot-clé.
OpenAI Function Calling and Structured Outputs Explained
Combiner les deux : le pattern réaliste
La plupart des fonctionnalités en production utilisent les deux ensemble. Imaginez un assistant support qui répond à une question de remboursement :
- Le modèle appelle
lookup_order(order_id)(tool calling) pour récupérer des données réelles. - Vous renvoyez l'enregistrement de la commande.
- Le modèle produit une réponse finale contrainte par un schéma
RefundDecision(Structured Outputs) : un booléeneligible, un enumreasonet une chaînecustomer_message.
Le code de votre application ne parse jamais de texte libre. Il lit decision.eligible et branche. C'est tout l'intérêt : le modèle gère le langage et le jugement, votre code gère le flux de contrôle, et la frontière entre les deux est un contrat typé.
Une subtilité à connaître : les *paramètres* de tools et les Structured *Outputs* sont deux emplacements de schéma distincts. L'un façonne l'appel qui entre, l'autre la réponse qui sort. Vous pouvez utiliser l'un seul ou les deux à la fois dans la même requête.
Vérification des acquis
1. Quelle est la différence fondamentale entre le function (tool) calling et les Structured Outputs ?
2. Dans la boucle de tool calling, que se passe-t-il réellement quand le modèle « appelle » une fonction ?
3. Pourquoi la leçon insiste-t-elle sur le fait d'écrire la « description » d'un tool comme une documentation destinée à un développeur junior ?
4. Sélectionnez TOUTES les affirmations correctes sur les champs du schéma de paramètres des tools abordés dans la leçon.
Sélectionnez toutes les réponses correctes.
5. Sélectionnez TOUS les scénarios qui reflètent correctement les usages prévus décrits dans la leçon.
Sélectionnez toutes les réponses correctes.
Erreurs, coûts et modes de défaillance
Les vrais systèmes cassent de façons précises. Anticipez-les.
- Le modèle hallucine un tool qui n'existe pas, ou de mauvais arguments. Avec des schémas de tools stricts, c'est rare, mais si une fonction échoue réellement, renvoyez l'erreur *comme sortie du tool* (par exemple
{"error": "order not found"}) plutôt que de lever une exception. Le modèle peut la lire et se rattraper, en demandant à l'utilisateur un identifiant de commande correct. - Refus. Les Structured Outputs peuvent malgré tout refuser pour des raisons de sécurité. Le SDK le remonte, donc vérifiez le champ de refus avant de faire confiance à
output_parsed. Traitez-le explicitement au lieu de considérer un refus comme des données mal formées. - Troncature. Si le modèle atteint la limite de tokenstokensUn token est l'unité de base de texte que traitent les modèles de langage : le plus souvent un fragment de mot, un mot entier ou un signe de ponctuation, plutôt qu'un simple caractère.Voir la définition complète → de sortie en plein JSON, vous obtenez des données incomplètes même en mode strict, car la contrainte garantit une *structure valide*, pas une *complétion*. Vérifiez si le statut de la réponse est
incompleteet augmentez votremax_output_tokenspour les gros objets. - Latence à la première utilisation d'un nouveau schéma. La toute première requête avec un schéma strict inédit peut être plus lente, le temps que l'API prépare la contrainte. Les schémas répétés sont rapides. Réutilisez les schémas plutôt que de les générer dynamiquement à chaque requête.
- Tokens. Les définitions de tools et les schémas vivent dans la fenêtre de contexte et comptent comme tokens d'entrée à chaque appel. Des schémas longs et verbeux sur de nombreux tools s'additionnent. Gardez les descriptions serrées et n'attachez que les tools dont le modèle pourrait plausiblement avoir besoin pour cette requête.
Où cela se situe dans l'écosystème
Vous avez vu la mécanique brute. Les produits OpenAI de plus haut niveau sont construits exactement sur ces primitives :
- Les GPT Actions dans les Custom GPTs sont du function calling piloté par une spec OpenAPI. Vous décrivez votre API une fois, et le GPT l'appelle via le même pattern requête/exécution, simplement géré par ChatGPT plutôt que par votre code.
- L'Agents SDK encapsule la boucle de tools, les retries et les handoffs pour que vous cessiez d'écrire la boucle
whileà la main. Quand votre orchestration de tools devient complexe (plusieurs agents, guardrailsguardrailsRègles et contrôles qui maintiennent un système d'IA dans des limites sûres, légales et conformes à la marque, en bloquant les sorties et actions hors cadre.Voir la définition complète →, tracing), passez-y plutôt que de maintenir du code de boucle sur mesure. Voir la documentation de l'Agents SDK. - Les tools intégrés comme la recherche web et la recherche de fichiers sont des function calls que l'API exécute côté serveur. Vous les activez dans
toolssans écrire l'exécuteur.
Comprendre d'abord la boucle nue fait qu'aucun de ces éléments ne ressemble à de la magie. Ce sont des commodités posées sur le contrat que vous venez d'apprendre.
Points clés
- Le tool calling est une boucle qui vous appartient. Le modèle *demande* une fonction ; votre code l'exécute, renvoie la sortie indexée par
call_id, et rappelle le modèle. Bouclez jusqu'à ce qu'il ne reste plus d'appels de tools, avec un plafond d'itérations strict. - Utilisez des Structured Outputs en `strict: true` pour toute réponse que votre code parse. Cela garantit la forme et élimine le JSON fragile obtenu au petit bonheur du prompt. En Python, définissez un modèle Pydantic et lisez
output_parsed. - Concevez les schémas de façon défensive. Traitez tous les champs comme requis (utilisez des unions avec null pour l'optionnel), contraignez les choix avec des enums, écrivez des descriptions de champs, et vérifiez que vos mots-clés font partie du sous-ensemble supporté.
- Renvoyez les erreurs de tools comme des données, et vérifiez les refus et la troncature. Laissez le modèle se rattraper après un appel échoué ; vérifiez le statut de la réponse avant de faire confiance au payload.
- Passez à l'Agents SDK ou aux GPT Actions quand la boucle grossit. Ils sont construits sur ces primitives exactes, donc le modèle mental se transpose directement.
À faire, tiré de cette leçon
Ces actions sont compilées dans le plan d'action du rôle.
- Démarrez tout nouveau code sur l'API Responses et lisez response.usage dès le premier jour