RHETORICA

Plateforme de débat en temps réel

Contexte & Motivation

Pourquoi RHETORICA ?

Le problème

  • S'entraîner à l'argumentation nécessite un adversaire disponible et un arbitre impartial
  • Le feedback humain est subjectif, lent, coûteux
  • Pas de métrique de progression objective

La solution

  • Plateforme compétitive avec arbitre IA en temps réel
  • Feedback immédiat sur chaque argument
  • Progression mesurable via ELO
  • Adversaires IA disponibles 24/7

Chess.com, mais pour le débat

Matchmaking ELO Tour par tour · 90s Arbitre IA live Progression mesurable Adversaires IA
tpalt.rboud.com
120+ sujets FR/EN 12 messages max/partie Score 0 → 100 3 modes de jeu
Concept

Déroulé d'une partie

sequenceDiagram
    participant J as Joueur
    participant MM as Matchmaking
    participant S as Serveur
    participant IA as Groq / Llama

    J->>MM: rejoint la file (mode + ELO)
    MM-->>J: match trouvé → debateId

    loop 12 tours (6/joueur · 90s)
        J->>S: envoie un message
        S->>IA: évalue le message
        IA-->>S: move_quality + events + score_delta
        S-->>J: score live mis à jour
    end

    S->>IA: analyse post-débat complète
    IA-->>S: sophismes · scores · forces · faiblesses
    S->>J: ELO mis à jour + achievements débloqués
  
Architecture

Stack technique

Technologie Pourquoi ce choix
Next.js 15 + App Router + React 19 SSR pour le dashboard, API Routes co-localisées, streaming natif
WebSocket custom (server.ts) Next.js ne supporte pas les connexions WS long-lived donc serveur HTTP+WS sur le même port
Groq API · Llama 3.3 70B Inférence <1s → évaluation pendant le débat réalisable sans bloquer l'UX (et aussi parce que c'est gratuit)
Prisma + PostgreSQL (prod) Gère les Migrations et le schéma. Permet d'avoie SQLite en dev via même interface Prisma
TypeScript 5 strict Typage bout en bout entre routes API et hooks client
TailwindCSS 4 TanStack Query 5 Docker · Node 20 pnpm · ESLint 9 i18n FR / EN
Architecture

Vue d'ensemble système

Flux de données

graph LR
    Browser -->|REST / SSR| Next["Next.js 15"]
    Browser -->|WebSocket| WS["server.ts"]
    Next --> WS
    WS --> PG[("PostgreSQL")]
    WS --> Groq["Groq API"]
      
SQLite en dev · PostgreSQL en prod
Redis : envisageable pour le cache leaderboard & sessions

Modèle de données

