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

Custom Guardrails : bloquer avant le modèle

Leçon 2 sur 12 Mis à jour le 3 novembre 2026 Développeur
Sur cette page
  1. Le principe des Custom Guardrails
  2. Comment ça fonctionne
  3. Le format du paramètre guardrails
  4. Les onze catégories
  5. Les trois niveaux d’application
  6. Ce que renvoient les guardrails
  7. Choisir le bon niveau
  8. Points clés à retenir

Leçon réécrite le 3 novembre 2026. Elle décrivait un paramètre guardrails inventé (un dictionnaire avec enabled), des catégories qui n’existent pas (hate, violence), une méthode conversation.send() absente du SDK et des réponses JSON imaginaires. Le format officiel est une liste de configurations moderation_llm_v2, et les réponses ci-dessous sont celles de la documentation. Le code a été vérifié contre les types du SDK mistralai 3.7.0. Sources : Moderation & Guardrailing · SDK Python mistralai.

Le principe des Custom Guardrails

Les Custom Guardrails de Mistral fonctionnent comme un filtre en entrée : ils analysent le prompt de l’utilisateur avant qu’il n’atteigne le modèle de génération. Si le contenu dépasse l’un des seuils que vous avez fixés, la requête est bloquée avec une erreur HTTP 403. Le modèle ne voit jamais le prompt problématique.

La documentation insiste sur deux limites, à garder en tête dès le départ. Les guardrails ne modèrent que l’entrée : ce que le modèle génère n’est pas vérifié, c’est le rôle de l’API Moderation (leçon 5). Et ils remplacent un travail que vous auriez sinon codé vous-même : appeler l’API Moderation, comparer les scores à vos seuils, refuser la requête.

Comment ça fonctionne

Lorsque vous ajoutez des guardrails à une requête, l’enchaînement côté Mistral est le suivant :

  1. Votre application envoie la requête avec le paramètre guardrails
  2. Le classifieur mistral-moderation-2603 note le prompt dans chaque catégorie
  3. Si un score dépasse son seuil, l’API renvoie une erreur 403 et le modèle n’est pas appelé
  4. Sinon, le modèle traite la requête normalement
  5. La réponse contient un champ guardrails avec les scores évalués
Flux : prompt → [mistral-moderation-2603] → score > seuil ? → 403 (bloqué)
                                          → sinon        → [LLM] → réponse + scores

La décision repose sur un score comparé à un seuil que vous fixez, catégorie par catégorie, entre 0 et 1. Un seuil bas bloque davantage ; un seuil à 1 désactive explicitement la catégorie.

Le format du paramètre guardrails

Le paramètre est une liste. Chaque élément est un objet qui contient une configuration moderation_llm_v2 et, à côté, l’option block_on_error :

guardrails = [
    {
        "block_on_error": True,          # bloquer si la modération elle-même échoue
        "moderation_llm_v2": {
            "custom_category_thresholds": {
                "jailbreaking": 0.3,
                "criminal": 0.2,
            },
            "ignore_other_categories": False,
            "action": "block",
        },
    }
]
CléOùRôle
custom_category_thresholdsdans moderation_llm_v2Seuil de 0 à 1 par catégorie ; 1 désactive la catégorie
ignore_other_categoriesdans moderation_llm_v2True : seules les catégories listées sont évaluées
actiondans moderation_llm_v2"block" pour bloquer la requête en cas de dépassement
model_namedans moderation_llm_v2, facultatifAutre modèle de modération que celui par défaut, mistral-moderation-2603
block_on_errorà côté de moderation_llm_v2True : la requête est bloquée si l’évaluation échoue

Vous pouvez placer plusieurs objets dans la liste : la requête est bloquée dès que l’un d’eux se déclenche. Chaque objet ne contient qu’une seule configuration moderation_llm_v2. Une configuration plus ancienne, moderation_llm_v1, existe encore dans le SDK, avec les catégories de l’ancien modèle mistral-moderation-2411, déprécié le 31 mars 2026 : ne l’utilisez pas pour un nouveau projet.

Les onze catégories

