Et si préparer un cours, c'était poser une question, et obtenir les bons passages des bons programmes, sources à l'appui, en quelques secondes ?

C'est le pari de wag.titcheur.fr, un moteur de recherche sémantique sur les référentiels et programmes de l'enseignement secondaire français : collège, lycée général et technologique, voie professionnelle.

Le problème

Les programmes officiels sont publics, mais éparpillés dans 5 680 documents PDF, soit 4,9 Go de texte, des milliers de pages, des dizaines de disciplines. Trouver « ce que dit le programme de maths de 4e sur le théorème de Pythagore » relève de l'exploit. Les moteurs classiques cherchent des mots ; ils ne comprennent pas les questions.

L'idée en 30 secondes

Au lieu de chercher des mots-clés, l'outil comprend le sens.

Chaque document est découpé en briques de ~700 caractères (avec chevauchement). Un modèle d'IA bilingue français/anglais (bilingual-embedding-large, 559 M de paramètres) transforme chacune de ces briques en un vecteur de 1024 nombres, une sorte de coordonnée dans un atlas sémantique : deux textes qui parlent de la même chose atterrissent au même endroit, même formulés différemment.

Votre question subit la même transformation. Le système cherche alors ses voisines les plus proches parmi 234 893 briques indexées : la recherche devient un simple calcul de distances. Résultat : une requête en français naturel, avec ou sans faute de frappe, renvoie les passages officiels pertinents, triés par proximité de sens, avec un lien direct vers le PDF source.

Et avec « Élaborer », un second modèle rédige une réponse structurée uniquement à partir des extraits retrouvés, sources citées entre parenthèses.

Choisir le bon cerveau par benchmark

Un moteur sémantique vaut ce que vaut son modèle d'embeddings. Plutôt que de suivre une tendance ou un blog-post, j'ai mesuré.

J'ai construit un harnais de benchmark isolé (500 documents, 22 393 briques, 30 questions réécrites à la main, 8 questions typées par discipline) et opposé trois modèles :

