For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to the page URL.
Navigation principale
Theme

Appels programmatiques d’outils

Permettez aux modèles d’écrire et d’exécuter du JavaScript pour coordonner les appels d’outils.

Les appels programmatiques d’outils permettent à un modèle d’écrire et d’exécuter du JavaScript pour coordonner ses outils. Un programme peut appeler des outils en parallèle, utiliser des boucles et des conditions, et conserver les résultats intermédiaires dans l’environnement d’exécution hébergé. Ce mode est utile lorsqu’une tâche nécessite une suite d’appels d’outils liés entre eux ou que les outils renvoient un volume important de données à traiter avant de produire un résultat.

Avec l’API Responses, votre application décide d’activer ou non les appels programmatiques d’outils. Pour chaque outil compatible, elle détermine si le modèle peut l’appeler directement, depuis un programme ou des deux façons. Elle continue d’exécuter tous les appels d’outils qui relèvent du client. L’API Agents active les appels programmatiques d’outils par défaut et gère la boucle de l’agent pour vous.

Consultez la page des modèles avant d’activer les appels programmatiques d’outils.

Comprendre l’environnement d’exécution

OpenAI exécute chaque programme généré dans un nouvel environnement V8 isolé. Cet environnement prend en charge JavaScript et permet d’utiliser await au niveau supérieur. Il ne fournit ni Node.js, ni mécanisme d’installation de paquets, ni accès direct au réseau, ni système de fichiers généraliste, ni possibilité d’exécuter des sous-processus, ni console. Il ne conserve pas non plus l’état JavaScript d’une exécution à l’autre. Pour interagir avec des systèmes externes, les programmes peuvent uniquement utiliser les outils activés dans la requête. Ils peuvent produire une sortie avec text(...) ou image(...).

Pour les requêtes de l’API Responses, les appels programmatiques d’outils prennent en charge les workflows soumis à la politique de non-conservation des données (ZDR) sans nécessiter de conteneur persistant pour exécuter le code. La ZDR doit être activée pour l’organisation ou le projet. Le paramètre store: false permet de poursuivre sans état, mais ne suffit pas à activer la ZDR. L’éligibilité à cette politique et les règles de conservation dépendent de la requête dans son ensemble, y compris du modèle, des outils et des services tiers utilisés. Consultez les contrôles des données.

Choisir quand utiliser les appels programmatiques d’outils

Utilisez les appels programmatiques d’outils lorsque le déroulement d’une étape est prévisible et que le code peut renvoyer un résultat structuré plus compact. Utilisez les appels directs lorsqu’un seul appel suffit, que le modèle doit réévaluer la situation à chaque résultat ou que la tâche nécessite une approbation ou la préservation de citations ou d’artefacts dans leur format natif.

Type de tâche Mode recommandé
Une seule consultation ou action Utilisez un appel direct.
Plusieurs résultats que le code peut filtrer, joindre, classer, dédupliquer, agréger ou valider Utilisez les appels programmatiques d’outils si le programme peut renvoyer un résultat structuré plus compact.
Appels interdépendants avec un flux de données prévisible Utilisez les appels programmatiques d’outils si le code peut déduire les arguments des appels suivants et si les limites et le comportement en cas d’échec sont clairement définis.
Recherche adaptative ou évaluation sémantique Utilisez les appels directs si chaque résultat doit orienter la prochaine décision du modèle.
Opérations d’écriture ou actions soumises à approbation Utilisez les appels directs par défaut pour délimiter clairement les actions autorisées.
Validation finale des citations ou des artefacts dans leur format natif Utilisez les appels directs, sauf si le programme préserve le format natif de la sortie et valide chaque élément requis.

Configurer les appels programmatiques d’outils

Pour l’API Responses, ajoutez l’outil hébergé programmatic_tool_calling à la requête. Définissez ensuite allowed_callers pour chaque outil compatible que le programme pourra appeler.

Activer les appels programmatiques d’outils
[
  {
    "type": "function",
    "name": "get_inventory",
    "description": "Return an object with sku (string) and available_units (number).",
    "parameters": {
      "type": "object",
      "properties": {
        "sku": { "type": "string" }
      },
      "required": ["sku"],
      "additionalProperties": false
    },
    "output_schema": {
      "type": "object",
      "properties": {
        "sku": { "type": "string" },
        "available_units": { "type": "number" }
      },
      "required": ["sku", "available_units"],
      "additionalProperties": false
    },
    "allowed_callers": ["programmatic"]
  },
  {
    "type": "programmatic_tool_calling"
  }
]

