Contexte. Cet article documente l'architecture de ma première plateforme d'IA éducative, que j'ai conçue et opérée en production en 2025-2026. Le projet est aujourd'hui terminé ; j'ai depuis pivoté vers l'ODERSA et l'éducation gratuite. La démarche technique, elle, reste valable telle quelle.
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. Ce que j'ai construit n'était pas un wrapper : un orchestrateur qui contraint le LLM à questionner plus qu'il n'affirme.
Voici l'architecture réelle qui tournait en production. Pas un schéma théorique : du code, des choix concrets, des compromis.
Le problème en une ligne
Un LLM moderne résout sans forcer la grande majorité des exercices scolaires français, du collège à la terminale. Servi nu, il désengage l'élève en quelques 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 passait 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 était 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
});
Latence minime, coût marginal. Bénéfice : on peut refuser
immédiatement les requêtes solution sans les router vers le modèle
principal, ce qui économise l'essentiel du coût d'inférence.
Étage 2 : la génération contrainte
Cœur du système. Le LLM principal (Claude Sonnet) recevait un prompt combinant trois couches :
- Persona socratique : instruction comportementale stable
- Contexte pédagogique : niveau, matière, programme officiel
- RAG : extraits de la base de connaissances correspondant à la notion
const response = await claude.messages.create({
model: "claude-sonnet-4-5",
system: SOCRATIC_SYSTEM_PROMPT, // persona socratique, stable
messages: [
{ role: "user", content: contextEleve }, // niveau, matière
{ role: "user", content: ragSnippets }, // 3-5 chunks pertinents
{ role: "user", content: élève_input }
],
max_tokens: 800
});
Le SOCRATIC_SYSTEM_PROMPT contenait 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 ajoutait donc un validateur post-hoc vérifiant que la réponse n'était pas une solution finale déguisé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;
}
En production, ce validateur ne se déclenchait que rarement, mais chaque déclenchement évitait une réponse qui aurait trahi la promesse pédagogique. Coût marginal : un appel Haiku par message. C'est le prix de la cohérence.
Le RAG : une base de connaissances maison
Le LLM seul ne connaît pas le programme scolaire français en détail. Pour donner des indices contextualisés, il a besoin d'ancrage. C'était le rôle de la base de connaissances : un fonds d'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_documents", {
query_embedding: embedding,
match_threshold: SEUIL_SIMILARITE, // ajusté empiriquement
match_count: 5,
filter_niveau: élève.niveau,
filter_matiere: exercice.matiere
});
Les embeddings étaient stockés dans Supabase (PostgreSQL + extension
pgvector), en région AWS Paris. La recherche était quasi instantanée
et aucune donnée ne quittait l'Union européenne.
Choix techniques structurants
Pourquoi Claude (Anthropic) plutôt que GPT-4 ?
- Tenue du système prompt sur conversations longues : Claude dérive moins quand l'élève insiste pour la solution
- DPA + SCCs signés avec opt-out training par défaut, pour l'alignement RGPD
- Coût compétitif sur Sonnet pour des prompts pédagogiques courts
- API stable avec versioning explicite des modèles
Pourquoi Supabase + pgvector plutôt que Pinecone ?
- Souveraineté : Supabase héberge en AWS Paris, Pinecone aux États-Unis
- Une seule base pour les données utilisateurs et les vecteurs : moins de jointures, moins de complexité
- Coût :
pgvectorest inclus dans Supabase, Pinecone facture par index - RLS PostgreSQL couvre nativement la sécurité des accès vectoriels
Pourquoi un classifier Haiku séparé plutôt qu'un seul appel Sonnet ?
- Coût : les requêtes
solutionfiltrées en amont n'atteignent jamais le modèle cher, et c'est là que part l'essentiel du budget - Latence : Haiku répond presque instantanément, on peut afficher un état de chargement contextuel
- Robustesse : un échec de Sonnet ne se propage pas, Haiku a déjà donné l'intention
Limites & angles morts
Le système n'était pas magique. Trois faiblesses connues :
- L'élève déterminé restait capable de contourner le garde-fou en demandant des indices ultra-spécifiques jusqu'à reconstruire la solution. Pas grave : à ce stade, il avait déjà fait la moitié du travail cognitif.
- Les exercices très ouverts (dissertations, sujets de philo) tolèrent mal la classification en intentions discrètes. On désactivait le garde-fou pour ces cas.
- Les coûts d'embedding à très grande échelle n'ont jamais été confrontés au réel : le projet s'est arrêté avant.
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. Moins spectaculaire qu'un agent autonome, mais ça a tourné en production, dans le respect du RGPD, à coût maîtrisé. Et si la plateforme n'a pas survécu à son modèle économique, cette architecture, elle, reste bonne à prendre.