Modération de contenu avec l'API
Mis à jour le 29 juillet 2026
Modération de contenu avec l’API
L’API de modération d’OpenAI permet de détecter automatiquement les contenus inappropriés. C’est un outil gratuit et essentiel pour toute application qui accepte du contenu utilisateur. La leçon précédente vous a montré comment écrire des filtres maison ; celle-ci vous donne un classifieur entraîné qui repère ce qu’aucune expression régulière ne saura jamais attraper — l’ironie menaçante, l’insulte détournée, la formulation implicite.
L’API Moderation
L’appel est volontairement minimal : un modèle, un texte, et un résultat qui tient dans un booléen. Le champ flagged répond à la question binaire « ce contenu viole-t-il les politiques d’usage ? », tandis que categories détaille lesquelles. Cet endpoint étant gratuit, il n’y a aucune raison économique de s’en priver, même sur de gros volumes.
from openai import OpenAI
client = OpenAI()
# L'API de modération est GRATUITE
moderation = client.moderations.create(
model="omni-moderation-latest",
input="Bonjour, comment allez-vous ?"
)
result = moderation.results[0]
print(f"Contenu flaggé : {result.flagged}")
print(f"Catégories : {result.categories}")
# Résultat :
# Contenu flaggé : False
# Catégories : Categories(harassment=False, hate=False, ...)
Les catégories de modération
Derrière le booléen se cache une grille fine. Le modèle rend une décision par catégorie — harcèlement, harcèlement avec menace, haine, haine avec menace, automutilation avec ses variantes instructions et intention, contenu sexuel, contenu sexuel impliquant des mineurs, violence et violence graphique — ainsi qu’un score de confiance compris entre 0.0 et 1.0 pour chacune.
Ces scores valent mieux que la décision binaire quand vous voulez calibrer votre politique. Une plateforme de forum professionnel peut vouloir bloquer dès 0.3 sur le harcèlement, là où un service de fiction accepterait 0.7 sur la violence. La fonction ci-dessous expose les deux niveaux de lecture : les catégories effectivement déclenchées, et les scores bruts pour vos propres seuils.
def analyser_moderation(texte: str) -> dict:
"""Analyse détaillée de la modération."""
moderation = client.moderations.create(
model="omni-moderation-latest",
input=texte
)
result = moderation.results[0]
# Catégories disponibles
categories = {
"harassment": result.categories.harassment,
"harassment_threatening": result.categories.harassment_threatening,
"hate": result.categories.hate,
"hate_threatening": result.categories.hate_threatening,
"self_harm": result.categories.self_harm,
"self_harm_instructions": result.categories.self_harm_instructions,
"self_harm_intent": result.categories.self_harm_intent,
"sexual": result.categories.sexual,
"sexual_minors": result.categories.sexual_minors,
"violence": result.categories.violence,
"violence_graphic": result.categories.violence_graphic,
}
# Scores de confiance (0.0 a 1.0)
scores = {
"harassment": result.category_scores.harassment,
"hate": result.category_scores.hate,
"self_harm": result.category_scores.self_harm,
"sexual": result.category_scores.sexual,
"violence": result.category_scores.violence,
}
return {
"flagged": result.flagged,
"categories_detectees": [k for k, v in categories.items() if v],
"scores_principaux": {k: round(v, 4) for k, v in scores.items()},
}
# Test
resultat = analyser_moderation("Bonjour tout le monde !")
print(f"Flaggé : {resultat['flagged']}")
print(f"Scores : {resultat['scores_principaux']}")
Pipeline de modération complet
Modérer les entrées utilisateur
Encapsulons cet appel dans un composant réutilisable. ContentModerator rend un couple explicite — acceptable ou non, avec la raison — plutôt qu’un objet brut que chaque appelant devrait réinterpréter. La traduction des catégories en français a son importance : c’est le vocabulaire qui apparaîtra dans votre interface et dans vos journaux, il doit être compréhensible par l’équipe de modération humaine qui prendra le relais.
class ContentModerator:
"""Modérateur de contenu pour les applications."""
def __init__(self, seuil_custom: float = 0.5):
self.client = OpenAI()
self.seuil = seuil_custom
def moderer(self, texte: str) -> tuple[bool, dict]:
"""Modère un texte. Retourne (est_acceptable, détails)."""
moderation = self.client.moderations.create(
model="omni-moderation-latest",
input=texte
)
result = moderation.results[0]
if result.flagged:
categories = []
if result.categories.harassment:
categories.append("harcèlement")
if result.categories.hate:
categories.append("discours haineux")
if result.categories.sexual:
categories.append("contenu sexuel")
if result.categories.violence:
categories.append("violence")
if result.categories.self_harm:
categories.append("automutilation")
return False, {
"raison": "Contenu inapproprié détecté",
"categories": categories
}
return True, {"raison": "Contenu acceptable"}
def moderer_conversation(self, messages: list[str]) -> list[dict]:
"""Modère une liste de messages."""
resultats = []
for msg in messages:
acceptable, details = self.moderer(msg)
resultats.append({
"message": msg[:50] + "..." if len(msg) > 50 else msg,
"acceptable": acceptable,
**details
})
return resultats
# Utilisation
moderateur = ContentModerator()
acceptable, details = moderateur.moderer("Bonjour, comment puis-je vous aider ?")
print(f"Acceptable : {acceptable}")
print(f"Détails : {details}")
Intégration dans le pipeline API
La modération encadre l’appel des deux côtés. Filtrer l’entrée évite de payer une génération sur un contenu qu’on refusera de toute façon ; filtrer la sortie protège contre le cas où le modèle, provoqué habilement, produirait lui-même quelque chose d’inacceptable. Remarquez la différence de traitement : sur l’entrée, on peut dire à l’utilisateur ce qui a été détecté, puisqu’il en est l’auteur ; sur la sortie, le message reste vague, car détailler reviendrait à décrire ce que le modèle vient de produire.
def chatbot_modere(user_input: str) -> str:
"""Chatbot avec modération des entrées ET des sorties."""
moderateur = ContentModerator()
# 1. Modérer l'entrée
input_ok, input_details = moderateur.moderer(user_input)
if not input_ok:
return (f"Votre message a été bloqué : "
f"{', '.join(input_details['categories'])}")
# 2. Générer la réponse
response = client.responses.create(
model="gpt-5.6-terra",
instructions="Répondez de manière professionnelle et sûre.",
input=user_input
)
# 3. Modérer la sortie
output_ok, output_details = moderateur.moderer(response.output_text)
if not output_ok:
return "Je ne suis pas en mesure de fournir cette réponse."
return response.output_text
# Test
print(chatbot_modere("Quelle est la capitale de la France ?"))
Modération en batch
Ce double appel ajoute de la latence, ce qui reste acceptable en conversation mais devient prohibitif quand vous devez traiter dix mille avis clients importés d’un fichier. Pour modérer de gros volumes efficacement, passez directement un tableau : l’API traite tous les textes en une seule requête et rend les résultats dans le même ordre, ce qui permet de les réassocier par index.
def moderer_batch(textes: list[str]) -> list[dict]:
"""Modère plusieurs textes en une seule requête."""
resultats = []
# L'API accepte un tableau d'inputs
moderation = client.moderations.create(
model="omni-moderation-latest",
input=textes
)
for i, result in enumerate(moderation.results):
resultats.append({
"index": i,
"texte_preview": textes[i][:50],
"flagged": result.flagged,
})
flagged_count = sum(1 for r in resultats if r["flagged"])
print(f"Modéré {len(textes)} textes : {flagged_count} flaggés")
return resultats
# Utilisation
textes = [
"Bonjour, comment allez-vous ?",
"Super produit, je recommande !",
"Le service client est excellent.",
]
resultats = moderer_batch(textes)
Logging de la modération
Une décision de modération est une décision qui affecte un utilisateur, et vous devrez tôt ou tard la justifier — devant un client qui conteste un blocage, ou devant un régulateur. Loguez donc systématiquement le verdict, l’identifiant de l’utilisateur, les scores et un extrait du texte. Le niveau de log distingue les deux cas : warning pour les contenus signalés, qui méritent une revue humaine, info pour le reste, qui alimente vos statistiques.
import json
import logging
from datetime import datetime
logger = logging.getLogger("moderation")
def moderer_avec_log(texte: str, user_id: str = "anonymous") -> bool:
"""Modère et logue le résultat pour audit."""
moderation = client.moderations.create(
model="omni-moderation-latest",
input=texte
)
result = moderation.results[0]
log_entry = {
"timestamp": datetime.now().isoformat(),
"user_id": user_id,
"flagged": result.flagged,
"text_preview": texte[:100],
"scores": {
"harassment": round(result.category_scores.harassment, 4),
"hate": round(result.category_scores.hate, 4),
"violence": round(result.category_scores.violence, 4),
}
}
if result.flagged:
logger.warning(json.dumps(log_entry))
else:
logger.info(json.dumps(log_entry))
return not result.flagged
# Utilisation
est_ok = moderer_avec_log("Message de test", user_id="user-123")
Un dernier avertissement avant de passer à la suite : la modération classe le contenu, elle ne comprend pas l’intention. Un message parfaitement poli qui tente de détourner votre assistant passera tous ces filtres sans être signalé une seule fois. C’est l’objet de la leçon suivante.
Points clés à retenir
- L’API de modération est gratuite — utilisez-la systématiquement
- Modérez les entrées (contenu utilisateur) ET les sorties (réponses du modèle)
- Le modèle
omni-moderation-latestcouvre toutes les catégories de contenu - Loguez toutes les décisions de modération pour l’audit
- Utilisez le batch pour modérer efficacement de gros volumes
- La modération est un complément aux instructions système, pas un remplacement