Les clés de custom_category_thresholds sont fixées ; une autre clé, comme violence ou hate, ne correspond à rien :

CléCe qu’elle détecte
sexualContenu sexuel explicite
hate_and_discriminationHaine ou discrimination fondée sur des caractéristiques protégées
violence_and_threatsViolence physique décrite, glorifiée ou menacée
dangerousComportements extrêmement dangereux
criminalActivités illégales
selfharmAutomutilation, suicide, troubles alimentaires
healthDemande de conseil médical détaillé ou personnalisé
financialDemande de conseil financier détaillé ou personnalisé
lawDemande de conseil juridique détaillé ou personnalisé
piiDonnées personnelles demandées ou partagées
jailbreakingTentative de contourner les règles du modèle

La leçon 6 revient sur chacune et sur le choix des seuils.

Les trois niveaux d’application

Le même paramètre s’accepte à trois endroits. Le choix détermine où vit votre politique de sécurité, et qui peut l’oublier.

Niveau 1 : une requête Chat Completions

Vous passez guardrails dans l’appel chat.complete(). Chaque requête porte ses propres règles : pratique pour explorer, risqué à grande échelle, car il suffit d’un appel sans le paramètre pour qu’il passe sans contrôle.

import os

from mistralai.client import Mistral

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

response = client.chat.complete(
    model="mistral-large-latest",
    messages=[{"role": "user", "content": "Quelle est la distance de la Terre à la Lune ?"}],
    guardrails=[
        {
            "block_on_error": True,
            "moderation_llm_v2": {
                "custom_category_thresholds": {"sexual": 0.1, "selfharm": 0.1},
                "ignore_other_categories": False,
                "action": "block",
            },
        }
    ],
)
print(response.choices[0].message.content)

Cas d’usage : prototypage, appels ponctuels, réglage des seuils.

Niveau 2 : une conversation

Avec l’API Conversations, vous passez guardrails au démarrage, dans client.beta.conversations.start() :

conversation = client.beta.conversations.start(
    model="mistral-large-latest",
    inputs="Bonjour, pouvez-vous m'aider à préparer un entretien ?",
    guardrails=[
        {
            "block_on_error": True,
            "moderation_llm_v2": {
                "custom_category_thresholds": {
                    "sexual": 0.2,
                    "hate_and_discrimination": 0.1,
                    "violence_and_threats": 0.3,
                    "selfharm": 0.1,
                },
                "action": "block",
            },
        }
    ],
)
for entree in conversation.outputs:
    if entree.type == "message.output":
        print(entree.content)

Les messages suivants s’envoient avec client.beta.conversations.append(conversation_id=conversation.conversation_id, inputs=...). Cette méthode n’accepte pas de paramètre guardrails dans le SDK 3.7.0, et la documentation ne précise pas si ceux du démarrage s’appliquent encore aux tours suivants. Pour une protection garantie à chaque tour, attachez les guardrails à un agent.

Cas d’usage : conversation ponctuelle sans agent, ou remplacement des guardrails d’un agent pour une requête donnée.

Niveau 3 : un agent

Les guardrails passés à client.beta.agents.create() font partie de la définition de l’agent. D’après la documentation, toutes les conversations qui utilisent cet agent en héritent, sans les répéter. Une requête de conversation peut toutefois les remplacer en passant son propre guardrails.

agent = client.beta.agents.create(
    model="mistral-large-latest",
    name="Assistant sécurisé",
    instructions="Vous êtes un assistant professionnel du service client.",
    guardrails=[
        {
            "block_on_error": True,
            "moderation_llm_v2": {
                "custom_category_thresholds": {
                    "sexual": 0.1,
                    "hate_and_discrimination": 0.1,
                    "violence_and_threats": 0.2,
                    "criminal": 0.1,
                    "selfharm": 0.1,
                    "jailbreaking": 0.3,
                },
                "ignore_other_categories": False,
                "action": "block",
            },
        }
    ],
)

conversation = client.beta.conversations.start(agent_id=agent.id, inputs="Bonjour !")

Cas d’usage : production, quand plusieurs équipes ou services appellent le même assistant.