allowed_callers détermine comment le modèle peut appeler un outil :

Valeur Comportement
Champ omis ou ["direct"] Le modèle peut appeler l’outil directement.
["programmatic"] Seul le code d’un élément program peut appeler l’outil.
["direct", "programmatic"] Le modèle peut appeler l’outil directement ou depuis un programme.

parameters décrit les arguments de la fonction. Lorsqu’une fonction renvoie des données structurées de forme prévisible, output_schema décrit l’objet JSON encodé dans la chaîne function_call_output.output. Définissez les deux pour que le code JavaScript généré puisse exploiter les champs renvoyés de manière fiable.

Outils compatibles

Les types d’outils suivants prennent en charge allowed_callers: ["programmatic"] :

  • function et custom
  • mcp
  • apply_patch
  • shell en version locale ou hébergée
  • code_interpreter

Pour les outils MCP, la règle require_approval de l’outil peut suspendre le programme jusqu’à ce que vous approuviez l’appel.

Pour les outils hébergés par OpenAI, consultez les consignes de l’outil en matière de conservation des données et de sécurité avant de l’activer dans un programme.

Utiliser la recherche d’outils avec les appels programmatiques

La recherche d’outils s’exécute au niveau supérieur de l’API Responses, en dehors du code JavaScript généré. Les outils de type fonction, les outils personnalisés et les outils MCP configurés avec defer_loading: true ne sont pas accessibles au programme au départ. Une fois que le modèle a chargé un outil correspondant à sa recherche, un programme lancé par la suite peut l’appeler via tools.* si le champ allowed_callers de l’outil contient "programmatic". Un programme en cours d’exécution ne peut pas lancer de recherche d’outils. Le modèle doit donc charger les outils à chargement différé avant de lancer un programme qui en a besoin.

Guider le choix du mode d’appel lorsque les deux modes sont disponibles

Lorsque votre application permet au modèle d’appeler une fonction directement ou depuis un programme, associez chaque mode d’appel à une étape précise du workflow. Une instruction générale comme « utilisez efficacement les appels programmatiques d’outils » ne précise pas à quelles étapes les utiliser. Par exemple :

<tool_orchestration>
Pour [bounded stage], utilisez les appels programmatiques d’outils et appelez uniquement [eligible tools].
Exécutez les appels indépendants en parallèle lorsque vous pouvez le faire sans risque.
Utilisez uniquement les champs d’entrée et de sortie documentés des outils.

Traitez les résultats intermédiaires et réduisez leur volume. Renvoyez ensuite un résultat conforme
à la structure exacte [program result shape], en y incluant les éléments nécessaires pour étayer la réponse finale.

Arrêtez-vous dès que la condition [condition] est remplie. En cas d’échec temporaire,
limitez-vous à [R] nouvelles tentatives. Ne répétez pas les appels déjà terminés
et n’effectuez aucune action ayant des effets de bord. Si un résultat requis manque toujours,
renvoyez une erreur structurée qui indique clairement l’échec.

Utilisez les appels directs d’outils pour [semantic judgment, approval, or final validation].
</tool_orchestration>

Voici un exemple d’utilisation de ce modèle d’instructions :

<tool_orchestration>
Utilisez les appels programmatiques d’outils pour comparer le stock disponible à la demande
pour sku_123. Appelez uniquement get_inventory et get_demand. Exécutez les deux appels
en parallèle. Utilisez uniquement les champs d’entrée et de sortie documentés des outils.

Traitez les résultats intermédiaires et réduisez leur volume, puis renvoyez exactement un objet JSON
contenant les champs sku, available_units, requested_units et shortage_units. Calculez
shortage_units avec la formule max(requested_units - available_units, 0). Incluez les valeurs
des champs available_units et requested_units pour justifier le calcul.

Arrêtez-vous dès que les deux résultats contiennent chacun les champs requis. En cas d’échec temporaire,
effectuez au maximum 1 nouvelle tentative. Ne répétez pas les appels déjà terminés et n’effectuez
aucune action ayant des effets de bord. Si un résultat requis manque toujours, renvoyez une
erreur structurée qui indique clairement l’échec.

