CI4 : Modèles de langage
Dans ce TP, vous allez implémenter une pipeline Retrieval-Augmented Generation (RAG) en local, en combinant un modèle de langage (LLM) et une base de connaissances construite à partir de documents. L’objectif est de produire un assistant capable de répondre à des questions en s’appuyant sur des sources explicites, afin d’améliorer la fiabilité et la traçabilité des réponses. Le corpus utilisé mélange deux types de documents réalistes : des emails (communications et consignes) et des PDF administratifs (règlements/procédures).
Vous utiliserez Ollama pour exécuter un modèle localement (sur votre machine) ou via le cluster mis à disposition (accès GPU). Le stockage vectoriel sera géré par Chroma, et l’ensemble de l’index sera persistant pour éviter de recalculer les embeddings à chaque exécution. Le TP est modérément guidé : une grande partie du code est fournie, mais vous devrez compléter des sections marquées par _______. Un rapport court, pragmatique et orienté ingénierie sera rendu sous forme de fichier Markdown.
- Construire un index vectoriel (Chroma) à partir d’un corpus multi-sources (emails + PDFs).
- Mettre en place un pipeline RAG complet : retrieval top-k → construction de contexte → génération avec Ollama.
- Forcer des réponses groundées : citations obligatoires et politique d’abstention si le contexte est insuffisant.
- Concevoir un mini jeu de test d’évaluation (questions) et produire une analyse simple de performance (retrieval + qualité des réponses).
- Rédiger un rapport court et exploitable (captures d’écran, commandes, extraits de résultats, réflexion).
Démarrage d'Ollama (local ou cluster)
Vous avez deux options pour faire tourner le LLM via Ollama :
- Option A — Sur votre machine (facile, mais plus lent suivant le modèle)
- Option B — Sur le cluster (utile si vous avez besoin d’un GPU ou si votre machine est trop lente)
Si Ollama est déjà installé, vous pouvez passer à la question suivante.
Méthode simple (recommandée) : installation native via le site Ollama https://docs.ollama.com/quickstart, puis lancement du service.
Méthode alternative : installation via Docker (si vous préférez containeriser). Voir l'article de blog https://ollama.com/blog/ollama-is-now-available-as-an-official-docker-image
Modifiez votre fichier ~/.ssh/config pour pouvoir vous connecter aux nœuds GPU via ProxyJump.
Copiez/collez le bloc fourni dans l’énoncé (node1-tsp … node4-tsp).
Depuis le cluster, faites une réservation interactive (exemple ci-dessous). Une fois réservé, identifiez le nom du nœud (via hostname).
Attention : chaque étudiant doit utiliser un port différent.
Choisissez un port (ex: 11435) et remplacez le port dans la commande ci-dessous. Si vous avez un message d'erreur, prenez un autre port (et notez-le).
Sur votre machine, créez un tunnel SSH vers le port Ollama de votre nœud (exemple avec node1-tsp pour le node 1).
Ensuite, testez l’accès à http://127.0.0.1:PORT.
Choisissez un modèle (ici)adapté au français. Ne prenez pas un modèle trop grand (8b max).
Remplissez MODEL_NAME puis lancez la commande ollama pull et un premier test.
- mistral
- llama3.1:8b (ou variante plus petite si besoin)
- qwen2.5:7b
- qwen3:8b
Faites une capture d’écran montrant :
- le résultat de curl http://127.0.0.1:PORT
- le résultat de ollama run MODEL_NAME ...
- le port choisi (et si cluster : la commande SSH tunnel)
Constituer le dataset (PDF administratifs + emails IMAP) et installer les dépendances
À la racine du dépôt, tout votre travail doit être dans le dossier TP4/.
Créez l’arborescence suivante :
- TP4/data/admin_pdfs/ (vos PDF administratifs)
- TP4/data/emails/ (emails exportés en fichiers texte)
- TP4/data/cache/ (cache SQLite / logs)
Pour les PDF, utilisez les fichiers trouvés sur ecampus contenant divers règlements (ici. Placez-les dans TP4/data/admin_pdfs/. Par exemple, le règlement de la FISE et le règlement intérieur.
L’objectif est d’avoir un petit corpus réaliste, sans fichiers inutiles.
Installez explicitement toutes les dépendances utiles (LangChain + Chroma + PDF).
Vous pouvez installer dans un venv ou un environnement conda.
- langchain, langchain-community : loaders, splitters, chaînes
- langchain-ollama : intégration Ollama côté LangChain (Chat + Embeddings)
- chromadb + langchain-chroma : base vectorielle locale + wrapper LangChain
- pypdf : extraction texte PDF
- tqdm : barre de progression (confort)
Vous allez utiliser un petit programme Python fourni ci-dessous.
Il :
- se connecte en IMAP SSL sur host = "z.imt.fr", port = 993
- demande votre email, puis votre mot de passe (input cachée)
- télécharge tous les emails après une date donnée (par défaut : ~30 jours)
- sauvegarde chaque email dans un fichier .md facile à indexer ensuite
- utilise un cache SQLite pour éviter de retélécharger les mêmes emails
Enregistrez le script ci-dessus dans TP4/download_emails_imap.py, puis exécutez-le.
Vérifiez que des fichiers .md sont bien créés dans TP4/data/emails/.
Faites une capture d’écran montrant :
- la commande d’exécution du script
- le nombre de fichiers créés dans TP4/data/emails/
- le contenu d’un email (début du fichier) avec head
Indexation : charger PDFs + emails, chunker, créer l’index Chroma (persistant)
Vous allez écrire un script TP4/build_index.py qui :
- charge les emails (TP4/data/emails/) et les PDFs (TP4/data/admin_pdfs/)
- ajoute des métadonnées minimales (doc_type, source)
- découpe en chunks (chunking)
- calcule les embeddings et crée un index Chroma persistant
- le réutiliser tel quel,
- ou le supprimer/reconstruire (au choix, mais documentez votre choix dans le rapport).
Dans le code ci-dessous, complétez les valeurs marquées _______ (chunking, k, modèle embeddings).
Copiez le code dans TP4/build_index.py, complétez les trous, puis exécutez le script.
Vérifiez que le dossier TP4/chroma_db/ contient bien des fichiers et n’est pas vide.
Faites une capture d’écran montrant :
- la sortie console de python TP4/build_index.py (nb docs + nb chunks)
- un ls -la TP4/chroma_db prouvant que l’index est créé
Retrieval : tester la recherche top-k (sans LLM) et diagnostiquer la qualité
Vous allez écrire TP4/test_retrieval.py qui :
- charge l’index Chroma persistant (TP4/chroma_db/)
- interroge le retriever avec une question
- affiche les top-k chunks retournés (source + extrait)
Remplissez les trous _______ : modèle d’embedding (le même que pour l’index) et valeur de k.
Testez au minimum les 2 questions suivantes :
- Quels sont les sujets de PFE supplémentaires proposés par Luca Benedetto ?
- Comment valider une UE ?
Pour chaque question, regardez :
- Est-ce que les 1–3 premiers chunks contiennent déjà la réponse ?
- Est-ce que les chunks sont redondants (même source répétée) ?
- Est-ce que le type de document semble logique (emails vs PDF) ?
- diminuez TOP_K (ex: 3 → 5 est souvent suffisant)
- diminuez CHUNK_SIZE si les chunks sont trop longs (ex: 800 → 500)
- augmentez TOP_K (ex: 3 → 6)
- augmentez légèrement l’overlap
Faites une capture d’écran montrant :
- la commande exécutée (au moins une question)
- les 3 premiers résultats (sources + extraits)
- votre valeur de TOP_K
RAG complet : génération avec Ollama + citations obligatoires
Vous allez écrire TP4/rag_answer.py qui :
- récupère les top-k chunks pertinents
- construit un contexte numéroté (avec doc_id)
- appelle un LLM via Ollama
- retourne une réponse en français avec des citations
Remplissez les trous _______ : modèle LLM, modèle embeddings, k, et éventuellement le prompt.
Vérifiez que :
- la réponse est en français
- les citations [doc_i] apparaissent
- si la preuve manque, le modèle répond Information insuffisante.
Posez une question qui ne peut pas être répondue par vos emails/PDF.
Vérifiez que le système n’hallucine pas et déclenche bien l’abstention.
- rendez le prompt plus strict (abstention obligatoire)
- diminuez TOP_K si le contexte est trop bruité
- réduisez CHUNK_SIZE pour des preuves plus ciblées
Faites une capture d’écran montrant :
- une exécution complète de TP4/rag_answer.py
- la réponse générée avec citations
- la liste des sources récupérées affichée à la fin
Évaluation : créer un mini dataset de questions + mesurer Recall@k + analyse d’erreurs
Vous allez :
- créer un mini dataset de questions réalistes (10 à 15)
- mesurer une métrique retrieval-only (Recall@k sur le type de document)
- évaluer qualitativement quelques réponses générées
- analyser 2 cas d’échec et proposer une amélioration
Créez le fichier TP4/eval/questions.json avec 10 à 15 questions.
Pour chaque question, indiquez le type de source attendu :
- email si la réponse est plutôt dans les mails
- admin_pdf si la réponse est plutôt dans les règlements PDF
- questions sur des PFE, deadlines, consignes (souvent emails)
- questions de règles officielles (souvent PDF)
- questions ambiguës ou difficiles (intéressantes pour l’analyse d’erreurs)
Écrivez TP4/eval_recall.py qui :
- lit TP4/eval/questions.json
- fait un retrieval top-k pour chaque question
- vérifie si au moins un chunk dans top-k a le doc_type attendu
- calcule un score global (Recall@k proxy)
Remplissez les trous _______ : modèle embeddings (le même), valeur de k.
Lancez le script et observez les erreurs. Un score parfait n’est pas nécessaire, l’important est de savoir expliquer pourquoi cela échoue.
Choisissez 3 questions de votre dataset et générez une réponse via TP4/rag_answer.py.
Pour chacune, donnez un score :
- 2 : correct + sourcé + actionnable
- 1 : partiellement correct / incomplet / citations faibles
- 0 : faux, halluciné, ou hors sujet
Choisissez 2 cas où le résultat est mauvais (retrieval ou génération) et analysez :
- cause probable : retrieval miss / chunks trop longs / bruit / prompt trop faible
- correction proposée : modifier TOP_K, chunking, prompt, filtre, etc.
Faites une capture d’écran montrant :
- votre fichier questions.json (un extrait)
- la sortie de python TP4/eval_recall.py avec le score final
- au moins une exécution de TP4/rag_answer.py sur une question de votre dataset
Ajoutez un paragraphe final très court (5–8 lignes max) :
- ce qui a bien marché
- la principale limite rencontrée
- une amélioration prioritaire si vous deviez le déployer