Pour le RAG, convertissez les PDF en Markdown si vous voulez des fragments qui suivent les sections du document : chaque titre devient un point de découpe, et les titres situés au-dessus d’un fragment l’accompagnent comme métadonnées. Vérifiez les titres, les tableaux et le texte OCR avant l’indexation, puis testez la recherche avec des questions dont vous connaissez déjà les réponses.

Ce guide s’adresse aux développeurs qui construisent une recherche sur des documents privés : politiques internes, manuels, contrats, recherche. Il suppose que vous avez déjà en tête un modèle d’embeddings et une base vectorielle.

Des piles de fiches lignées et des bandes de papier à côté d’un ordinateur portable, une pile nouée d’un ruban lilas.
Des fragments qui suivent les sections du document.

Pourquoi les titres donnent de meilleurs fragments

Un découpeur à taille fixe coupe le texte en morceaux de longueur donnée, où que tombe la coupe. Un fragment peut commencer au milieu d’une section et perdre le titre qui dit de quoi il parle. « Les requêtes sont limitées à 100 par minute » n’a pas le même sens sous « Sandbox » et sous « Production ». Sans le titre, ni le moteur de recherche ni le modèle ne peuvent savoir de quoi il s’agit.

Le Markdown conserve le plan du document sous forme de lignes qui commencent par #, ## et ###. Un découpeur par titres coupe à ces lignes, si bien que chaque fragment couvre une section, et il enregistre les titres situés au-dessus du fragment : son chemin de section. Le guide de LangChain sur le découpage du Markdown décrit la même idée : garder le texte avec les titres auxquels il appartient.

Les sections n’ont pas toutes la même longueur : une section longue doit donc encore être découpée par taille. Le chemin de section s’applique à chacun de ses morceaux.

Convertir les PDF et vérifier les titres

Dans MD Factory, ajoutez le dossier qui contient vos PDF. Chaque fichier Markdown est enregistré à côté de son PDF sous le même nom, avec ses images dans un dossier voisin. Les PDF scannés passent par l’OCR sur votre PC. Les fichiers Word et les autres formats du dossier sont convertis de la même façon.

Ouvrez quelques résultats avant d’écrire le moindre code de découpage :

  • Vérifiez que les chapitres, sections et sous-sections ressortent en #, ## et ###. Dans un PDF, un titre n’est souvent marqué que par une police plus grande ou plus grasse : tout convertisseur doit donc déduire où commencent les sections et comment elles s’imbriquent. Corrigez les niveaux là où ils sont faux.
  • Dans les fichiers Word, les titres sont conservés si l’auteur a utilisé les styles de titre de Word. Un texte simplement en gras ou en grand reste un paragraphe ordinaire.
  • Repérez les en-têtes, pieds de page et numéros de page qui se retrouvent au milieu du texte.
  • Cliquez sur À vérifier à côté d’un fichier pour voir ce qu’il faut contrôler. Pour un PDF, la liste peut inclure des pages où l’OCR n’a pas pu lire une partie du texte ou n’a trouvé aucun texte.

Corrigez les problèmes dans le fichier Markdown avant de l’indexer. Chaque fragment en hérite.

Découper par titres dans le code

Le MarkdownHeaderTextSplitter de LangChain découpe selon les niveaux de titre indiqués et place chaque titre dans les métadonnées du fragment. Un second découpeur limite la taille à l’intérieur de chaque section :

from pathlib import Path
from langchain_text_splitters import (
    MarkdownHeaderTextSplitter,
    RecursiveCharacterTextSplitter,
)

corpus = Path("corpus")
by_heading = MarkdownHeaderTextSplitter(
    [("#", "h1"), ("##", "h2"), ("###", "h3")], strip_headers=False
)
by_size = RecursiveCharacterTextSplitter(chunk_size=1500, chunk_overlap=150)

chunks = []
for path in sorted(corpus.rglob("*.md")):
    sections = by_heading.split_text(path.read_text(encoding="utf-8"))
    for chunk in by_size.split_documents(sections):
        chunk.metadata["source"] = path.relative_to(corpus).as_posix()
        chunk.metadata["section"] = " > ".join(
            chunk.metadata[h] for h in ("h1", "h2", "h3") if h in chunk.metadata
        )
        chunks.append(chunk)

La taille des fragments et le chevauchement indiqués ici ne sont qu’un point de départ ; ajustez-les à votre modèle d’embeddings. Pour garder les lignes de titre dans le texte du fragment, afin que le texte vectorisé nomme aussi sa section, l’exemple définit strip_headers=False.

Le MarkdownNodeParser de LlamaIndex fait le même découpage par titres. Si vous chargez les fichiers avec SimpleDirectoryReader, chaque nœud porte aussi le nom du fichier :