Utilisez les appels directs d’outils uniquement pour obtenir une approbation avant toute action modifiant le stock.
</tool_orchestration>

Pour les workflows qui nécessitent les deux modes, définissez un seul point de passage de l’un à l’autre. Évitez les allers-retours et la répétition du travail déjà effectué. Si une solution de repli sûre existe, définissez-la une seule fois et limitez le nombre de nouvelles tentatives.

Comprendre les éléments de réponse d’un programme

Chaque appel d’API renvoie toujours l’objet de réponse standard de l’API Responses. Les appels programmatiques d’outils n’ajoutent pas d’enveloppe de réponse distincte. Lorsque le modèle utilise ce mode, le tableau output de la réponse peut contenir :

  • Un élément program contenant le code JavaScript généré, un call_id et une valeur opaque fingerprint qui permet de reprendre ou de rejouer le programme.
  • Un élément function_call correspondant à un appel effectué par le programme. Il possède son propre call_id, que votre application utilise pour renvoyer le résultat de la fonction. Son champ caller.caller_id correspond au call_id du programme.
  • Un élément program_output contenant le résultat final du programme et son état. Son call_id correspond au call_id du programme et son champ status vaut completed ou incomplete.

Ces éléments sont distincts et figurent au niveau supérieur de response.output. Le champ caller indique quel programme est à l’origine d’un appel.

Par exemple, un programme peut se mettre en pause pendant que votre application exécute get_inventory et get_demand :

Programme et appels de fonctions imbriqués
[
  {
    "type": "program",
    "id": "prog_123",
    "call_id": "call_prog_123",
    "code": "const [stock, demand] = await Promise.all([tools.get_inventory({ sku: 'sku_123' }), tools.get_demand({ sku: 'sku_123' })]); text(JSON.stringify({ sku: stock.sku, available_units: stock.available_units, requested_units: demand.requested_units, shortage_units: Math.max(demand.requested_units - stock.available_units, 0) }));",
    "fingerprint": "opaque_replay_state"
  },
  {
    "type": "function_call",
    "id": "fc_123",
    "call_id": "call_inventory_123",
    "name": "get_inventory",
    "arguments": "{\"sku\":\"sku_123\"}",
    "caller": {
      "type": "program",
      "caller_id": "call_prog_123"
    }
  },
  {
    "type": "function_call",
    "id": "fc_456",
    "call_id": "call_demand_123",
    "name": "get_demand",
    "arguments": "{\"sku\":\"sku_123\"}",
    "caller": {
      "type": "program",
      "caller_id": "call_prog_123"
    }
  }
]

Ces exemples présentent uniquement les éléments pertinents de response.output. L’objet Responses standard qui les contient est omis. Une fois que votre application a renvoyé les résultats des appels de fonctions imbriqués, une réponse ultérieure peut contenir l’élément program_output complet :

Sortie du programme
{
  "type": "program_output",
  "id": "prog_out_123",
  "call_id": "call_prog_123",
  "result": "{\"sku\":\"sku_123\",\"available_units\":42,\"requested_units\":31,\"shortage_units\":0}",
  "status": "completed"
}

La chaîne JSON de program_output.result respecte la structure de résultat définie dans vos instructions. L’élément program_output qui la contient respecte le contrat de l’API présenté ci-dessus. Ces deux contrats sont distincts. L’élément message final peut arriver avec la sortie du programme ou dans une réponse ultérieure. Poursuivez donc jusqu’à la réception de ce message.

OpenAI exécute le code JavaScript généré par le modèle dans l’environnement hébergé. Votre application exécute les appels de fonctions côté client que lui renvoie l’API. Elle n’exécute pas le code JavaScript généré.

Renvoyez le résultat de la fonction sous la forme d’un élément function_call_output. Copiez le champ caller de l’appel de fonction sans le modifier. Le service utilise cette valeur pour reprendre l’exécution du programme correspondant.

Poursuivre après les appels de fonctions côté client

Un programme peut se mettre en pause à plusieurs reprises lorsqu’il appelle des outils côté client. Poursuivez jusqu’à ce que la réponse contienne un message final de l’assistant :

  1. Envoyez la requête avec l’outil hébergé et les fonctions qui autorisent les appels programmatiques.
  2. Exécutez tous les appels de fonctions côté client renvoyés par l’API.
  3. Renvoyez le résultat de chaque fonction avec les valeurs d’origine de call_id et de caller.
  4. Si la réponse est incomplète, traitez ce cas avant de poursuivre.
  5. Si la réponse ne contient aucun élément function_call en attente ni aucun élément message final, poursuivez à partir de cette réponse. Avec store: false, renvoyez ses éléments de sortie. Pour une réponse stockée, utilisez previous_response_id.
  6. Arrêtez-vous lorsque la réponse contient un élément message final. Lisez response.output_text ou, en cas de refus, le contenu du refus dans le message.

