Architecture d'un tuteur IA socratique : LLM + RAG + garde-fous pédagogiques

Comment construire un assistant IA qui guide les élèves vers la réponse au lieu de la leur donner. Le système nerveux d'ORBITECH AI ACADEMY, déballé.

Le wrapper ChatGPT est le McDonald's de l'EdTech : rapide, accessible, et pédagogiquement vide. Donner à un élève un assistant qui crache la solution finale au premier prompt, c'est court-circuiter exactement le mécanisme cognitif qu'on prétend renforcer. ORBITECH n'est pas un wrapper. C'est un orchestrateur qui contraint le LLM à questionner plus qu'il n'affirme.

Cet article décrit l'architecture technique réelle qui tourne en production sur ORBITECH AI ACADEMY. Pas un schéma théorique : du code, des choix concrets, des compromis.

Le problème en une ligne

Un LLM moderne (Claude Sonnet, GPT-4, Gemini) peut résoudre 95 % des exercices scolaires français du collège à la terminale. Servi nu, il désengage l'élève en moins de 30 secondes : copier-coller énoncé, copier-coller réponse, devoir rendu. À la fin du trimestre, le bulletin est correct et la tête est vide.

Le défi technique : forcer un modèle entraîné pour répondre à se comporter comme un professeur qui questionne. Un changement de polarité comportementale qui ne tient ni dans un seul prompt, ni dans un seul appel API.

Architecture haute-niveau

Trois étages successifs. Chaque message élève passe par les trois avant qu'une réponse ne soit affichée :

# Étage 1 — Classification d'intention
élève → [Classifier] → { veut_solution | veut_indice | vérifie_calcul | autre }

# Étage 2 — Génération contrainte par persona
intention → [Prompt engineering] → [LLM Claude Sonnet] → réponse_brute

# Étage 3 — Garde-fou de complétude
réponse_brute → [Validateur] → réponse_finale OU régénération forcée

Étage 1 : la classification d'intention

Avant tout traitement coûteux, on classe le message. Pourquoi ? Parce qu'un élève qui demande « vérifie mon calcul » ne mérite pas le même traitement qu'un élève qui demande « quelle est la réponse ». La classification est faite par un modèle léger (Claude Haiku) avec un prompt très court :

const intent = await classify({
  model: "claude-haiku-4-5",
  system: "Classe l'intention en UN mot parmi: solution|indice|vérification|hors_sujet",
  message: élève_input
});

Coût : ~0,001 € par message. Latence : ~200 ms. Bénéfice : on peut refuser immédiatement les requêtes solution sans les router vers le modèle principal, économisant 100 % du coût de l'inférence.

Étage 2 : la génération contrainte

Cœur du système. Le LLM principal (Claude Sonnet) reçoit un prompt qui combine trois couches :

const response = await claude.messages.create({
  model: "claude-sonnet-4-6",
  system: SOCRATIC_SYSTEM_PROMPT,         // 380 tokens, stable
  messages: [
    { role: "user", content: contextEleve },     // niveau, matière
    { role: "user", content: ragSnippets },      // 3-5 chunks Alterra
    { role: "user", content: élève_input }
  ],
  max_tokens: 800
});

Le SOCRATIC_SYSTEM_PROMPT contient les règles dures : ne jamais donner la solution finale en premier ; toujours commencer par une question ; identifier ce que l'élève sait déjà ; proposer un pas, pas un saut.

Étage 3 : le garde-fou de complétude

Même avec un système prompt béton, Claude lâche occasionnellement la réponse complète — surtout si l'élève insiste à coups de « mais donne-moi juste le résultat ». On ajoute donc un validateur post-hoc qui vérifie que la réponse n'est pas une solution finale dégrisée.

async function validateSocratic(response, exercise) {
  const verdict = await classify({
    model: "claude-haiku-4-5",
    system: "La réponse contient-elle la solution complète? Réponds: oui|non",
    message: `Exercice: ${exercise}\nRéponse IA: ${response}`
  });

  if (verdict === "oui") {
    return regenerate({ stricter: true });
  }
  return response;
}

Sur 1 000 messages traités en production, le validateur force une régénération dans environ 4 % des cas. Coût marginal : un appel Haiku par message. Bénéfice : la promesse pédagogique tient à 99 %.

Le RAG : Alterra comme base de connaissances

Le LLM seul ne connaît pas le programme scolaire français en détail. Pour qu'il donne des indices contextualisés, il a besoin d'ancrage. C'est le rôle de la base de connaissances Alterra : 4 300+ articles structurés par programme officiel, indexés vectoriellement, requêtables par embedding.

// À chaque message élève, on récupère les 3-5 chunks les plus pertinents
const embedding = await embedQuery(élève_input);
const chunks = await supabase.rpc("match_alterra", {
  query_embedding: embedding,
  match_threshold: 0.78,
  match_count: 5,
  filter_niveau: élève.niveau,
  filter_matiere: exercice.matiere
});

Les embeddings sont stockés dans Supabase (PostgreSQL + extension pgvector), en région AWS Paris. Recherche < 50 ms en moyenne. Aucun data ne quitte l'UE.

Choix techniques structurants

Pourquoi Claude (Anthropic) plutôt que GPT-4 ?

Pourquoi Supabase + pgvector plutôt que Pinecone ?

Pourquoi un classifier Haiku séparé plutôt qu'un seul appel Sonnet ?

Métriques de production

Métrique Valeur
Latence p50 message complet1,2 s
Latence p953,1 s
Coût moyen par message~0,008 €
Taux de régénération validator~4 %
Taux de classification correcte~96 %
Coût mensuel pipeline complet~85 € pour 10 k messages

Limites & angles morts

Le système n'est pas magique. Trois faiblesses connues :

Conclusion

Construire un tuteur socratique avec un LLM, c'est moins un problème de modèle qu'un problème de contraintes. Le modèle est puissant — trop puissant pour la pédagogie. Le travail d'architecte consiste à brider intelligemment cette puissance pour qu'elle serve l'apprentissage au lieu de le court-circuiter.

Trois étages, deux modèles, une base vectorielle, un système prompt béton. C'est moins spectaculaire qu'un agent autonome — mais ça marche en production, ça respecte le RGPD, et ça coûte 8 millicentimes par message.

Article suivant
IPv6, fail-fast et hardening : retour sur un ban Google
À lire aussi
LBO : le guide complet du Leveraged Buyout