Appel de fonctions : Responses API face à Chat Completions
Comparaison au niveau du protocole des définitions de fonctions, identifiants d’appel, résultats, arguments diffusés, autorisation, idempotence et compatibilité des routes.

Responses et Chat Completions peuvent demander à une application d’exécuter une fonction, mais représentent la boucle d’outils différemment. Chat Completions définit les fonctions sous tools[].function, retourne message.tool_calls et reçoit les résultats sous forme de messages role: "tool". Responses utilise des définitions plates, des éléments de sortie function_call et des function_call_output liés par call_id.
Le modèle n’exécute pas la fonction. Le code de l’application doit valider les arguments, autoriser l’opération, l’exécuter, retourner le résultat et empêcher la répétition d’un effet de bord lors d’une nouvelle tentative.
La boucle d’outils comporte quatre étapes
1. Déclarer une fonction autorisée et son schéma d’arguments
2. Recevoir une ou plusieurs propositions du modèle
3. Valider, autoriser et exécuter chaque appel dans l’application
4. Renvoyer chaque résultat avec l’identifiant de corrélation de l’appel
Le modèle ne peut produire une réponse fondée sur l’outil qu’après la quatrième étape. Un nouvel outil crée un nouvel identifiant d’appel. Le guide OpenAI sur Function Calling décrit cet échange en plusieurs étapes.
Comparer les contrats de transport
| Sujet | Responses API | Chat Completions |
|---|---|---|
| Définition | Élément plat dans tools[] |
Champs sous tools[].function |
| Appel proposé | Élément de sortie function_call |
tool_calls[] du message de l’assistant |
| Corrélation | call_id |
id, renvoyé comme tool_call_id |
| Nom | function_call.name |
tool_calls[].function.name |
| Arguments | Chaîne dans function_call.arguments |
Chaîne dans tool_calls[].function.arguments |
| Résultat | function_call_output |
Message role: "tool" |
| Diffusion | Événements d’arguments typés | Fragments delta.tool_calls[] |
| Texte final | Éléments de sortie / outil du SDK | choices[0].message.content |
Ne corrélez jamais les appels par leur position dans un tableau. Les appels parallèles et les fragments peuvent se terminer dans un autre ordre. L’identifiant explicite est la clé stable.
Définir un schéma de fonction strict
{
"type": "object",
"properties": {
"order_id": {
"type": "string",
"pattern": "^ORDER-[0-9]{4}$"
}
},
"required": ["order_id"],
"additionalProperties": false
}
strict: true demande au modèle compatible de respecter le schéma, mais l’application doit toujours analyser et valider les arguments. La prise en charge de pattern varie ; le même test de compatibilité que pour Structured Outputs s’applique.
Implémenter la boucle avec Responses API
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.MODELFLARE_API_KEY,
baseURL: "https://modelflare.dev/v1",
});
const model = process.env.MODEL_ID;
if (!model) throw new Error("MODEL_ID est obligatoire");
const orders = new Map([
["ORDER-1001", { status: "in_transit", eta: "2026-08-06" }],
]);
const tools = [
{
type: "function",
name: "get_delivery_status",
description: "Renvoie l’état actuel de livraison d’une commande.",
parameters: {
type: "object",
properties: {
order_id: { type: "string", pattern: "^ORDER-[0-9]{4}$" },
},
required: ["order_id"],
additionalProperties: false,
},
strict: true,
},
];
function executeTool(name, rawArguments) {
if (name !== "get_delivery_status") {
throw new Error(`Outil non autorisé : ${name}`);
}
const args = JSON.parse(rawArguments);
if (!/^ORDER-[0-9]{4}$/.test(args.order_id)) {
throw new Error("order_id non valide");
}
return orders.get(args.order_id) ?? { status: "not_found" };
}
const first = await client.responses.create({
model,
input: "Où en est la commande ORDER-1001 ?",
tools,
parallel_tool_calls: false,
});
const calls = first.output.filter((item) => item.type === "function_call");
if (calls.length === 0) {
console.log(first.output_text);
process.exit(0);
}
const toolOutputs = calls.map((call) => ({
type: "function_call_output",
call_id: call.call_id,
output: JSON.stringify(executeTool(call.name, call.arguments)),
}));
const final = await client.responses.create({
model,
input: [...first.output, ...toolOutputs],
tools,
parallel_tool_calls: false,
});
console.log(final.output_text);
Le call.call_id du modèle doit correspondre exactement au call_id du résultat. Un échange sans état renvoie les éléments de sortie précédents avec les résultats. Ne supposez pas que chaque route compatible avec OpenAI conserve l’état de la réponse.
Implémenter la boucle avec Chat Completions
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.MODELFLARE_API_KEY,
baseURL: "https://modelflare.dev/v1",
});
const model = process.env.MODEL_ID;
if (!model) throw new Error("MODEL_ID est obligatoire");
const orders = new Map([
["ORDER-1001", { status: "in_transit", eta: "2026-08-06" }],
]);
const tools = [
{
type: "function",
function: {
name: "get_delivery_status",
description: "Renvoie l’état actuel de livraison d’une commande.",
parameters: {
type: "object",
properties: {
order_id: { type: "string", pattern: "^ORDER-[0-9]{4}$" },
},
required: ["order_id"],
additionalProperties: false,
},
strict: true,
},
},
];
function executeTool(name, rawArguments) {
if (name !== "get_delivery_status") {
throw new Error(`Outil non autorisé : ${name}`);
}
const args = JSON.parse(rawArguments);
if (!/^ORDER-[0-9]{4}$/.test(args.order_id)) {
throw new Error("order_id non valide");
}
return orders.get(args.order_id) ?? { status: "not_found" };
}
const messages = [
{ role: "user", content: "Où en est la commande ORDER-1001 ?" },
];
const first = await client.chat.completions.create({
model,
messages,
tools,
parallel_tool_calls: false,
});
const assistant = first.choices[0].message;
const calls = assistant.tool_calls ?? [];
if (calls.length === 0) {
console.log(assistant.content);
process.exit(0);
}
messages.push(assistant);
for (const call of calls) {
const result = executeTool(call.function.name, call.function.arguments);
messages.push({
role: "tool",
tool_call_id: call.id,
content: JSON.stringify(result),
});
}
const final = await client.chat.completions.create({
model,
messages,
tools,
parallel_tool_calls: false,
});
console.log(final.choices[0].message.content);
Le message de l’assistant contenant les appels doit précéder les résultats dans messages. Son absence ou un tool_call_id non apparié rend l’historique invalide.
Traiter les arguments diffusés comme des fragments
{"order est un JSON incomplet, pas invalide. Maintenez un tampon par appel, ajoutez les incréments, attendez que les arguments ou la réponse soient terminés, analysez la chaîne une fois, puis validez et exécutez. Responses utilise des événements typés ; Chat Completions emploie choices[].delta.tool_calls[]. N’exécutez jamais au premier fragment. Voir le guide de la diffusion en continu des API d’IA.
Rendre l’exécution sûre et idempotente
- autoriser uniquement les fonctions enregistrées ;
- borner la taille et valider le schéma complet ;
- autoriser l’utilisateur ou la charge de travail pour la ressource ;
- séparer lecture et effets de bord ;
- retirer les secrets du résultat ;
- fixer un délai et limiter les nouvelles tentatives vers les services dépendants ;
- enregistrer une référence sûre.
Un outil à effet de bord doit utiliser une clé d’idempotence dérivée de la requête stable et de l’identifiant d’appel. Enregistrez le résultat avant de le retourner. Une nouvelle tentative doit renvoyer ce résultat plutôt que déclencher un second remboursement, message, déploiement ou changement de base de données.
Le nom et les arguments produits par le modèle sont des données non fiables. Un schéma valide n’accorde pas d’autorisation et ne remplace pas une transaction.
Décider du traitement de plusieurs appels
parallel_tool_calls: false simplifie la machine à états. En parallèle, corrélez par identifiant d’appel, limitez le nombre d’appels et la concurrence, définissez le comportement des échecs, retournez un résultat pour chaque appel accepté et sérialisez les effets dont l’ordre compte. Le parallélisme accélère les lectures indépendantes, mais complique l’autorisation, les nouvelles tentatives et les échecs partiels.
Comprendre la limite de compatibilité Modelflare
Modelflare conserve les définitions strictes et établit les correspondances entre les formes prises en charge par Responses et Chat Completions sur les routes OpenAI/Codex éligibles. Cette correspondance couvre volontairement moins que l’ensemble des outils de Responses.
Les fonctions définies par l’application sont représentables dans les deux formats. Les outils hébergés exécutés par le fournisseur — recherche ou exécution de code — ne sont pas équivalents. Les autres familles compatibles avec OpenAI utilisent Chat Completions sans transformation jusqu’à leur vérification ; le contrat du fournisseur décide de la prise en charge de tools, strict, des appels parallèles, des arguments diffusés et de tool_choice.
| Test | Preuve requise |
|---|---|
| Appel en lecture | Nom, arguments, identifiant d’appel, liaison du résultat, texte final |
| Arguments invalides | Échec explicite sans exécution |
| Fonction inconnue | Rejet par la liste d’autorisation |
| Aucun outil | Texte normal sans appel inventé |
| Diffusion | Arguments reconstruits et identifiant correct |
| Requête répétée | Un effet ou résultat mémorisé |
| Plusieurs appels | Corrélation indépendante de l’ordre |
| Route de repli | Même modèle, protocole, schéma et contrat |
Utilisez le routage fiable des API d’IA pour les changements de route et la sécurité des clés d’API d’IA pour séparer les charges de travail et les autorisations.
Choisir le format selon le contrat applicatif
Responses convient aux éléments de sortie typés, à son modèle d’événements, à la continuité d’état et aux capacités vérifiées. Chat Completions convient à un historique stable lorsque le contrat des outils du fournisseur est testé. Voir Responses API ou Chat Completions.
La norme reste la même : liste d’autorisation explicite, schéma d’arguments strict, validation applicative, autorisation avant exécution, corrélation par identifiant d’appel, idempotence des effets et résultat complet retourné au modèle. La fiabilité vient de ces contrôles, pas du nom du point de terminaison.