Aller au contenu principal
Leçon 3 sur 12 · Modération et Sécurité IA avec Mistral

Configurer les guardrails en Python

Leçon 3 sur 12 Mis à jour le 3 novembre 2026 Développeur
Sur cette page
  1. Passer de la théorie au code
  2. Le paramètre guardrails en détail
  3. Exemple complet : chatbot avec guardrails
  4. Gérer les erreurs de blocage proprement
  5. Bonnes pratiques de configuration
  6. Points clés à retenir

Leçon réécrite le 3 novembre 2026. Elle configurait les guardrails avec un dictionnaire inventé (enabled), des catégories inexistantes (hate, violence, unpredictable), affirmait que les catégories non listées gardaient des « seuils par défaut », et testait e.status_code == 3201, alors que 3201 est un code d’erreur du corps de la réponse, pas un statut HTTP. La configuration suit désormais le format officiel, et le code a été vérifié contre les types du SDK mistralai 3.7.0. Sources : Moderation & Guardrailing · SDK Python mistralai.

Passer de la théorie au code

Vous connaissez maintenant le principe des guardrails. Dans cette leçon, vous allez les configurer précisément : choisir vos catégories, définir vos seuils, et traiter les blocages dans votre code Python.

Le point de départ : le SDK Mistral installé (les guardrails demandent la version 2 ou plus), et une clé d’API lue depuis l’environnement plutôt qu’écrite dans le code.

pip install "mistralai>=2"
import os

from mistralai.client import Mistral

client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])

Le paramètre guardrails en détail

Le paramètre guardrails est une liste. Chaque élément associe l’option block_on_error à une configuration moderation_llm_v2, qui contient elle-même les seuils, l’option ignore_other_categories et l’action :

from mistralai.client.models import GuardrailConfigTypedDict

GUARDRAILS: list[GuardrailConfigTypedDict] = [
    {
        # Bloquer si l'évaluation elle-même échoue
        "block_on_error": True,
        "moderation_llm_v2": {
            # Seuils par catégorie, de 0 à 1 (1 désactive la catégorie)
            "custom_category_thresholds": {
                "sexual": 0.2,
                "hate_and_discrimination": 0.1,
                "violence_and_threats": 0.3,
                "dangerous": 0.2,
                "criminal": 0.1,
                "selfharm": 0.1,
                "health": 0.5,
                "financial": 0.5,
                "law": 0.5,
                "pii": 0.2,
                "jailbreaking": 0.1,
            },
            "ignore_other_categories": False,
            # Action en cas de dépassement
            "action": "block",
        },
    }
]

Le modèle de modération par défaut est mistral-moderation-2603 ; la clé facultative model_name permet d’en désigner un autre.

L’annotation list[GuardrailConfigTypedDict] n’est pas obligatoire, mais elle vaut d’être prise : le SDK décrit chaque clé autorisée, et un vérificateur de types comme pyright ou mypy signale alors une catégorie mal orthographiée (violence au lieu de violence_and_threats), que l’API, elle, ne vous signalerait pas.

custom_category_thresholds

C’est le cœur de la configuration. Chaque catégorie reçoit un seuil entre 0 et 1, comparé au score que le classifieur attribue au prompt. Plus le seuil est bas, plus le filtre bloque, au risque d’écarter des requêtes légitimes. Un seuil à 1 désactive explicitement la catégorie. Mistral ne publie pas de grille de valeurs recommandées : c’est à vous de les calibrer sur vos propres cas (leçon 7).

Les valeurs n’ont de sens que rapportées à votre application. Un chatbot grand public verrouille les catégories sensibles au plus bas, alors qu’un outil interne destiné à des professionnels de santé doit laisser passer les questions médicales tout en restant strict sur les données personnelles.

# Chatbot grand public : seuils très bas
seuils_public = {
    "sexual": 0.1,
    "hate_and_discrimination": 0.1,
    "violence_and_threats": 0.1,
    "criminal": 0.1,
    "selfharm": 0.1,
}

# Outil interne médical : la santé est désactivée, les données personnelles restent surveillées
seuils_medical = {
    "sexual": 0.2,
    "hate_and_discrimination": 0.1,
    "violence_and_threats": 0.3,
    "health": 1,   # désactive la catégorie : les questions médicales sont le cœur de l'outil
    "pii": 0.1,
}

Les catégories que vous ne listez pas

Que deviennent les catégories absentes de custom_category_thresholds ? La documentation ne le dit pas en toutes lettres, mais son exemple de blocage est parlant : les catégories non listées y figurent avec un seuil de 1.0, notées mais sans effet. Ne comptez donc pas sur un seuil par défaut : listez chaque catégorie qui doit pouvoir bloquer.

L’option ignore_other_categories règle seulement ce qui est évalué et renvoyé. À False, toutes les catégories évaluées apparaissent dans le résultat ; à True, seules celles que vous avez listées sont évaluées.

