Accueil / Articles / Comprendre les éléments et les messages dans l’API de réponses d’OpenAI

Comprendre les éléments et les messages dans l’API de réponses d’OpenAI

Explique comment l’API Responses d’OpenAI réorganise les sorties du modèle en éléments plutôt qu’en messages, et pourquoi ce changement est important pour les appels d’outils et les workflows agents.

1735 mots

Chat Completions : conçu autour des messages

Chat Completions a longtemps été le format de prédilection pour interagir avec les LLM. Une requête typique se présente ainsi :

response = client.chat.completions.create(
    model="...",
    messages=[
        {
            "role": "user",
            "content": "Explain RAG"
        }
    ]
)

Vous envoyez une liste de messages, et le modèle renvoie le prochain message de l’assistant. C’est assez simple. En revanche, l’API Responses diffère déjà un peu au départ.

response = client.responses.create(
    model="...",
    input="Explain RAG"
)

À première vue, cela pourrait sembler être simplement un autre point d’entrée où input remplace messages. Mais la véritable différence réside dans l’abstraction sous-jacente utilisée par chaque API. Chat Completions est axé sur les messages, tandis que l’API Responses est organisée autour des éléments et réponses. Lorsque l’on intègre des outils et des agents, cette distinction devient beaucoup plus importante.

Une conversation dans les complétions de chat n’est rien d’autre qu’un tableau d’objets de message.

messages = [
    {
        "role": "system",
        "content": "Act as a helpful AI assistant."
    },
    {
        "role": "user",
        "content": "What is a vector database?"
    }
]

Chaque message contient un rôle ainsi que du contenu.

Les rôles typiques que vous verrez sont :

  • system
  • user
  • assistant

Vous pouvez vous représenter le flux de cette manière :

Messages (list)-> Model -> Assistant Message

Le modèle prend en compte la conversation jusqu’à présent et génère le prochain message de l’assistant. Un cycle de conversation minimal se présente ainsi :

# Human message appended to the messages list
messages.append({
    "role": "user",
    "content": user_input
})
response = client.chat.completions.create(
    model="...",
    messages=messages
)
# AI message appended to the messages list
messages.append(
    response.choices[0].message
)

Votre application est chargée de suivre l’historique des messages, que ce soit en mémoire, dans une base de données ou selon votre choix. Chaque nouveau message de l’utilisateur est ajouté à cette liste, la liste complète est envoyée au modèle, et la réponse du modèle est à son tour ajoutée. Ce schéma correspond naturellement au fonctionnement des interfaces de chat.

User Message (str)->
Message History (list[dict])->
Model (llm)->
Assistant Message (str)->
Message History (list[dict])

Pour la génération de texte brut et les cas d’usage typiques de type chat, cette configuration est tout à fait adéquate. Mais ses limites apparaissent dès qu’une application alimentée par un LLM doit faire plus que simplement converser.

Les applications LLM ne sont pas seulement des applications de chat

Prenons une demande comme celle-ci :

Recherchez les derniers développements en IA, rédigez un résumé des points importants et envoyez ce résumé dans ma boîte de réception.

Pour répondre à cette demande, le modèle doit lancer une recherche sur Internet et se connecter à un service de messagerie électronique.

D’un coup, le parcours d’exécution implique bien plus qu’un simple échange message utilisateur-message assistant.

User Request ->
Model ->
Web Search ->
Search Results ->
Summarize (Model)->
Send Mail ->
Final Response

Les applications plus complexes peuvent faire appel à plusieurs outils différents assemblés les uns après les autres.

À ce stade, le modèle est un participant actif dans une chaîne d’exécution plus large, et non simplement un générateur de texte. Une partie de ce qu’il produit n’est jamais destinée à parvenir à l’utilisateur final — elle existe uniquement pour un traitement interne. Le modèle peut invoquer une outil, et le résultat de cette invocation peut nécessiter un traitement supplémentaire, ce qui peut déclencher une autre invocation d’outil. Ce n’est qu’une fois tout cela résolu que le modèle produit une réponse finale, et même cette réponse n’a pas nécessairement la forme d’un message de texte.