Modèle Rappel à 10 Pureté disciplinaire Latence
bilingual-embedding-large (fr/en, 559M) 0,867 0,900 56 ms
multilingual-e5-base (278M, l'ancien) 0,800 0,825 33 ms
gte-multilingual-base (305M) 0,767 0,862 40 ms

Benchmark des modèles d'embeddings : rappel@10, pureté disciplinaire et latence (500 documents, 22 393 briques)

Verdict : le modèle bilingue franco-anglais bilingual-embedding-large gagne de 7,5 points de pureté disciplinaire et de 6,7 points de rappel. Moralité : sur un corpus français, un modèle généraliste multilingue est battu par un modèle entraîné pour le français. La bascule est faite. Mais changer de modèle, c'est changer de dimension de vecteurs : la base entière devient illisible. L'histoire de cette migration mérite son chapitre.

La bifurcation : 234 000 briques calculées en un soir sur un cluster "jetable"

Changer de modèle = changer la géométrie des vecteurs (768 → 1024 dimensions). La base existante est incompatible : pas de delta possible, tout doit être ré-encodé : 234 000 briques.

Première estimation sur mon laptop (Apple Silicon M2/16) : ~2 s/document au début… puis 9 à 10 s/document sur les gros PDF. Extrapolation : une nuit entière, la machine immobilisée.

Le pivot : un cluster k3s existant sur Azure : 4 nœuds, 32 vCPU, sans GPU, un free-tiers qui servait déjà à d'autres expérimentations. Zéro infrastructure à créer. La recette, en ~1 heure :

  1. Exporter l'état, pas repartir de zéro : 5 Go de PDF + la base SQLite + l'index partiel déjà calculé (~900 Mo) transférés sur le cluster. Le job est resumable : il reprend exactement où il s'est arrêté ; les ~2 h de calcul du laptop n'ont pas été perdues.
  2. Sharder le corpus en 4 plages avec chevauchement, une par nœud, indexation en parallèle.
  3. Fusionner les shards par upsert (les chevauchements se dédupliquent tout seuls), transférer l'index final, basculer atomiquement avec rollback immédiat possible.

Résultats :

  • 234 893 briques ré-encodées en ~4 h de calcul réparti, contre une nuit sur une machine unique
  • Coût total : moins de 10 $, facturation à la minute, les 4 VM déallouées sitôt le job fini
  • Production servie sans interruption pendant tout le calcul ; la recherche publique est restée en ligne avec l'ancien index jusqu'à la bascule
  • Trois pièges ChromaDB neutralisés au passage (pagination des lectures, limite de 5 461 entrées par insertion, atomicité par document), corrigés et versionnés

La leçon que je retiens : un job batch bien conçu (état portable, reprise, sharding, merge) est une donnée, pas un process. On le déplace d'un laptop à un cluster en une heure, sans perdre une brique, sans payer une infra dédiée qui dormirait 99 % du temps.

Les stacks

  • Python / FastAPI pour l'API, interface web épurée en pur JS
  • ChromaDB (HNSW, espace cosinus) pour la base vectorielle
  • Sentence-Transformers pour les embeddings (bilingual-embedding-large, 1024 dimensions), avec un détail qui compte : les préfixes passage: / query: exigés par le modèle, sans lesquels la qualité s'effondre
  • DeepSeek en API pour la synthèse ; la recherche, elle, est 100 % locale et instantanée (< 1 s)
  • Docker non-root (cap_drop: ALL), nginx + Let's Encrypt, CI/CD GitLab avec SAST et secret detection, et un cycle nocturne à 3 h du matin qui recrawle les sources, ré-indexe les nouveautés et fait une rotation de la base sans effacer l'ancienne
  • k3s sur Azure (4 nœuds, 32 vCPU, sans GPU) pour les jobs batch jetables : réindexation complète en un soir, VM déallouées sitôt le job terminé

Ce que le chantier m'a appris (la partie qui intéresse les ingénieurs)

  1. Le post-filtrage d'une base vectorielle est un piège : filtrer après la recherche ANN tronque les résultats de façon invisible. J'y ai laissé une journée, tests comparatifs à l'appui.
  2. Un moteur public coûte de l'argent par requête dès qu'un LLM entre dans la boucle : rate-limiting par IP (6 req/min) + bannissement automatique (fail2ban), sinon c'est la facture qui trinque.
  3. La sécurité d'un service exposé se gagne par petits gestes : échappement XSS des métadonnées, CORS restreint, clés SSH uniquement, pare-feu minimal, et un journal d'auth qui montrait déjà des tentatives de brute-force.
  4. Les données passent avant le code : rotation staging/live de la base vectorielle, rollback automatique si le nouveau jeu d'index ne répond pas, jamais d'écrasement à chaud.
  5. Benchmarker sur ses propres données : les classements publics (MTEB) ne disent pas tout. Sur 500 docs réels, l'écart entre deux modèles crédibles s'est avéré de 7 points. Ce sont ces points qui font la différence entre un outil utile et un jouet.

Le tout est observable en direct sur la page : un micro-graphique montre l'évolution incrémentale de l'index, nuit après nuit.

Pourquoi c'est utile

Pour un enseignant : « probabilités conditionnelles en première » → les contenus, capacités attendues et démonstrations du programme, extraits exacts et PDF d'origine. Pour un formateur, un inspecteur, un parent curieux : la même porte d'entrée vers ce que l'institution dit vraiment.

Et ce n'est qu'un début : le moteur est conçu pour avaler d'autres corpus, premier degré, supérieur, textes réglementaires…


Essayez-le : https://wag.titcheur.fr, c'est gratuit, sans compte, et ça répond en français.

Question, retour, idée de corpus ? wag@titcheur.fr


Projet développé pour 199A Consulting : ingestion, indexation, durcissement et automatisation de bout en bout.

RAG #SemanticSearch #EdTech #IA #Python #ChromaDB #FastAPI #GitLabCI #Benchmark #Kubernetes #Cloud #InnovationPédagogique #TechForEducation