Une seule ligne de tableau peut contenir la réponse à une question précise. Voici comment récupérer uniquement ce qui compte vraiment.

Imaginez un contrat d'assurance automobile de 40 lignes. Chaque ligne représente un événement couvert : vol de véhicule, incendie, vandalisme, etc. Les colonnes indiquent le plafond d'indemnisation, la franchise, et les conditions d'éligibilité. Maintenant, posez la question : « Quel est le plafond pour le vol de véhicule ? »

La méthode classique consiste à envoyer tout le tableau à l'intelligence artificielle. Résultat ? Elle reçoit 39 lignes sans rapport avec votre question, et doit deviner laquelle vous intéresse. C'est comme chercher une aiguille dans une botte de foin, sauf que la botte de foin est un tableau de données.

Le problème n'est pas la question, mais la façon dont on la traite. Une seule ligne contient la réponse, pas tout le tableau.

UNE NOUVELLE FAÇON DE STRUCTURER LES DONNÉES

Dans une série dédiée à l'intelligence artificielle documentaire, une approche révolutionnaire propose de traiter chaque ligne d'un tableau comme un chunk indépendant. Un chunk, c'est un morceau de texte que l'IA peut récupérer et analyser séparément. Au lieu de considérer un tableau comme un bloc unique, on le découpe en lignes individuelles, chacune accompagnée de ses en-têtes de colonnes.

Cette méthode s'appelle la récupération au niveau des lignes. Elle permet de répondre précisément à une question ciblée, sans noyer l'IA sous des informations inutiles. Mais attention : cette approche ne remplace pas la récupération au niveau du tableau entier. Elle la complète, en offrant deux échelles de Recherche selon la nature de la question.

Pour comprendre cette innovation, il faut d'abord maîtriser les trois couches techniques qui composent un système RAG (Retrieval-Augmented Generation) :

  • Le prompt : la requête elle-même, formulée pour l'IA.
  • Le contexte : les informations récupérées pour enrichir la réponse.
  • La boucle : le processus qui détermine quand relancer une recherche ou arrêter le processus.

Cette série explore chaque couche en détail. L'article présent se concentre sur un cas précis : lorsque la réponse à une question se trouve dans une ligne spécifique d'un tableau.

COMMENT ÇA MARCHE ? LE CODE QUI FAIT LA DIFFÉRENCE

Pour transformer un tableau en lignes récupérables, il faut d'abord le parser. Les outils comme Docling ou Azure Document Intelligence extraient les tableaux d'un document PDF et les représentent sous forme de lignes au format markdown-pipe :

| Col1 | Col2 | . |
| --- | --- | . |
| Valeur1 | Valeur2 | . |

Chaque tableau est donc décomposé en plusieurs lignes :

  • Une ligne d'en-tête (| Col1 | Col2 | . |).
  • Une ligne de séparation (| --- | --- | . |).
  • Une ligne de données par ligne de tableau.

La fonction serializetablerows prend en entrée ces lignes parsées et produit un nouveau dataframe où chaque ligne de données devient un chunk indépendant. Voici son fonctionnement :

def serializetablerows(line_df: pd.DataFrame) -> pd.DataFrame:
    """Un chunk récupérable par ligne de données de chaque tableau."""
    lines = linedf.sortvalues(["pagenum", "linenum"])
    tables = groupcontiguouspipe_rows(lines)   # regroupe les lignes de tableaux

    out = []
    for tid, table in enumerate(tables, start=1):
        sep = firstseparatorrow(table)         # ligne de séparation (| --- |)
        headers = split_cells(table[sep - 1])    # ligne d'en-tête au-dessus de la séparation
        headers, body = foldmultirowheader(headers, table[sep + 1:])
        for row_idx, line in enumerate(body):
            cells = split_cells(line.text)
            out.append({
                "table_id": f"t{tid}",
                "pagenum": line.pagenum, 
                "linenum": line.linenum,
                "column_headers": headers, 
                "row_cells": cells,
                # Les en-têtes accompagnent les valeurs pour une lecture naturelle
                "row_serialized":
                    " | ".join(f"{h}: {c}" for h, c in zip(headers, cells)),
            })
    return pd.DataFrame(out)

Le résultat est un nouveau dataframe où chaque ligne de données est transformée en un chunk structuré. Par exemple, une ligne de tableau comme :

| Couvert | Plafond | Franchise | Condition |
| --- | --- | --- | --- |
| Vol de véhicule | 25 000 EUR | 500 EUR | Tracker installé et actif |

Devient :