L’exemple suivant utilise store: false, conserve chaque élément de réponse et renvoie au programme le résultat de chaque fonction :

Exécuter une boucle d’appels programmatiques d’outils
import json

from openai import OpenAI

client = OpenAI()
model = "gpt-6-astra"


def get_inventory(sku):
    return {"sku": sku, "available_units": 42}


def get_demand(sku):
    return {"sku": sku, "requested_units": 31}


implementations = {
    "get_inventory": get_inventory,
    "get_demand": get_demand,
}

tools = [
    {
        "type": "function",
        "name": "get_inventory",
        "description": "Return an object with sku (string) and available_units (number).",
        "parameters": {
            "type": "object",
            "properties": {"sku": {"type": "string"}},
            "required": ["sku"],
            "additionalProperties": False,
        },
        "output_schema": {
            "type": "object",
            "properties": {
                "sku": {"type": "string"},
                "available_units": {"type": "number"},
            },
            "required": ["sku", "available_units"],
            "additionalProperties": False,
        },
        "allowed_callers": ["programmatic"],
    },
    {
        "type": "function",
        "name": "get_demand",
        "description": "Return an object with sku (string) and requested_units (number).",
        "parameters": {
            "type": "object",
            "properties": {"sku": {"type": "string"}},
            "required": ["sku"],
            "additionalProperties": False,
        },
        "output_schema": {
            "type": "object",
            "properties": {
                "sku": {"type": "string"},
                "requested_units": {"type": "number"},
            },
            "required": ["sku", "requested_units"],
            "additionalProperties": False,
        },
        "allowed_callers": ["programmatic"],
    },
    {"type": "programmatic_tool_calling"},
]

input_items = [
    {
        "role": "user",
        "content": "Compare inventory with demand for sku_123.",
    }
]

while True:
    response = client.responses.create(
        model=model,
        store=False,
        input=input_items,
        tools=tools,
    )

    if response.status != "completed":
        raise RuntimeError(f"Response ended with status {response.status}")

    # Preserve every output item, including program and reasoning items.
    input_items.extend(item.model_dump(exclude_none=True) for item in response.output)

    calls = [item for item in response.output if item.type == "function_call"]
    if not calls:
        message = next(
            (item for item in response.output if item.type == "message"), None
        )
        if message:
            refusal = next(
                (part.refusal for part in message.content if part.type == "refusal"),
                "",
            )
            print(response.output_text or refusal)
            break
        continue

    for call in calls:
        run = implementations.get(call.name)
        if run is None:
            raise ValueError(f"Unknown tool: {call.name}")

        result = run(**json.loads(call.arguments))
        input_items.append(
            {
                "type": "function_call_output",
                "call_id": call.call_id,
                "output": json.dumps(result),
                # Preserve caller so the runtime can resume the correct program.
                "caller": call.caller.model_dump() if call.caller else None,
            }
        )

Si vous stockez les réponses, vous pouvez poursuivre à partir de previous_response_id sans renvoyer tous les éléments des réponses précédentes. Envoyez les nouveaux éléments function_call_output en entrée de la requête suivante. Avec store: false, renvoyez la séquence complète dans l’ordre, en incluant chaque élément program, chaque élément de raisonnement, chaque appel de fonction, chaque résultat d’appel de fonction et chaque élément program_output.

Pour les requêtes sans état adressées à un modèle de raisonnement, renvoyez chaque élément de raisonnement reçu. Chacun contient encrypted_content par défaut. Consultez le guide sur l’état de la conversation pour connaître la procédure générale à suivre en mode sans état.

