Memoire IA : historique de conversation et tokens
Points clés à retenir : Donner de la mémoire à une IA conversationnelle repose sur trois mécanismes : une sliding window avec priorités (system prompt + derniers échanges toujours inclus), un budget tokens contrôlé pour maîtriser les coûts (une conversation de 10 échanges coûte 5x plus), et un système de fallback entre modèles LLM pour garantir la disponibilité. C'est la différence entre un outil et un véritable assistant.
La première version de l'IA dans TAMSIV était amnésiaque. "Ajoute du pain à ma liste de courses" fonctionnait parfaitement. Mais enchaîner avec "Mets-la pour demain" échouait lamentablement — l'IA ne savait pas de quelle tâche on parlait. Chaque échange repartait de zéro.
Ce problème est fondamental dans toute application IA conversationnelle. Les LLM n'ont aucune mémoire native. À chaque requête, il faut renvoyer l'ensemble du contexte. Et ça change tout — en termes d'UX, de coût, et d'architecture.
Voici comment j'ai donné de la mémoire à l'IA de TAMSIV.
Pourquoi les LLM n'ont-ils pas de mémoire ?
C'est contre-intuitif quand on utilise ChatGPT ou Claude, mais ces modèles n'ont aucune persistance entre les requêtes. Chaque appel API est indépendant. La "mémoire" que tu perçois dans une conversation ChatGPT, c'est l'application qui renvoie l'historique complet à chaque message.
Concrètement, quand tu envoies ton 5e message dans une conversation, l'API reçoit :
- Le system prompt (les instructions de comportement)
- Le message 1 + la réponse 1
- Le message 2 + la réponse 2
- Le message 3 + la réponse 3
- Le message 4 + la réponse 4
- Ton message 5
À chaque échange supplémentaire, le payload grossit. Et avec lui, deux problèmes : la fenêtre de contexte (limite technique du modèle) et le coût en tokens (chaque token est facturé).
C'est ce que la documentation d'OpenAI appelle la gestion de l'état de conversation.
Comment fonctionne la sliding window de TAMSIV ?
La solution classique est la sliding window : on ne garde que les N derniers échanges. Mais une fenêtre glissante brute est trop simpliste pour une app comme TAMSIV, où l'IA doit comprendre des références à des actions passées.
J'ai implémenté une sliding window avec priorités :
- Priorité maximale : le system prompt (toujours inclus, jamais tronqué)
- Priorité haute : les 2 derniers échanges (le contexte immédiat)
- Priorité haute : les function calls et leurs résultats (les actions effectuées — création de tâche, modification de mémo, etc.)
- Priorité moyenne : les échanges précédents (3 à N-2)
- Priorité basse : les échanges anciens, résumés ou supprimés selon le budget tokens
Pourquoi les function calls sont-ils prioritaires ? Parce que dans TAMSIV, quand l'utilisateur dit "Modifie la priorité de la tâche qu'on vient de créer", l'IA doit savoir quelle tâche a été créée. Cette information est dans le function_result d'un échange précédent. Sans elle, l'IA ne peut pas résoudre le pronom "la".
Comment gérer le budget tokens sans exploser les coûts ?
C'est le nerf de la guerre. Une conversation de 10 échanges peut coûter 5 fois plus qu'un échange isolé, parce que le payload cumulatif est renvoyé à chaque fois.
Exemple concret avec un modèle à 0.001$ / 1000 tokens :
- Échange 1 : ~500 tokens (system prompt + message) = 0.0005$
- Échange 5 : ~2500 tokens (historique 1-4 + message 5) = 0.0025$
- Échange 10 : ~5000 tokens (historique 1-9 + message 10) = 0.005$
- Total conversation 10 échanges : ~25 000 tokens cumulés = 0.025$
Pour une app avec des milliers d'utilisateurs, ces centimes s'additionnent vite. J'ai mis en place trois garde-fous :
- Limite de 20 échanges par conversation : au-delà, on recommande de démarrer une nouvelle conversation. Ça évite les conversations-monstres à 100 échanges.
- Compteur de tokens estimé : avant chaque appel, le backend estime le nombre de tokens du payload complet. Si ça dépasse le budget, les échanges les plus anciens sont tronqués.
- Choix du modèle selon la complexité : un message simple ("Ajoute du pain") utilise un modèle léger. Une demande complexe ("Réorganise mes tâches de la semaine par priorité") utilise un modèle plus puissant.
Comment fonctionne le fallback entre modèles LLM ?
En production, la fiabilité est non-négociable. Si le modèle principal (configuré via OPENROUTER_MODEL) renvoie une erreur 429 (rate limit) ou 503 (service unavailable), l'utilisateur ne doit rien remarquer.
Le backend de TAMSIV implémente un fallback automatique :
- Appel au modèle principal via OpenRouter
- Si erreur 429/503 → retry avec
OPENROUTER_FALLBACK_MODEL - L'
AlertServiceenvoie un email à l'admin pour signaler le fallback - L'utilisateur reçoit sa réponse normalement — aucune dégradation perceptible
En production, TAMSIV fait 2 à 3 fallbacks par semaine. C'est minime, mais ça arrive. Sans ce mécanisme, l'utilisateur verrait un message d'erreur générique — et probablement quitterait l'app.
Le choix d'OpenRouter comme proxy LLM (plutôt qu'appeler directement l'API OpenAI ou Anthropic) est stratégique : il permet de changer de modèle sans modifier le code. Si un nouveau modèle sort, il suffit de changer une variable d'environnement. Le pipeline vocal complet reste identique.
Quel impact la mémoire a-t-elle sur l'expérience utilisateur ?
La différence est spectaculaire. Avant l'historique de conversation, chaque échange était indépendant. Après, l'IA devient un véritable assistant :
- "Modifie la priorité de la tâche qu'on vient de créer" → l'IA sait quelle tâche, et la modifie
- "Finalement, mets ça à vendredi" → l'IA comprend que "ça" fait référence au dernier événement créé
- "Ajoute aussi un mémo là-dessus" → l'IA reprend le sujet de la conversation pour créer le mémo
- "Non, j'ai dit demain, pas après-demain" → l'IA corrige en comprenant la référence temporelle
C'est la différence entre un formulaire vocal (répète tout à chaque fois) et un assistant qui écoute et se souvient. C'est ce qui rend le Dictaphone de TAMSIV utilisable au quotidien.
Comment le system prompt influence-t-il la qualité des réponses ?
Le system prompt est le document le plus important de l'application. C'est lui qui définit le comportement de l'IA : ton, format de réponse, function tools disponibles, contraintes.
Le system prompt de TAMSIV inclut :
- Le persona : "Tu es l'assistant vocal de TAMSIV, une application de gestion de tâches et mémos."
- Les function tools : les 7 fonctions que l'IA peut appeler (
create_task,update_task,create_memo,update_memo,create_calendar_event,ask_clarification,end_conversation) - Les contraintes : réponses courtes (l'utilisateur écoute via TTS), pas de markdown, pas de listes longues
- Le contexte utilisateur : la langue préférée, le fuseau horaire, le plan (Free/Pro/Team)
Un system prompt bien conçu réduit drastiquement les hallucinations et les réponses hors-sujet. C'est un investissement qui se répercute sur chaque conversation.
Comment gérer les conversations multimodales (voix + texte) ?
Dans TAMSIV, l'utilisateur parle mais l'IA répond en voix (via OpenAI TTS) ET en texte (affiché à l'écran). L'historique stocke les deux formes :
- Côté utilisateur : le texte transcrit par le STT (pas l'audio brut — trop lourd en tokens)
- Côté IA : le texte de la réponse (envoyé au TTS et affiché)
Le pipeline complet est : Audio → STT → Texte → LLM (avec historique) → Texte → TTS → Audio. L'historique ne vit que dans la couche texte, ce qui simplifie énormément l'architecture.
Quelles alternatives à la sliding window existent ?
La sliding window n'est pas la seule approche. D'autres stratégies existent :
- Summarization : résumer les anciens échanges en un paragraphe condensé. Avantage : compression maximale. Inconvénient : perte de détails, et un appel LLM supplémentaire pour le résumé.
- RAG (Retrieval Augmented Generation) : stocker l'historique dans une base vectorielle et ne récupérer que les échanges pertinents par similarité sémantique. Puissant mais lourd à implémenter.
- Memory explicite : stocker des "faits" extraits des conversations (préférences utilisateur, tâches récurrentes) dans une base structurée. C'est ce que fait ChatGPT avec sa feature "Memory".
Pour TAMSIV, la sliding window avec priorités est le meilleur compromis : simple à implémenter, efficace en tokens, et suffisante pour des conversations de 5 à 15 échanges. Si les conversations devenaient beaucoup plus longues, j'envisagerais le RAG.
Comment implémenter un historique de conversation dans ton propre projet ?
Si tu construis une app avec un LLM, voici les étapes :
- Stocke l'historique côté serveur : ne fais jamais confiance au client pour maintenir l'état de la conversation. Le backend est la source de vérité.
- Implémente une sliding window : commence simple (garder les N derniers échanges), puis ajoute des priorités si nécessaire.
- Budgétise les tokens : compte les tokens avant chaque appel. Tronque intelligemment si le budget est dépassé.
- Ajoute un fallback : au minimum, un modèle de secours en cas d'erreur du modèle principal.
- Mesure les coûts : logue chaque appel avec le nombre de tokens utilisés. Ça permet d'optimiser le système sur la durée.
L'admin dashboard de TAMSIV affiche les métriques de conversation en temps réel : nombre moyen d'échanges, coût moyen par conversation, taux de fallback. Ces données sont essentielles pour optimiser les coûts en production.
FAQ
Combien d'échanges un utilisateur moyen fait-il par conversation ?
Dans TAMSIV, la moyenne est de 3 à 5 échanges. L'utilisateur type dit "Ajoute une tâche pour demain : rappeler le dentiste", confirme la preview, et enchaîne avec une modification ("Mets-la en priorité haute"). Les conversations longues (10+ échanges) représentent moins de 10% du volume.
Le coût de l'historique de conversation est-il significatif ?
Oui, c'est le poste de dépense principal du pipeline IA. Chaque échange supplémentaire augmente le coût cumulé. Pour TAMSIV, le coût moyen par conversation est d'environ 0.01 à 0.03 EUR avec le modèle principal. Le fallback model est généralement moins cher, ce qui compense partiellement les surcoûtes.
Faut-il stocker l'historique en base de données ?
Pour TAMSIV, l'historique vit en mémoire (côté backend) pendant la session WebSocket. Il n'est pas persisté en base — quand la connexion se ferme, l'historique est perdu. C'est un choix délibéré : les conversations sont courtes et transitoires. Si tu as besoin de reprendre des conversations plus tard, il faudra persister en base.
OpenRouter vs appeler directement les APIs des fournisseurs LLM ?
OpenRouter agit comme un proxy qui unifie l'accès à des dizaines de modèles (OpenAI, Anthropic, Google, etc.) via une seule API. L'avantage : changer de modèle sans modifier le code. L'inconvénient : une couche supplémentaire (latence marginale). Pour une app comme TAMSIV qui a besoin de flexibilité et de fallback, c'est un choix évident.
La mémoire de conversation fonctionne-t-elle avec le mode Realtime ?
TAMSIV a trois modes WebSocket. En mode LiveWebSocket (le mode par défaut), l'historique est géré manuellement comme décrit dans cet article. En mode Realtime (OpenAI Realtime API), l'historique est géré nativement par l'API — c'est un protocole bidirectionnel avec état. Le mode legacy fonctionne en batch sans persistance.