L'API Moderation d'OpenAI
Mis à jour le 28 juillet 2026
Un outil de modération intégré
OpenAI fournit une API de modération gratuite qui analyse le texte pour détecter du contenu potentiellement dangereux. C’est la première ligne de défense à intégrer dans toute application utilisant les modèles OpenAI, et rien n’oblige à s’y limiter : comme elle prend du texte brut en entrée, elle sert aussi à filtrer les entrées destinées à d’autres modèles. Sa gratuité change la façon de raisonner. Les arbitrages habituels — faut-il modérer toutes les requêtes ou seulement les suspectes ? — perdent leur objet, puisque le seul coût résiduel est celui de la latence.
Ce que renvoie l’API
L’appel de base tient en une ligne ; c’est la réponse qui demande de l’attention. L’API renvoie trois choses distinctes : un booléen flagged qui traduit la décision d’OpenAI, la liste des catégories effectivement déclenchées, et surtout les scores continus par catégorie. Ce sont ces scores qui font la différence en production, car ils vous permettent de décider vous-même où placer la frontière plutôt que d’hériter d’un seuil pensé pour un usage générique.
from openai import OpenAI
client = OpenAI()
def moderer_contenu(texte: str) -> dict:
"""Analyse un texte via l'API Moderation d'OpenAI."""
response = client.moderations.create(input=texte)
result = response.results[0]
return {
"bloque": result.flagged,
"categories": {
cat: flagged
for cat, flagged in result.categories.__dict__.items()
if flagged
},
"scores": {
cat: round(score, 4)
for cat, score in result.category_scores.__dict__.items()
if score > 0.01
},
}
# Test avec différents contenus
exemples = [
"Comment cuisiner un gâteau au chocolat ?",
"Je vais te tuer si tu ne me réponds pas",
"Explique-moi comment fonctionne le chiffrement AES",
]
for texte in exemples:
resultat = moderer_contenu(texte)
statut = "BLOQUÉ" if resultat["bloque"] else "OK"
print(f"{statut} : {texte[:50]}...")
if resultat["categories"]:
print(f" Catégories : {list(resultat['categories'].keys())}")
Les trois exemples de test ne sont pas anodins : le premier doit passer, le deuxième doit être bloqué, et le troisième — une question technique parfaitement légitime — vous montre comment votre pipeline se comporte face à du vocabulaire de sécurité. C’est exactement le genre de requête que reçoit une équipe cyber au quotidien.
Les catégories détectées
L’API classe le contenu problématique selon plusieurs axes, chacun correspondant à un type de préjudice distinct.
| Catégorie | Description |
|---|---|
| harassment | Harcèlement, menaces, intimidation |
| hate | Discours haineux basé sur l'identité |
| self-harm | Automutilation, suicide |
| sexual | Contenu sexuel explicite |
| violence | Violence graphique, menaces |
Chaque catégorie a aussi des sous-catégories (ex: harassment/threatening, violence/graphic) pour un filtrage plus fin. Cette granularité vous évite des arbitrages grossiers : une plateforme de modération de forums peut vouloir bloquer harassment/threatening tout en tolérant harassment simple, qui recouvre beaucoup de disputes ordinaires.
Lorsque vous devez traiter un volume — l’analyse rétrospective d’un historique de conversations, par exemple — l’API accepte une liste et renvoie un résultat par élément, ce qui évite de multiplier les allers-retours réseau.
def moderer_lot(messages: list[str]) -> list[dict]:
"""Modère plusieurs messages en un seul appel."""
response = client.moderations.create(input=messages)
resultats = []
for i, result in enumerate(response.results):
resultats.append({
"index": i,
"texte": messages[i][:50],
"bloque": result.flagged,
"score_max": max(
score
for score in result.category_scores.__dict__.values()
),
})
return resultats
Modérer les deux extrémités du pipeline
Une erreur fréquente consiste à ne modérer que les entrées. Elle repose sur une intuition fausse : si le message entrant est acceptable, la réponse le sera aussi. Or un modèle peut produire du contenu problématique à partir d’une requête neutre, notamment quand il s’appuie sur des documents récupérés dont personne n’a vérifié le contenu. La classe ci-dessous applique donc le même traitement dans les deux sens, avec en plus un seuil personnalisé qui vient durcir la décision par défaut de l’API.
class PipelineModeration:
"""Pipeline de modération entrée + sortie pour une application LLM."""
def __init__(self, seuil_custom: float = 0.7):
self.client = OpenAI()
self.seuil = seuil_custom
def moderer_entree(self, message: str) -> tuple[bool, dict]:
"""Modère le message AVANT envoi au modèle."""
result = self.client.moderations.create(input=message).results[0]
scores = result.category_scores.__dict__
categories_elevees = {
cat: score for cat, score in scores.items() if score > self.seuil
}
bloque = result.flagged or bool(categories_elevees)
return bloque, {"flagged_api": result.flagged, "categories_custom": categories_elevees}
def moderer_sortie(self, reponse: str) -> tuple[bool, dict]:
"""Modère la réponse du modèle AVANT envoi à l'utilisateur."""
return self.moderer_entree(reponse)
def traiter_message(self, message_utilisateur: str, prompt_systeme: str) -> str:
"""Pipeline complet : modération entrée, LLM, modération sortie."""
# Étape 1 : modérer l'entrée
bloque_entree, details = self.moderer_entree(message_utilisateur)
if bloque_entree:
return "Votre message ne peut pas être traité. Veuillez le reformuler."
# Étape 2 : appeler le modèle
response = self.client.chat.completions.create(
model="gpt-5.6-sol",
messages=[
{"role": "system", "content": prompt_systeme},
{"role": "user", "content": message_utilisateur},
],
)
reponse_modele = response.choices[0].message.content
# Étape 3 : modérer la sortie
bloque_sortie, _ = self.moderer_sortie(reponse_modele)
if bloque_sortie:
return "La réponse a été filtrée pour non-conformité."
return reponse_modele
# Utilisation
pipeline = PipelineModeration(seuil_custom=0.5)
reponse = pipeline.traiter_message(
"Comment protéger mon application contre les injections SQL ?",
"Vous êtes un expert en cybersécurité défensive."
)
print(reponse)
Ce que cette API ne fait pas
Sa limite la plus importante est aussi la plus mal comprise : elle ne détecte pas la prompt injection. Un message poli et parfaitement inoffensif du point de vue du contenu, mais qui demande au modèle d’ignorer ses instructions, passera sans déclencher la moindre catégorie. Attendre d’elle une protection contre la manipulation revient à installer un détecteur de fumée en espérant qu’il arrête les cambriolages.
Les faux positifs constituent la deuxième limite, particulièrement sensible dans un contexte de sécurité ou médical, où décrire une menace fait partie du travail. Un appel réseau supplémentaire s’ajoute par ailleurs à chaque requête, ce qui compte quand vous visez une réponse en flux tendu. La détection reste plus fiable en anglais que dans les autres langues, angle mort que les attaquants connaissent parfaitement. Enfin, les catégories et les seuils évoluent au fil des mises à jour : un réglage calibré il y a six mois mérite d’être revérifié.
Ces limites dictent la manière de s’en servir. Modérez dans les deux sens, ajustez les seuils catégorie par catégorie plutôt que globalement, et combinez systématiquement avec les autres couches — détection d’injection, filtrage métier — que les leçons suivantes détaillent. Journalisez les résultats, car c’est en observant vos propres faux positifs que vous saurez où déplacer les curseurs. Et prévoyez le cas où l’API est indisponible : décidez maintenant, à froid, si votre application doit alors bloquer par défaut ou laisser passer, plutôt que de découvrir ce comportement un jour d’incident.
Points clés à retenir
- L’API Moderation est gratuite, rapide et simple à intégrer
- Elle détecte le contenu offensant mais pas les injections ni les jailbreaks
- Le pipeline entrée vers modèle vers sortie doit être modéré aux deux extrémités
- Les seuils par défaut conviennent rarement — adaptez-les à votre contexte
- C’est une couche parmi d’autres, jamais la seule ligne de défense