Skip to main content
Documentation
Centre d’apprentissage

Maîtrisez la modération de contenu grâce à des guides complets, des tutoriels et une documentation API

Liens rapides

Faire évoluer la modération de contenu

Une configuration de modération qui fonctionne avec 1 000 messages par jour ne tient plus à un million : les limites de débit se font sentir, la latence de chaque appel s’accumule et les coûts explosent. Ce guide explique comment intégrer la modération Discuse dans des pipelines à haut débit — quand bloquer, quand vérifier en arrière-plan, quoi mettre en cache et comment contourner les pics de charge.

Qu’est-ce qui se dégrade lorsque la modération passe à l’échelle ?

Chaque problème de montée en charge a une cause précise et une solution adaptée :

Défi Impact Solution
Croissance du volume Limites de débit de l’API, hausse des coûts Traitement asynchrone, mise en cache
Exigences de latence Mauvaise expérience utilisateur Pré-modération, mise en file d’attente
Maîtrise des coûts Contraintes budgétaires Routage intelligent, mise en cache
Cohérence Application inégale des règles Configuration centralisée
Gestion des pics Dégradation du service Auto-scaling, files d’attente

Quel modèle d’architecture dois-je utiliser ?

Modèle 1 : Pré-modération synchrone

Idéal pour : les contenus à faible volume et à fort enjeu (paiements, juridique)

User Input → API Call → Moderation → Decision → Response
                ↓
           (Blocking call)
// Simple synchronous flow
async function createPost(content) {
  // Check moderation before saving
  const result = await checkModeration(content);

  if (result.has_violations) {
    throw new ModerationError(result.message);
  }

  // Safe to publish
  return await savePost(content);
}

Avantages : simple, retour immédiat Inconvénients : ajoute de la latence, limite le débit

Modèle 2 : Post-modération asynchrone

Idéal pour : les contenus à fort volume et à moindre enjeu (commentaires, messages)

User Input → Save (Pending) → Publish → Background Check → Action
                                              ↓
                                    ┌────────┴────────┐
                                    ↓                 ↓
                                  Safe            Violation
                                    ↓                 ↓
                                  Keep             Remove
// Async flow with queue
async function createPost(content) {
  // Save immediately with pending status
  const post = await savePost(content, { status: 'pending' });

  // Queue for async moderation
  await moderationQueue.add('check-content', {
    postId: post.id,
    content: content
  });

  return post;
}

// Background worker
moderationQueue.process('check-content', async (job) => {
  const result = await checkModeration(job.data.content);

  if (result.has_violations) {
    await removePost(job.data.postId);
    await notifyUser(job.data.postId, 'content_removed');
  } else {
    await updatePost(job.data.postId, { status: 'approved' });
  }
});

Avantages : non bloquant, adapté aux gros volumes Inconvénients : du contenu préjudiciable peut être visible brièvement

Modèle 3 : Modération par niveaux

Idéal pour : une approche équilibrée avec optimisation des coûts

User Input → Quick Check → High Confidence? ──Yes──► Auto-decision
                              │
                              No
                              ↓
                         Full Analysis ──► Decision
async function tieredModeration(content) {
  // Tier 1: Fast local checks (regex, blocklists)
  const localResult = await quickLocalCheck(content);
  if (localResult.definiteViolation) {
    return { action: 'block', source: 'local' };
  }

  // Tier 2: Cached API results
  const cacheKey = hashContent(content);
  const cached = await cache.get(cacheKey);
  if (cached) {
    return { action: cached.action, source: 'cache' };
  }

  // Tier 3: Full API check
  const apiResult = await checkModeration(content);
  await cache.set(cacheKey, apiResult, TTL);

  return { action: determineAction(apiResult), source: 'api' };
}

Modèle 4 : Distribution avec agrégation

Idéal pour : plusieurs types de vérifications avec mise à l’échelle indépendante

                    ┌──► Text Check ──┐
                    │                  │
User Input ──► Router ──► Image Check ──► Aggregator ──► Decision
                    │                  │
                    └──► Link Check ──┘
async function parallelModeration(content) {
  const checks = [];

  if (content.text) {
    checks.push(checkText(content.text));
  }

  if (content.images?.length) {
    checks.push(checkImages(content.images));
  }

  if (content.links?.length) {
    checks.push(checkLinks(content.links));
  }

  // Run all checks in parallel
  const results = await Promise.all(checks);

  // Aggregate results
  return aggregateResults(results);
}

function aggregateResults(results) {
  // Most severe result wins
  const hasViolation = results.some(r => r.has_violations);
  const maxScore = Math.max(...results.map(r => r.max_score || 0));

  return {
    has_violations: hasViolation,
    max_score: maxScore,
    details: results
  };
}

