Architecture du function calling dans Responses API
Architecture du function calling dans Responses API
Le function calling est le mécanisme central qui permet aux modèles OpenAI d’interagir avec le monde extérieur. Dans la Responses API, ce mécanisme a été repensé pour être plus robuste, plus prévisible et nativement intégré aux outils built-in comme Web Search ou Code Interpreter.
Le principe fondamental
Le modèle ne fait jamais rien directement. Il émet des intentions structurées que votre code intercepte et exécute. Formulé ainsi, cela ressemble à une limitation technique ; c’est en réalité le choix de conception qui rend tout le reste possible.
Voyez ce qu’implique l’alternative. Si le modèle exécutait lui-même, il faudrait lui confier vos accès base de données, vos clés d’API et vos droits d’écriture — et vous n’auriez aucun point où vérifier quoi que ce soit. Avec le découplage, chaque intention passe par votre code : vous validez les arguments, vous appliquez vos règles métier, vous refusez si nécessaire. Le modèle propose, votre code dispose, et c’est cette frontière qui permet de mettre du function calling en production sans y perdre le contrôle.
from openai import OpenAI
client = OpenAI()
# Déclarer un outil personnalise
tools = [{
"type": "function",
"name": "rechercher_client",
"description": "Rechercher un client dans le CRM par nom ou email",
"parameters": {
"type": "object",
"properties": {
"terme": {
"type": "string",
"description": "Nom, email ou identifiant du client"
},
"champ": {
"type": "string",
"enum": ["nom", "email", "id"],
"description": "Champ dans lequel chercher"
}
},
"required": ["terme"]
}
}]
response = client.responses.create(
model="gpt-5.6-terra",
input="Trouve le client Jean Dupont",
tools=tools
)
Anatomie de la réponse
Une réponse ne contient pas soit du texte soit des appels d’outils : output est une liste hétérogène qui peut mélanger les deux. Le réflexe à prendre dès le premier développement est donc de parcourir cette liste plutôt que de lire output[0] — un modèle qui explique sa démarche avant d’appeler une fonction produit exactement ce cas, et le code qui lit le premier élément casse le jour où il se met à commenter.
for item in response.output:
if item.type == "function_call":
print(f"ID appel : {item.call_id}")
print(f"Fonction : {item.name}")
print(f"Arguments : {item.arguments}")
Le call_id est essentiel : il lie l’appel à son résultat quand vous renvoyez la réponse au modèle. Sur un appel unique, on le remarque à peine ; sur trois appels parallèles, c’est lui seul qui empêche d’attribuer la météo de Lyon à la requête sur Marseille. Renvoyez toujours le résultat avec le call_id reçu, jamais avec un identifiant reconstruit de votre côté.
La boucle complète
Ce cycle en quatre temps est invariant : il vaut pour une fonction comme pour vingt, pour un appel simple comme pour un agent qui enchaîne dix outils. Toute la complexité que vous rencontrerez ensuite — parallélisme, erreurs, fallbacks — consiste à instrumenter cette boucle, jamais à la remplacer.
import json
# 1. Premier appel avec les outils déclarés
response = client.responses.create(
model="gpt-5.6-terra",
input="Quel est le solde du client [email protected] ?",
tools=tools
)
# 2. Détecter et exécuter les appels de fonction
resultats = []
for item in response.output:
if item.type == "function_call":
if item.name == "rechercher_client":
args = json.loads(item.arguments)
resultat = votre_crm.rechercher(args["terme"])
resultats.append({
"type": "function_call_output",
"call_id": item.call_id,
"output": json.dumps(resultat)
})
# 3. Renvoyer les résultats au modèle
response_finale = client.responses.create(
model="gpt-5.6-terra",
input=response.output + resultats,
tools=tools
)
# 4. Le modèle formule sa reponse naturelle
print(response_finale.output_text)
Différences clés avec Chat Completions
La Responses API apporte plusieurs améliorations architecturales.
Gestion native des outils built-in
Les outils intégrés se déclarent exactement comme vos fonctions, mais ils s’exécutent chez OpenAI. La conséquence pratique est agréable : vous n’avez aucune boucle à écrire pour eux. Le modèle appelle, obtient le résultat et poursuit, le tout dans un unique aller-retour. La boucle décrite plus haut ne concerne que vos fonctions à vous — celles qui tournent sur votre machine.
# Mélanger outils built-in et personnalises
tools = [
{"type": "web_search_preview"},
{"type": "code_interpreter"},
{
"type": "function",
"name": "ma_fonction",
"description": "Ma fonction personnalisee",
"parameters": {"type": "object", "properties": {}}
}
]
Identifiants d’appel stables
Chaque appel de fonction reçoit un call_id unique qui permet de tracer précisément quel résultat correspond à quel appel, même en cas d’appels parallèles.
Flux simplifié
Plus besoin de gérer manuellement les rôles assistant et tool dans les messages. Le champ input accepte directement la concaténation des outputs précédents avec les résultats de fonction.
Quand le modèle appelle-t-il une fonction ?
Le modèle décide d’appeler une fonction quand la requête de l’utilisateur nécessite une information qu’il n’a pas, quand la description de la fonction correspond au besoin identifié, et quand les paramètres requis peuvent être déduits du contexte.
Trois conditions doivent donc être réunies simultanément, et c’est utile pour déboguer : quand une fonction n’est pas appelée alors qu’elle devrait l’être, la cause est presque toujours la deuxième — une description qui ne recouvre pas la façon dont l’utilisateur a formulé sa demande. Le paramètre tool_choice permet de forcer le comportement quand la description ne suffit pas :
# Laisser le modèle décider
tool_choice = "auto"
# Forcer l'appel d'une fonction spécifique
tool_choice = {"type": "function", "name": "rechercher_client"}
# Interdire tout appel de fonction
tool_choice = "none"
# Exiger au moins un appel sans préciser lequel
tool_choice = "required"
Points clés à retenir
- Le function calling est un protocole d’intention, pas d’exécution
- La boucle appel-exécution-retour est le pattern fondamental
- Les outils built-in et personnalisés coexistent dans le même tableau
tools - Le
call_idassure la traçabilité de chaque appel tool_choicepermet de contrôler finement le comportement du modèle