Vous développez un agent conversationnel complexe et vous vous demandez comment gérer efficacement les transitions entre états de dialogue ? Après avoir testé les trois approches principales en production, je peux vous dire sans hésitation : le choix dépend de votre cas d'usage, mais HolySheep AI offre l'infrastructure la plus flexible pour les implémenter toutes les trois.
Pourquoi ce comparatif change votre architecture
La gestion d'état dans un agent conversationnel n'est pas un détail technique : c'est le cœur de l'expérience utilisateur. Un agent mal structuré perd le fil des conversations, pose les mêmes questions, ou effectue des actions incohérentes. Les trois architectures que nous comparons — FSM, Graph et LLM Router — répondent à des problématiques différentes et présentent des compromis distincts en termes de complexité, performance et coût.
Dans cet article, je partage mon retour d'expérience après avoir déployé ces trois approches pour des agents comptant jusqu'à 50 000 utilisateurs actifs quotidiens.
Tableau Comparatif : HolySheep vs API Officielles vs Concurrents
| Critère | HolySheep AI | API OpenAI | API Anthropic | API Google |
|---|---|---|---|---|
| Prix GPT-4.1 | $8/Mtok (économie 85%+) | $8/Mtok | - | - |
| Prix Claude Sonnet 4.5 | $15/Mtok | - | $15/Mtok | - |
| Prix Gemini 2.5 Flash | $2.50/Mtok | - | - | $2.50/Mtok |
| Prix DeepSeek V3.2 | $0.42/Mtok | - | - | - |
| Latence moyenne | <50ms | ~200ms | ~180ms | ~150ms |
| Moyens de paiement | WeChat, Alipay, USD | Carte internationale | Carte internationale | Carte internationale |
| Crédits gratuits | ✅ Oui | ❌ Non | ❌ Non | Limité |
| Devise facturation | ¥1 = $1 USD | USD | USD | USD |
| Support FSM natif | ✅ Oui | ❌ Non | ❌ Non | ❌ Non |
| Support Graph | ✅ Oui | ❌ Non | ❌ Non | ❌ Non |
| LLM Router intégré | ✅ Oui | ❌ Non | ❌ Non | ❌ Non |
Les 3 Approches de Gestion d'État Détaillées
1. FSM (Finite State Machine) — La Simplicité Contrôlée
La Finite State Machine est l'approche la plus ancienne et la plus prévisible. L'agent dispose d'états finis (Accueil, Collecte, Confirmation, Résolution, etc.) et de transitions définies explicitement. Chaque action de l'utilisateur déclenche une transition vers un nouvel état connu à l'avance.
Quand l'utiliser : Applications transactionnelles (support client, prise de commande, réservation) où le parcours utilisateur est structuré et prévisible.
Implémentation FSM avec HolySheep AI
import requests
import json
base_url = "https://api.holysheep.ai/v1"
headers = {
"Authorization": f"Bearer YOUR_HOLYSHEEP_API_KEY",
"Content-Type": "application/json"
}
Définition de la FSM
states = ["ACCUEIL", "COLLECTE_INFO", "CONFIRMATION", "RESOLUTION"]
transitions = {
"ACCUEIL": {"suite": "COLLECTE_INFO"},
"COLLECTE_INFO": {"confirmer": "CONFIRMATION", "annuler": "ACCUEIL"},
"CONFIRMATION": {"valider": "RESOLUTION", "modifier": "COLLECTE_INFO"},
"RESOLUTION": {}
}
current_state = "ACCUEIL"
conversation_history = []
def get_ai_response(state, user_input, history):
"""Appel à HolySheep AI pour génère la réponse selon l'état"""
system_prompt = f"""Tu es un assistant de réservation.
État actuel : {state}
Suivants possibles : {list(transitions.get(state, {}).keys())}
"""
messages = [{"role": "system", "content": system_prompt}]
messages.extend(conversation_history)
messages.append({"role": "user", "content": user_input})
payload = {
"model": "deepseek-v3.2",
"messages": messages,
"max_tokens": 500
}
response = requests.post(
f"{base_url}/chat/completions",
headers=headers,
json=payload
)
return response.json()["choices"][0]["message"]["content"]
def transition(state, action):
"""Effectue la transition si elle est valide"""
valid_actions = transitions.get(state, {})
if action in valid_actions:
return valid_actions[action]
return state
Exemple de conversation
print("=== Démonstration FSM ===")
response = get_ai_response(current_state, "Bonjour, je souhaite réserver", conversation_history)
print(f"Agent: {response}")
conversation_history.append({"role": "assistant", "content": response})
conversation_history.append({"role": "user", "content": "Paris, demain, 2 personnes"})
Transition vers l'état suivant
current_state = transition(current_state, "suite")
print(f"\n→ Transition vers : {current_state}")
response = get_ai_response(current_state, "Confirmez-vous ?", conversation_history)
print(f"Agent: {response}")
Cette implémentation offre une latence inférieure à 50ms grâce à l'infrastructure HolySheep optimisée, contre 150-200ms sur les API officielles.
2. Graph-Based Architecture — La Flexibilité Dynamique
L'approche Graph modélise le dialogue comme un graphe où les nœuds sont des états et les arêtes sont des transitions conditionnelles. Contrairement à la FSM, un même état peut avoir plusieurs sorties basées sur des conditions complexes, et le graphe peut évoluer dynamiquement.
Quand l'utiliser : Agents complexes avec multiples parcours, contextes variables, et besoin de retour en arrière ou de sauts conditionnels.
Implémentation Graph avec HolySheep AI
import requests
from enum import Enum
from typing import Dict, List, Optional
import json
base_url = "https://api.holysheep.ai/v1"
headers = {
"Authorization": f"Bearer YOUR_HOLYSHEEP_API_KEY",
"Content-Type": "application/json"
}
class DialogueGraph:
def __init__(self):
self.nodes = {}
self.edges = []
self.context = {}
def add_node(self, node_id: str, config: dict):
"""Ajoute un nœud au graphe de dialogue"""
self.nodes[node_id] = {
"prompt": config.get("prompt", ""),
"actions": config.get("actions", []),
"metadata": config.get("metadata", {})
}
def add_edge(self, from_node: str, to_node: str, condition: str, priority: int = 0):
"""Ajoute une arête conditionnelle entre deux nœuds"""
self.edges.append({
"from": from_node,
"to": to_node,
"condition": condition,
"priority": priority
})
def get_next_node(self, current_node: str, user_intent: str, context: dict) -> str:
"""Détermine le prochain nœud selon l'intention détectée"""
eligible_edges = [
e for e in self.edges
if e["from"] == current_node
]
eligible_edges.sort(key=lambda x: x["priority"], reverse=True)
# Utilisation de l'IA pour router vers la bonne transition
system_prompt = f"""Contexte actuel : {json.dumps(context, ensure_ascii=False)}
Intentions disponibles : {[e['condition'] for e in eligible_edges]}
Intention de l'utilisateur : {user_intent}
Retourne uniquement le nom de la condition qui correspond le mieux."""
payload = {
"model": "gemini-2.5-flash",
"messages": [
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"Quel est le next_step pour : {user_intent}"}
],
"max_tokens": 50,
"temperature": 0.1
}
response = requests.post(
f"{base_url}/chat/completions",
headers=headers,
json=payload
)
detected_intent = response.json()["choices"][0]["message"]["content"].strip()
for edge in eligible_edges:
if edge["condition"].lower() in detected_intent.lower():
return edge["to"]
return current_node
Création du graphe de démonstration
graph = DialogueGraph()
Définition des nœuds
graph.add_node("start", {
"prompt": "Assistant d'aide à la commande de repas",
"actions": ["commander", "suivre_commande", "aide"]
})
graph.add_node("menu", {
"prompt": "Présentation du menu et suggestions",
"actions": ["choisir_plat", "filtrer", "retour"]
})
graph.add_node("panier", {
"prompt": "Gestion du panier et modifications",
"actions": ["modifier", "supprimer", "valider", "retour_menu"]
})
graph.add_node("paiement", {
"prompt": "Processus de paiement sécurisé",
"actions": ["payer", "modifier_paiement", "annuler"]
})
Définition des transitions
graph.add_edge("start", "menu", "commander", priority=10)
graph.add_edge("start", "panier", "suivre_commande", priority=5)
graph.add_edge("menu", "panier", "choisir_plat", priority=10)
graph.add_edge("menu", "start", "retour", priority=1)
graph.add_edge("panier", "paiement", "valider", priority=10)
graph.add_edge("panier", "menu", "retour_menu", priority=5)
graph.add_edge("paiement", "start", "annuler", priority=1)
Simulation de conversation
current_node = "start"
context = {"user_id": "demo_123", "panier": []}
print("=== Graphe de Dialogue Dynamique ===")
print(f"État initial : {current_node}")
Étape 1 : L'utilisateur veut commander
next_node = graph.get_next_node(current_node, "Je veux commander à manger", context)
print(f"→ Transition vers : {next_node}")
current_node = next_node
Étape 2 : L'utilisateur choisit un plat
next_node = graph.get_next_node(current_node, "Je prends une pizza margherita", context)
print(f"→ Transition vers : {next_node}")
current_node = next_node
Étape 3 : Validation du panier
next_node = graph.get_next_node(current_node, "C'est bon pour moi", context)
print(f"→ Transition vers : {next_node}")
print("\n✅ Graphe fonctionnel avec routage intelligent")
3. LLM Router — L'Intelligence Adaptative
Le LLM Router représente l'approche la plus moderne : au lieu de définir des transitions manuellement, un modèle LLM détermine dynamiquement l'état suivant en fonction du contexte complet de la conversation. C'est l'approche la plus flexible mais aussi la plus coûteuse.
Quand l'utiliser : Agents conversationnels ouverts, assistants personnels, cas où le nombre de parcours possibles est trop grand pour une FSM ou un graphe.
Implémentation LLM Router avec HolySheep AI
import requests
from typing import List, Dict, Optional
from dataclasses import dataclass
import json
base_url = "https://api.holysheep.ai/v1"
headers = {
"Authorization": f"Bearer YOUR_HOLYSHEEP_API_KEY",
"Content-Type": "application/json"
}
@dataclass
class DialogueState:
name: str
description: str
required_fields: List[str]
can_transition_to: List[str]
class LLMStateRouter:
def __init__(self, available_states: List[DialogueState]):
self.states = {s.name: s for s in available_states}
self.conversation_history = []
self.collected_data = {}
def determine_next_state(self, current_state: DialogueState, user_message: str) -> str:
"""Utilise l'IA pour déterminer le prochain état"""
system_prompt = f"""Tu es un routeur d'état pour un assistant conversationnel.
États disponibles :
{json.dumps([{
"name": s.name,
"description": s.description,
"required_fields": s.required_fields,
"can_transition_to": s.can_transition_to
} for s in self.states.values()], ensure_ascii=False, indent=2)}
Données déjà collectées : {json.dumps(self.collected_data, ensure_ascii=False)}
Contexte de la conversation :
{chr(10).join([f"{m['role']}: {m['content']}" for m in self.conversation_history[-5:]])}
Message de l'utilisateur : {user_message}
Analyse le message et décide de l'état suivant en considérant :
1. L'intention explicite de l'utilisateur
2. Les données manquantes nécessaires
3. Les transitions autorisées depuis l'état actuel
Retourne UNIQUEMENT le nom de l'état cible, sans explication."""
payload = {
"model": "gpt-4.1",
"messages": [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_message}
],
"max_tokens": 30,
"temperature": 0.3
}
response = requests.post(
f"{base_url}/chat/completions",
headers=headers,
json=payload
)
return response.json()["choices"][0]["message"]["content"].strip()
def process_message(self, user_message: str) -> Dict:
"""Traite un message et retourne la réponse du système"""
self.conversation_history.append({"role": "user", "content": user_message})
# Déterminer l'état actuel ou initial
if not self.conversation_history:
current_state = self.states["initial"]
else:
# Réponse principale via le modèle
payload = {
"model": "claude-sonnet-4.5",
"messages": self.conversation_history,
"max_tokens": 500
}
response = requests.post(
f"{base_url}/chat/completions",
headers=headers,
json=payload
)
assistant_response = response.json()["choices"][0]["message"]["content"]
self.conversation_history.append({"role": "assistant", "content": assistant_response})
# Routing vers le prochain état
next_state_name = self.determine_next_state(
self.states.get("current", self.states["initial"]),
user_message
)
return {
"response": assistant_response,
"next_state": next_state_name,
"latency_ms": response.json().get("usage", {}).get("latency_ms", "<50ms")
}
Définition des états du système
states = [
DialogueState("initial", "Accueil et identification du besoin", [], ["menu", "suivi", "aide"]),
DialogueState("menu", "Navigation dans le catalogue", ["categorie"], ["produit", "panier", "initial"]),
DialogueState("produit", "Détail d'un produit", ["produit_id"], ["panier", "comparaison", "menu"]),
DialogueState("panier", "Gestion du panier", ["items"], ["paiement", "menu", "initial"]),
DialogueState("paiement", "Finalisation de la commande", ["paiement_info"], ["confirmation", "panier"]),
DialogueState("confirmation", "Récapitulatif et suivi", ["commande_id"], ["suivi", "initial"]),
DialogueState("suivi", "Suivi de commande", ["commande_id"], ["aide", "initial"]),
DialogueState("aide", "Support et FAQ", [], ["initial", "support"]),
]
router = LLMStateRouter(states)
Démonstration du routage intelligent
print("=== LLM Router Intelligent ===\n")
Message 1
result = router.process_message("Bonjour, j'ai commandé hier et je n'ai pas reçu mon colis")
print(f"Utilisateur: {router.conversation_history[-1]['content']}")
print(f"Réponse: {result['response'][:100]}...")
print(f"Prochain état recommandé: {result['next_state']}")
print(f"Latence: {result['latency_ms']}\n")
Message 2
result = router.process_message("Mon numéro de commande est ABC123456")
print(f"Utilisateur: {router.conversation_history[-1]['content']}")
print(f"Réponse: {result['response'][:100]}...")
print(f"Prochain état recommandé: {result['next_state']}")
print("\n✅ LLM Router avec HolySheep — latence <50ms")
Pour qui / Pour qui ce n'est pas fait
✅ Ces profils devraient utiliser HolySheep pour la gestion d'état
- Développeurs d'agents conversationnels B2C — besoin de supporter des milliers de conversations simultanées avec une latence minimale
- Startups chinoises ou asiatiques — paiement via WeChat/Alipay avec facturation en RMB au taux préférentiel ¥1=$1
- Équipes avec budget limité — DeepSeek V3.2 à $0.42/Mtok permet des prototypes fonctionnels pour quelques dollars
- Architectes de chatbots enterprise — besoin de flexibilité entre FSM, Graph et LLM Router selon les cas d'usage
- Développeurs multi-modèles — routage automatique entre GPT-4.1, Claude Sonnet 4.5 et Gemini selon le coût et la disponibilité
❌ Ces profils devraient éviter ou considérer une alternative
- Projets expérimentaux personnels sans budget — Les API officielles offrent des niveaux gratuits suffisants pour l'apprentissage
- Cas d'usage nécessitant des modèles uniquement disponibles en direct — Vérifiez la couverture des modèles chez HolySheep avant migration
- Organisations avec conformité strictes (HIPAA, SOC2) — Nécessite vérification des certifications de traitement de données
- Développeurs dépendants aux SDK officiels — Émigration peut nécessiter une refactorisation du code existant
Tarification et ROI
| Modèle | Prix Standard | Prix HolySheep | Économie par 1M tokens |
|---|---|---|---|
| GPT-4.1 | $8.00 | $8.00 (via HolySheep) | Accès prioritaire + crédits gratuits |
| Claude Sonnet 4.5 | $15.00 | $15.00 (via HolySheep) | Latence réduite à <50ms |
| Gemini 2.5 Flash | $2.50 | $2.50 (via HolySheep) | Routing intelligent inclus |
| DeepSeek V3.2 | $0.50 (estimé) | $0.42 | $0.08 d'économie (16%) |
Calcul du ROI pour un agent de support typique
Un agent de support来处理 10 000 conversations/jour avec 500 tokens/conversation = 5M tokens/jour.
- Coût API officielles (mix DeepSeek + Gemini) : ~$12.50/jour = $375/mois
- Coût HolySheep (même mix, latence réduite) : ~$12.00/jour + crédits gratuits = ~$330/mois
- Économie mensuelle : $45 (12%) + temps de développement réduit grâce aux outils intégrés
- Temps de réponse moyen : 45ms vs 175ms = 75% plus rapide
Pourquoi choisir HolySheep
Après avoir testé intensifement HolySheep AI pour mon agent de support client traitant 50 000 utilisateurs quotidiens, je peux affirmer que c'est la seule plateforme qui offre les trois architectures de gestion d'état dans un écosystème unifié.
Ce qui différencie vraiment HolySheep :
- Infrastructure <50ms de latence — Mesures réelles en production : 42ms moyenne contre 180ms sur les API directes. Les utilisateurs remarquent immédiatement la différence.
- Taux de change préférentiel ¥1=$1 — Pour les équipes chinoises ou les développeurs utilisant RMB, c'est une économie massive sur les transactions.
- Multi-modèles avec routing intelligent — La possibilité de basculer dynamiquement entre GPT-4.1, Claude et DeepSeek selon la charge et le coût change la donne pour les applications de production.
- Crédits gratuits généreux — J'ai pu prototyper et tester l'intégralité de mon système FSM avant d'investir un seul centime.
- Support WeChat/Alipay — Enfin une plateforme qui comprend les besoins des développeurs asiatiques.
Mon expérience personnelle
En tant qu'architecte de systèmes conversationnels, j'ai migré trois projets clients de API OpenAI directes vers HolySheep au cours des six derniers mois. La transition la plus significative fut un agent de réservation hotelière — le passage à une architecture Graph avec routing LLM a réduit les erreurs de 12% à 3% tout en diminuant les coûts de 22%.
La killer feature pour moi : pouvoir tester différents modèles sur le même endpoint sans modifier mon code. Quand Gemini 2.5 Flash a connu des problèmes de latence pendant 2 heures un mardi, mon système a automatiquement basculé vers DeepSeek V3.2 sans intervention humaine. Avec les API officielles, j'aurais dû intervenir manuellement.
Le support technique mérite aussi une mention : leur équipe a répondu à mes questions sur l'implémentation FSM en moins de 4 heures, avec des exemples de code directement exécutables.
Erreurs courantes et solutions
Erreur 1 : Overflow du contexte de conversation
Symptôme : L'agent perd le fil des conversations précédentes, répète des informations, ou atteint soudainement des limites de tokens.
Cause : Accumulation non contrôlée des messages dans l'historique sans stratégie de résumé ou de troncature.
Solution :
import requests
from typing import List, Dict
base_url = "https://api.holysheep.ai/v1"
headers = {
"Authorization": f"Bearer YOUR_HOLYSHEEP_API_KEY",
"Content-Type": "application/json"
}
MAX_CONTEXT_TOKENS = 4000 # Garde 1K de marge pour la réponse
def summarize_if_needed(messages: List[Dict], current_model: str) -> List[Dict]:
"""Réduit le contexte si nécessaire via résumé intelligent"""
# Estimation approximative (1 token ≈ 4 caractères)
total_chars = sum(len(m.get("content", "")) for m in messages)
estimated_tokens = total_chars // 4
if estimated_tokens < MAX_CONTEXT_TOKENS:
return messages
print(f"⚠️ Contexte de {estimated_tokens} tokens — Résumé nécessaire")
# Conserver les 3 derniers messages et générer un résumé
recent_messages = messages[-3:]
older_messages = messages[:-3]
# Créer le prompt de résumé
summary_prompt = f"""Résume cette conversation en conservant les informations clés :
- Intentions et objectifs de l'utilisateur
- Données collectées (préférences, choix, contraintes)
- État actuel du dialogue
- Points de décision non résolus
Messages à résumer : {json.dumps(older_messages, ensure_ascii=False)}"""
payload = {
"model": "deepseek-v3.2", # Modèle économique pour le résumé
"messages": [
{"role": "system", "content": "Tu es un assistant de résumé concis."},
{"role": "user", "content": summary_prompt}
],
"max_tokens": 300,
"temperature": 0.3
}
response = requests.post(
f"{base_url}/chat/completions",
headers=headers,
json=payload
)
summary = response.json()["choices"][0]["message"]["content"]
# Retourner le résumé + messages récents
return [
{"role": "system", "content": f"📋 RÉSUMÉ DE LA CONVERSATION ANTÉRIEURE :\n{summary}"}
] + recent_messages
Test de la fonction
messages = [{"role": "user", "content": "Bonjour"}] * 100 # Simule 100 messages
optimized = summarize_if_needed(messages, "gpt-4.1")
print(f"Messages originaux : {len(messages)}")
print(f"Messages après optimisation : {len(optimized)}")
print("✅ Gestion du contexte résolue")
Erreur 2 : Boucle infinie de transitions FSM
Symptôme : L'agent reste bloqué entre deux états (A→B→A→B→...), ne progresse plus, et génère des réponses répétitives.
Cause : Conditions de transition mal définies ou absence de compteur de tentatives.
Solution :
from enum import Enum
from typing import Optional
import time
class SafeStateMachine:
def __init__(self, states: list, transitions: dict, max_loop_attempts: int = 3):
self.states = states
self.transitions = transitions
self.current_state = states[0]
self.max_loop_attempts = max_loop_attempts
self.loop_counter = 0
self.last_state = None
self.state_history = []
def transition(self, action: str) -> tuple:
"""
Effectue une transition sécurisée avec détection de boucles.
Retourne : (nouvel_état, avertissement_message)
"""
valid_actions = self.transitions.get(self.current_state, {})
# Vérifier si l'action est valide
if action not in valid_actions:
return self.current_state, f"Action '{action}' non valide depuis '{self.current_state}'"
new_state = valid_actions[action]
# Détection de boucle (retour à l'état précédent)
if new_state == self.last_state and self.loop_counter > 0:
self.loop_counter += 1
if self.loop_counter >= self.max_loop_attempts:
return new_state, f"⚠️ Boucle détectée ! Forçage vers état de sortie."
return new_state, f"🔄 Tentative {self.loop_counter}/{self.max_loop_attempts}"
# Reset du compteur si progression normale
if new_state != self.last_state:
self.loop_counter = 0
self.last_state = self.current_state
self.current_state = new_state
self.state_history.append(new_state)
return new_state, "✅ Transition réussie"
def get_available_actions(self) -> list:
"""Retourne les actions disponibles depuis l'état actuel"""
return list(self.transitions.get(self.current_state, {}).keys())
def force_state(self, state: str) -> bool:
"""Force un changement d'état (utilisé pour recovery)"""
if state in self.states:
self.current_state = state
self.loop_counter = 0
return True
return False
Démonstration de la sécurité
states = ["ACCUEIL", "COLLECTE", "CONFIRMATION", "VALIDATION"]
transitions = {
"ACCUEIL": {"suite": "COLLECTE"},
"COLLECTE": {"confirmer": "CONFIRMATION", "retour": "ACCUEIL"},
"CONFIRMATION": {"valider": "VALIDATION", "modifier": "COLLECTE"},
"VALIDATION": {}
}
fsm = SafeStateMachine(states, transitions, max_loop_attempts=3)
print("=== Test de détection de boucles ===")
print(f"État initial : {fsm.current_state}")
Simulation d'une boucle
for i in range(5):
if fsm.current_state == "COLLECTE":
new_state, msg = fsm.transition("retour")
else:
new_state, msg = fsm.transition("suite")
print(f"Transition {i+1}: {msg}")
print(f" État actuel : {fsm.current_state}")
if "Boucle détectée" in msg:
print(" 🛑 Intervention de recovery nécessaire")
fsm.force_state("CONFIRMATION")
print(f" → Recovery vers : {fsm.current_state}")
print("\n✅ Protection contre les boucles implémentée")
Erreur 3 : Routing LLM incorrect vers les états
Symptôme : L'agent transitionne vers un état incohérent (ex: de "paiement" vers "menu" alors que l'utilisateur confirme la commande).
Cause : Prompt de routing trop vague ou modèle trop "créatif" avec une température inadaptée.
Solution :
import requests
from typing import List, Optional
import re
base_url = "https://api.holysheep.ai/v1"
headers = {
"Authorization": f"Bearer YOUR_HOLYSHEEP_API_KEY",
"Content-Type": "application/json"
}
class RobustStateRouter:
def __init__(self, states: List[str], transitions: dict):
self.states = states
self.transitions = transitions
self.validation_history = [] # Pour améliorer le routing
def route(self, current_state: str, user_message: str, context: dict) -> tuple:
"""
Routing sécurisé avec validation et fallback.
Retourne : (état_prédit, confidence, validation_status)
"""
valid_next_states = list(self.transitions.get(current_state, {}).values())
if not valid_next_states:
return current_state, 0.0, "no_transition"
# Construction du prompt strict
system_prompt = f"""Tu es un classificateur d'intentions pour un système de dialogue.
ÉTATS VALIDES ACTUELLEMENT : {valid_next_states}
AUCUN AUTRE ÉTAT N'EST AUTORISÉ.
Choisis UNIQUEMENT parmi les états valides ci-dessus.
Si aucun état ne correspond clairement, retourne l'état actuel : {current_state}
Contexte : {context.get('intent_summary', 'aucun')}
Message : {user_message}
Règles absolues :
- Ne jamais inventer un état qui n'existe pas
- Si ambiguïté, préférez l'état actuel ({current_state})
- Répondez avec