Stratégies de mise en cache

Mise en cache basée sur le contenu

Mettre en cache les résultats pour les contenus identiques :

const cache = new Redis();

async function checkWithCache(content) {
  const hash = crypto.createHash('sha256')
    .update(content.text || '')
    .update(JSON.stringify(content.images || []))
    .digest('hex');

  // Check cache first
  const cached = await cache.get(`mod:${hash}`);
  if (cached) {
    return { ...JSON.parse(cached), cached: true };
  }

  // API call
  const result = await callModerationAPI(content);

  // Cache for 24 hours
  await cache.setex(`mod:${hash}`, 86400, JSON.stringify(result));

  return { ...result, cached: false };
}

Mise en cache basée sur l’utilisateur

Mettre en cache les décisions pour les récidivistes :

async function checkUserHistory(userId) {
  const recentViolations = await cache.get(`violations:${userId}`);

  if (recentViolations > 5) {
    // Flag for enhanced scrutiny
    return { enhancedModeration: true };
  }

  return { enhancedModeration: false };
}

async function recordViolation(userId) {
  await cache.incr(`violations:${userId}`);
  await cache.expire(`violations:${userId}`, 86400 * 7); // 7 days
}

Gestion des files d’attente

Files d’attente prioritaires

Traitez les différents types de contenu avec des priorités différentes :

const queues = {
  critical: new Queue('moderation-critical'),  // Reports, appeals
  high: new Queue('moderation-high'),          // Public posts
  normal: new Queue('moderation-normal'),      // Comments, DMs
  low: new Queue('moderation-low')             // Profile updates
};

async function queueForModeration(content, priority = 'normal') {
  const queue = queues[priority] || queues.normal;

  return queue.add('check', {
    contentId: content.id,
    type: content.type,
    data: content
  }, {
    priority: getPriorityNumber(priority),
    timeout: getTimeout(priority)
  });
}

Respecter les limites de débit

Chaque clé API dispose d’une limite de requêtes par minute ; si vous la dépassez, l’API rejette l’appel avec une erreur "rate limit exceeded". Mettez en place une limitation de votre côté pour rester sous ce seuil et éviter de consommer votre quota avec des appels redondants. Définissez le plafond par minute du limiteur de façon à correspondre au RPM configuré pour votre clé :

const Bottleneck = require('bottleneck');

const limiter = new Bottleneck({
  maxConcurrent: 50,                   // Cap parallel requests
  minTime: 20,                         // Min 20ms between requests
  reservoir: YOUR_KEY_RPM,             // Match your API key's per-minute limit
  reservoirRefreshAmount: YOUR_KEY_RPM,
  reservoirRefreshInterval: 60 * 1000  // Refill each minute
});

async function rateLimitedCheck(content) {
  return limiter.schedule(() => checkModeration(content));
}

Le quota est distinct de la limite de débit : chaque appel est décompté de l’allocation mensuelle de requêtes API de votre forfait, et l’objet usage de la réponse indique api_requests_used, api_requests_limit et api_requests_remaining afin que vous puissiez surveiller votre marge restante.

Files d’attente de lettres mortes

Gérez les tentatives de modération échouées :

moderationQueue.process('check-content', async (job) => {
  try {
    const result = await checkModeration(job.data.content);
    await applyDecision(job.data.contentId, result);
  } catch (error) {
    if (job.attemptsMade < 3) {
      throw error; // Retry
    }

    // Move to dead letter queue after 3 attempts
    await deadLetterQueue.add('failed-moderation', {
      ...job.data,
      error: error.message,
      attempts: job.attemptsMade
    });

    // Apply safe default (hold for review)
    await holdForManualReview(job.data.contentId);
  }
});

Optimisation des coûts

Routage intelligent

N’appelez les APIs payantes que lorsque c’est nécessaire :

async function smartModeration(content) {
  // Step 1: Free local checks
  const localResult = runLocalFilters(content);
  if (localResult.isDefinitelySpam) {
    return { action: 'block', cost: 0 };
  }

  // Step 2: Check cache (free)
  const cached = await getCachedResult(content);
  if (cached) {
    return { action: cached.action, cost: 0 };
  }

  // Step 3: Risk-based API call
  const riskScore = calculateRiskScore(content, localResult);

  if (riskScore < 0.2) {
    // Low risk: approve without API call
    return { action: 'approve', cost: 0 };
  }

  // Step 4: API call for uncertain content
  const apiResult = await checkModeration(content);
  return {
    action: determineAction(apiResult),
    cost: calculateApiCost(content)
  };
}

Contrôle de la concurrence

