Conversation state et gestion de session
Mis à jour le 29 juillet 2026
Conversation state et gestion de session
La Responses API prend en charge nativement l’état de la conversation, ce qui simplifie considérablement le maintien du contexte entre les échanges. Vous n’avez plus à reconstituer l’historique à chaque requête : le serveur le fait pour vous, et vous ne transportez qu’un identifiant.
Le chaînage avec previous_response_id
Le mécanisme tient en un champ. Chaque réponse possède un id ; passez-le dans previous_response_id à l’appel suivant, et le modèle dispose de tout ce qui précède. Dans l’échange ci-dessous, la troisième question — « et comment les additionner ? » — n’a aucun sens isolée : c’est le chaînage qui permet au modèle de savoir qu’il s’agit de fractions, et que son interlocuteur débute.
from openai import OpenAI
client = OpenAI()
# Premier échange
r1 = client.responses.create(
model="gpt-5.6-terra",
instructions="Vous êtes un professeur de mathématiques patient.",
input="Je voudrais apprendre les fractions."
)
print(f"R1 : {r1.output_text}")
# Deuxième échange — le contexte est conservé automatiquement
r2 = client.responses.create(
model="gpt-5.6-terra",
input="Pouvez-vous me donner un exemple concret ?",
previous_response_id=r1.id
)
print(f"R2 : {r2.output_text}")
# Troisième échange — toute la conversation est accessible
r3 = client.responses.create(
model="gpt-5.6-terra",
input="Et comment les additionner ?",
previous_response_id=r2.id
)
print(f"R3 : {r3.output_text}")
# Le modèle sait qu'on parle de fractions depuis le début
Mesurez l’écart avec l’approche antérieure. Avec Chat Completions, la conversation vivait dans une liste que vous entreteniez vous-même et que vous réexpédiiez intégralement à chaque tour, avec un coût qui croissait à mesure que l’échange s’allongeait.
# ANCIEN — vous deviez tout gérer vous-même
historique = [
{"role": "system", "content": "Vous êtes un professeur de maths."},
{"role": "user", "content": "Je voudrais apprendre les fractions."},
{"role": "assistant", "content": "Bien sûr ! Les fractions..."},
{"role": "user", "content": "Un exemple concret ?"},
{"role": "assistant", "content": "Prenons une pizza..."},
{"role": "user", "content": "Comment les additionner ?"},
]
# Tout renvoyé à chaque appel = coûts croissants
Le même échange se réduit désormais à une référence :
# NOUVEAU — un seul ID suffit
r = client.responses.create(
model="gpt-5.6-terra",
input="Comment les additionner ?",
previous_response_id="resp_precedent_id"
)
# Le serveur gère l'historique pour vous
Faire cohabiter plusieurs conversations
Dès que votre application sert plusieurs utilisateurs, la question devient : quel identifiant appartient à qui ? La réponse tient en une table d’association entre l’utilisateur et le dernier response.id de sa conversation. Le gestionnaire ci-dessous encapsule cette mécanique : il ajoute previous_response_id uniquement si une session existe déjà, puis mémorise le nouvel identifiant. Alice et Bob dialoguent ainsi en parallèle sans que leurs contextes se mélangent, et « nouvelle session » se résume à oublier l’identifiant courant.
class SessionManager:
"""Gestionnaire de sessions de conversation."""
def __init__(self):
self.sessions: dict[str, str] = {} # user_id -> last_response_id
def envoyer_message(self, user_id: str, message: str,
instructions: str = "") -> str:
"""Envoie un message dans la session de l'utilisateur."""
kwargs = {
"model": "gpt-5.6-terra",
"input": message,
}
# Ajouter les instructions si fournies
if instructions:
kwargs["instructions"] = instructions
# Chaîner avec la réponse précédente si elle existe
if user_id in self.sessions:
kwargs["previous_response_id"] = self.sessions[user_id]
response = client.responses.create(**kwargs)
# Sauvegarder l'ID pour le prochain tour
self.sessions[user_id] = response.id
return response.output_text
def nouvelle_session(self, user_id: str):
"""Démarre une nouvelle conversation pour l'utilisateur."""
if user_id in self.sessions:
del self.sessions[user_id]
# Utilisation
manager = SessionManager()
# Utilisateur Alice
print(manager.envoyer_message("alice", "Bonjour, parlez-moi de Python."))
print(manager.envoyer_message("alice", "Quels sont ses avantages ?"))
# Utilisateur Bob (conversation indépendante)
print(manager.envoyer_message("bob", "Bonjour, parlez-moi de Rust."))
# Alice continue sa conversation sur Python
print(manager.envoyer_message("alice", "Comment débuter ?"))
Quand reprendre la main sur l’historique
Le chaînage automatique a une limite : vous ne pouvez pas modifier ce qui a déjà été dit. Si votre application doit résumer les anciens tours, supprimer un message contenant des données personnelles ou réinjecter un contexte reformulé, repassez à l’input structuré et gérez la liste vous-même. Vous y perdez la simplicité, vous y gagnez le contrôle total — et la possibilité de suivre l’inflation des tokens tour après tour.
def conversation_manuelle():
"""Conversation avec gestion manuelle de l'historique."""
historique = []
while True:
user_input = input("Vous : ")
if user_input.lower() == "quit":
break
historique.append({"role": "user", "content": user_input})
response = client.responses.create(
model="gpt-5.6-terra",
instructions="Vous êtes un assistant concis.",
input=historique
)
assistant_msg = response.output_text
historique.append({"role": "assistant", "content": assistant_msg})
print(f"Assistant : {assistant_msg}")
print(f" (Tokens cumulés : {response.usage.input_tokens})")
# conversation_manuelle()
Survivre à un redémarrage
Un dictionnaire en mémoire disparaît au premier redéploiement, et avec lui toutes les conversations en cours. En production, persistez les identifiants — le fichier JSON ci-dessous illustre le principe, que vous transposerez à votre base de données ou à Redis. Notez qu’on enregistre au passage l’horodatage et la consommation, deux informations qui serviront respectivement à l’expiration et au suivi des coûts.
import json
from datetime import datetime
class PersistentSessionManager:
"""Sessions persistantes avec sauvegarde."""
def __init__(self, db_path: str = "sessions.json"):
self.db_path = db_path
self.sessions = self._charger()
def _charger(self) -> dict:
try:
with open(self.db_path, "r") as f:
return json.load(f)
except FileNotFoundError:
return {}
def _sauvegarder(self):
with open(self.db_path, "w") as f:
json.dump(self.sessions, f, indent=2)
def envoyer(self, session_id: str, message: str) -> str:
kwargs = {"model": "gpt-5.6-terra", "input": message}
if session_id in self.sessions:
kwargs["previous_response_id"] = self.sessions[session_id]["last_id"]
response = client.responses.create(**kwargs)
self.sessions[session_id] = {
"last_id": response.id,
"updated_at": datetime.now().isoformat(),
"total_tokens": response.usage.total_tokens
}
self._sauvegarder()
return response.output_text
Prévoyez enfin une expiration. Sans nettoyage, votre table de sessions ne fait que croître, et un utilisateur qui revient trois semaines plus tard reprendrait une conversation dont il ne se souvient plus. Vingt-quatre heures constituent un point de départ raisonnable, à ajuster selon votre usage.
from datetime import datetime, timedelta
def nettoyer_sessions(sessions: dict, max_age_heures: int = 24) -> dict:
"""Supprime les sessions expirées."""
maintenant = datetime.now()
actives = {}
for sid, data in sessions.items():
updated = datetime.fromisoformat(data["updated_at"])
if maintenant - updated < timedelta(hours=max_age_heures):
actives[sid] = data
supprimees = len(sessions) - len(actives)
if supprimees > 0:
print(f"{supprimees} session(s) expirée(s) supprimée(s)")
return actives
En résumé de ces choix : utilisez previous_response_id pour les conversations simples, c’est le plus efficace ; passez à l’input structuré quand vous devez modifier l’historique ; stockez les response.id de façon fiable, en base et non en mémoire ; nettoyez les sessions inactives ; et surveillez les tokens cumulés, faute de quoi la facturation vous rappellera à l’ordre.
Points clés à retenir
previous_response_idchaîne les réponses automatiquement sans renvoyer l’historique- Chaque réponse a un
idunique utilisable pour le chaînage - Gérez des sessions multiples avec un dictionnaire
user_id -> response_id - L’input structuré (liste de messages) offre plus de contrôle sur l’historique
- Implémentez la persistance et l’expiration des sessions en production