from llama_index.core import SimpleDirectoryReader
from llama_index.core.node_parser import MarkdownNodeParser

documents = SimpleDirectoryReader(
    "corpus", recursive=True, required_exts=[".md"]
).load_data()
nodes = MarkdownNodeParser().get_nodes_from_documents(documents)
print(nodes[0].metadata["file_name"], nodes[0].metadata["header_path"])

header_path liste les titres situés au-dessus d’un nœud, par exemple /Employee handbook/Expenses/ ; le titre propre au nœud est la première ligne de son texte.

Garder la source avec chaque fragment

Enregistrez au moins trois informations avec chaque fragment :

  • Le fichier. Un fichier Markdown porte le même nom que son PDF : un fragment issu de handbook.md renvoie donc à handbook.pdf. Enregistrez le chemin relatif au dossier du corpus, car deux fichiers nommés terms.md dans des dossiers différents sont des documents différents.
  • Le chemin de section, pour qu’une réponse puisse nommer la section utilisée et qu’un lecteur aille directement au passage.
  • L’édition ou la date. Quand une politique change, vous supprimez les fragments de l’ancien fichier et indexez les nouveaux, au lieu de servir les deux versions.

Si des personnes aux droits d’accès différents interrogent le même index, enregistrez aussi qui peut lire chaque document, et filtrez sur ce champ au moment de la requête. Un moteur de recherche renvoie tout fragment qui correspond à la question.

Le Markdown produit par MD Factory ne contient pas de numéros de page. Si les réponses doivent citer des pages, retrouvez le passage dans le PDF d’origine.

Traiter avec soin les tableaux et le texte OCR

Un tableau Markdown n’a sa ligne d’en-tête qu’une fois, en haut. Quand un tableau dépasse la taille de vos fragments, le découpeur par taille le coupe, et les morceaux suivants arrivent sans noms de colonnes. Dans notre test avec LangChain (langchain-text-splitters 1.1.3), un tableau de 40 lignes découpé en fragments de 300 caractères a donné quatre morceaux, et seul le premier contenait la ligne d’en-tête.

Deux façons d’y remédier : garder chaque tableau dans un seul fragment, même s’il est plus long que les autres, ou répéter la ligne d’en-tête au début de chaque morceau.

Pour les fichiers Word et Excel, MD Factory marque un document À vérifier lorsque des cellules fusionnées ou complexes ont été simplifiées. Les tableaux des PDF ne reçoivent pas ce signalement : comparez-en quelques-uns avec l’original, surtout ceux dont les en-têtes sont fusionnés.

Le texte OCR demande sa propre vérification. Des chiffres, noms et codes mal reconnus passent tels quels dans les réponses, et rien dans le fragment ne semble faux. Comparez les chiffres de quelques fragments avec le scan, et laissez les pages signalées hors de l’index tant que vous ne les avez pas corrigées. Ce qu’il faut vérifier est expliqué dans notre guide pour vérifier l’OCR des PDF scannés.

Tester la recherche sur de vraies questions

Notez les questions que les gens poseront vraiment et, pour chacune, le fichier et la section qui contiennent la réponse. Commencez avec une ou deux douzaines.

  1. Passez chaque question dans votre moteur de recherche et regardez les premiers résultats.
  2. Notez si la bonne section en fait partie.
  3. Pour chaque échec, comparez les fragments renvoyés avec celui qui aurait dû l’être. Causes fréquentes : un mauvais niveau de titre, un tableau séparé de son en-tête, une erreur d’OCR dans le terme clé ou une section si longue que la réponse s’y perd.
  4. Changez une seule chose, par exemple la taille des fragments, les niveaux de titre ou la façon de découper les tableaux, puis repassez les mêmes questions.

Gardez les questions dans un fichier à côté du corpus et relancez-les chaque fois que les documents ou le code de découpage changent.

Quand utiliser MD Factory et quand utiliser une bibliothèque

MD Factory est une application de bureau pour Windows. Elle convient à une collection privée que vous voulez convertir sur votre propre PC et vérifier à l’œil : ajoutez un dossier, regardez les statuts, ouvrez les fichiers à vérifier, corrigez-les, puis pointez votre script d’indexation vers le dossier.

MD Factory garde les documents sur votre PC pendant la conversion. Si vous vectorisez ensuite les fragments avec une API d’embeddings hébergée ou les envoyez à un modèle hébergé, leur texte part chez ce fournisseur. Utilisez un modèle local si les documents ne doivent pas quitter votre réseau.

Si les documents arrivent en continu, se trouvent sur un serveur ou doivent être convertis sans intervention humaine, utilisez plutôt une bibliothèque de conversion dans votre code. Pour les options, voir notre comparatif des convertisseurs PDF vers Markdown.