Couvert: Vol de véhicule | Plafond: 25 000 EUR | Franchise: 500 EUR | Condition: Tracker installé et actif

Cette transformation permet à l'IA de lire la ligne comme une phrase naturelle, avec les en-têtes qui contextualisent chaque valeur. C'est bien plus efficace que de lui envoyer tout le tableau.

GÉRER LES EN-TÊTES COMPLEXES : UN DÉFI TECHNIQUE

Tous les tableaux ne se contentent pas d'un en-tête simple. Certains ont des en-têtes sur deux lignes, comme dans le tableau suivant :

| Modèle | BLEU | Coût d'entraînement |
| --- | --- | --- |
| EN-DE | . | . |
| EN-FR | . | . |

Ici, la première ligne indique les modèles (EN-DE, EN-FR), et la deuxième ligne précise les métriques (BLEU, Coût d'entraînement). Le parser peut représenter cela comme :

| Modèle | BLEU |  | Coût d'entraînement |
| --- | --- | --- | --- |
| EN-DE | . | . | . |

Sans traitement spécifique, une recherche pour « EN-DE » pourrait cibler la première ligne d'en-tête, qui ne contient pas de données numériques. Pour éviter cela, la fonction foldmultirowheader intervient :

def foldmultirowheader(headers, body):
    """Transforme un en-tête sur deux lignes en un seul niveau d'en-tête."""
    if "" not in headers or len(body) < 2:
        return headers, body

    headers = forward_fill(headers)  # Remplit les cellules vides
    subline = split_cells(body[0].text)
    is_subheader = (
        len(subline) == len(headers)
        and not any(has_digit(c) for c in subline)  # Toutes les étiquettes
        and any(has_digit(c) for c in split_cells(body[1].text))  # Nombres en dessous
    )
    if is_subheader:
        headers = [f"{t} {s}".strip() for t, s in zip(headers, subline)]
        body = body[1:]  # Supprime la ligne d'en-tête secondaire
    return headers, body

Cette fonction détecte les en-têtes sur deux lignes et les fusionne en un seul niveau. Par exemple, elle transforme :

| Modèle | BLEU |  | Coût d'entraînement |

En :

| Modèle | BLEU EN-DE | BLEU EN-FR | Coût d'entraînement EN-DE | Coût d'entraînement EN-FR |

Ainsi, une recherche pour « EN-DE » cible directement les lignes de données, et non plus les en-têtes.

Un en-tête mal géré peut faire échouer toute la recherche. Cette fonction évite ce piège silencieux.

DEUX ÉCHELLES POUR DEUX TYPES DE QUESTIONS

La récupération au niveau des lignes ne remplace pas la récupération au niveau du tableau. Elle l'enrichit. Selon la nature de la question, le système choisit l'échelle la plus adaptée :

  • Question ciblée : « Quel est le plafond pour le vol de véhicule ? » → On récupère uniquement la ligne concernée.
  • Question synthétique : « Quels événements sont couverts ? » → On récupère tout le tableau.

Un seuil permet de basculer automatiquement entre les deux échelles. Par exemple, si plus de 60 % des lignes d'un tableau correspondent à une question, le système considère qu'il s'agit d'une question synthétique et récupère tout le tableau. Sinon, il reste au niveau des lignes.

Prenons un exemple concret avec un contrat d'assurance de 40 lignes. Une recherche pour « plafond pour vol de véhicule » retourne une seule ligne de 122 caractères :

Couvert: Vol de véhicule | Plafond: 25 000 EUR | Franchise: 500 EUR | Condition: Tracker installé et actif

Le même tableau complet fait 943 caractères. La récupération ligne par ligne permet donc un gain de 7,7 fois sur ce seul extrait. Sur l'ensemble du contrat, le gain est de 40 fois, car le ratio est directement lié au nombre de lignes.

Autre exemple avec l'article Attention Is All You Need (Vaswani et al., 2017). Son tableau 1 (page 6) compare quatre types de couches : Self-Attention, Recurrent, Convolutional, et Self-Attention (restricted). Une question comme « Quelle est la complexité de l'attention par couche ? » cible uniquement la ligne Self-Attention, soit 124 caractères sur les 528 du tableau complet. Un gain de 4,3 fois.

La récupération ligne par ligne réduit la taille des données envoyées à l'IA, ce qui améliore la précision et réduit les coûts.

UNE SOLUTION QUI S'INTÈGRE SANS PROBLÈME

Le plus impressionnant dans cette méthode, c'est sa simplicité d'intégration. Le nouveau dataframe de lignes (row_df) est généré à partir du dataframe existant (line_df), sans modifier ce dernier. Le contrat de parsing reste inchangé :

  • Le dataframe original (line_df) conserve les informations de géométrie du document (position, page, boîte englobante).
  • Le nouveau dataframe (row_df) est un index supplémentaire, dédié à la récupération ligne par ligne.

Les outils existants qui ne gèrent pas la récupération ligne par ligne continuent de fonctionner sans modification. Ceux qui l'activent lisent explicitement le nouveau dataframe. Pas besoin de vérifier des balises ou des types de chunks.

Cette approche parallèle garantit une compatibilité totale avec les systèmes existants. Elle ne casse rien, elle ajoute une nouvelle fonctionnalité.

UNE AVANCÉE POUR L'INTELLIGENCE ARTIFICIELLE DOCUMENTAIRE

Les tableaux sont partout : contrats, rapports financiers, articles scientifiques, présentations. Pourtant, ils sont rarement exploités efficacement par les systèmes d'IA. La plupart du temps, ces systèmes traitent un tableau comme un bloc de texte uniforme, sans tenir compte de sa structure interne.

La récupération au niveau des lignes change la donne. Elle permet de :

  • Réduire la quantité de données envoyées à l'IA, ce qui améliore la précision des réponses.
  • Économiser des ressources, car moins de données signifie moins de calculs.
  • Répondre précisément à des questions ciblées, sans noyer l'IA sous des informations parasites.

Cette méthode s'inscrit dans une série plus large dédiée à l'intelligence artificielle documentaire. Chaque article explore une brique technique différente d'un système RAG d'entreprise :

  • La structure des prompts.
  • La gestion du contexte.
  • Les boucles de récupération.
  • La récupération au niveau des lignes (celle-ci).

Ensemble, ces briques forment un système complet, capable de traiter des documents complexes avec une précision inégalée.

COMMENT TESTER CETTE MÉTHODE ?

Pour voir cette méthode en action, rien de plus simple. Un notebook interactif est disponible sur GitHub :

doc-intel/notebooks-vol1

Il permet de :

  • Charger le tableau 1 de l'article Attention Is All You Need.
  • Appliquer la fonction serializetablerows pour le transformer en lignes récupérables.
  • Lancer une recherche ciblée (par exemple, « complexité de l'attention par couche »).
  • Voir l'IA récupérer uniquement la ligne pertinente, et non tout le tableau.

Ce notebook utilise les services d'OpenAI, soumis à leurs conditions d'utilisation. Le parsing est réalisé avec Docling, sous licence MIT.

LIMITES ET PERSPECTIVES

Cette méthode n'est pas une solution miracle. Elle a ses limites :

  • Elle ne fonctionne que sur les tableaux bien structurés. Les tableaux mal formatés ou irréguliers peuvent poser problème.
  • Elle nécessite un parsing précis. Si le parser rate une ligne ou une colonne, la récupération ligne par ligne sera faussée.
  • Elle ne remplace pas les autres méthodes de récupération. Elle les complète.

Cependant, pour les tableaux bien structurés, cette méthode offre un gain significatif en précision et en efficacité. Elle ouvre la voie à des systèmes RAG plus intelligents, capables de comprendre la structure fine des documents.

À l'avenir, on peut imaginer des parsers encore plus performants, capables de normaliser automatiquement les en-têtes complexes. Cela rendrait la récupération ligne par ligne encore plus robuste et universelle.

CONCLUSION : UN PAS DE PLUS VERS L'IA DOCUMENTAIRE INTELLIGENTE

Les tableaux ne sont plus un obstacle pour l'IA. Grâce à la récupération au niveau des lignes, ils deviennent une source précieuse d'informations structurées. Cette méthode permet de :

  • Répondre précisément à des questions ciblées, sans surcharge d'informations.
  • Économiser des ressources, en envoyant uniquement ce qui est nécessaire à l'IA.
  • Améliorer la précision des réponses, en évitant les erreurs de contexte.

Elle s'intègre parfaitement aux systèmes RAG existants, sans les perturber. Et surtout, elle change la façon dont on interagit avec les documents structurés : plus besoin de parcourir manuellement des tableaux interminables pour trouver une information précise.

La récupération au niveau des lignes est une avancée majeure pour l'intelligence artificielle documentaire. Elle montre que l'IA peut comprendre la structure fine des documents, et pas seulement leur contenu textuel brut.

L'avenir de l'IA documentaire passe par la compréhension des structures, pas seulement des mots.
Sources :
  • 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