GUARDRAILS_CIBLES: list[GuardrailConfigTypedDict] = [
    {
        "block_on_error": True,
        "moderation_llm_v2": {
            "custom_category_thresholds": {
                "hate_and_discrimination": 0.1,
                "violence_and_threats": 0.2,
            },
            "ignore_other_categories": True,  # n'évalue que ces deux catégories
            "action": "block",
        },
    }
]

Attention : avec cette configuration, une tentative de jailbreak ou une demande de données personnelles passerait sans obstacle. Ne restreignez la liste que pour une raison précise.

Exemple complet : chatbot avec guardrails

Voici une fonction minimale : elle envoie le message avec la configuration complète et transforme un blocage en réponse lisible pour l’utilisateur.

from mistralai.client import errors


def chat_securise(message_utilisateur: str) -> str:
    """Envoie un message protégé par les guardrails."""
    try:
        response = client.chat.complete(
            model="mistral-large-latest",
            messages=[
                {"role": "system", "content": "Vous êtes un assistant professionnel."},
                {"role": "user", "content": message_utilisateur},
            ],
            guardrails=GUARDRAILS,
        )
    except errors.SDKError as e:
        if e.status_code == 403:
            return "Désolé, cette requête ne peut pas être traitée."
        raise

    contenu = response.choices[0].message.content
    return contenu if isinstance(contenu, str) else ""


print(chat_securise("Expliquez-moi le machine learning"))

Deux remarques. message.content peut être une chaîne ou une liste de morceaux selon le modèle, d’où le test isinstance. Et la réponse de chat.complete() n’expose pas les scores des guardrails dans le SDK 3.7.0 : pour les journaliser, passez par l’API Conversations, dont la réponse les porte dans guardrails.

Gérer les erreurs de blocage proprement

Il faut distinguer deux échecs qui n’appellent ni le même message ni la même alerte : un blocage par la politique de contenu, qui renvoie un statut HTTP 403, et une panne de l’évaluation avec block_on_error activé, que le corps de la réponse signale par le code 3201. Les deux arrivent sous forme d’errors.SDKError, dont body contient le JSON renvoyé par l’API :

import json
from typing import Any

from mistralai.client import errors


def chat_avec_gestion_erreurs(message_utilisateur: str) -> dict[str, Any]:
    """Chat qui distingue blocage par la politique et panne de la modération."""
    try:
        response = client.chat.complete(
            model="mistral-large-latest",
            messages=[{"role": "user", "content": message_utilisateur}],
            guardrails=GUARDRAILS,
        )
    except errors.SDKError as e:
        try:
            corps = json.loads(e.body)
        except json.JSONDecodeError:
            raise e

        if corps.get("code") == 3201:
            return {
                "status": "error",
                "content": "Service de modération indisponible. Requête bloquée par précaution.",
            }
        if e.status_code == 403:
            decisions = corps["guardrails"]["results"]["moderation_llm_v2"]["decisions"]
            return {
                "status": "blocked",
                "content": "Votre message a été bloqué par notre politique de sécurité.",
                "categories": [nom for nom, d in decisions.items() if d["violated"]],
            }
        raise

    return {"status": "ok", "content": response.choices[0].message.content}

Le test du code 3201 vient en premier, car la documentation ne précise pas le statut HTTP de cette erreur. Les catégories violées sont tirées de decisions, où chaque entrée donne threshold, score et violated : c’est ce que vous journaliserez pour expliquer un blocage.

Bonnes pratiques de configuration

Démarrez restrictif, puis desserrez. Des seuils bas au lancement vous font découvrir vos faux positifs sur un volume encore maîtrisable, et chaque relèvement se décide sur des cas observés plutôt que sur une intuition :

# Phase 1 : lancement (très restrictif)
phase1 = {"hate_and_discrimination": 0.1, "violence_and_threats": 0.1, "criminal": 0.1}

# Phase 2 : après analyse des faux positifs
phase2 = {"hate_and_discrimination": 0.15, "violence_and_threats": 0.25, "criminal": 0.1}

# Phase 3 : production stable
phase3 = {"hate_and_discrimination": 0.2, "violence_and_threats": 0.3, "criminal": 0.15}

Ces valeurs illustrent la démarche ; les vôtres sortiront de vos propres mesures. Gardez block_on_error à True en production : sa valeur par défaut dans le SDK est False, et une panne de la modération laisserait alors passer du contenu non vérifié. Enfin, la documentation prévient que le modèle de modération évolue : des politiques fondées sur les scores peuvent demander un recalibrage.

Points clés à retenir

  • guardrails est une liste d’objets {"block_on_error": ..., "moderation_llm_v2": {...}}
  • Dans moderation_llm_v2 : custom_category_thresholds, ignore_other_categories, action et, en option, model_name
  • Seuils de 0 à 1 ; plus bas, plus strict ; 1 désactive une catégorie. Listez chaque catégorie qui doit bloquer
  • Un blocage lève errors.SDKError avec status_code == 403 ; une panne avec block_on_error porte le code 3201 dans le corps
  • Gardez block_on_error=True en production, la valeur par défaut étant False