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

Appel d’outils par programmation

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

L’appel d’outils par programmation permet à 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é. Cette approche est utile lorsqu’une tâche nécessite une série d’appels d’outils liés entre eux ou le traitement de sorties d’outils volumineuses avant de renvoyer un résultat.

Dans l’API Responses, votre application détermine si l’appel d’outils par programmation est disponible et quels outils éligibles le modèle peut appeler directement, depuis un programme ou des deux façons. Elle continue d’exécuter tous les appels d’outils gérés côté client. L’API Agents active l’appel d’outils par programmation par défaut et gère la boucle de l’agent pour vous.

Consultez la page du modèle avant d’activer l’appel d’outils par programmation.

Comprenez 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 avec await au niveau supérieur, mais ne fournit ni Node.js, ni installation de paquets, ni accès direct au réseau, ni système de fichiers à usage général, ni exécution de sous-processus, ni console, ni état JavaScript persistant entre les exécutions de programmes. Les programmes ne peuvent interagir avec des systèmes externes que par l’intermédiaire des outils activés dans la requête et peuvent produire des sorties avec text(...) ou image(...).

Pour les requêtes à l’API Responses, l’appel d’outils par programmation prend en charge les workflows soumis à une politique de non-conservation des données (ZDR), sans nécessiter de conteneur persistant pour l’exécution du code. La ZDR doit être activée pour l’organisation ou le projet ; le paramètre store: false permet de poursuivre l’exécution sans état, mais n’active pas à lui seul la ZDR. L’éligibilité et la conservation des données dépendent de l’ensemble de la requête, notamment du modèle, des outils et des services tiers utilisés ; consultez les contrôles des données.

Choisissez quand utiliser l’appel d’outils par programmation

Utilisez l’appel d’outils par programmation lorsqu’une étape présente un flux de contrôle prévisible et que le code peut renvoyer un résultat structuré plus compact. Utilisez l’appel direct d’outils lorsqu’un seul appel suffit, que chaque résultat nécessite une nouvelle appréciation du modèle ou que le travail exige une approbation ou la préservation des citations ou des artefacts natifs.

Type de tâcheMode recommandé
Une seule recherche ou actionUtilisez l’appel direct d’outils.
Plusieurs résultats que le code peut filtrer, joindre, classer, dédupliquer, agréger ou validerUtilisez l’appel d’outils par programmation lorsque le programme peut renvoyer un résultat structuré plus compact.
Appels dépendants avec un flux de données prévisibleUtilisez l’appel d’outils par programmation lorsque le code peut déduire les arguments des appels suivants et que les limites et le comportement en cas d’échec sont explicites.
Recherche adaptative ou évaluation sémantiqueUtilisez l’appel direct d’outils lorsque chaque résultat doit influencer la décision suivante du modèle.
Écritures ou actions nécessitant une attention particulière à l’approbationUtilisez l’appel direct d’outils par défaut pour maintenir une limite d’autorisation claire.
Validation finale des citations ou des artefacts natifsUtilisez l’appel direct d’outils, sauf si le programme préserve la sortie native et valide chaque élément requis.

Configurez l’appel d’outils par programmation

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

Activez l’appel d’outils par programmation
[
  {
    "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 contrôle la façon dont le modèle peut appeler un outil :

ValeurComportement
Non renseigné ou ["direct"]Le modèle peut appeler l’outil directement.
["programmatic"]Seul le code contenu dans 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 prévisibles, output_schema décrit l’objet JSON encodé dans sa chaîne function_call_output.output. Définissez les deux pour que le JavaScript généré puisse utiliser les champs renvoyés de manière fiable.

Outils pris en charge

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

  • function et custom
  • mcp
  • apply_patch
  • shell local et hébergé
  • code_interpreter

Pour les outils MCP, la politique 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 relatives à la conservation des données et à la sécurité avant de l’activer dans un programme.

La recherche d’outils s’exécute en tant qu’outil de premier niveau de l’API Responses, et non depuis le JavaScript généré. Les outils de type fonction, personnalisés et MCP configurés avec defer_loading: true ne sont pas initialement disponibles pour un programme. Une fois que le modèle a chargé un outil correspondant, un programme ultérieur peut l’appeler via tools.* si son paramètre allowed_callers inclut "programmatic". Un programme déjà en cours d’exécution ne peut pas appeler la recherche d’outils : le modèle doit donc charger les outils différés avant de démarrer un programme qui en a besoin.

Guidez 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, attribuez chaque mode d’appel à une étape précise du workflow. Des instructions génériques comme « utilisez efficacement l’appel d’outils par programmation » ne précisent pas la limite souhaitée. Par exemple :

<tool_orchestration>
Use Programmatic Tool Calling for [bounded stage] using only [eligible tools].
Run independent calls concurrently when safe. Use only documented tool input
and output fields.

Process and reduce the intermediate results, then emit exactly [program result shape],
including the evidence needed for the final answer.

Stop when [condition] is met. Retry transient failures at most [R] times.
Do not repeat completed calls or perform side-effecting actions. If a required
result is still missing, return a clear structured failure.

Use direct tool calls for [semantic judgment, approval, or final validation].
</tool_orchestration>

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

<tool_orchestration>
Use Programmatic Tool Calling to compare inventory with demand for sku_123
using only get_inventory and get_demand. Run both calls concurrently. Use
only documented tool input and output fields.

Process and reduce the intermediate results, then emit exactly one JSON object
with sku, available_units, requested_units, and shortage_units, where
shortage_units is max(requested_units - available_units, 0). Include
available_units and requested_units as evidence for the calculation.

Stop when both tool results contain the required fields. Retry transient
failures at most 1 time. Do not repeat completed calls or perform
side-effecting actions. If a required result is still missing, return a clear
structured failure.

Use direct tool calls only for approval before any inventory-changing action.
</tool_orchestration>

Pour les workflows qui nécessitent les deux modes, définissez un seul passage de relais et évitez d’alterner entre les modes d’appel ou de répéter le travail. Si une solution de repli sûre existe, définissez-la une seule fois et limitez le nombre de tentatives.

Comprenez les éléments de réponse des programmes

Chaque appel à l’API renvoie toujours l’objet standard de l’API Responses. L’appel d’outils par programmation n’introduit pas d’enveloppe de réponse distincte. Lorsque le modèle utilise l’appel d’outils par programmation, le tableau output de la réponse peut contenir :

  • Un élément program contenant le JavaScript généré, un call_id et une valeur opaque fingerprint servant à reprendre ou à rejouer le programme.
  • Un élément function_call créé 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 et l’état du programme. Son champ call_id correspond au call_id du programme, et son champ status vaut completed ou incomplete.

Ce sont des éléments distincts de premier niveau dans response.output ; le champ caller consigne la relation entre leurs exécutions.

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 ne montrent que les éléments pertinents de response.output ; ils omettent l’objet Responses standard qui les englobe. Une fois que votre application a renvoyé les résultats des fonctions imbriquées, 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 dans program_output.result respecte la structure du résultat du programme définie dans vos instructions. L’élément program_output qui l’entoure respecte le contrat de l’API présenté ci-dessus. Ces deux contrats sont distincts. Un élément message final peut arriver avec la sortie du programme ou dans une réponse ultérieure. Continuez donc jusqu’à recevoir ce message.

OpenAI exécute le JavaScript généré par le modèle dans l’environnement d’exécution hébergé. Votre application exécute les appels de fonction renvoyés qui relèvent du client ; elle n’exécute pas le JavaScript généré.

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

Poursuivez après les appels de fonction gérés côté client

Un programme peut se mettre en pause plusieurs fois lorsqu’il atteint des outils gérés côté client. Continuez 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 par programmation.
  2. Exécutez chaque appel de fonction renvoyé qui relève du client.
  3. Renvoyez chaque résultat de fonction avec les valeurs d’origine de call_id et de caller.
  4. Traitez toute réponse incomplète avant de continuer.
  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 le contenu de refus du message.

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

Exécutez une boucle d’appels d’outils par programmation
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,
            }
        )

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

