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 d’outils asynchrones

Laissez le modèle poursuivre son travail pendant que votre application exécute des outils en arrière-plan.

Les appels d’outils asynchrones permettent au modèle de poursuivre son travail après avoir appelé un outil, sans attendre le résultat. Utilisez-les pour lancer au plus tôt les recherches qui prennent du temps, répondre aux parties indépendantes d’une demande et transmettre les résultats dès que votre application en dispose.

Fonctionnement des outils asynchrones

Lors d’un appel de fonction ordinaire, le modèle suspend son tour en attendant la réponse de l’outil. Ajoutez async: true à la définition d’une fonction ou d’un outil personnalisé pour que le modèle puisse poursuivre son travail après l’appel, avant que votre application ne renvoie le résultat.

C’est toujours votre application qui exécute l’outil. Les outils asynchrones ne délèguent pas l’exécution à OpenAI et ne gèrent pas vos tâches en arrière-plan.

Le mode d’arrière-plan rend asynchrone la génération de la réponse. Avec les appels d’outils asynchrones, le modèle poursuit son travail pendant que votre application exécute un outil.

Lorsqu’une tâche se termine, incluez son résultat dans une requête ultérieure à l’API Responses. Utilisez le call_id fourni par l’API lors de l’appel initial pour associer le résultat à cet appel.

Type d’outil Élément d’appel Élément de résultat
Fonction function_call function_call_output
Outil personnalisé custom_tool_call custom_tool_call_output

Appelez un outil asynchrone

Ajoutez async: true à la définition de l’outil. Les éléments d’appel correspondants dans response.output contiennent async: true.

Consultez la météo en arrière-plan
import json
from concurrent.futures import ThreadPoolExecutor

from openai import OpenAI
from openai.types.responses import FunctionToolParam


def get_weather(city):
    # Demo data. Replace this function with your weather service.
    weather = {
        "Paris": {
            "city": "Paris",
            "temperature_c": 22,
            "condition": "Clear",
            "source": "demo weather snapshot",
        }
    }
    return weather[city]


worker = ThreadPoolExecutor()


def main():
    client = OpenAI()
    model = "gpt-6-astra"
    tools: list[FunctionToolParam] = [
        {
            "type": "function",
            "name": "get_weather",
            "description": "Read the demo weather snapshot for a city.",
            "async": True,
            "strict": True,
            "parameters": {
                "type": "object",
                "properties": {"city": {"type": "string"}},
                "required": ["city"],
                "additionalProperties": False,
            },
        },
    ]

    instructions = (
        "Start the weather lookup and answer the independent packing "
        "question without waiting. Use the actual tool result when it "
        "arrives; never invent it. Identify the weather as demo data."
    )
    response = client.responses.create(
        model=model,
        tools=tools,
        instructions=instructions,
        input=(
            "Check the demo weather in Paris. Meanwhile, "
            "list three essentials for any city trip."
        ),
    )

    call = next(item for item in response.output if item.type == "function_call")
    arguments = json.loads(call.arguments)
    if call.name != "get_weather" or arguments != {"city": "Paris"}:
        raise ValueError("Expected a weather lookup for Paris")

    latest_response_id = response.id
    if call.async_:
        job = worker.submit(get_weather, **arguments)
        print(response.output_text)
        # Independent work or conversation turns can happen here.
        # Update latest_response_id after each continuation.
        result = job.result()
    else:
        result = get_weather(**arguments)

    response = client.responses.create(
        model=model,
        tools=tools,
        instructions=instructions,
        previous_response_id=latest_response_id,
        input=[
            {
                "type": "function_call_output",
                "call_id": call.call_id,
                "output": json.dumps(result),
            },
        ],
    )
    print(response.output_text)


if __name__ == "__main__":
    try:
        main()
    finally:
        worker.shutdown(wait=True)

La réponse peut contenir à la fois l’appel asynchrone et une réponse à la demande de l’utilisateur. Si la conversation se poursuit avant la fin de la tâche, mettez à jour latest_response_id pour reprendre à partir de la dernière réponse, tout en conservant le call_id de l’appel initial.

Avec la diffusion en continu, vous pouvez lancer la tâche plus tôt. Démarrez-la dès que vous recevez l’élément d’appel complet, puis continuez à lire le flux de réponse.

Ajoutez un outil d’attente

