Gestion des Erreurs
Les erreurs sont inévitables
En production, les fonctions échouent. L’API externe est indisponible, l’identifiant n’existe pas, les paramètres sont invalides, le timeout est dépassé. La question n’est pas « est-ce que ça va échouer ? » mais « comment gérer l’échec proprement ? ».
Le modèle Mistral est capable de comprendre les messages d’erreur que vous lui renvoyez et d’adapter sa réponse en conséquence. C’est une fonctionnalité puissante que vous devez exploiter.
Renvoyer les erreurs au modèle
La règle d’or : ne jamais cacher une erreur au modèle. Renvoyez-la dans le message tool comme vous renverriez un résultat normal :
def retrieve_payment_status(df, transaction_id: str) -> str:
"""Récupère le statut avec gestion d'erreurs complète."""
try:
if transaction_id not in df["transaction_id"].values:
return json.dumps({
"error": "transaction_not_found",
"message": f"La transaction {transaction_id} n'existe pas dans la base."
})
status = df[df["transaction_id"] == transaction_id]["payment_status"].values[0]
return json.dumps({"status": status, "transaction_id": transaction_id})
except Exception as e:
return json.dumps({
"error": "internal_error",
"message": f"Erreur lors de la récupération : {str(e)}"
})
Le modèle recevra le message d’erreur et pourra répondre intelligemment :
# L'utilisateur demande une transaction qui n'existe pas
messages = [{"role": "user", "content": "Statut du paiement T9999 ?"}]
# Le modèle appelle retrieve_payment_status(transaction_id="T9999")
# Votre fonction retourne : {"error": "transaction_not_found", ...}
# Le modèle formule :
# → "Je n'ai pas trouvé de transaction avec l'identifiant T9999.
# Pourriez-vous vérifier le numéro et réessayer ?"
Pattern try/except complet
Voici un pattern robuste pour exécuter les tool_calls avec gestion d’erreurs :
def execute_tool_call(tool_call, available_functions):
"""Exécute un tool_call avec gestion d'erreurs complète."""
function_name = tool_call.function.name
# Vérifier que la fonction existe
if function_name not in available_functions:
return {
"role": "tool",
"name": function_name,
"content": json.dumps({
"error": "function_not_found",
"message": f"La fonction {function_name} n'est pas disponible."
}),
"tool_call_id": tool_call.id
}
# Parser les arguments
try:
function_params = json.loads(tool_call.function.arguments)
except json.JSONDecodeError as e:
return {
"role": "tool",
"name": function_name,
"content": json.dumps({
"error": "invalid_arguments",
"message": f"Arguments JSON invalides : {str(e)}"
}),
"tool_call_id": tool_call.id
}
# Exécuter la fonction
try:
result = available_functions[function_name](**function_params)
return {
"role": "tool",
"name": function_name,
"content": result,
"tool_call_id": tool_call.id
}
except TypeError as e:
return {
"role": "tool",
"name": function_name,
"content": json.dumps({
"error": "invalid_params",
"message": f"Paramètres incompatibles : {str(e)}"
}),
"tool_call_id": tool_call.id
}
except Exception as e:
return {
"role": "tool",
"name": function_name,
"content": json.dumps({
"error": "execution_error",
"message": f"Erreur d'exécution : {str(e)}"
}),
"tool_call_id": tool_call.id
}
Stratégie de retry
Pour les erreurs transitoires (timeout réseau, service temporairement indisponible), implémentez un retry avec backoff :
import time
def execute_with_retry(func, params, max_retries=3, base_delay=1.0):
"""Exécute une fonction avec retry exponentiel."""
for attempt in range(max_retries):
try:
result = func(**params)
return result
except ConnectionError as e:
if attempt < max_retries - 1:
delay = base_delay * (2 ** attempt)
time.sleep(delay)
else:
return json.dumps({
"error": "service_unavailable",
"message": f"Service indisponible après {max_retries} tentatives.",
"details": str(e)
})
Erreurs côté API Mistral
L’appel à l’API Mistral elle-même peut échouer. Gérez ces cas séparément :
from mistralai import Mistral
from mistralai.models import HTTPValidationError
def safe_chat_complete(client, model, messages, tools):
"""Appel API Mistral avec gestion d'erreurs."""
try:
return client.chat.complete(
model=model,
messages=messages,
tools=tools
)
except HTTPValidationError as e:
print(f"Erreur de validation : {e}")
return None
except Exception as e:
print(f"Erreur API : {e}")
return None
Logging des erreurs
En production, loggez toutes les erreurs de function calling pour le monitoring :
import logging
logger = logging.getLogger("function_calling")
def execute_and_log(tool_call, available_functions):
"""Exécute et logge le résultat."""
function_name = tool_call.function.name
function_params = json.loads(tool_call.function.arguments)
logger.info(f"Appel : {function_name}({function_params})")
result = execute_tool_call(tool_call, available_functions)
content = json.loads(result["content"])
if "error" in content:
logger.error(f"Échec {function_name}: {content[error]} - {content[message]}")
else:
logger.info(f"Succès {function_name}: {content}")
return result
Points clés à retenir
- Renvoyez toujours les erreurs au modèle dans le message
tool— il sait les interpréter - Utilisez des codes d’erreur structurés (
transaction_not_found,invalid_arguments, etc.) - Implémentez un retry avec backoff pour les erreurs transitoires
- Gérez séparément les erreurs de vos fonctions et les erreurs de l’API Mistral
- Loggez chaque appel et chaque erreur pour le monitoring en production