erDiagram
    User ||--o{ Debate : plays
    Debate ||--o{ Message : contains
    Debate ||--o| Analysis : has
    User ||--o{ UserAchievement : unlocks
Temps réel

Serveur WebSocket custom

graph LR
    jq["join_queue"] -->|match trouvé| matched["matched"]
    jq -->|annulation| cq["cancel_queue"]
    matched --> join["join"]
    join --> msg["message"]
    msg --> tc["turn_changed"]
    tc --> nm["new_message"]
    msg -->|inactif| to["timeout"]
    nm -->|fin| de["debate_ended"]
  

1. Broadcast avant écriture DB

Le message est diffusé aux clients avant la persistance en base. Priorité à la latence perçue sur la cohérence stricte, l'erreur DB est loggée sans bloquer l'UX

2. Heartbeat ping/pong 15s

Détection et terminaison des connexions zombies, évite l'accumulation de clients stales en mémoire et permet de détecter les déconnexions
Temps réel : Matchmaking

Trouver un adversaire & mettre à jour le rating

① Trouver un adversaire (file d'attente)

Casual

FIFO, Premier arrivé, premier servi · Aucun filtre de niveau
+15 victoire · −10 défaite

Classé : fenêtre ELO expansive

Fenêtre trop petite → ratings extrêmes jamais matchés (starvation)
Solution : élargir avec le temps d'attente

0 s → ±150  ·  10 s → ±200  ·  20 s → ±250  ·  … → max ±500

② Après la partie, mise à jour ELO (classé)

Formule Elo standard

E1 = 1 / (1 + 10(ELO2 − ELO1) / 400)
ELO1' = max(100, round(ELO1 + 32 × (score − E1)))
  • K = 32, volatilité standard
  • Floor à 100, pas de rating négatif
  • ELO initial : 1200
  • Battre un joueur plus fort → gain plus important
Intelligence Artificielle

5 Prompts spécialisés

Prompt Déclencheur Temp. Pourquoi cette température Sortie
Évaluation live Après chaque message 0.1 Déterminisme → scores cohérents move_quality, events[], score_delta
Adversaire IA Tour IA (entraînement) 0.9 / 0.7 / 0.4 Proxy de créativité vs rigueur selon le niveau Texte argumentatif libre
Analyse post-débat Fin de partie 0.3 Détection de sophismes stable et reproductible Scores, sophismes, biais, forces (JSON)
Conseil (hint) Bouton "Conseil" 0.5 Équilibre créativité / pertinence 1–2 phrases d'amélioration
Fact-checking À la demande 0.2 Fiabilité factuelle maximale Vrai / Faux / Partiel + explication
Intelligence Artificielle : Évaluation en direct

Arbitrage message par message

Règle prioritaire : wrong_side

Si un joueur défend la position de l'adversaire (intentionnellement ou non) :
move_quality = "blunder" · delta forcé négatif
quelle que soit la qualité rhétorique du message

Règle de portée (absolue)

Les événements ne concernent que le message courant, le context_history est fourni uniquement pour la cohérence conversationnelle, jamais jugé

Types d'événements détectés

claim
+4
evidence
+8
nuance
+5
counter_argument
+6
sophism
−6
ad_hominem
−8
irrelevance / repetition
−3
wrong_side
−10
  "move_quality": "brilliant|excellent|good|inaccuracy|mistake|blunder",
  "events": [ { type, severity, description } ],
  "score_update": { player, delta, new_score },
  "live_metrics": { clarity, logic, strategic_value }
Intelligence Artificielle : Adversaire

3 niveaux, 3 prompts distincts

Facile. temp. 0.9

  • Arguments peu structurés, parfois hors-sujet
  • Répète ses points sans les développer
  • Commet des sophismes volontairement
  • Registre familier et approximatif
  • 2–3 phrases max

Moyen, temp. 0.7

  • Structure thèse + justification brève
  • Répond à ce que l'adversaire a dit
  • Raccourcis logiques acceptés
  • Registre semi-formel
  • 3–4 phrases

Fort, temp. 0.4

  • Contre-argue directement le point précis soulevé
  • Structure : point → argument → exemple
  • Expose les failles logiques adverses
  • Ne répète jamais & rigueur absolue
  • 4–5 phrases denses
// Contexte injecté dans chaque appel (buildOpponentUserPrompt)
Affirmation : "{{ topic }}"
Ta position : {{ opponentPosition }}  ·  Position adverse : {{ userPosition }}
Historique du débat :
{{ historyText }}   // N derniers messages injectés pour la cohérence contextuelle
// Limite absolue : ~120 mots (MAX_AI_WORDS) · max_tokens = ceil(120 × 1.8)
Intelligence Artificielle : Analyse post-débat

Bilan complet après la partie

5 critères d'évaluation

Qualité des arguments
Style rhétorique
Cohérence logique
Fact-checking
Score global
74
Scores individuels : player1Score vs player2Score
Forces et faiblesses listées. Chaque sophisme avec contexte exact dans le débat

Sophismes & biais détectés

Ad Hominem Homme de paille Pente glissante Fausse dichotomie Appel à l'autorité Généralisation hâtive Biais de confirmation Hors-sujet Répétition

Format de retour

  • name : nom normalisé du sophisme
  • count : nombre d'occurrences dans le débat
  • context : citation exacte ou contexte

Biais cognitifs

Détectés séparément des sophismes logiques
ex. biais de confirmation, biais d'ancrage, biais de disponibilité, etc.
Fonctionnalités

Features avancées

Anti-triche (mode classé)

  • Détection via Visibility API du navigateur
  • Avertissement visuel à chaque changement d'onglet
  • ≥ 3 infractions → pénalité −30 ELO appliquée en post-débat

Système d'achievements

  • 24 succès déclaratifs + 1 méta "Grand Master"
  • 6 catégories : Premiers pas, Victoires, Séries, Qualité, Progression, Ultime
  • Barres de progression
  • Calcul on-the-fly depuis l'historique des débats

Hint & Fact-check in-game

  • Conseil : 1–2 phrases ciblées sur le prochain argument (temp. 0.5)
  • Fact-check : vrai / faux / partiel + explication (temp. 0.2)
  • Disponibles pendant le tour du joueur uniquement
  • Désactivés en mode classé pour équité

i18n FR / EN

  • UI entièrement bilingue via un système de traductions custom
  • Prompts IA disponibles dans les deux langues
  • Sujets de débat traduits (120+ FR & 120+ EN)
  • Locale détectée automatiquement et persistée
Infrastructure

Déploiement & CI/CD

Pipeline CI/CD
graph LR
    Dev["Dev"] -->|git push main| GH["GitHub"]
    GH -->|webhook| Coolify["Coolify"]
    Coolify -->|docker build + deploy| VPS["VPS · Docker"]
  
Chemin de la requête
graph LR
    User["Utilisateur"] -->|HTTPS| CF["Cloudflare Tunnel"]
    CF -->|masque IP · DDoS| Traefik["Traefik"]
    Traefik -->|reverse proxy + SSL| VPS2["VPS · Docker"]
  

Coolify

Self-hosted PaaS, CI/CD automatique sur chaque git push main via GitHub webhook

Cloudflare Tunnel

IP non exposée · DDoS · SSL · domaine sans port-forwarding

Traefik

Reverse proxy & nom de domaine

Base de donée

SQLite en dev, PostgreSQL prod
Synthèse

RHETORICA

Gameplay complet
3 modes · ELO · 120+ sujets · 24 achievements · replay
Temps réel
WebSocket custom · matchmaking dynamique · score live
IA au cœur
5 prompts · Groq <1s · 3 adversaires · sophismes
Next.js 15 + TypeScript WebSocket custom Groq · Llama 3.3 70B Prisma + PostgreSQL Coolify · Traefik · Cloudflare

Perspectives

Redis pour cache Fine-tuning sur débats réels Replay commenté par l'IA