Le flux ne peut plus être réduit à :

Messages (list)-> Model -> Assistant Message

Il existe désormais des sorties intermédiaires et des actions au niveau de l’application qui doivent être conservées dans le contexte, et c’est précisément là que l’abstraction basée uniquement sur les messages commence à sembler trop restreinte.

Toute sortie de modèle != message

Lorsqu’un modèle est connecté à des outils, une grande partie de ce qu’il génère consiste en des appels de fonction plutôt que en du texte conversationnel.

Prenons ceci comme exemple :

Function Call
Name: get_weather
Arguments:
{
    "city": "Bengaluru"
}

Ce n’est pas quelque chose destiné à être lu par l’utilisateur — c’est une instruction adressée à votre application. Votre code exécute la fonction correspondante et renvoie le résultat au modèle, et ce résultat peut, ou non, être affiché à l’utilisateur.

Dans la pratique, un modèle peut produire au moins deux types distincts de sorties lors d’une exécution :

  1. Appel de fonction
  2. Message

Faire entrer ces deux éléments sous le même terme de « message d’assistant » ne reflète pas ce qui se passe réellement pendant l’exécution. Ce manque de correspondance constitue le problème de conception fondamental que l’API Responses cherche à résoudre.

API Responses : une abstraction différente

Plutôt que d’être organisée autour de l’échange de messages, l’API Responses est conçue autour du concept de réponse, qui peut regrouper plusieurs éléments de sortie.

response = client.responses.create(
    model="...",
    input="Explain LangGraph"
)
print(response.output)
# response.output is a list of output items.

Pour une demande en texte brut, cette sortie peut n’être qu’un seul message. Mais pour tout ce qui concerne des agents ou des outils, une réponse peut inclure plusieurs types d’éléments différents. Une vue simplifiée de cette structure est la suivante :

Response
| Reasoning Item
| Function Call Item
| Message Item

Dans ce modèle, un message n’est qu’un des plusieurs types de sortie possibles, et non l’ensemble de ce que représente une réponse.

Différence :

Différence :

Messages (list)-> Model -> Assistant Message

Complétions de chat

Input -> Model -> Response

Response:
| Output Item
| Output Item
| Output Item

API Responses

Avec Chat Completions, c’est la conversation elle-même qui est modélisée, tandis que l’API Responses présente l’exécution du modèle comme une réponse composée d’éléments de sortie. Pour une simple complétion de texte, cette distinction a peu d’importance. Elle devient significative dès l’apparition d’outils, de modèles capables de raisonnement ou de workflows agents.

Messages vs Éléments

La véritable différence entre ces deux API se manifeste dans la manière dont chacune structure sa réponse.

Avec Chat Completions, tout est centré sur le message.

print(response.choices[0].message.content)
# The generated text is inside the assistant message.
# response
# | choices
#     | message
#         | content

L’API Responses organise ses résultats de manière différente.

print(response.output)
# A simplified structure:
# response
# | output
#   | reasoning
#   | function_call
#   | message
# The important difference is that output is not a list of messages,
# but output items.

Un message n’est qu’un type d’élément ; une appel de fonction en est un autre ; et les modèles qui prennent en charge le raisonnement peuvent également générer des éléments de raisonnement. Cela change la façon dont on perçoit ce qu’un modèle renvoie réellement.

Chat Completions
Model Output = Assistant Message

Responses API
Model Output = List of Output Items

- The structure which is useful for tool calling.

Appels d’outils en tant qu’éléments de sortie

Prenez une fonction simple qui indique la météo (l’exemple classique utilisé partout).

def get_weather(city: str):
    return f"The weather in {city} is 28°C"
# The weather is ofcoure hardcoded.