Ce que renvoient les guardrails

Quand la requête passe, la réponse de l’API contient, à côté des champs habituels, un champ guardrails : une liste, avec un résultat par guardrail. Voici ce champ dans l’exemple de la documentation :

{
  "guardrails": [
    {
      "moderation_llm_v2": {
        "action": "pass",
        "categories": {
          "sexual": { "score": 0.03, "violated": false },
          "selfharm": { "score": 0.05, "violated": false },
          "violence_and_threats": { "score": 0.0, "violated": false },
          "hate_and_discrimination": { "score": 0.0, "violated": false }
        }
      }
    }
  ]
}

Avec ignore_other_categories à True, seules les catégories listées dans vos seuils figurent dans ce résultat. Dans le SDK Python, la réponse d’une conversation expose ce champ (conversation.guardrails). La réponse de chat.complete(), en revanche, n’a pas de champ prévu pour lui dans le SDK 3.7.0 : il s’y perd. Si vous voulez journaliser les scores, et c’est utile pour savoir plus tard si vos seuils sont trop lâches ou trop serrés, passez par les conversations ou lisez la réponse HTTP brute.

Quand un seuil est dépassé, l’API renvoie une erreur 403 dont le corps détaille la décision, catégorie par catégorie :

{
  "error": { "message": "Content blocked by guardrail", "status": 403 },
  "guardrails": {
    "results": {
      "moderation_llm_v2": {
        "model_name": "mistral-moderation-2603",
        "decisions": {
          "sexual": { "threshold": 0.1, "score": 0.3, "violated": true },
          "selfharm": { "threshold": 0.1, "score": 0.05, "violated": false }
        },
        "violated": true,
        "action": "block"
      }
    }
  }
}

Le SDK lève alors errors.SDKError, dont status_code vaut 403 et body contient ce JSON :

import json

from mistralai.client import errors

try:
    response = client.chat.complete(
        model="mistral-large-latest",
        messages=[{"role": "user", "content": message_utilisateur}],
        guardrails=guardrails,
    )
except errors.SDKError as e:
    if e.status_code != 403:
        raise
    decisions = json.loads(e.body)["guardrails"]["results"]["moderation_llm_v2"]["decisions"]
    violees = [nom for nom, d in decisions.items() if d["violated"]]
    print(f"Requête bloquée : {violees}")

C’est cette charge utile qui vous permettra d’expliquer un blocage lors d’une réclamation : quelle catégorie, quel score, quel seuil.

Reste le cas dégradé. Si l’évaluation elle-même échoue et que block_on_error vaut True, la requête est refusée avec le code d’erreur 3201 et le message Request blocked due to error in guardrail evaluation and block_on_error is set to True. C’est le mode fail-safe : en cas de doute, on bloque. Avec block_on_error à False, sa valeur par défaut dans le SDK, la requête passerait sans contrôle.

Choisir le bon niveau

CritèreChat CompletionsConversationAgent
Où se déclarent les guardrailsDans chaque appelAu démarrage (start)À la création de l’agent
HéritageAucunNon documenté pour les tours suivantsToutes les conversations de l’agent
Remplaçables——Oui, par une requête de conversation
Cas d’usageTests, réglage des seuilsConversation ponctuelleProduction

En pratique, réglez vos seuils sur des requêtes Chat Completions avec des cas réels, puis déplacez la politique dans un agent dès que le code sort de votre machine : il n’y aura plus d’appel oublié.

Points clés à retenir

  • Les guardrails bloquent les prompts avant le modèle, par une erreur 403 ; ils ne vérifient pas la sortie
  • Le paramètre est une liste d’objets {"block_on_error": ..., "moderation_llm_v2": {...}}, et non un dictionnaire avec enabled
  • Onze catégories aux noms fixés (hate_and_discrimination, violence_and_threats…), un seuil de 0 à 1 chacune ; 1 désactive
  • Trois niveaux : chat.complete(), conversations.start(), agents.create() ; les agents transmettent leurs guardrails à toutes leurs conversations
  • block_on_error=True assure le comportement fail-safe (erreur 3201)