Conversation multi-tours

L'API CloudZIR est sans état : elle ne garde aucune mémoire entre deux requêtes. Pour tenir une conversation, c'est vous qui renvoyez l'historique à chaque appel.

Le principe#

Chaque appel contient la liste complète des messages échangés jusqu'ici. Le modèle lit cette liste et produit le message suivant :

Tour 1  → [system, user₁]                              → assistant₁
Tour 2  → [system, user₁, assistant₁, user₂]           → assistant₂
Tour 3  → [system, user₁, assistant₁, user₂, ...]      → assistant₃

Exemple complet#

from openai import OpenAI

client = OpenAI(api_key="zir_ai_...", base_url="https://api.cloudzir.com/v1")

messages = [{"role": "system", "content": "Tu es un assistant concis, en français."}]

def demander(texte: str) -> str:
    messages.append({"role": "user", "content": texte})
    reponse = client.chat.completions.create(model="mina-low", messages=messages)
    contenu = reponse.choices[0].message.content
    # ÉTAPE CLÉ : on remet la réponse dans l'historique
    messages.append({"role": "assistant", "content": contenu})
    return contenu

print(demander("Quelle est la capitale de l'Algérie ?"))
print(demander("Et sa population ?"))   # « sa » = celle d'Alger : le contexte est conservé
L'oubli le plus fréquent

Ne pas réinsérer la réponse de l'assistant dans messages. Le modèle perd alors le fil et se répète : chaque tour lui paraît être le premier.

Les rôles#

RôleUsage
systemInstructions permanentes : ton, langue, périmètre, règles. Placé en premier.
userCe que dit l'utilisateur final.
assistantCe que le modèle a répondu (à renvoyer tel quel).
toolLe résultat d'un outil que vous avez exécuté (appels d'outils).

developer est accepté comme synonyme de system.

Maîtriser la longueur (et le coût)#

L'historique est refacturé à chaque tour. Trois stratégies, du plus simple au plus fin :

1. Fenêtre glissante#

MAX_TOURS = 12

def tronquer(messages: list[dict]) -> list[dict]:
    """Garde le message système + les N derniers messages."""
    systeme = [m for m in messages[:1] if m["role"] == "system"]
    reste = messages[len(systeme):]
    return systeme + reste[-MAX_TOURS * 2:]

2. Résumé des anciens tours#

def compacter(client, messages: list[dict]) -> list[dict]:
    """Remplace les vieux échanges par un résumé - moins cher que de tout garder."""
    systeme, anciens, recents = messages[:1], messages[1:-8], messages[-8:]
    if len(anciens) < 6:
        return messages
    resume = client.chat.completions.create(
        model="mina-flash",           # le résumé n'a pas besoin du gros modèle
        messages=[
            {"role": "system", "content": "Résume ces échanges en 5 points factuels."},
            {"role": "user", "content": str(anciens)},
        ],
        max_tokens=300,
    ).choices[0].message.content
    return systeme + [{"role": "system", "content": f"Résumé des échanges précédents : {resume}"}] + recents

3. Vérifier avant d'envoyer#

Si le contexte estimé dépasse la fenêtre du modèle, l'API répond 400 avec un message explicite. Anticipez avec l'estimation « caractères ÷ 4 » décrite dans Jeton et utilisation, et choisissez un modèle à plus grande fenêtre si besoin (mina-full monte à 256 000 jetons ; voir Modèles et prix).

Persister une conversation#

Stockez la liste messages dans votre base (une ligne par message, avec l'identifiant de conversation) et rechargez-la au tour suivant. Ne stockez jamais la clé API côté navigateur : l'appel doit partir de votre serveur.

Confidentialité

CloudZIR n'utilise pas le contenu de vos prompts pour entraîner des modèles. Pour les appels synchrones de conversation, les routes de l'API transmettent les messages et images au moteur sans les enregistrer dans la base de l'API; les métadonnées d'usage nécessaires à la facturation et au support sont enregistrées. Les fichiers déposés via l'API Fichiers sont stockés séparément et supprimés par DELETE /v1/files/{file_id}; aucune expiration automatique ne leur est appliquée. Voir aussi Mise en cache du contexte et la FAQ.

Paramètres

Réponse API