De la version naïve au RAG en production pour les déclarations 10-K de la SEC : segmentation en blocs, recherche hybride.
Pourquoi le pipeline RAG d’un manuel scolaire a échoué avec des documents 10-K de 300 pages, et les solutions : blocs adaptés aux sections, recherche hybride, réclassement par encodeur croisé, validation et citations de métadonnées.
Sources: **How I Built a RAG Pipeline on Legal & Financial Documents and What Actually Broke?**
Un pipeline qui semblait correct mais qui fournissait de mauvaises informations
Imaginez un pipeline RAG conçu pour les déclarations SEC 10-K, dont la construction a pris trois semaines, utilisant LangChain pour les connexions, ChromaDB comme stockage vectoriel, un script Python propre pour l’ingestion des données et une interface Streamlit soignée. Un premier test simple consiste à demander quels sont les points d’audit importants relatifs à la reconnaissance des revenus. Le système répond, avec une certitude absolue, à propos de la rémunération des dirigeants, alors que cela concerne une section complètement différente. Le terme « hallucination » n’est pas tout à fait approprié : le texte est bien réel, mais il provient de la mauvaise section d’un document de 300 pages, sans que le système ne s’en aperçoive.
Avoir l’aspect d’un système RAG et fonctionner comme tel sont deux choses distinctes. Ci-dessous sont présentées les décisions de segmentation, les échecs de récupération, les expériences d’incorporation, l’ensemble d’évaluation mis en place par nécessité, ainsi que la version qui a finalement permis de réduire de 65 % les vérifications manuelles. Toute personne qui développe des fonctionnalités basées sur des documents réels, et non sur des PDF fictifs, peut éviter des semaines d’apprentissage stérile.
Pourquoi les documents 10-K sont difficiles à récupérer
Considérer un document 10-K comme un PDF ordinaire est une sous-estimation. Un rapport émis par une entreprise publique de taille moyenne ou grande compte de 150 à 400 pages, comprenant des tableaux financiers, des notes de bas de page renvoyant à d’autres notes, des textes juridiques s’étendant sur des dizaines de pages, des exposés sur les risques et des commentaires de la direction. L’objectif de la récupération était les questions d’audit critiques (Critical Audit Matters, CAM), où les auditeurs expliquent les aspects les plus complexes ou nécessitant le plus de jugement dans leur travail.
Ce que le lecteur souhaite trouver, comme les termes liés aux risques ou les jugements d’audit, est distribué de manière inégale. Il est concentré dans certaines sections, rédigé en vocabulaire spécialisé, et associé à des chiffres qui n’ont peu de sens hors contexte. Si l’on traite le document comme un ensemble de fragments indépendants, le système de récupération choisit la plupart du temps les mauvais éléments, ce qui est exactement ce que la première version a fait.
Deux processus parallèles
La différence entre les versions contient toutes les leçons à retenir.
Version 1 suivait une approche classique : elle importait le PDF, le divisait en fragments de 500 tokens avec une superposition de 50 tokens, les intégrait à l’aide du modèle text-embedding-ada-002 d’OpenAI, les stockait dans ChromaDB, récupérait les 5 meilleurs fragments pour une requête, les transmettait à GPT-4 afin de générer un résumé. Cette méthode fonctionnait bien pour des requêtes simples sur des documents simples, mais échouait complètement avec un dossier dense de 300 pages de trois manières spécifiques.
Version 2 comprend beaucoup plus d’étapes : l’ingestion de PDF, une couche de détection de sections, un découpage en blocs tenant compte des sections avec des tailles variables selon le type de section, l’ajout de métadonnées pour chaque bloc, un modèle d’incorporation conscient du domaine, un stockage ChromaDB avec des métadonnées détaillées, la réécriture des requêtes, une recherche hybride (sémantique et par mots-clés), un réclassement, une validation et enfin la génération. Chaque étape a une raison d’être, qui sera expliquée ci-après.
Problème n°1 : les blocs fixes séparent le contexte
L’erreur la plus fondamentale de la V1 résidait dans l’utilisation de blocs de 500 tokens, ce qui entraînait une segmentation au hasard du comptage. Un document 10-K est naturellement structuré : il comprend un en-tête, une description, la réponse de l’auditeur et souvent un lien vers une note financière, et sa longueur peut atteindre 600 ou 800 tokens. Une segmentation à 500 tokens coupait souvent un tel document en deux, avec la description dans un bloc et la réponse dans un autre, de sorte que aucun des deux blocs n’était utile seul.
La solution consistait en une couche de détection de sections placée avant le découpage. pdfplumber analysait la structure du document pour identifier les limites des sections, les titres, les sous-titres et les bords des tableaux, permettant ainsi un découpage au sein de ces sections et non entre elles, en utilisant des tailles respectant les ruptures naturelles. Les tableaux financiers denses étaient divisés en petits blocs de 200 à 300 tokens environ, afin de conserver la possibilité de rechercher des détails au niveau des lignes. Les sections narratives telles que les CAM et les mentions de risque étaient divisées en grands blocs allant jusqu’à 800 à 900 tokens, pour préserver l’intégrité de chaque unité logique. Le chevauchement entre les blocs restait à 10 % de leur taille. Ce seul changement a considérablement amélioré la pertinence des résultats de recherche avant même que quoi que ce soit d’autre ne soit modifié.
Deuxième échec : des embeddings qui ignoraient le langage financier
text-embedding-ada-002 est un modèle polyvalent très performant, bien qu’il n’ait pas été entraîné spécifiquement sur des textes financiers ou juridiques, ce qui se ressent avec des documents d’audit complexes. Une requête portant sur les domaines de jugement relatifs à la reconnaissance des revenus a produit des extraits concernant les revenus ainsi que des éléments liés au jugement en général. Certains extraits provenaient de discussions de la direction, d’autres de notes de bas de page, et quelques-uns de mentions de risques non liés ; par ailleurs, la CAM relative à la reconnaissance des revenus n’a pas pu être trouvée de manière fiable.
Deux alternatives ont été testées. La version plus récente de OpenAI, text-embedding-3-large, a amélioré de manière mesurable la qualité de la récupération des informations. Une approche hybride combinant une recherche vectorielle dense avec une recherche par mots-clés BM25 sparses et fusionnant les résultats a surpassé chacune des méthodes utilisées séparément. Le principe est simple : la recherche dense permet de trouver du contenu conceptuellement lié, tandis que la recherche sparses permet de trouver des termes exacts. Dans les textes financiers, l’exactitude des formulations est primordiale. « Critical audit matter » n’est pas la même chose que « significant audit issue » en langage juridique, et la présence des deux termes améliore considérablement la précision. La partie basée sur la recherche sparses utilisait la bibliothèque Python rank_bm25, et une fonction de fusion simple basée sur le rang réciproque combinait les deux listes de résultats avant de procéder à un nouveau classement.
Troisième échec : absence de validation, réponses fausses mais affirmées
C’était le problème le plus silencieux et le plus dangereux. Dans V1, de mauvais fragments extraits continuaient de pousser le LLM à répondre, et GPT-4 est très doué pour paraître sûr de lui avec un contexte limité. Ses réponses n’étaient pas manifestement fausses ; il s’agissait de résumés plausibles de tout fragment reçu, même lorsque ces fragments provenaient de la mauvaise partie du document.
Seul un petit ensemble d’évaluation a révélé le problème : 30 paires question-réponse vérifiées manuellement à partir de dossiers spécifiques, chacune étiquetée avec les fragments sources exacts qui auraient dû être utilisés. Selon ces critères, V1 parvenait à récupérer les bons fragments sources environ 55 % du temps, tandis que les 45 % restants produisaient du texte fluide basé sur des éléments irrelevants.
Deux modifications ont résolu le problème. La première consistait en une étape de réclassement : un cross-encoder de HuggingFace nommé ms-marco-MiniLM-L-6-v2 a réévalué les dix extraits obtenus, et seuls les trois meilleurs ont été transmis au générateur. Les cross-encoders fonctionnent plus lentement que les bi-encoders, mais ils effectuent une classification bien plus précise, car la requête et un extrait candidat sont traités comme une seule entrée plutôt que comme deux entrées distinctes.
La deuxième modification était une étape de validation avant la génération. Elle vérifiait que les extraits obtenus contenaient les identifiants de section attendus selon les métadonnées du moment de l’ingestion. Par exemple, lorsque une question concernant les CAMs renvoyait des extraits provenant des tables de compensation, la validation échouait, et le système réécrivait la requête et tentait à nouveau jusqu’à deux fois avant de qualifier la réponse comme peu fiable. Cela distingue une démonstration d’un système de production : la démonstration couvre le scénario idéal, tandis que le système de production gère les 45 % de cas où ce scénario ne s’applique pas.
Metadata pour chaque morceau
Une décision de conception qui apparaît rarement dans les tutoriels RAG : des métadonnées riches associées à chaque morceau. Chaque morceau de la collection ChromaDB contenait ces champs :
section_type, la partie du 10-K d’où il provient, telle que CAM, divulgation des risques, états financiers, notes de bas de page ou discussion de la directionsection_title, le titre exact situé au-dessus du contenupage_range, les pages du document source couvertes par le morceaudocument_id, le numéro de dépôt, sous forme de symbole boursier de l’entreprise accompagné de l’année fiscalehas_table, un booléen indiquant la présence de données tabulairesfiscal_year, la période de rapport
Les métadonnées avaient deux fonctions. Elles permettaient une recherche filtrée : pour une question liée à l’audit, la recherche pouvait pré-filtrer les résultats en se concentrant sur le type de section « critical_audit_matter » avant de procéder à une recherche vectorielle, ce qui réduisait considérablement l’éventail des résultats. Elles alimentaient également la couche de citation de l’interface utilisateur, car chaque résumé indiquait les pages et sections dont il était issu, permettant aux auditeurs de vérifier immédiatement la source.
Dans les outils d’audit financier, les citations sont obligatoires. Un auditeur incapable de vérifier l’origine d’un résumé ne peut pas s’y fier, et la génération de citations à partir des métadonnées a transformé le système d’un outil de simple curiosité en un outil réellement utilisable par ses utilisateurs.
Le système final
Le pipeline complet qui a réduit l’effort de révision des audits de 65 % comporte trois niveaux.
- Ingestion : pdfplumber, détection des sections, segmentation variable, extraction des métadonnées, incorporation avec text-embedding-3-large, puis stockage dans ChromaDB avec l’ensemble des métadonnées.
- Requête : réécriture de la requête (reformulation par un LLM pour plus de précision), récupération filtrée grâce à un pré-filtre par type de section le cas échéant, récupération hybride dense et BM25 pour les 10 meilleurs résultats, réclassement par cross-encoder pour obtenir les 3 meilleurs, vérification de validation, génération par GPT-4, ajout de citations et affichage dans l’interface utilisateur.
- Évaluation : ensemble de données de 30 éléments, suivi de la précision de la récupération et notation de la qualité des réponses, utilisation d’un LLM comme juge pour évaluer la pertinence, la fidélité et la complétude.
L’interface Streamlit offrait aux auditeurs une zone de saisie de requête, le résumé généré, des citations sources affichables sur demande, ainsi qu’un indicateur de confiance issu de l’étape de validation.
Que faire différemment
- Créez d’abord l’ensemble de données d’évaluation. Il a été assemblé par désespoir après l’échec de la version 1, et il devrait exister dès le premier jour. Rien ne s’améliore sans mesure, et « ça semble mieux » n’est pas une métrique.
- Concevez le schéma des métadonnées tôt dans le processus. Il a fallu du temps pour le mettre au point, mais chaque heure investie s’est révélée très rentable en termes de qualité de récupération des données et des fonctionnalités d’interface qu’il a permises. Les métadonnées méritent une place de premier plan dans l’architecture.
- Prévoyez que le parseur PDF manquera des éléments. pdfplumber est excellent, mais les cellules fusionnées, les en-têtes rotatés ou les marqueurs de notes de bas de page à l’intérieur des cellules dans les tableaux financiers peuvent encore le dérouter. Enregistrez chaque anomalie de extraction et consultez ce journal régulièrement, car les échecs silencieux de l’extraction sont les bugs les plus difficiles à retrouver par la suite.
- Mettre en doute chaque paramètre par défaut. La taille par défaut des blocs de données, le modèle d’incorporation par défaut et la valeur top-k par défaut existent parce qu’ils fonctionnent de manière acceptable en moyenne, or un type de document peut ne pas être représentatif de cette moyenne. Tester chacun d’eux avec des données réelles.
Le résultat
Après déploiement, le système a extrait et résumé les éléments clés des audits avec 65 % moins d’effort de révision manuelle que le processus qu’il a remplacé. Agissant comme un réviseur en première lecture, il a identifié les sections pertinentes et rédigé des résumés structurés, permettant ainsi aux auditeurs de passer de l’extraction et de la synthèse à la vérification et au jugement.
Ce dernier point est le plus important. L’objectif n’a jamais été de remplacer le jugement de l’auditeur, mais d’éliminer les étapes mécaniques et chronophages afin que le jugement expert soit appliqué là où il compte vraiment. Les systèmes LLM bien conçus ne remplacent pas l’expertise ; ils la redirigent.
Lectures complémentaires
- AegisOps : Un agent SRE autonome doté de quatre couches de mémoire — Comment un agent LangGraph ReAct résout les incidents Kubernetes, grâce à une mémoire épisodique pgvector, à des niveaux d’autonomie contrôlés par des politiques, à seize outils et à un suivi complet des audits.
- Comparaison de 16 architectures RAG : quel modèle convient à quel problème — Présentation de seize modèles RAG répartis en quatre catégories, allant de la récupération standard et hybride aux architectures agnitives, graphiques et fédérées, avec un ordre de développement mois par mois.