Un outil d’attente permet au modèle de choisir à quel moment attendre un résultat encore indisponible. Il peut, par exemple, lancer deux recherches de prix, effectuer un travail indépendant et attendre les résultats uniquement lorsqu’il est prêt à comparer les prix.

Ajoutez un argument task_handle à chaque outil asynchrone. Le modèle attribue un identifiant de tâche à chaque appel. Votre application associe cet identifiant au call_id fourni par l’API lors de l’appel initial et à la tâche en cours. Utilisez des identifiants uniques sur toute la durée de la conversation, y compris pour les tâches terminées et les recherches répétées.

Définissez l’outil d’attente comme une fonction synchrone ordinaire. Omettez async ou attribuez-lui la valeur false. Votre application définit le schéma et le comportement de cet outil. wait_for_tasks ne fait pas partie des outils intégrés à l’API Responses.

Utilisez ces définitions dans le tableau tools de la requête.

[
  {
    "type": "function",
    "name": "lookup_price",
    "async": true,
    "description": "Look up a product price in the background. Choose a fresh task_handle unique within this conversation, including completed tasks.",
    "strict": true,
    "parameters": {
      "type": "object",
      "properties": {
        "sku": { "type": "string" },
        "task_handle": { "type": "string" }
      },
      "required": ["sku", "task_handle"],
      "additionalProperties": false
    }
  },
  {
    "type": "function",
    "name": "wait_for_tasks",
    "description": "Wait for selected tasks whose results you need. Pass a nonempty list of distinct task_handles from your earlier async tool calls. Results arrive on their original calls; this tool returns status only. Do not wait again for results that have already arrived.",
    "strict": true,
    "parameters": {
      "type": "object",
      "properties": {
        "task_handles": {
          "type": "array",
          "items": { "type": "string" }
        }
      },
      "required": ["task_handles"],
      "additionalProperties": false
    }
  }
]

Enregistrez chaque tâche

Enregistrez et démarrez chaque tâche avant de traiter un appel d’attente qui en dépend. Les appels peuvent arriver dans une même réponse ou dans des réponses distinctes. Les éléments de sortie ci-dessous illustrent le lancement de deux tâches suivi d’un appel qui attend leurs deux résultats.

[
  {
    "type": "function_call",
    "name": "lookup_price",
    "async": true,
    "call_id": "call_widget",
    "arguments": "{\"sku\":\"WIDGET\",\"task_handle\":\"widget_price_1\"}"
  },
  {
    "type": "function_call",
    "name": "lookup_price",
    "async": true,
    "call_id": "call_gadget",
    "arguments": "{\"sku\":\"GADGET\",\"task_handle\":\"gadget_price_1\"}"
  },
  {
    "type": "function_call",
    "name": "wait_for_tasks",
    "call_id": "call_wait",
    "arguments": "{\"task_handles\":[\"widget_price_1\",\"gadget_price_1\"]}"
  }
]

Le registre de votre application associe chaque identifiant de tâche à l’appel initial et à la tâche en cours.

Identifiant de tâche Identifiant de l’appel initial Tâche
widget_price_1 call_widget Recherche du prix de WIDGET
gadget_price_1 call_gadget Recherche du prix de GADGET

Conservez le registre pendant toute la conversation pour éviter de réutiliser l’identifiant d’une tâche terminée.

Transmettez les résultats avant le statut de l’appel d’attente

Retrouvez dans le registre les tâches correspondant aux identifiants demandés et attendez uniquement ces tâches. Renvoyez le résultat de chaque tâche qui vient de se terminer avec le call_id de son appel initial. Renvoyez ensuite le statut avec le call_id de l’appel d’attente. Le modèle dispose ainsi des résultats lorsqu’il reprend son travail.

Par exemple, envoyez les éléments de sortie ci-dessous dans le tableau input de la requête suivante. Les prix sont donnés à titre d’exemple.

[
  {
    "type": "function_call_output",
    "call_id": "call_widget",
    "output": "{\"task_handle\":\"widget_price_1\",\"price_cents\":1200,\"currency\":\"USD\"}"
  },
  {
    "type": "function_call_output",
    "call_id": "call_gadget",
    "output": "{\"task_handle\":\"gadget_price_1\",\"price_cents\":1500,\"currency\":\"USD\"}"
  },
  {
    "type": "function_call_output",
    "call_id": "call_wait",
    "output": "{\"status\":\"completed\",\"completed_task_handles\":[\"widget_price_1\",\"gadget_price_1\"]}"
  }
]