Concevoir des outils pour les programmes

  • Renvoyez des données structurées et compactes que le code JavaScript peut examiner sans avoir à analyser du texte libre.
  • Utilisez output_schema pour définir les champs que chaque outil doit renvoyer et leur type. Documentez également le comportement de l’outil en cas d’erreur. Si la structure du résultat n’est pas connue à l’avance, réservez l’outil aux appels directs pour que le modèle puisse examiner le résultat.
  • Définissez précisément la structure du résultat du programme et les éléments nécessaires pour le justifier. Si le programme ne peut pas produire de résultat valide, renvoyez une erreur structurée qui indique clairement l’échec.
  • Rendez les appels de fonctions idempotents lorsque c’est possible. Une nouvelle tentative ou la réexécution d’un appel ne doit pas reproduire un effet de bord dangereux.
  • Vérifiez les arguments et les autorisations de chaque appel dans votre application, même si l’appel provient d’un programme hébergé.
  • Donnez aux outils des noms et des descriptions précis pour que le modèle puisse les combiner correctement.
  • Exigez une approbation au niveau de l’application avant toute action aux conséquences importantes, quelle que soit l’origine de l’appel.

Évaluer les appels programmatiques d’outils

Les appels programmatiques d’outils peuvent réduire le volume de résultats intermédiaires ajoutés au contexte du modèle. Le gain dépend de la tâche et des réponses des outils. Prenez d’abord les appels directs comme référence, puis comparez les deux modes sur des tâches représentatives.

Avant de mesurer l’efficacité, définissez le niveau de qualité attendu de la réponse finale et les éléments nécessaires pour la justifier. Évaluez le nombre de tokens et d’appels d’outils en parallèle de l’exactitude et de l’exhaustivité des réponses. Vérifiez dans quelle mesure les éléments fournis étayent ces réponses et précisez les compromis que vous acceptez en matière de qualité.

Mesurez les aspects suivants :

  • L’exactitude et l’exhaustivité de la réponse finale, ainsi que la part de la réponse étayée par les éléments fournis.
  • Le nombre de tokens en entrée et au total, la latence de bout en bout et le coût.
  • Le nombre de tours du modèle, d’appels d’outils et de nouvelles tentatives, ainsi que le comportement lors de la reprise après un échec.
  • Les résultats en matière de sécurité, en particulier pour les effets de bord et le respect des exigences d’approbation.
  • L’adéquation entre le mode d’appel effectivement utilisé et l’étape prévue du workflow.

Agents API

Dans l’API Agents, les appels programmatiques d’outils s’exécutent dans le harnais de l’agent géré par OpenAI et sont activés par défaut. Le harnais fournit à l’agent un outil exec et rend ses outils existants accessibles depuis le code JavaScript généré. Vous n’avez pas besoin de créer un programme en ligne de commande pour chacun de ces outils ni de les installer dans le bac à sable.

Pour désactiver les appels programmatiques d’outils, ajoutez cette entrée à agent.tools :

{
  "type": "programmatic_tool_calling",
  "enabled": false
}

Si vous omettez cette entrée ou son champ enabled, les appels programmatiques d’outils restent activés. Une entrée qui spécifie uniquement le type, { "type": "programmatic_tool_calling" }, les laisse également activés. La configuration de allowed_callers et la boucle de traitement des réponses présentées plus haut concernent l’intégration avec l’API Responses.

Les appels programmatiques d’outils fonctionnent aussi dans les sessions limitées à la conversation où environment.type vaut none. Bash, les MCP d’exécution et les autres outils qui s’exécutent dans un bac à sable nécessitent toujours un environnement d’exécution.

Orchestrer un outil en JavaScript ne change pas son lieu d’exécution. Un appel au shell exécute les commandes dans le bac à sable. L’environnement JavaScript ne lance pas lui-même de processus système. Les MCP d’exécution continuent d’utiliser le bac à sable, et les outils de type fonction continuent d’appeler votre serveur d’application. L’agent traite leurs résultats avant de décider quelles données intégrer au contexte du modèle.

Suivez les indications ci-dessus sur le choix du mode d’appel pour définir les étapes du workflow qui doivent utiliser du code. Consultez les guides Fonctions et Connexions MCP pour configurer l’API Agents et traiter les appels.

Guides associés

  • Utilisez l’appel de fonction pour définir des fonctions côté client.
  • Utilisez la recherche d’outils pour différer le chargement des définitions d’outils volumineuses jusqu’à ce que le modèle en ait besoin.
  • Utilisez l’état de la conversation pour poursuivre une série de requêtes auprès de l’API Responses, que les réponses soient stockées ou que les échanges soient sans état.
  • Consultez les contrôles des données avant de choisir un mode de stockage.