Pour les requêtes sans état adressées à un modèle de raisonnement, renvoyez chaque élément de raisonnement reçu. Chaque élément inclut encrypted_content par défaut. Consultez le guide sur l’état de la conversation pour connaître le fonctionnement général des échanges sans état.

Concevez des outils pour les programmes

  • Renvoyez des données structurées et compactes que JavaScript peut examiner sans avoir à analyser du texte rédigé.
  • Utilisez output_schema pour définir les champs et les types attendus dans les résultats de chaque outil, et documentez son comportement en cas d’erreur. Si la structure du résultat n’est pas connue à l’avance, conservez l’appel direct de l’outil afin que le modèle puisse examiner le résultat.
  • Définissez la structure exacte du résultat du programme et les éléments de preuve requis. Renvoyez une indication d’échec claire et structurée lorsque le programme ne peut pas produire de résultat valide.
  • Rendez les appels de fonction idempotents lorsque c’est possible. Une nouvelle tentative ou une réexécution ne devrait pas reproduire un effet de bord dangereux.
  • Vérifiez les arguments et les autorisations de chaque appel dans votre application, même lorsqu’il 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 à fort impact, quel que soit l’appelant.

Évaluez l’appel d’outils par programmation

L’appel d’outils par programmation peut réduire la quantité de résultats intermédiaires d’outils ajoutés au contexte du modèle, mais son effet dépend de la tâche et des réponses des outils. Prenez d’abord l’appel direct d’outils comme référence, puis comparez les deux approches sur des tâches représentatives.

Définissez le niveau de qualité attendu de la réponse finale et les éléments de preuve requis avant de mesurer l’efficacité. Évaluez la consommation de tokens et les appels d’outils, ainsi que l’exactitude, l’exhaustivité et la couverture des éléments de preuve. Explicitez tout compromis accepté sur la qualité.

Mesurez :

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

API Agents

Dans l’API Agents, l’appel d’outils par programmation s’exécute dans le harnais d’agent géré par OpenAI et est activé par défaut. Le harnais fournit à l’agent un outil exec et rend ses outils existants accessibles depuis le JavaScript généré. Vous n’avez pas besoin d’encapsuler ces outils dans des programmes en ligne de commande ni de les installer dans le bac à sable.

Pour désactiver l’appel d’outils par programmation, ajoutez cette entrée dans agent.tools :

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

Si vous omettez l’entrée ou son champ enabled, l’appel d’outils par programmation reste activé. Une entrée ne contenant que le type, { "type": "programmatic_tool_calling" }, le laisse également activé. La configuration de allowed_callers et la boucle de continuation Responses présentées ci-dessus décrivent l’intégration avec l’API Responses.

L’appel d’outils par programmation fonctionne également dans les sessions limitées à la conversation, avec environment.type défini sur none. Bash, les serveurs 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 l’endroit où il s’exécute. Un appel au shell exécute des commandes dans le bac à sable ; l’environnement d’exécution JavaScript ne lance pas lui-même de processus système. Les serveurs MCP d’exécution utilisent toujours le bac à sable, et les outils de type fonction appellent toujours votre serveur d’application. L’agent traite leurs résultats avant de décider quels éléments intégrer au contexte du modèle.

Utilisez les recommandations 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 gérer les appels.

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