Aller au contenu principal

Responses API vs Chat Completions : pourquoi migrer

Mis à jour le 29 juillet 2026

Responses API vs Chat Completions

La Chat Completions API a été le standard pendant des années, et une grande partie du code que vous trouverez en ligne l’utilise encore. Depuis 2025, OpenAI la considère comme legacy et recommande la Responses API pour tous les nouveaux projets. Cette leçon compare les deux interfaces sur les points où la différence se voit réellement dans votre code.

Ce qui change fondamentalement

Avec Chat Completions, vous construisiez une liste de messages et vous la transmettiez intégralement à chaque appel. La conversation vivait dans votre programme, jamais côté serveur :

from openai import OpenAI
client = OpenAI()

# Ancien modèle — Chat Completions (legacy)
response = client.chat.completions.create(
    model="gpt-5.6-terra",
    messages=[
        {"role": "system", "content": "Vous êtes un assistant utile."},
        {"role": "user", "content": "Quelle est la capitale de la France ?"},
    ]
)
print(response.choices[0].message.content)
# Résultat : "La capitale de la France est Paris."

La Responses API ramène ce même appel à deux paramètres et à un accès direct au texte produit. Le message système devient le paramètre instructions, et l’extraction du contenu ne passe plus par le chemin choices[0].message.content :

from openai import OpenAI
client = OpenAI()

# Nouveau modèle — Responses API
response = client.responses.create(
    model="gpt-5.6-terra",
    input="Quelle est la capitale de la France ?"
)
print(response.output_text)
# Résultat : "La capitale de la France est Paris."

La gestion de session côté serveur

C’est le gain le plus immédiat. L’API conserve l’état de la conversation et vous n’avez plus qu’à référencer la réponse précédente par son identifiant. Imaginez un assistant de support : au dixième tour, l’approche Chat Completions vous obligeait à réexpédier les neuf échanges précédents, donc à payer leurs tokens une nouvelle fois. Ici, un seul champ suffit :

# Première question
response = client.responses.create(
    model="gpt-5.6-terra",
    input="Je m'appelle Marie."
)

# Deuxième question — le contexte est conservé automatiquement
followup = client.responses.create(
    model="gpt-5.6-terra",
    input="Comment je m'appelle ?",
    previous_response_id=response.id
)
print(followup.output_text)
# Résultat : "Vous vous appelez Marie."

Un JSON garanti plutôt qu’espéré

Le deuxième apport concerne la sortie structurée. Vous décrivez le format attendu, et la Responses API garantit que la réponse respecte votre schéma JSON — vous pouvez donc la désérialiser sans filet de sécurité. Un formulaire d’inscription qui extrait des informations d’un texte libre cesse ainsi de casser dès que le modèle décide d’ajouter une phrase d’introduction avant son JSON :

from pydantic import BaseModel

class Ville(BaseModel):
    nom: str
    pays: str
    population: int

response = client.responses.create(
    model="gpt-5.6-terra",
    input="Donnez-moi les informations sur Paris.",
    text={"format": {"type": "json_schema", "json_schema": {
        "name": "ville",
        "strict": True,
        "schema": Ville.model_json_schema()
    }}}
)
print(response.output_text)
# Résultat : {"nom": "Paris", "pays": "France", "population": 2161000}

Function calling et streaming

La déclaration des outils suit la même logique de simplification : le descripteur de fonction est déclaré à plat dans tools, et l’appel demandé par le modèle apparaît directement dans response.output, sans niveau d’imbrication supplémentaire.

response = client.responses.create(
    model="gpt-5.6-terra",
    input="Quelle météo fait-il à Lyon ?",
    tools=[{
        "type": "function",
        "name": "get_meteo",
        "description": "Obtenir la météo d'une ville",
        "parameters": {
            "type": "object",
            "properties": {
                "ville": {"type": "string", "description": "Nom de la ville"}
            },
            "required": ["ville"]
        }
    }]
)

# L'API retourne directement l'appel de fonction à exécuter
for item in response.output:
    if item.type == "function_call":
        print(f"Fonction : {item.name}, Arguments : {item.arguments}")
# Résultat : Fonction : get_meteo, Arguments : {"ville": "Lyon"}

Le streaming, lui, ne renvoie plus un flux de fragments indifférenciés mais des événements typés. Vous filtrez sur event.type et vous savez précisément ce que vous manipulez — un fragment de texte, un argument de fonction ou la fin de la génération :

stream = client.responses.create(
    model="gpt-5.6-terra",
    input="Racontez une courte histoire.",
    stream=True
)

for event in stream:
    if event.type == "response.output_text.delta":
        print(event.delta, end="", flush=True)

Guide de migration rapide

Chat CompletionsResponses API
client.chat.completions.create()client.responses.create()
messages=[...]input="..." ou input=[...]
response.choices[0].message.contentresponse.output_text
Historique manuelprevious_response_id
response_formattext={"format": {...}}
tools + tool_choicetools (simplifié)

Faut-il migrer tout de suite ?

La Chat Completions API reste fonctionnelle et n’est pas supprimée. Un code existant qui tourne, n’évolue plus et repose sur un pipeline de tests construit autour de l’ancien format n’a rien à gagner à être réécrit dans l’urgence. De même, si vous dépendez d’une bibliothèque tierce qui ne prend pas encore en charge la Responses API, la migration attendra sa mise à jour. En revanche, pour tout nouveau projet, la question ne se pose pas : la Responses API est le point de départ, et la suite de cette formation part de ce postulat.

Points clés à retenir

  • La Responses API remplace Chat Completions avec une interface plus simple
  • La gestion de session côté serveur élimine le besoin de renvoyer l’historique
  • Le Structured Output et le function calling sont nativement intégrés
  • La migration est directe : les concepts restent les mêmes, seule la syntaxe change