Leçon 1 — Le cas d'usage et l'architecture

Quand le texte ne suffit pas

6 min de lecture

Quand le texte ne suffit pas

Le cas d'usage

Une analyste reçoit chaque trimestre le rapport d'exploitation des entrepôts. Elle veut interroger le document en langage naturel : « quel site consomme le plus ? », « quel mois a-t-on raté l'objectif de disponibilité ? », « que trouve-t-on entre la passerelle et la base de données ? ». Toutes ces réponses sont dans des figures.

Type de contenuExemple dans le rapportLu par extract_text() ?
Paragraphes« Le chiffre d'affaires atteint 18,4 millions d'euros »oui
Graphique en barresconsommation par site (412, 287, 355, 198 MWh)non
Courbe avec seuildisponibilité mensuelle, objectif 98,5 %non
SchémaSondes → Passerelle → Broker MQTT → TimescaleDB → Tableau de bordnon
Tableau dessiné (image)coûts de maintenance par équipementnon

Générer le document

Le script make_pdf.py dessine les figures avec Pillow (bibliothèque Python de traitement d'images) et assemble le PDF avec reportlab (bibliothèque de génération de PDF) :

files/make_pdf.py
("2. Consommation énergétique",
 ["La figure 1 présente la consommation électrique de chaque entrepôt sur le trimestre.",
  "Le plan de réduction cible en priorité le site le plus consommateur, où l'isolation des quais sera refaite."],
 bar_chart("Figure 1 - Consommation électrique T3 2026 (MWh)", ["Lyon", "Nantes", "Lille", "Toulouse"],
           [412, 287, 355, 198], "MWh")),

Remarquez le piège volontaire : le texte dit « le site le plus consommateur » sans le nommer. Pour répondre à « quel site est visé en priorité ? », il faut combiner le texte et la figure.

cd files
pip install -r requirements.txt
python make_pdf.py        # → rapport_verdalis_t3.pdf (6 pages, 4 figures)

Pourquoi le rapport du lab est-il généré par un script plutôt que pris sur Internet ?

Ce que voit un RAG texte seul

flowchart TB
  subgraph PAGE["Page 2 du PDF"]
    T1["Titre + 2 phrases"]
    F1["Graphique en barres<br/>Lyon 412 · Nantes 287 · Lille 355 · Toulouse 198"]
  end
  T1 -->|"extract_text()"| OK["« La figure 1 présente la consommation... »"]
  F1 -->|"extract_text()"| KO["rien"]

Trois architectures possibles

ArchitecturePrincipeAvantagesLimites
A. Description à l'indexation (ce lab)un modèle de vision décrit chaque figure une fois ; on indexe le texte de la descriptionrecherche texte classique, explicable, peu coûteuse à l'usagela description peut omettre un détail
B. Embeddings d'imageon calcule un embedding de l'image elle-même (gemini-embedding-2 est multimodal)pas de description à générerdifficile à déboguer : on ne « lit » pas un vecteur
C. Pages en image (type ColPali)chaque page entière est rendue en image et indexée par un modèle spécialisérobuste à toutes les mises en pagemodèles et index spécifiques, plus lourd

Ce lab implémente A, puis ajoute une sécurité : au moment de répondre, on joint l'image originale à la question. La description sert à trouver la figure ; l'image sert à répondre.

Dans l'architecture A, à quel moment Gemini regarde-t-il les images ? (plusieurs réponses)

Résultat obtenu

Exécution du 2026-10-15 : texte extrait par pypdf et images détectées, page par page.

$ python make_pdf.py
PDF écrit : .../files/rapport_verdalis_t3.pdf
$ python -c "from pypdf import PdfReader
for i, p in enumerate(PdfReader('rapport_verdalis_t3.pdf').pages):
    print(i + 1, repr(p.extract_text()[:120]), [(im.name, im.image.size) for im in p.images])"
1 "1. Synthèse du trimestre\nCe rapport présente l'activité des quatre entrepôts frigorifiques Verdalis..." []
2 '2. Consommation énergétique\nLa figure 1 présente la consommation électrique de chaque entrepôt...' [(... .png, (1000, 620))]
3 '3. Disponibilité des chambres froides\nLa figure 2 montre le taux de disponibilité mensuel...'      [(... .png, (1000, 620))]
4 '4. Architecture de supervision\nLa chaîne de supervision des températures est décrite par la figure 3.' [(... .png, (1000, 620))]
5 "5. Maintenance\nLe tableau 1 récapitule les coûts de maintenance par famille d'équipement..."      [(... .png, (1000, 620))]
6 "6. Perspectives\nAu quatrième trimestre, Verdalis ouvrira un cinquième entrepôt à Rennes..." []

Quatre images de 1000 × 620 pixels, et aucune valeur chiffrée des figures dans le texte.

Exercice : inventorier un vrai document

Prenez un PDF de votre activité (rapport, présentation exportée). Écrivez 5 lignes de Python qui affichent, pour chaque page, le nombre de caractères de texte extrait et le nombre d'images. Quelles pages seraient muettes pour un RAG texte seul ?

Voir la solution
from pypdf import PdfReader
for n, page in enumerate(PdfReader("mon_rapport.pdf").pages, start=1):
    print(n, len(page.extract_text() or ""), "car.", len(page.images), "image(s)")

Les pages avec peu de texte et au moins une image sont les candidates. Attention : un PDF scanné n'a souvent aucun texte du tout (chaque page est une image) ; il faut alors une reconnaissance de caractères (OCR) ou une approche par page entière (architecture C). Une figure vectorielle (dessinée en traits PDF, pas en image) n'apparaît pas dans page.images : il faut rendre la page en image pour la décrire.

Je sais faire

  • Expliquer pourquoi un RAG texte seul est aveugle aux figures
  • Générer un PDF de test dont je maîtrise le contenu texte et image
  • Comparer les architectures « description », « embedding d'image » et « page en image »
  • Repérer dans un vrai document les pages qu'un extracteur de texte rend muettes
Suivant