Le point de terminaison /api/v2/check évalue une soumission par appel. Pour traiter le travail « par lots », regroupez les éléments dans votre worker et distribuez-les simultanément au sein d’un pool limité, plutôt que d’attendre une requête unique contenant plusieurs éléments. Une vérification qui inclut plusieurs URLs de médias constitue elle-même un seul appel les couvrant toutes : un appel riche en images est moins coûteux que de nombreux appels avec une seule image.

// Bounded concurrency: many items, capped parallel calls.
async function processBatch(items, concurrency = 10) {
  const results = [];
  for (let i = 0; i < items.length; i += concurrency) {
    const slice = items.slice(i, i + concurrency);
    const settled = await Promise.allSettled(
      slice.map(item => checkModeration(item.content))
    );
    results.push(...settled);
  }
  return results;
}

Une seule requête peut contenir jusqu’à 10 URLs d’images, 5 URLs de GIF, 3 URLs de vidéos, 5 URLs de documents et 20 liens, ainsi qu’un texte de 10 000 caractères maximum ; consolidez donc les médias d’une soumission dans un seul appel au lieu de les séparer.

Surveillance et alertes

Tableau de bord des indicateurs clés

const metrics = {
  // Volume metrics
  total_requests: new Counter('moderation_requests_total'),
  requests_per_second: new Gauge('moderation_rps'),

  // Latency metrics
  latency_p50: new Histogram('moderation_latency_p50'),
  latency_p99: new Histogram('moderation_latency_p99'),

  // Queue metrics
  queue_depth: new Gauge('moderation_queue_depth'),
  processing_time: new Histogram('moderation_processing_time'),

  // Cost metrics
  api_calls: new Counter('moderation_api_calls'),
  estimated_cost: new Counter('moderation_estimated_cost'),

  // Accuracy metrics
  violations_detected: new Counter('moderation_violations'),
  false_positives: new Counter('moderation_false_positives')
};

Règles d’alerte

# Alert on queue backup
- alert: ModerationQueueBackup
  expr: moderation_queue_depth > 10000
  for: 5m
  labels:
    severity: warning
  annotations:
    summary: Moderation queue backing up

# Alert on high latency
- alert: ModerationLatencyHigh
  expr: moderation_latency_p99 > 5000
  for: 2m
  labels:
    severity: critical
  annotations:
    summary: Moderation latency exceeding 5 seconds

# Alert on high error rate
- alert: ModerationErrorRate
  expr: rate(moderation_errors_total[5m]) / rate(moderation_requests_total[5m]) > 0.01
  for: 5m
  labels:
    severity: critical
  annotations:
    summary: Moderation error rate above 1%

Mise à l’échelle de l’infrastructure

Mise à l’échelle horizontale

# Kubernetes HPA for moderation workers
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: moderation-worker
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: moderation-worker
  minReplicas: 3
  maxReplicas: 50
  metrics:
  - type: External
    external:
      metric:
        name: queue_depth
      target:
        type: AverageValue
        averageValue: 100

Réduire la latence pour les utilisateurs du monde entier

L’API Discuse est accessible via une URL de base unique, https://api.discuse.com. Pour maintenir une faible latence sur le trafic mondial, exécutez vos workers de modération au plus près de vos utilisateurs et laissez-les appeler l’API, plutôt que de l’appeler de manière synchrone depuis le chemin de requête exposé à l’utilisateur dans une région éloignée. Associez cela aux modèles de post-modération asynchrone et de mise en cache décrits ci-dessus afin qu’un aller-retour lent ne bloque jamais un utilisateur.

Résumé des bonnes pratiques

  1. Mettez en cache de manière agressive : un contenu identique renvoie un résultat en cache. L’API déduplique également les contenus identiques par projet pendant une courte période et renvoie cached: true.
  2. Utilisez des files d’attente : traitement asynchrone pour les gros volumes.
  3. Mettez en place des niveaux : local → cache → API.
  4. Surveillez tout : profondeur des files d’attente, latence et utilisation par rapport à votre quota.
  5. Prévoyez les échecs : files d’attente de messages non distribuables et solutions de repli sûres.
  6. Mettez à l’échelle horizontalement : plus de workers, pas de plus grosses machines.
  7. Optimisez les coûts : routage intelligent, moins d’appels redondants et médias regroupés par requête.

Prochaines étapes

Rédigé par Équipe Discuse · Dernière mise à jour June 2026

Articles associés

Guide de modération de contenu par AI

Comment l’apprentissage automatique alimente les systèmes modernes de modération de contenu

Configuration des seuils de détection

Équilibrez les faux positifs et les faux négatifs pour votre cas d’utilisation