Aller au contenu principal

Bonnes pratiques et récapitulatif

Mis à jour le 29 juillet 2026

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 rassemble ce qui fait la différence au quotidien : la façon de choisir entre les deux approches, les réglages qui ne se discutent pas, les pièges qui reviennent le plus souvent, et ce qu’il faut surveiller une fois en production.

Choisir son approche

Quatre questions suffisent à trancher, dans cet ordre. Avez-vous un schéma de sortie défini ? Si la réponse est non, restez en JSON Mode : vous êtes en exploration ou en prototypage, et figer un modèle de données serait prématuré. Si elle est oui, passez à la question suivante.

Travaillez-vous en Python ou en TypeScript ? Dans l’affirmative, les Custom Structured Outputs vous tendent les bras avec Pydantic ou Zod. Sinon, le JSON Mode assorti d’une validation manuelle reste votre voie.

Êtes-vous en production ? Si oui, les Custom Structured Outputs s’imposent, toujours. Sinon, le JSON Mode suffit largement pour prototyper.

Le schéma change-t-il fréquemment ? Un schéma encore mouvant rend le JSON Mode plus pratique au début, le temps que les besoins se stabilisent ; un schéma figé plaide pour les Custom Structured Outputs et la fiabilité qu’ils apportent.

Les dix règles d’or

Les cinq premières concernent la façon d’écrire vos appels. Fixez toujours temperature=0 en production : la variabilité de la température est un ennemi de la fiabilité, et une extraction n’a aucun besoin de créativité.

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

Vérifiez toujours finish_reason. C’est le seul indicateur qui vous prévient d’une réponse coupée avant d’avoir tenté de l’exploiter.

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}")

En JSON Mode, instruisez toujours le modèle dans le prompt. Le paramètre garantit la syntaxe, pas la structure : c’est à vous d’énoncer les clés et leurs types.

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

En Custom, documentez vos champs. Une description précise vaut mieux qu’un long prompt, et elle voyage avec le schéma dans les deux langages.

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"),
});

Séparez le contexte du contenu. Les instructions vivent dans le message système, la donnée à traiter dans le message utilisateur — jamais l’inverse, sous peine de voir le modèle confondre consigne et texte source.

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]"}
]

Les deux règles suivantes concernent votre filet de sécurité. Implémentez un mécanisme de retry : ne faites jamais un seul appel sans filet, le retry avec feedback étant le pattern minimal en production. Et validez au-delà du schéma, puisque celui-ci garantit la structure tandis que la validation sémantique — bornes, cohérence, logique métier — reste votre responsabilité.

Les trois dernières relèvent du pilotage. Choisissez le bon modèle pour le bon usage : mistral-large-latest pour les tâches complexes et l’extraction multi-champs, mistral-small-latest pour la classification simple et l’extraction basique, ministral-8b-latest pour la haute fréquence et la latence minimale. Loguez chaque appel en production en enregistrant le modèle utilisé, la durée, le finish_reason et l’issue : sans logs, vous êtes aveugle. Enfin, ne sur-optimisez pas le prompt trop tôt — commencez simple, mesurez, itérez ; l’optimisation prématurée du prompt est un piège courant, alors que le schéma et la validation sont les vrais leviers.

Trois pièges qui coûtent cher

Le premier est un max_tokens trop bas. Le JSON sera tronqué sur les réponses longues, et l’erreur ne se manifestera que sur les documents volumineux, souvent après la mise en production.

# 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 deuxième est la température non nulle, qui rend vos résultats non reproductibles pour un gain nul sur une tâche d’extraction.

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

Le troisième est le réflexe du modèle unique. N’utilisez pas mistral-large pour tout : une classification binaire ne nécessite pas le modèle le plus puissant, et ministral-8b fera le travail 5x plus vite pour moins cher.

Ce qu’il faut surveiller

Quatre métriques racontent la santé de votre pipeline. Le taux de succès mesure le pourcentage d’appels qui retournent un objet valide au premier essai ; le taux de retry, celui des appels qui ont eu besoin d’une seconde chance. La latence P50/P95 donne le temps de réponse médian et au 95e percentile, plus révélateur que la moyenne. Le coût par extraction, exprimé en tokens consommés par appel, garde la facture sous contrôle.

Sur ces métriques, quelques seuils d’alerte tiennent bien la route : un taux de succès sous 95 % justifie une alerte jaune, sous 85 % une alerte rouge ; une latence P95 supérieure à 10 s mérite une investigation ; et un circuit breaker ouvert appelle une alerte immédiate.

Aller plus loin

Quatre directions prolongent naturellement ce cours. Le function calling utilise les sorties structurées pour alimenter des appels de fonctions dans un agent. Le streaming permet de recevoir la sortie structurée progressivement, ce qui compte sur les réponses longues. Le fine-tuning ajuste un modèle Mistral pour améliorer la qualité de l’extraction sur vos données spécifiques. L’évaluation, enfin, consiste à mettre en place un framework automatique reposant sur un golden dataset et des métriques — c’est ce qui vous dira si un changement de prompt améliore réellement les choses.

Un pipeline complet

Le code suivant réunit les pratiques du cours dans un seul flux : un schéma décrit champ par champ, un validateur qui refuse les scores hors bornes, le modèle dimensionné à la tâche, temperature=0, un max_tokens explicite, la vérification de finish_reason et un log dans les deux issues possibles.

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

Une trentaine de lignes, et vous disposez d’un composant que l’on peut brancher sans crainte au milieu d’un système : c’est exactement ce que les sorties structurées permettent d’obtenir.

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

Testez vos connaissances

JSON Mode, schémas, retry : la sortie structurée sous contrôle ?

1. JSON Mode ou Custom Structured Outputs : quelle différence ?

Réponse : Le JSON Mode garantit un JSON syntaxiquement valide ; les Custom Structured Outputs imposent en plus votre schéma exact — champs, types, structure : le contrat complet.

2. Qu'apporte client.chat.parse() avec Pydantic ?

Réponse : La boucle complète : le schéma Pydantic déclare la structure, l’API contraint la génération, et la réponse arrive déjà validée et typée en objet Python — zéro parsing manuel.

3. Et côté TypeScript ?

Réponse : Le même confort avec Zod : le schéma Zod définit et valide la structure — une source de vérité unique pour la génération et le typage.

4. Que faire quand la sortie ne respecte pas le schéma ?

Réponse : Valider systématiquement, et prévoir une stratégie de retry : re-demander avec l’erreur en contexte, ou dégrader proprement — jamais de confiance aveugle dans la sortie.

5. Quand la sortie structurée est-elle indispensable ?

Réponse : Dès qu’un programme consomme la réponse sans relecture humaine : extraction, intégrations, pipelines — le format cesse d’être une politesse, c’est une interface.

Schéma déclaré, génération contrainte, validation en sortie : la triade qui rend l’IA branchable sur du vrai logiciel.