Attribuez à previous_response_id l’identifiant de la dernière réponse et incluez les outils et les instructions dans la requête de continuation. Votre application peut aussi transmettre les résultats dès qu’ils sont disponibles, sans appel d’attente. Utilisez l’outil d’attente uniquement si la prochaine étape du modèle dépend de résultats encore indisponibles.

Ajoutez un outil pour interroger l’utilisateur

Un outil asynchrone peut poser une question à l’utilisateur pendant que le modèle poursuit un travail indépendant. Le modèle peut, par exemple, demander à qui s’adresse un rapport, recueillir des informations pendant que l’utilisateur répond, puis adapter le rapport au public indiqué.

Définissez une fonction avec async: true pour demander une information manquante ou connaître une préférence. Votre application affiche la question, recueille la réponse et la renvoie comme résultat de l’outil. Votre application définit le schéma et le comportement de cet outil. request_user_input_async ne fait pas partie des outils intégrés à l’API Responses.

Ajoutez cette définition au tableau tools de la requête, aux côtés de wait_for_tasks.

{
  "type": "function",
  "name": "request_user_input_async",
  "async": true,
  "description": "Ask the user for missing information or a preference. Choose a fresh task_handle unique within this conversation, including completed tasks. Continue independent work while the answer is pending. Use wait_for_tasks when your next step depends on the answer.",
  "strict": true,
  "parameters": {
    "type": "object",
    "properties": {
      "question": { "type": "string" },
      "task_handle": { "type": "string" }
    },
    "required": ["question", "task_handle"],
    "additionalProperties": false
  }
}

Affichez la question

Dès que vous recevez l’élément d’appel complet, enregistrez le task_handle de la question et le call_id de l’appel initial, puis affichez la question à l’utilisateur. Dans l’exemple ci-dessous, la question porte sur le public du rapport.

{
  "type": "function_call",
  "name": "request_user_input_async",
  "async": true,
  "call_id": "call_audience",
  "arguments": "{\"question\":\"Who is the report for: executives or engineers?\",\"task_handle\":\"report_audience_1\"}"
}

Gardez la question en attente dans le registre utilisé par l’outil d’attente. Pendant que l’utilisateur répond, continuez à lire la réponse du modèle. Ne clôturez pas l’appel en confirmant que vous avez affiché la question. Le résultat attendu est la réponse de l’utilisateur.

Renvoyez la réponse de l’utilisateur

Lorsque l’utilisateur répond, envoyez sa réponse avec le call_id de l’appel qui a posé la question. Par exemple, incluez cet élément de sortie dans le tableau input de la requête suivante.

[
  {
    "type": "function_call_output",
    "call_id": "call_audience",
    "output": "{\"task_handle\":\"report_audience_1\",\"answer\":\"Executives\"}"
  }
]

Attribuez à previous_response_id l’identifiant de la dernière réponse et incluez les outils et les instructions dans la requête de continuation. Si le modèle a appelé wait_for_tasks pour report_audience_1, renvoyez la réponse avant le statut de l’appel d’attente, comme dans l’exemple d’utilisation de l’outil d’attente.

L’exécution asynchrone ne maintient pas la réponse du modèle ouverte jusqu’à ce que l’utilisateur réponde. Demandez au modèle de poursuivre le travail indépendant et d’attendre avant de passer à une étape qui nécessite la réponse de l’utilisateur. Si votre application permet à l’utilisateur d’ignorer la question ou fixe un délai pour y répondre, signalez explicitement l’absence de réponse dans le résultat lorsque l’utilisateur ignore la question ou que le délai expire. Le modèle pourra ainsi décider de la suite.

Compatibilité

GPT-6 Astra et les modèles ultérieurs prennent en charge les appels d’outils asynchrones.

L’exécution asynchrone s’applique aux fonctions et aux outils personnalisés que votre application exécute. Elle ne s’applique pas aux outils intégrés hébergés. Appelez les outils directement. Ne configurez pas les outils asynchrones pour les appels d’outils par programmation.

En mode multi-agent, ne combinez pas les outils asynchrones avec des appels d’outils parallèles.