Une équipe a gaspillé six mois à régler les paramètres de son système de Recherche documentaire par IA. Sans résultat. Le vrai bug était ailleurs. Explications.
LE PIÈGE DANS LEQUEL TOUT LE MONDE TOMBE
Une équipe dédiée a passé six mois à peaufiner son pipeline RAG (Retrieval-Augmented Generation, en français : génération augmentée par récupération). Résultat : la précision en production n’a jamais bougé. Les tests pilotes continuaient de produire les mêmes erreurs. Au bout de six mois, le problème venait du parseur, ce petit programme qui décortique les documents avant de les analyser. RAG n’est pas du machine learning, et la boîte à outils du ML résout le mauvais problème. C’est l’erreur la plus coûteuse dans les projets RAG en entreprise aujourd’hui. Elle fait perdre des mois de travail, mobilise les mauvaises personnes sur les mauvaises tâches, et érode discrètement la confiance dans le système.
POURQUOI RAG N’EST PAS DU MACHINE LEARNING
Le machine learning (apprentissage automatique) résout des problèmes où la réponse vraie est inconnue et doit être prédite. Par exemple : ce client va-t-il résilier son abonnement ? Quelle est la probabilité que cette transaction soit frauduleuse ? Ce chat est-il un siamois ? On ne connaît pas la réponse à l’avance. C’est pour cela qu’on entraîne un modèle. Le modèle apprend à partir d’exemples étiquetés, généralise à de nouvelles données, et produit une prédiction. La performance se mesure de manière globale, sur des milliers de cas de test, car des prédictions individuelles peuvent être fausses tout en restant utiles globalement.
Le RAG résout un problème différent. La réponse à la question « Quelle est la date d’effet de ce contrat ? » existe, écrite en page 1 du document, ou n’existe pas du tout. Il n’y a rien à prédire. Le système doit soit trouver la réponse dans le document et la restituer fidèlement, soit échouer et l’indiquer clairement. La performance est binaire au niveau de chaque question (réussite ou échec), même si on mesure des taux globaux sur de nombreuses questions.
Cette différence change complètement les outils à utiliser quand quelque chose ne fonctionne pas. Les cas recensés dans l’Article 2 tombent exactement ici : négations, identifiants exacts, acronymes internes, dilution du signal dans un contexte long, proximité thématique qui surpasse la réponse réelle. Aucun de ces problèmes ne se résout en changeant de modèle d’embeddings ou en ajustant la taille des chunks. Ce ne sont pas des bugs qu’un modèle peut apprendre à corriger, car il n’existe aucun signal étiqueté disant « voici la bonne ligne » sur lequel le modèle pourrait s’entraîner. La solution est structurelle (analyse des questions, mots-clés experts, récupération qui connaît la structure du document), et les sections suivantes expliquent pourquoi trois réflexes du ML échouent ici.
TROIS OUTILS DU ML QUI S’APPLIQUENT À TORT AU RAG
Trois méthodes du ML sont systématiquement importées dans les projets RAG : l’optimisation d’hyperparamètres, les jeux de données d’évaluation avec séparations train/test, et les cadres d’explicabilité par attribution de caractéristiques. Chacune est raisonnable en soi dans le contexte du ML. Chacune échoue ici.
La méthode la plus courante ressemble à ceci : taille des chunks, chevauchement, top_k, seuil de similarité. Ce sont des hyperparamètres, et il faut les optimiser comme on optimise des modèles de ML, avec des outils comme Optuna ou Ray Tune. On lance un balayage, on trace les courbes, on choisit la meilleure configuration.
Dans ces configurations, topk est le nombre de passages que le récupérateur conserve, et similaritythreshold est le score de similarité minimale qu’un passage doit atteindre pour être retenu. Le code ci-dessous déclare ces quatre paramètres comme des nombres à optimiser :
# Ce que les équipes écrivent généralement (et pourquoi c'est une mauvaise idée)
import optuna
def objective(trial):
chunksize = trial.suggestint("chunk_size", 100, 2000)
chunkoverlap = trial.suggestint("chunk_overlap", 0, 200)
topk = trial.suggestint("top_k", 1, 20)
threshold = trial.suggest_float("threshold", 0.5, 0.95)
accuracy = runragpipelineandscore(
chunksize, chunkoverlap, top_k, threshold
)
return accuracy
study = optuna.create_study(direction="maximize")
study.optimize(objective, n_trials=200) # deux semaines de calcul plus tard.
Il y a un grain de vérité ici. Ces variables influencent effectivement la qualité de la récupération, et il est utile de les ajuster. Le problème commence avec le mot « hyperparamètre », qui apporte avec lui des métaphores aux hypothèses cachées.
En machine learning, un hyperparamètre contrôle la manière dont un modèle apprend : taux d’apprentissage, force de régularisation, nombre de couches. Le modèle lui-même est ce qui change pendant l’entraînement ; l’hyperparamètre façonne ce changement. Dans le RAG, il n’y a pas d’apprentissage. La taille des chunks ne contrôle pas comment quelque chose apprend. Elle contrôle comment une fonction découpe le texte, toujours de la même manière, quel que soit ce qu’on lui a donné auparavant.
Ce qui ressemble à un hyperparamètre est un choix de configuration, le genre de décision qu’on prendrait pour configurer un moteur de recherche. L’expertise nécessaire pour bien l’ajuster n’est pas l’optimisation statistique. C’est la compréhension de la structure de vos documents et de la forme de vos questions. Une taille de chunk de 512 tokens peut fonctionner parfaitement sur des articles académiques denses et échouer lamentablement sur des contrats d’assurance où une seule clause s’étend sur 800 tokens et où la diviser en deux fait perdre le conditionnel qui donne à la clause son sens. Aucune recherche en grille ne vous le dira. Il faut lire vos documents.
Le piège classique : une équipe lance Optuna sur chunksize, topk et similaritythreshold pendant deux semaines, et aboutit à chunksize=487 sans savoir pourquoi. La réponse honnête à « pourquoi 487 ? » est « parce qu’Optuna l’a dit ». Cette réponse ne survit pas à un vrai échec en production, et ne se généralise pas quand la distribution des documents change. Une taille de chunk de 500 choisie parce que c’est à peu près la taille d’un paragraphe dans ce corpus est plus défendable que 487 choisi parce qu’un balayage l’a déterminé.
L’activité qui convient n’est pas d’ajuster des nombres. C’est de décider structurellement comment découper les documents. Par section ? Par paragraphe ? Par les entrées de la table des matières ? Par type de question, avec des découpeurs différents pour les recherches ponctuelles courtes versus les clauses longues ? Ces décisions se prennent en regardant les documents et les questions, pas en traçant des courbes d’optimisation.
Il y a une raison plus profonde pour laquelle la taille des chunks résiste à l’optimisation : par construction, aucune taille de chunk unique ne peut servir à toutes les questions. Prenons deux questions sur le même contrat d’assurance :
- « Quelle est la date d’effet ? »
- « Quelles sont les exclusions ? »
Il n’existe aucun nombre qui satisfasse les deux. Une taille de chunk de 200 tokens découpe la section des exclusions en fragments incohérents. Une taille de chunk de 2000 tokens enterre la date d’effet dans un bruit environnant.
Essayer de trouver « la meilleure taille de chunk » n’est donc pas un problème d’optimisation. Le cadre lui-même est brisé : aucun nombre unique ne peut servir une distribution de questions dont les réponses ont des longueurs différentes.
On pourrait, en principe, faire en sorte que la taille des chunks réponde à la question en entraînant un petit modèle qui prédit le bon découpeur à partir des caractéristiques de la question : classer l’intention, régresser sur la longueur attendue de la réponse, sortir une stratégie. Ce serait du machine learning appliqué légitimement à un problème où quelque chose est appris.
Mais ce n’est pas nécessaire. On peut écrire la règle. En regardant une question, on peut dire si elle demande une date, une section, ou une comparaison. Un expert du domaine peut aussi le faire. Dix lignes de Python avec des conditions écrites à la main sur des mots-clés suffisent. La raison profonde pour laquelle le RAG n’est pas du machine learning est que, pour la plupart des décisions à l’intérieur du système, vous connaissez déjà la réponse, ou quelqu’un dans votre équipe la connaît. Le machine learning est l’outil pour les problèmes où personne ne connaît la réponse à l’avance.
La bonne approche est d’arrêter de chercher une taille de chunk unique et de commencer à router différents types de questions vers différentes stratégies de récupération :
# Ce qu'il faut faire à la place : router par type de question
def chunkforquestion(question: str, linedf, tocdf):
intent = classify_intent(question)
if intent == "point_lookup": # "quelle est la date d'effet ?"
return chunkbyline(line_df)
elif intent == "section_retrieval": # "quelles sont les exclusions ?"
return chunkbytocsection(linedf, toc_df)
elif intent == "comparison": # "comparez les clauses A et B"
return chunkbyfullsection(linedf, toc_df)
Les deux blocs de code ci-dessus résument tout l’argument de cette section. Le premier lance Optuna sur quatre nombres pendant deux semaines et produit une valeur que personne ne peut défendre. Le second prend une décision structurelle par type de question et produit un système dont le comportement peut être expliqué par n’importe qui.
Les articles suivants expliquent comment classer l’intention (Article 6, sur la compréhension des questions) et comment les différentes méthodes et granularités de récupération sont mises en œuvre (Article 7, sur la récupération). Le point ici est simplement que l’activité n’est pas l’ajustement, mais le routage.
L’ÉVALUATION : MESURER CE QUI COMPTE VRAIMENT
Le raisonnement est le suivant : le RAG, comme tout système de ML, a besoin d’un jeu de données d’évaluation approprié : des questions appariées avec des réponses attendues, séparées en ensembles d’entraînement et de test, notées avec précision et rappel. Des cadres comme RAGAS ont rendu cela encore plus tentant, en proposant des métriques pour la fidélité, la pertinence des réponses et le rappel du contexte qui ressemblent étrangement à du ML.
L’évaluation est utile. Le problème n’est pas de savoir s’il faut évaluer. C’est de savoir ce que signifient les métriques. En machine learning, l’évaluation vous indique si un modèle a généralisé à partir des données d’entraînement vers des exemples invisibles. La séparation train/test existe parce qu’on veut détecter le surapprentissage : un modèle qui a mémorisé l’ensemble d’entraînement plutôt que d’apprendre un motif transférable.
Dans le RAG, il n’y a rien à généraliser. Le surapprentissage (quand un modèle mémorise des exemples d’entraînement plutôt que d’apprendre un motif transférable à de nouvelles données) ne peut pas se produire ici : le système ne change pas entre les requêtes. Le récupérateur calcule les mêmes distances cosinus à chaque fois. Le générateur suit le même modèle de prompt. Il n’y a pas de modèle qui s’adapte aux données.
Ce que mesure l’évaluation dans le RAG, ce sont trois choses, toutes liées à la couverture et à la qualité, et non à la généralisation statistique :
- Le corpus couvre-t-il tous les sujets documentés ?
- Le récupérateur trouve-t-il les bons passages pour chaque question ?
- Le générateur produit-il des réponses fidèles, dans le bon format, avec des citations appropriées, et un « non trouvé » propre quand le passage ne contient pas la réponse ?
Chacune de ces questions pointe vers une correction spécifique. Les mélanger sous un score global « précision » fait perdre de l’information. Une précision de 75 % due à « le corpus manque 25 % des sujets documentés » exige une action différente de celle d’une précision de 75 % due à « le récupérateur manque le bon passage 25 % du temps ». Le premier cas demande d’ingérer plus de documents. Le second demande de corriger le récupérateur. Une métrique globale qui traite ces deux cas de la même manière cache le diagnostic.
Cela explique aussi pourquoi les équipes utilisant des cadres comme RAGAS rapportent parfois d’excellents scores sur un ensemble de test isolé, puis voient le système échouer en production. L’ensemble de test couvrait des sujets où le corpus avait des réponses et où le récupérateur avait la chance de les trouver. En production, les questions portent sur des sujets dont les réponses ne sont pas dans le corpus, et le système hallucine ou échoue à dire « non trouvé ». La métrique était élevée sur l’ensemble de test parce que cet ensemble était favorable. Le système n’était pas cassé. L’évaluation l’était.
Ce qu’il faut évaluer, décomposé par type de question, tient en dix lignes :
# Rappel de récupération, par question, par intention
def evaluateretrieval(referenceset, retrieve_fn):
rows = []
for ref in reference_set:
retrievedlines = retrievefn(ref.question)
recall = len(set(retrievedlines) & set(ref.expectedlines)) / len(ref.expected_lines)
rows.append({
"question": ref.question,
"intent": ref.intent,
"recall": recall,
"hit": recall > 0,
})
return pd.DataFrame(rows)
# Toujours décomposer par type de question, jamais seulement un agrégat
df.groupby("intent")["hit"].mean()
# point_lookup 0.92
# section_retrieval 0.41 <-- c'est le vrai problème
# comparison 0.55
Un seul score global de précision de 63 % aurait caché la catastrophe sur sectionretrieval. La décomposition par intention révèle le problème instantanément. Ici, recall signifie : sur les questions où la réponse existe dans le corpus, le récupérateur a-t-il trouvé le bon passage ? Grouper par intention (pointlookup, section_retrieval, etc.) montre quel type de question échoue, et donc quelle partie du pipeline corriger.
Le RAG a deux surfaces d’évaluation avec des formes très différentes.
La surface de récupération est un problème de recherche : le bon passage a-t-il été présenté au modèle ? Mesurer cela signifie vérifier, sur un jeu de référence de questions, si les bonnes lignes ou pages ont été récupérées. La métrique est le rappel au niveau qui vous intéresse (rappel par ligne, par page, par section), et elle est spécifique à votre corpus. Personne d’autre ne peut exécuter cette évaluation pour vous. Votre corpus est unique. C’est là que la majorité de l’effort d’évaluation doit être consacré.
La surface de génération est différente. Une fois que le bon passage a été récupéré, la question devient : le modèle a-t-il produit une réponse fidèle, dans le bon format, avec des citations appropriées, et un « non trouvé » propre quand le passage ne contenait pas la réponse ? Une partie de cela s’évalue vous-même, mais une grande partie est déjà évaluée par les fournisseurs de LLM. OpenAI, Anthropic et Mistral dépensent des ressources énormes pour tester si leurs modèles suivent des schémas JSON, refusent d’inventer et respectent les instructions de prompt. Ce sont les dimensions sur lesquelles ils améliorent leurs modèles. En tant que constructeur de RAG, vous n’entraînez pas le générateur. Vous le consommez. Si le modèle échoue lamentablement à retourner du JSON structuré ou reste infidèle à ses entrées, vous le remarquerez dans l’heure qui suit son intégration. Ce n’est pas une métrique à mettre en place ; c’est une vérification de bon sens qui est soit évidente, soit correcte.
Ce que cela signifie en pratique : la plupart de votre temps d’évaluation doit être consacré à la récupération (qui est spécifique au corpus et que seul vous pouvez faire), et non à la génération (qui est surtout le problème du fournisseur, et qui montre des échecs évidents rapidement). Les équipes qui passent des semaines à construire des suites d’évaluation de génération élaborées reportent généralement le travail plus difficile de récupération qui améliorerait le résultat.
L’EXPLICABILITÉ : UN PROBLÈME QUI N’EXISTE PAS
Le machine learning a son propre jeu d’outils pour l’explicabilité. Les valeurs SHAP pour attribuer les prédictions aux caractéristiques. LIME pour des approximations locales de modèles complexes. La visualisation d’attention pour les transformers. Quand les gens commencent à demander de l’explicabilité RAG (« pourquoi le système a-t-il donné cette réponse ? »), ils se tournent naturellement vers ces outils. Ils veulent noter la pertinence de la récupération, pondérer les contributions des documents, visualiser quels tokens ont influencé la sortie.
L’ironie est que le RAG est plus explicable par conception que la plupart des modèles de ML. Il n’y a pas besoin de SHAP. Il n’y a pas d’opacité à percer. Le système a récupéré ces passages spécifiques depuis ces sources spécifiques, et la réponse a été construite à partir d’eux. C’est l’explication. Elle est documentaire, pas statistique.
Cela pointe vers une asymétrie plus profonde entre le machine learning et le RAG. En machine learning, l’humain a des intuitions mais ne peut pas les quantifier. Demandez qui a survécu au Titanic et les gens répondront « richesse », « âge », « classe » : aucune réponse n’est fausse, aucune n’est précise. Le modèle n’a pas ce doute : ajustez un arbre de décision et la racine est « sexe », la prochaine coupure est un seuil d’âge exact que personne n’aurait deviné, puis la classe. Chaque division est un nombre qu’aucune intuition seule n’aurait pu produire. Le modèle existe pour coucher ces nombres sur papier.
Pour les données textuelles, la direction s’inverse. L’utilisateur peut lire la source. Un avocat parcourant un contrat voit les conditions, les exceptions, les dates. Un responsable conformité lit une politique et sait si un comportement la viole. Le texte ne cache pas son sens, et l’expert est déjà un lecteur fluide.
Il y a des exceptions : l’ironie et le sarcasme sont les classiques, où les LLM modernes captent parfois ce qu’un lecteur littéral rate. Mais dans les contextes d’entreprise, l’utilisateur est l’expert du domaine.
Le modèle n’est pas là pour expliquer le texte. Il est là pour faire la lecture à l’échelle du corpus, et une citation suffit pour que l’expert vérifie n’importe quelle réponse en quelques secondes.
Quand un utilisateur demande « pourquoi cette réponse ? », la bonne réponse n’est pas une carte thermique des poids d’attention ou un score d’attribution de caractéristiques. C’est : « J’ai regardé les pages 12, 47 et 89 de ce contrat. Voici le texte exact que j’ai utilisé. La réponse découle de ce texte. » Si l’utilisateur n’est pas d’accord avec la réponse, il peut lire la source lui-même et juger. Il n’a pas besoin d’un cadre d’explicabilité. Il a besoin d’une citation.
Le pipeline de cinquante lignes de l’Article 1 a déjà montré cela. Le prompt demandait au modèle de retourner les numéros de ligne de début et de fin (avec leurs pages) en plus de la réponse, dans un JSON structuré ; l’annotateur mettait ensuite en surbrillance ces lignes exactes sur le PDF. Pas de SHAP, pas de LIME, pas de visualisation d’attention, pas de plateforme d’observabilité spécialisée. « L’explication » était un sous-produit de la façon dont le prompt était écrit. La citation fait partie de la réponse, pas une couche d’analyse ajoutée par-dessus.
La trace est l’explication. La lire ne demande aucune interprétation, juste de la lecture.
Importer l’explicabilité du ML dans le RAG revient à utiliser un scalpel pour ouvrir une boîte aux lettres. Le score de récupération est déjà un nombre que vous avez calculé sur des entrées que vous pouvez lire. Il n’y a rien à attribuer que vous ne voyez déjà.
L’échec plus profond du cadre de l’explicabilité du ML est qu’il vous fait vous concentrer sur la mauvaise chose. Vous commencez à essayer d’expliquer pourquoi une partie du système a échoué, alors que la vraie question est : « Quelle partie du pipeline doit être corrigée, et comment ? »
CE QU’IL FAUT RETENIR : RAG N’EST PAS DU ML
Le message clé est simple : RAG n’est pas du machine learning. L’embedding qui alimente la recherche vectorielle est lui-même un modèle de deep learning, mais vous ne l’entraînez pas, vous le consommez. Le système que vous construisez autour est un système de recherche, pas un modèle. Traiter le RAG comme un modèle gaspille du temps, choisit les mauvaises métriques, embauche les mauvaises personnes, et cache les vrais modes d’échec.
Le « RAG n’est pas du ML » est un élément de Enterprise Document Intelligence Volume 1, qui construit le RAG entreprise brique par brique. Les quatre briques (analyse syntaxique, analyse des questions, récupération, génération) forment l’outil d’ingénierie vers lequel cet article pointe. Chaque brique a ses propres règles, ses propres métriques, ses propres experts. Le ML a sa place, mais pas là où la plupart des équipes le placent aujourd’hui.
Si votre équipe passe des semaines à ajuster des hyperparamètres avec Optuna, à construire des jeux de données d’évaluation massifs, ou à produire des heatmaps d’attention, arrêtez. Regardez vos documents. Classez vos questions. Routez chaque type de question vers la bonne stratégie de récupération. Mesurez le rappel par type de question, pas un score global. Et surtout, exigez des citations. C’est là que commence la vraie optimisation.
- Towards Data Science
L'indépendance de CLODCO est votre garantie.
Pour que l'actualité de l'IA reste sans filtre et sans concession, votre soutien est indispensable. Votre contribution est le seul moteur de notre liberté éditoriale.
Soutenir CLODCO


