Ingérer des tableaux dans un RAG : le guide d’architecture définitif
Des limites du chunking naïf aux espaces vectoriels : comment traiter la donnée structurée sans perte sémantique.
Dans la plupart des déploiements RAG (Retrieval-Augmented Generation), l’ingestion de texte brut (articles de blog, documentation, comptes-rendus linéaires) donne des résultats honorables. Si vous débutez sur le sujet, n’hésitez pas à consulter mon article sur les fondations du chunking sémantique dans un pipeline RAG. Mais dès que le pipeline est confronté à des données denses et bidimensionnelles comme des tableaux récapitulatifs, la qualité des réponses s’effondre silencieusement. Un parser mal configuré transforme une matrice explicite en une suite de lignes incohérentes, et le retriever finit par renvoyer des informations erronées ou incomplètes.
Ce guide détaille la mécanique exacte à adopter pour découper, enrichir et vectoriser des tableaux complexes sans détruire leur structure sémantique.
1. L’anatomie du problème : le piège de la linéarisation
Un modèle d’embedding ne perçoit pas un tableau Markdown à la façon d’un utilisateur humain. Là où nos yeux croisent instantanément les lignes et les colonnes pour établir une relation logique entre une entité et une valeur, le modèle d’embedding lit une chaîne de caractères strictement linéaire.
Prenons un exemple concret extrait d’une compétition cycliste :
| Étape | Rang | Coureur | Équipe | Temps | Écart |
|---|---|---|---|---|---|
| 14 | 1 | Tadej Pogačar | UAE Team Emirates | 04:21:15 | +00:00 |
| 14 | 2 | Jonas Vingegaard | Team Visma | 04:21:54 | +00:39 |
| 14 | 3 | Remco Evenepoel | Soudal Quick-Step | 04:22:25 | +01:10 |
À la lecture brute, le tokeniseur transforme ce bloc en une séquence continue :
Étape | Rang | Coureur | Équipe | Temps | Écart | 14 | 1 | Tadej Pogačar | UAE Team Emirates | 04:21:15 | +00:00 | 14 | 2 | Jonas Vingegaard | Team Visma | 04:21:54 | +00:39 | …Langage du code : Markdown (markdown)
Trois risques majeurs apparaissent lors de la recherche vectorielle dans Qdrant :
- La dilution sémantique : dans un tableau volumineux de 50 ou 100 lignes, la signature mathématique du vecteur représente une moyenne sémantique globale. La valeur précise
+00:39associée à Jonas Vingegaard perd sa centralité. - Le piège de la rupture de chunk : si le diviseur de texte tranche le tableau au milieu sans réinjecter l’en-tête, les lignes suivantes deviennent une suite de chiffres anonymes. Le modèle n’a plus aucun moyen de savoir à quoi correspond le temps
04:22:25. - Le semantic collapse à grande échelle : en présence de dizaines de tableaux d’étapes ayant des structures identiques, les distances cosinus entre leurs vecteurs se réduisent considérablement. Le retriever devient incapable de distinguer l’étape 14 de l’étape 15.
Point de vigilance supplémentaire : même après un chunking correct, un tableau isolé dans la base vectorielle perd la majorité de son signal contextuel. Si l’utilisateur demande « Quel était le temps de Pogačar lors de l’étape de montagne du 14 juillet ? », aucune ligne du tableau ne contient le terme « montagne » ni la date « 14 juillet ». C’est précisément ce que la section 4 adresse.
2. Modèles d’embedding : quel choix pour la donnée tabulaire ?
Tous les modèles d’embedding ne traitent pas la densité d’information de la même façon. La comparaison entre deux standards du marché l’illustre nettement :
| Critère | text-embedding-3-small (OpenAI) | voyage-3-large (Voyage AI) |
|---|---|---|
| Dimensions | 1536 | 2048 |
| Fenêtre de contexte | 8 191 tokens | 32 000 tokens |
| Compréhension des structures | Correcte sur requêtes explicites | Excellente sur matrices et code denses |
| Paramètres d’instruction | Non (espace symétrique unique) | Oui (input_type="document" / query) |
| Rapport coût / pertinence | Très économique / Usage général | Haute précision sémantique |
Pourquoi privilégier Voyage 3 Large sur des tableaux complexes ?
Pour un agent conversationnel confronté à des requêtes formulées en langage naturel (ex. « Qui est arrivé deuxième de l’étape 14 et avec quel retard ? »), voyage-3-large offre une meilleure séparation spatiale. Sa capacité à différencier les rôles au moyen des préfixes d’instruction (input_type="document" à l’indexation et input_type="query" à la recherche) garantit un score de similarité supérieur sur des passages denses.
Concrètement, Voyage AI préfixe automatiquement les entrées selon le type :
- Pour une requête :
"Represent the query for retrieving supporting documents: " - Pour un document :
"Represent the document for retrieval: "
Cette dissymétrie crée des vecteurs mieux alignés pour la recherche, un avantage mesurable sur des données tabulaires où le vocabulaire de la requête (langage naturel) diffère structurellement du contenu indexé (chiffres, abréviations, noms propres).
Ordres de grandeur (relevés publics début 2026, à considérer comme indicatifs et susceptibles d’évoluer) : sur le benchmark MTEB moyen, voyage-3-large se situe autour de 67, contre environ 62 pour text-embedding-3-small. Le coût par million de tokens est d’environ 0,18 $ pour voyage-3-large contre 0,02 $ pour text-embedding-3-small. Le choix doit donc se faire sur la criticité de la précision : pour un catalogue produit ou un tableau financier, l’investissement se justifie ; pour de la documentation généraliste, 3-small reste compétitif. Notez que le paysage évolue vite (générations suivantes chez Voyage AI comme chez OpenAI) : vérifiez les grilles tarifaires et les scores au moment de votre implémentation.
Les contraintes d’auto-hébergement et les alternatives à poids ouverts feront probablement l’objet d’un article dédié.
3. Stratégie de découpage (Chunking) pour fichiers Markdown
Le principe fondamental pour ingérer des tableaux est simple : un tableau court ne doit jamais être fragmenté, et un tableau long doit être découpé par blocs isolés incluant systématiquement l’en-tête.
Pour un agent conversationnel, la taille de chunk idéale oscille entre 1 500 et 3 000 caractères (environ 300 à 600 tokens), assortie d’un chevauchement (overlap) de 10 à 15 %. Cette échelle permet de conserver le contexte environnant du tableau sans noyer l’information.
Pattern n°1 : Le tableau court (moins de 20 lignes)
Conservez l’intégralité du tableau dans un chunk unique, encadré par ses paragraphes explicatifs amont et aval. Le chunk résultant doit inclure le paragraphe d’introduction et éventuellement le paragraphe de conclusion qui suit immédiatement le tableau.
Pattern n°2 : Le tableau long (découpage par blocs avec répétition d’en-tête)
Si un tableau d’étape comporte 150 coureurs, découpez-le en sous-tableaux autonomes. Chaque sous-chunk doit répéter la ligne d’en-tête :
<!-- Chunk 1 -->
### Classement Étape 14 - Tour de France (km 150 à 200)
| Étape | Rang | Coureur | Équipe | Temps | Écart |
|---|---|---|---|---|---|
| 14 | 1 | Tadej Pogačar | UAE Team Emirates | 04:21:15 | +00:00 |
...
| 14 | 25 | Guillaume Martin | Cofidis | 04:25:10 | +03:55 |
<!-- Chunk 2 -->
### Classement Étape 14 - Tour de France (km 150 à 200)
| Étape | Rang | Coureur | Équipe | Temps | Écart |
|---|---|---|---|---|---|
| 14 | 26 | Alexis Vuillermoz | TotalEnergies | 04:25:30 | +04:15 |
...Langage du code : Markdown (markdown)
Outils et bibliothèques : plusieurs bibliothèques Python implémentent déjà cette protection atomique des tableaux, et toutes sont disponibles sur PyPI au moment de la rédaction :
sayou-chunking(pip install sayou-chunking) : protection intégrée des blocs Markdown (tableaux et code) avec conservation de la hiérarchie des en-têtes. Fait partie de la plateforme Sayou Data Platform.structchunk(pip install structchunk) : découpe les tableaux surdimensionnés aux frontières de lignes en réinjectant les en-têtes de colonnes dans chaque chunk de continuation ; injecte également le titre H1 dans chaque chunk via une passe de post-traitement.chunkana(pip install chunkana) : garantit que les tableaux ne sont jamais brisés et expose des métadonnées de chemin d’en-tête (header_path) utilisables pour le filtrage.markdown-chunker(pip install markdown-chunker) : applique la même règle — les tableaux ne sont divisés que si nécessaire, avec préservation des en-têtes.
4. L’étape clé : l’enrichissement contextuel (Contextual Header)
Un tableau nu isolé dans une base vectorielle comme Qdrant perd la majorité de son signal contextuel. Si l’utilisateur demande « Quel était le temps de Pogačar lors de l’étape de montagne du 14 juillet ? », aucune ligne du tableau ne contient le terme « montagne » ni la date « 14 juillet ».
Pour résoudre ce problème, il faut appliquer la technique du chapeau contextuel : chaque tableau doit être précédé d’un paragraphe explicatif clair dans le fichier Markdown.
Exemple d’enrichissement sémantique :
### Étape 14 : Pau - Saint-Lary-Soulan (Étape de montagne des Pyrénées, 14 juillet 2024)
Ce tableau présente le classement officiel à l'arrivée au sommet du Pla d'Adet,
avec les temps scratch et les écarts au général.Langage du code : Markdown (markdown)
Lors de l’embedding, ce paragraphe se mélange sémantiquement aux données chiffrées du tableau. Le vecteur résultant capture simultanément la thématique globale (étape de montagne, Pyrénées, 14 juillet) et la précision des données chiffrées.
Au-delà du chapeau manuel : le Contextual Retrieval généré par LLM
La technique popularisée par Anthropic consiste à faire générer par un LLM un court contexte (50 à 100 tokens) pour chaque chunk, en lui fournissant le document complet et le chunk isolé. Le contexte généré est ensuite préfixé au chunk avant l’embedding. Anthropic rapporte une réduction significative du taux d’échec de récupération sur ses benchmarks internes (voir leur article de blog pour les chiffres exacts et la méthodologie).
Cependant, toutes les études ne confirment pas cet avantage sur les données structurées. Une comparaison contrôlée sur des articles Wikipédia et des papiers QASPER a montré que des chunks alignés sur la structure (avec chemins d’en-tête) surpassaient les fenêtres fixes contextualisées par LLM, le gain provenant principalement du placement aux frontières structurelles plutôt que du texte généré. Pour des tableaux, la leçon est claire : un en-tête structurel déterministe (chemin de section + légende de tableau) est souvent plus fiable et moins coûteux qu’une contextualisation générative. Réservez le Contextual Retrieval par LLM aux cas où le tableau est profondément imbriqué dans un document long et où son sujet ne peut pas être déduit des en-têtes Markdown seuls.
Métriques de validation : quelle que soit la méthode d’enrichissement choisie, mesurez son impact. Constituez un jeu de requêtes de test avec les sources attendues, puis comparez recall@k et nDCG entre baseline et version enrichie (pour aller plus loin sur cette démarche, vous pouvez lire mon guide sur comment évaluer rigoureusement un système RAG en production). Un enrichissement qui n’améliore pas ces métriques sur votre corpus est un coût inutile.
5. Synthèse des bonnes pratiques pour l’ingestion
- Choix du modèle : privilégiez
voyage-3-largesi le domaine exige une distinction fine sur des données structurées. Si vous optez pourtext-embedding-3-small, compensez par un formatage strict et un enrichissement contextuel rigoureux. Vérifiez les générations et tarifs disponibles au moment de votre implémentation (voir article à venir sur l’embedding auto-hébergé). - Intégrité du format : ne laissez pas les parsers automatiques convertir vos tableaux Markdown en texte brut non structuré. Utilisez une bibliothèque structure-aware (
sayou-chunking,structchunk,chunkana,markdown-chunker) qui traite les tableaux comme des blocs atomiques. - Répétition des structures : en cas de chunking sur un long tableau, dupliquez toujours la ligne d’en-tête dans chaque sous-chunk. Sans cela, les lignes de continuation sont sémantiquement orphelines.
- Enrichissement contextuel : préfixez chaque tableau d’un chapeau explicatif manuel (titre de section, date, lieu, sujet). Pour les cas complexes, évaluez le Contextual Retrieval généré par LLM, mais validez son gain par des métriques de retrieval avant de le généraliser.
- Isolation des index : tout changement de stratégie de chunking ou de modèle d’embedding nécessite la purge et la réindexation complète de la collection vectorielle. Les vecteurs de stratégies différentes ne sont pas comparables.
- Hybridation dense + sparse : pour les tableaux contenant des identifiants, des codes ou des valeurs numériques précises, envisagez une recherche hybride dans Qdrant (vecteur dense + BM25 sparse). Pour tout comprendre sur la mise en place de ce couplage, référez-vous à mon article sur la recherche hybride et le sparse retrieval avec Qdrant. Un token rare comme
+00:39sera mieux capté par la composante sparse que par un embedding dense moyenné.