Aller au contenu principal

Conversation state et gestion de session

Conversation state et gestion de session

La Responses API introduit une gestion de session native qui simplifie considérablement le maintien du contexte entre les échanges. Fini le besoin de renvoyer tout l’historique à chaque requête.

Le chaînage avec previous_response_id

La fonctionnalité la plus importante de la Responses API est le chaînage automatique des réponses :

from openai import OpenAI
client = OpenAI()

# Premier échange
r1 = client.responses.create(
    model="gpt-5.3",
    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.3",
    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.3",
    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

Comparaison avec l’ancien modèle

Avant (Chat Completions) — gestion manuelle

# 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

Maintenant (Responses API) — gestion automatique

# NOUVEAU — un seul ID suffit
r = client.responses.create(
    model="gpt-5.3",
    input="Comment les additionner ?",
    previous_response_id="resp_precedent_id"
)
# Le serveur gère l'historique pour vous

Gestion de sessions multiples

Pour gérer plusieurs conversations en parallèle (multi-utilisateurs) :

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.3",
            "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 ?"))

Gestion de l’historique avec input structuré

Vous pouvez aussi gérer l’historique manuellement quand vous avez besoin de plus de contrôle :

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.3",
            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()

Stratégies de persistance

Sauvegarde des sessions en base de données

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.3", "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

Expiration des sessions

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

Bonnes pratiques

  • Utilisez previous_response_id pour les conversations simples : c’est le plus efficace
  • Passez à l’input structuré quand vous avez besoin de modifier l’historique
  • Implémentez un nettoyage des sessions inactives
  • Surveillez les tokens cumulés pour éviter les surprises de facturation
  • Stockez les response.id de manière fiable (base de données, pas mémoire)

Points clés à retenir

  • previous_response_id chaîne les réponses automatiquement sans renvoyer l’historique
  • Chaque réponse a un id unique 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