Aller au contenu principal

Bonnes pratiques et récapitulatif

Récapitulatif du cours

Vous avez parcouru l’ensemble des mécanismes de sorties structurées proposés par l’API Mistral. Cette dernière leçon synthétise les bonnes pratiques à appliquer au quotidien, les pièges à éviter, et les prochaines étapes pour aller plus loin.

Arbre de décision : quelle approche choisir ?

Posez-vous ces questions dans l’ordre :

  1. Avez-vous un schéma de sortie défini ?

    • Non → JSON Mode (exploration, prototypage)
    • Oui → question suivante
  2. Travaillez-vous en Python ou TypeScript ?

    • Oui → Custom Structured Outputs (Pydantic / Zod)
    • Non → JSON Mode avec validation manuelle
  3. Êtes-vous en production ?

    • Oui → Custom Structured Outputs (toujours)
    • Non → JSON Mode suffit pour le prototypage
  4. Le schéma change-t-il fréquemment ?

    • Oui → JSON Mode peut être plus pratique au début
    • Non → Custom Structured Outputs pour la fiabilité

Les 10 règles d’or

1. Toujours définir temperature=0 en production

# Production : déterministe
response = client.chat.parse(
    model="mistral-large-latest",
    messages=messages,
    response_format=MonSchema,
    temperature=0  # Reproductibilité
)

La variabilité de la température est un ennemi de la fiabilité en production.

2. Toujours vérifier finish_reason

if response.choices[0].finish_reason != "stop":
    # La réponse est tronquée — augmentez max_tokens
    raise ValueError(f"Réponse incomplète : {response.choices[0].finish_reason}")

3. Toujours instruire le modèle dans le prompt (JSON Mode)

# JSON Mode — le prompt DOIT décrire la structure
messages = [
    {
        "role": "system",
        "content": "Retourne un JSON avec les clés : name (string), age (number), city (string)."
    },
    {"role": "user", "content": texte}
]

4. Utiliser des descriptions de champs (Custom)

class Product(BaseModel):
    name: str = Field(description="Nom commercial du produit")
    price: float = Field(description="Prix en euros HT, arrondi à 2 décimales")
const ProductSchema = z.object({
  name: z.string().describe("Nom commercial du produit"),
  price: z.number().describe("Prix en euros HT, arrondi à 2 décimales"),
});

5. Séparer le contexte (system) du contenu (user)

messages = [
    {"role": "system", "content": "Tu es un extracteur de données. [Instructions de format]"},
    {"role": "user", "content": "[Le texte à traiter — PAS d'instructions de format ici]"}
]

6. Implémenter un mécanisme de retry

Ne faites jamais un seul appel sans filet. Le retry avec feedback est le pattern minimal en production.

7. Valider au-delà du schéma

Le schéma garantit la structure. La validation sémantique (bornes, cohérence, logique métier) est votre responsabilité.

8. Choisir le bon modèle pour le bon usage

  • mistral-large-latest : tâches complexes, extraction multi-champs
  • mistral-small-latest : classification simple, extraction basique
  • ministral-8b-latest : haute fréquence, latence minimale

9. Logger chaque appel en production

Enregistrez : le modèle utilisé, la durée, le finish_reason, le succès ou l’erreur. Sans logs, vous êtes aveugle.

10. Ne pas sur-optimiser le prompt trop tôt

Commencez simple. Mesurez. Itérez. L’optimisation prématurée du prompt est un piège courant — concentrez-vous d’abord sur le schéma et la validation.

Pièges courants à éviter

Le piège du max_tokens trop bas

# Dangereux — le JSON sera tronqué pour les réponses longues
response = client.chat.parse(
    model="mistral-large-latest",
    messages=messages,
    response_format=MonSchema,
    max_tokens=128  # Trop bas pour un schéma complexe
)

Réglez max_tokens en fonction de la taille attendue de votre sortie. Une facture avec 20 lignes nécessite plus de tokens qu’une classification simple.

Le piège de la température non-nulle

# Dangereux en production — résultats non reproductibles
response = client.chat.parse(
    ...,
    temperature=0.7  # Variation inutile pour de l'extraction
)

Le piège du modèle unique

N’utilisez pas mistral-large pour tout. Une classification binaire ne nécessite pas le modèle le plus puissant — ministral-8b fera le travail 5x plus vite pour moins cher.

Monitoring en production

Métriques à suivre

  • Taux de succès : pourcentage d’appels qui retournent un objet valide au premier essai
  • Taux de retry : pourcentage d’appels nécessitant un retry
  • Latence P50/P95 : temps de réponse médian et au 95e percentile
  • Coût par extraction : tokens consommés par appel

Alertes recommandées

  • Taux de succès < 95% → alerte jaune
  • Taux de succès < 85% → alerte rouge
  • Latence P95 > 10s → investiguer
  • Circuit breaker ouvert → alerte immédiate

Prochaines étapes

Maintenant que vous maîtrisez les sorties structurées, voici les directions pour approfondir :

  • Function calling : utiliser les sorties structurées pour alimenter des appels de fonctions dans un agent
  • Streaming : recevoir la sortie structurée progressivement pour les réponses longues
  • Fine-tuning : ajuster un modèle Mistral pour améliorer la qualité de l’extraction sur vos données spécifiques
  • Évaluation : mettre en place un framework d’évaluation automatique (golden dataset + métriques)

Exemple final : pipeline complet

Voici un pipeline complet qui combine toutes les bonnes pratiques :

from mistralai import Mistral
from pydantic import BaseModel, Field, field_validator
import logging
import time

logger = logging.getLogger("pipeline")

class CustomerFeedback(BaseModel):
    sentiment: str = Field(description="positif, négatif ou neutre")
    score: float = Field(description="Score entre -1.0 et 1.0")
    category: str = Field(description="produit, service, livraison, prix ou autre")
    action_required: bool = Field(description="True si une action est nécessaire")
    summary: str = Field(description="Résumé en une phrase")

    @field_validator("score")
    @classmethod
    def score_in_range(cls, v):
        if not -1.0 <= v <= 1.0:
            raise ValueError(f"Score hors bornes : {v}")
        return v

def analyze_feedback(client: Mistral, text: str) -> CustomerFeedback:
    start = time.time()
    try:
        response = client.chat.parse(
            model="mistral-small-latest",
            messages=[
                {"role": "system", "content": "Analyse le feedback client."},
                {"role": "user", "content": text}
            ],
            response_format=CustomerFeedback,
            temperature=0,
            max_tokens=256
        )
        assert response.choices[0].finish_reason == "stop"
        result = response.choices[0].message.parsed
        logger.info(f"OK en {time.time()-start:.2f}s — {result.sentiment} ({result.score:+.1f})")
        return result
    except Exception as e:
        logger.error(f"ERREUR en {time.time()-start:.2f}s — {e}")
        raise

Points clés à retenir

  • Utilisez Custom Structured Outputs en production, JSON Mode pour le prototypage
  • temperature=0, vérification de finish_reason, et retry sont non négociables
  • Choisissez le modèle en fonction de la complexité de la tâche et du budget
  • Monitorez le taux de succès, la latence et le coût
  • Commencez simple, mesurez, puis optimisez