Vous pouvez exposer cette fonction au modèle en tant que définition d’outil.

tools = [
    {
        "type": "function",
        "name": "get_weather",
        "description": "Get the current weather for a city",
        "parameters": {
            "type": "object",
            "properties": {
                "city": {
                    "type": "string"
                }
            },
            "required": ["city"]
        }
    }
]

Cette définition d’outil est incluse dans votre requête.

response = client.responses.create(
    model="...",
    tools=tools,
    input="What is the weather in Bengaluru?"
)

À partir de là, le modèle dispose de deux chemins possibles.

Option 1: Generate a message
Option 2: Call get_weather

Puisque la valeur météo réelle de la fonction n’est pas intégrée à sa définition, le modèle a besoin de données en temps réel, ce qui le pousse à émettre un élément d’appel de fonction au lieu de répondre directement.

Function Call Item
name: get_weather
arguments:
{
    "city": "Bengaluru"
}

Vous pouvez localiser cet appel de fonction en parcourant les éléments de sortie de la réponse.

for item in response.output:
    if item.type == "function_call":
        print(item.name)
        print(item.arguments)

En l’exécutant, vous obtenez :

get_weather
{"city":"Bengaluru"}

À ce stade, le modèle n’a pas encore produit de réponse finale ; il a simplement demandé qu’une action soit exécutée ; c’est votre application qui doit exécuter la fonction elle-même.

result = get_weather("Bengaluru")

Ce résultat doit ensuite être renvoyé au modèle.

Résultat d’une appel de fonction

Vous représentez le résultat d’un outil à l’aide d’un élément de type function_call_output.

tool_output = {
    "type": "function_call_output",
    "call_id": item.call_id,
    "output": result
}

Le champ call_id relie ce résultat à l’appel de fonction spécifique qui l’a demandé.

Function Call
| call_id: call_123
Application Executes Tool
Function Call Output
| call_id: call_123

Cette correspondance devient essentielle lorsque plusieurs outils sont invoqués en même temps, par exemple une requête demandant la météo dans deux villes différentes simultanément.

Boucle d’exécution de base des outils

Les applications basées sur des outils suivent généralement un cycle répétitif.

User Input ->
Model ->
Response Output Items ->
Check for Function Calls ->
Execute Functions ->
Create Function Call Outputs ->
Model ->
Final Response

Voici à peu près à quoi cela ressemble dans le code.

response = client.responses.create(
    model="...",
    input=user_input,
    tools=tools
)
while True:
    function_calls = [
        item
        for item in response.output
        if item.type == "function_call"
    ]
    if not function_calls:
        break
    tool_outputs = []
    for call in function_calls:
        result = execute_tool(
            call.name,
            call.arguments
        )
        tool_outputs.append({
            "type": "function_call_output",
            "call_id": call.call_id,
            "output": result
        })
    response = client.responses.create(
        model="...",
        previous_response_id=response.id,
        input=tool_outputs,
        tools=tools
    )

Ce cycle continue de se répéter tant que le modèle renvoie des appels de fonction. Une fois qu’il cesse de demander des appels d’outils, la réponse contient le résultat final du modèle.

Model ->
Function Call ->
Tool Result ->
Model ->
Function Call ->
Tool Result ->
Model ->
Message

Cette boucle constitue la base de la plupart des implémentations d’agents chargés d’appeler des outils.

Conclusion

L’API Responses n’est pas simplement une interface repensée à partir de Chat Completions. Elle propose une structure qui reflète plus fidèlement le fonctionnement des applications LLM contemporaines lorsqu’elles utilisent des outils, du raisonnement et plusieurs types de sorties. Chat Completions reste un choix solide pour des cas d’usage conversationnels simples. Mais dès que votre flux de travail devient plus orienté vers des agents, l’API Responses est la solution idéale. Les messages gèrent la conversation ; les éléments gèrent l’exécution. C’est là toute l’idée.

Lectures complémentaires