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.

Appel de fonctions : Responses API face à Chat Completions

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.