Scores et seuils de modération
Mis à jour le 29 juillet 2026
Interpréter les scores de modération
Chaque catégorie retourne un score entre 0.0 et 1.0. Mais que signifient ces chiffres concrètement ? Et comment définir les seuils de décision pour votre application ? Cette leçon vous donne les clés pour transformer des scores bruts en décisions automatisées, c’est-à-dire pour passer d’une mesure à une politique.
Premier point à intégrer : les scores de modération ne sont pas des probabilités au sens strict. Ils représentent le degré de confiance du classifieur dans la détection de contenu problématique. Un score de 0.6 ne veut pas dire « six chances sur dix que ce texte soit dangereux », mais « le signal est fort ».
| Plage | Lecture |
|---|---|
| 0.0 - 0.1 | Contenu clairement sûr, aucun signal détecté |
| 0.1 - 0.3 | Signal faible, possiblement anodin mais ambigu |
| 0.3 - 0.5 | Signal modéré, contenu potentiellement problématique |
| 0.5 - 0.7 | Signal fort, contenu très probablement problématique |
| 0.7 - 1.0 | Signal très fort, contenu manifestement dangereux |
Définir vos seuils : la matrice de décision
Le choix du seuil est un compromis entre sécurité, bloquer le maximum, et utilisabilité, minimiser les faux positifs. Aucun réglage ne gagne sur les deux tableaux, et prétendre le contraire mène à des allers-retours permanents. La méthode qui fonctionne consiste à figer trois profils explicites et à rattacher chaque application à l’un d’eux.
Le profil strict vise une application grand public où des mineurs peuvent se trouver ; le profil équilibré correspond à une application professionnelle B2B avec des utilisateurs identifiés ; le profil permissif est réservé à un outil interne manipulé par des experts qui savent ce qu’ils demandent.
# Trois profils de seuils
PROFILS = {
"strict": {
# Application grand public, enfants possibles
"sexual": 0.1,
"hate": 0.1,
"violence": 0.1,
"criminal": 0.1,
"selfharm": 0.1,
"health": 0.2,
"financial": 0.2,
"law": 0.2,
"pii": 0.1,
"jailbreaking": 0.1,
"unpredictable": 0.2
},
"equilibre": {
# Application professionnelle B2B
"sexual": 0.2,
"hate": 0.15,
"violence": 0.25,
"criminal": 0.15,
"selfharm": 0.15,
"health": 0.4,
"financial": 0.4,
"law": 0.4,
"pii": 0.2,
"jailbreaking": 0.15,
"unpredictable": 0.35
},
"permissif": {
# Outil interne pour experts
"sexual": 0.3,
"hate": 0.2,
"violence": 0.4,
"criminal": 0.2,
"selfharm": 0.2,
"health": 0.7,
"financial": 0.7,
"law": 0.7,
"pii": 0.3,
"jailbreaking": 0.2,
"unpredictable": 0.5
}
}
Comparez les colonnes health d’un profil à l’autre : 0.2, puis 0.4, puis 0.7. Un même échange sur une posologie sera bloqué dans le premier cas, accepté dans le dernier. C’est le contexte d’usage qui tranche, pas la nature du texte.
Système de classification multi-niveaux
Bloquer ou accepter est une vision appauvrie de la réalité. Entre le contenu manifestement sûr et le contenu manifestement fautif s’étend une zone grise où la bonne décision est d’afficher tout en signalant. La fonction ci-dessous introduit ce troisième état : au-dessus du seuil on bloque, au-dessus de 60 % du seuil on avertit et on journalise, en dessous on laisse passer.
from enum import Enum
class NiveauRisque(Enum):
SAFE = "safe" # Vert : contenu sûr
WARNING = "warning" # Orange : contenu à surveiller
BLOCKED = "blocked" # Rouge : contenu bloqué
def classifier_contenu(scores: dict, seuils: dict) -> dict:
"""Classifie le contenu en 3 niveaux de risque."""
violations_bloquantes = []
avertissements = []
for categorie, seuil in seuils.items():
score = scores.get(categorie, 0)
if score > seuil:
violations_bloquantes.append({
"categorie": categorie,
"score": round(score, 3)
})
elif score > seuil * 0.6:
# Zone d'avertissement : 60% du seuil
avertissements.append({
"categorie": categorie,
"score": round(score, 3)
})
if violations_bloquantes:
return {
"niveau": NiveauRisque.BLOCKED,
"violations": violations_bloquantes,
"action": "bloquer_et_logger"
}
elif avertissements:
return {
"niveau": NiveauRisque.WARNING,
"avertissements": avertissements,
"action": "afficher_et_logger"
}
else:
return {
"niveau": NiveauRisque.SAFE,
"action": "afficher"
}
L’intérêt de la zone orange est autant opérationnel que pédagogique : elle vous montre ce qui frôle vos seuils. Si une catégorie génère beaucoup d’avertissements sans jamais franchir la ligne, vous savez qu’un ajustement de quelques centièmes basculerait un volume important de trafic.
Exemple concret : analyser et décider
L’assemblage tient en une fonction : appel au classifieur, application du profil, décision. La comparaison qui suit vaut démonstration — une posologie de paracétamol est bloquée en profil strict au titre de health et acceptée en profil permissif, sans qu’une seule ligne du texte n’ait changé.
from mistralai import Mistral
import os
client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
def moderer_et_decider(texte: str, profil: str = "equilibre") -> dict:
"""Modère un texte et prend une décision selon le profil."""
# Appel à l'API de modération
response = client.classifiers.moderate(
model="mistral-moderation-2603",
inputs=[texte]
)
scores = response.results[0].category_scores
# Appliquer les seuils du profil
seuils = PROFILS[profil]
classification = classifier_contenu(scores, seuils)
return {
"texte": texte[:80],
"profil": profil,
"scores_bruts": {k: round(v, 3) for k, v in scores.items()},
"decision": classification
}
# Test avec différents profils
texte = "Prenez 2 comprimés de paracétamol toutes les 6 heures"
# Profil strict : probablement bloqué (health)
resultat_strict = moderer_et_decider(texte, "strict")
# Profil permissif : probablement accepté
resultat_permissif = moderer_et_decider(texte, "permissif")
Ajuster les seuils dynamiquement
En production, vos seuils doivent évoluer, et cette évolution se pilote avec des chiffres. Le moniteur ci-dessous écrit chaque décision dans un fichier JSONL et tient à jour, catégorie par catégorie, les compteurs de blocages, d’avertissements et de passages. Le rapport qui en découle vous donne un taux de blocage par catégorie — la seule donnée qui permette de dire si un seuil est trop bas.
import json
from datetime import datetime
class ModerationMonitor:
"""Collecte les métriques de modération pour ajuster les seuils."""
def __init__(self, log_file: str = "moderation_logs.jsonl"):
self.log_file = log_file
self.stats = {cat: {"blocked": 0, "warned": 0, "passed": 0}
for cat in PROFILS["equilibre"].keys()}
def log_decision(self, scores: dict, seuils: dict, decision: str):
"""Enregistre une décision de modération."""
entry = {
"timestamp": datetime.utcnow().isoformat(),
"scores": scores,
"seuils": seuils,
"decision": decision
}
with open(self.log_file, "a") as f:
f.write(json.dumps(entry) + "\n")
# Mettre à jour les compteurs
for cat, score in scores.items():
seuil = seuils.get(cat, 1.0)
if score > seuil:
self.stats[cat]["blocked"] += 1
elif score > seuil * 0.6:
self.stats[cat]["warned"] += 1
else:
self.stats[cat]["passed"] += 1
def rapport(self) -> dict:
"""Génère un rapport pour ajuster les seuils."""
rapport = {}
for cat, stats in self.stats.items():
total = sum(stats.values())
if total > 0:
rapport[cat] = {
"taux_blocage": round(stats["blocked"] / total, 3),
"taux_warning": round(stats["warned"] / total, 3),
"total": total
}
return rapport
Les pièges à éviter
Le premier piège est le seuil unique appliqué à toutes les catégories. Chaque catégorie a sa propre distribution de scores : un seuil de 0.3 pour hate ne signifie pas la même chose que 0.3 pour health, où les contenus légitimes vivent naturellement plus haut dans l’échelle. Ajustez chaque catégorie individuellement, même si cela demande onze décisions au lieu d’une.
Le deuxième est d’ignorer les faux positifs. Un taux de blocage élevé dégrade la confiance des utilisateurs beaucoup plus vite qu’un incident isolé, parce qu’il touche des gens de bonne foi. Suivez le taux de blocage par catégorie et relevez les seuils quand les données le justifient.
Le troisième, plus insidieux, consiste à ne pas journaliser les décisions. Sans logs, vous ne pouvez pas ajuster vos seuils de manière informée et vous en revenez aux discussions d’opinion. Enregistrez systématiquement les scores, les seuils appliqués et les décisions prises — c’est ce corpus qui rendra la révision suivante rapide et incontestable.
Points clés à retenir
- Les scores de 0.0 à 1.0 représentent le degré de confiance du classifieur
- Définissez des profils de seuils adaptés à votre contexte (strict, équilibré, permissif)
- Implémentez un système à 3 niveaux : safe, warning, blocked
- Loggez toutes les décisions pour ajuster vos seuils en continu
- Chaque catégorie nécessite son propre seuil — pas de valeur unique