SPB Git forge

spb/immbot-ai

Public
1commits 1branches 0releases
1.5 MBsize
maindefault branch
20 days agolast push
TypeScript 98.3% CSS 0.9% Shell 0.7%
5.3 KB

# Architecture RAG — Immbot AI

# Vue d'ensemble

Corpus : les fichiers sources LaTeX des deux cours (diapositives beamer, plans de cours, ateliers, aide-mémoires, glossaire) — soit ~1 700 diapositives et ~40 documents. L'ingestion travaille sur la source (pas le PDF) : structure exacte, numéros de diapositives fiables, équations intactes.

text
Fichiers cours ──> Scanner ──> Parseur LaTeX ──> Chunker structurel ──> Fragments+métadonnées
                                                                          │
                                              SQLite : chunks + FTS5 + embeddings (blob 384d)
                                                                          │
Question ──> embedding local ──> [FTS5 BM25 ∥ cosinus] ──> fusion RRF ──> filtres/boost ──>
   expansion voisins ──> contexte numéroté [S1..Sn] ──> LLM ──> validation citations ──> réponse citée

# Espaces de connaissances (isolation)

official-imm1003 · official-imm1033 · student-temporary-upload (fichiers joints à une conversation, portée = cette conversation) · student-persistent-files (bibliothèque personnelle) · instructor-private (examens, blueprints, analyses — jamais servis aux étudiants) · general-knowledge (aucun fragment : étiquette pour les réponses hors RAG). Croisement inter-cours désactivé par défaut, activable par l'admin, toujours signalé dans la réponse.

# Fragmentation structurelle

Source Unité de fragment Métadonnées clés
Diapositives beamer 1 frame = 1 fragment (titre + contenu + boîtes sémantiques aplaties) ; fusion des frames de suite (1/2, 2/2) au même titre cours, séance, n° de diapositive, titre, section courante, type de boîte (définition/important/exemple/formule)
Plans de cours / articles LaTeX section/sous-section, redécoupée par paragraphes si > ~1 800 caractères, avec chevauchement d'une phrase cours, document, section, titre
Ateliers (énoncés/solutions) par exercice/question (\section, enumerate de premier niveau) cours, atelier, type (énoncé/solution)
Glossaire par entrée (terme FR/EN + définition) terme, cours
Markdown/TXT par titre #/## document, titre
Téléversements étudiants (PDF/DOCX/XLSX/CSV/images) extraction texte par page/feuille ; images passées telles quelles aux modèles vision conversation, page/feuille

Le texte LaTeX est détexifié pour l'indexation (macros sémantiques UQO converties en préfixes « Définition : », « Important : » ; équations conservées en notation $...$ pour l'affichage) tout en conservant la version affichable. Contexte minimal garanti : chaque fragment inclut cours + document + section pour rester compréhensible isolément.

# Embeddings

Xenova/multilingual-e5-small (384 d) exécuté localement via @huggingface/transformers (préfixes query: / passage: conformes à E5). Choix motivé : OpenRouter n'expose pas d'embeddings ; le modèle est multilingue (corpus français), léger (~120 Mo), et le corpus tient en mémoire (2-3k × 384 floats ≈ 4 Mo) → cosinus exact en < 10 ms, pas d'index ANN nécessaire.

# Recherche hybride

  1. Analyse de la requête : cours actif (jamais deviné : choisi dans l'interface), détection de séance/diapositive citée explicitement, type de question (définition/calcul/comparaison).
  2. Candidats : FTS5 bm25() (top 30, unicode61 + remove_diacritics) ∥ cosinus vectoriel (top 30).
  3. Fusion RRF (k=60) + boosts : correspondance exacte de terme du glossaire, fragments de type « définition » pour les questions de définition, tableaux pour les questions de données.
  4. Dédoublonnage par document/diapositive, expansion aux diapositives voisines (±1) quand le fragment gagnant est une suite (1/2 → 2/2).
  5. Budget de contexte : top 8-12 fragments équilibrés entre documents, plafonné en jetons.
  6. Contexte transmis : [S1] (IMM1003 — Séance 4 — Diapositive 18 — « Titre ») texte….

# Citations : contrat strict

  • Le modèle ne peut citer que les balises [Sx] du contexte fourni (politique dans prompts/citation-policy.md).
  • Post-traitement serveur : chaque [Sx] est résolu vers son fragment ; toute balise inconnue est retirée et comptée (invalid_citation_rate journalisé). Le client reçoit la liste résolue (document, séance, diapositive, extrait exact, contexte voisin) → panneau source cliquable.
  • Mode « Cours uniquement » sans contexte pertinent (scores sous seuil) → réponse de refus honnête standardisée, sans appel « créatif ».

# Ingestion incrémentale

Somme de contrôle SHA-256 par fichier ; réingestion seulement si modifiée ; suppression des fragments orphelins ; exécutions journalisées dans ingestion_runs (fichiers, fragments, erreurs, durée) ; rapport lisible dans docs/ingestion-report.md et l'admin.

# Évaluation

evaluation/imm1003-test-set.json et imm1033-test-set.json : questions dorées avec documents attendus. Script scripts/verify.sh → evaluation/rag-evaluation.md : rappel@k des sources, taux de citations valides, taux de refus corrects (questions hors corpus), isolation inter-cours (les questions IMM1033 ne doivent pas remonter de fragments IMM1003 quand le croisement est off).