localaiforum
Connexion
Guide › Travailler avec les modèles

RAG : donner vos données au modèle

Les connaissances d'un LLM ont gelé à sa date de coupure d'entraînement ; vos documents changent chaque semaine. La génération augmentée par récupération (RAG) les dégèle : au moment de la requête, rapatriez les tranches pertinentes de vos données, collez-les dans le prompt, et laissez le modèle répondre à partir de preuves avec citations. Le modèle fait le raisonnement ; l'index fait la mémoire.

La pipeline, un exemple détaillé

Prenez un manuel de maintenance de 12,000 jetons et parcourez-le de bout en bout :

  1. Fragmenter. Découpez d'abord sur les titres (les sections survivent intactes), puis redécoupez toute section au-delà de ~400 jetons, en gardant ~60 jetons de chevauchement (10–15%). Des tailles de fragment sensées vont de 200 à 800 jetons. Résultat : environ 35 fragments.
  2. Vectoriser. Chaque fragment devient un vecteur — bge-m3 émet 1,024 flottants par fragment. Sur CPU, cela prend des secondes pour tout le manuel.
  3. Indexer. Stockez vecteurs + texte + métadonnées (fichier source, chemin de titres) dans une base vectorielle.
  4. Récupérer. Vectorisez la question, rapatriez les k = 5 meilleurs fragments par similarité cosinus (4–10 est la fourchette saine).
  5. Générer. Assemblez le prompt : instructions, les 5 fragments avec étiquettes d'ID, la question.

Le découpage sur frontières sémantiques bat les fenêtres fixes. Un tableau séparé de sa ligne de titre, ou une procédure numérotée coupée à l'étape 4, est un déchet irrécupérable quel que soit le talent de l'embedder.

Deux extras qui se remboursent immédiatement : stockez le chemin de titres (manual › §4 › Bolts) en métadonnées pour filtrer et citer précisément, et vectorisez la requête exactement comme l'utilisateur l'a tapée — le « nettoyage » par prétraitement rend généralement la récupération pire, pas meilleure.

Embeddings : l'exigence multilingue

L'embedder décide ce que « similaire » signifie. Pour des corpus coréens et mixtes coréen/anglais, le choix est étroit — les embedders anglais uniquement échouent en silence sur les requêtes coréennes, renvoyant des fragments plausibles mais faux :

Modèle Dims Remarques
bge-m3 1024 multilingue, coréen solide — le défaut local sûr
nomic-embed-text-v1.5 768 solide en anglais, contexte long
all-MiniLM-L6-v2 384 rapide, centré anglais, faible en coréen

Les modèles d'embedding sont minuscules (0.1–0.6B paramètres) ; un cœur CPU mâche des milliers de fragments — inférence sur CPU. Vectorisez le corpus une fois, vectorisez les requêtes au moment de la question. Gardez l'embedder figé par index : changer de modèle en cours d'index fabrique des vecteurs d'espaces différents, et les scores de similarité deviennent du bruit.

Bases vectorielles

Base Quand
sqlite-vec utilisateur unique, jusqu'à ~1M vecteurs, zéro ops — c'est un fichier
ChromaDB prototypage local-first avec filtres de métadonnées
Qdrant production : filtrage, recherche hybride, échelle

Les trois tournent en local. Commencez avec sqlite-vec ; changez quand vous touchez vraiment son mur, pas avant.

Assembler le prompt

Réponds uniquement à partir des extraits ci-dessous. Cite chaque affirmation en [n].
Si les extraits ne contiennent pas la réponse, réponds « absent des docs ».

[1] (manuel §4.2) Serrer le boulon M8 à 12 Nm après lubrification...
[2] (manuel §7.1) Ne pas réutiliser les boulons étirés...

Question : quel couple s'applique au boulon M8 ?

Les citations laissent les utilisateurs vérifier au lieu de croire. La sortie « absent des docs » compte : sans elle, le modèle invente une réponse plutôt que de manquer le format.

Recherche hybride et reranking

La recherche vectorielle pure rate les chaînes exactes — codes d'erreur, noms de fonctions, références — que la recherche par mots-clés cloue. Hybride = BM25 + vecteurs, fusionnés par reciprocal rank fusion ; c'est quasi gratuit et régulièrement meilleur que chacun seul.

Pour la qualité, ajoutez un reranker : rapatriez 20–50 candidats, re-classez vers le top 5 avec un cross-encoder (la famille bge-reranker — même histoire multilingue que bge-m3). Le reranker lit fragment et requête ensemble, ce qu'un embedder mono-vecteur ne peut pas. Recette bon marché qui couvre la plupart des besoins : récupération hybride 25, rerank vers 5.

Diagnostiquer : échec de récupération ou de génération ?

Avant de régler quoi que ce soit, répondez à une question : le bon fragment a-t-il été récupéré ?

  1. Affichez les fragments top-k d'une requête échouée, avec scores.
  2. Si le fragment porteur de réponse n'est pas dans la liste, c'est un problème de récupération — corrigez le découpage, les embeddings, ou montez k.
  3. S'il y est mais que la réponse est fausse, c'est un problème de génération — plus gros modèle, meilleur prompt, rerank pour pousser dehors les distracteurs.

Ce seul test épargne des heures à régler la mauvaise moitié de la pipeline.

Modes de défaillance

Symptôme Cause Correctif
bons documents récupérés, mauvaise réponse distracteurs dans le top-k rerank ; réduire k
la réponse existe dans les documents mais ne surgit jamais famine du top-k ou mauvaises frontières monter k ; fragmenter par titres
réponses assurées mais périmées index plus vieux que les documents réindexer à chaque changement
les requêtes coréennes ratent les documents anglais embeddings monolingues bge-m3
les citations pointent le mauvais fragment le fragment a perdu ses métadonnées source stocker source + titre par fragment

Mesurez, ne vibrez pas

Gardez un jeu doré de 20–50 vraies questions à réponses connues. Scorez deux chiffres séparément : taux de réussite de récupération (le bon fragment est-il dans le top-k ?) et exactitude de la réponse. Suivez-les après chaque changement de taille de fragment, d'embedder ou de k. Le travail RAG sans jeu doré, c'est de l'astrologie.

Quand plutôt faire du fine-tuning

Le fine-tuning change le comportement — format, style, terminologie. C'est un mauvais moyen d'ajouter des faits : le modèle ne peut pas citer ce sur quoi il a été réglé, et des documents frais signifient un nouvel entraînement. Règle empirique : faits → RAG, comportement → LoRA, les deux seulement quand vous avez épuisé le prompting : fine-tuning.

Piles locales, de la plus petite d'abord

Open WebUI livre dépôt de documents et récupération hybride intégrés — commencez là et vous n'écrirez peut-être jamais de code de pipeline. Le cœur honnête du RAG fait ~50 lignes ; écrivez-le une fois pour savoir ce que font les frameworks :

chunks = chunk_by_header(read("manual.md"), max_tokens=400, overlap=60)
index = [(embed(c), c) for c in chunks]          # fait une fois

def answer(question):
    hits = topk_by_cosine(embed(question), index, k=5)
    ctx = "\n\n".join("[{}] {}".format(i, c) for i, (v, c) in enumerate(hits))
    prompt = TEMPLATE.replace("{ctx}", ctx).replace("{q}", question)
    return chat(prompt)                           # un petit modèle local suffit

Tendez-vous vers LlamaIndex ou LangChain quand vous avez besoin de permissions, de rafraîchissement planifié ou de harnais d'évaluation — pas pour la boucle ci-dessus. Pour les documents coréens spécifiquement, les embeddings multilingues (bge-m3) ne sont pas optionnels, et le découpage par titres devrait respecter la structure documentaire coréenne (titres 장/절) comme il respecte les titres markdown ##.

Voir aussi : fine-tuning · prompting · sortie structurée · inférence sur CPU