Accueil / Articles / Conception d’un pipeline RAG ancré dans la réalité : segmentation en blocs, filtrage et streaming.

Conception d’un pipeline RAG ancré dans la réalité : segmentation en blocs, filtrage et streaming.

Une présentation détaillée d’une base RAG ancrée dans la réalité : segmentation en blocs tenant compte de la structure, division sécurisée des tokens, filtrage de récupération en trois étapes, réassemblage des éléments associés, conception des prompts et streaming.

3895 mots

Les modèles de langage polyvalents raisonnent, écrivent et expliquent de manière remarquablement efficace tant que l’on ne leur pose pas des questions sur un système qu’ils n’ont jamais vu : votre documentation interne, vos workflows sectoriels, le comportement exact d’une application d’entreprise que votre équipe configure chaque jour. Ces connaissances ne figurent pas dans les données d’entraînement, et aucun subterfuge de prompt ne peut les y introduire. Pire encore, le modèle admet rarement cet écart ; il produit plutôt une supposition fluide et assurée. Ci-dessous se trouve la base d’un système de génération augmentée par la récupération d’informations ancrées (RAG) : comment les documents sont préparés, comment des fragments y sont créés, comment les candidats sont filtrés, comment le prompt est formulé et comment les réponses parviennent à l’utilisateur, avec la logique sous-jacente à chaque décision afin que vous puissiez l’appliquer à votre propre système.

Pourquoi l’ancrement change la tâche du modèle

La notion de base du RAG est facile à expliquer. Au lieu de faire confiance au modèle pour qu’il se souvienne d’une réponse, on lui fournit directement la réponse à lire. Votre documentation réelle est divisée en éléments pouvant être recherchés ; lorsqu’une question arrive, le système localise les passages ayant le plus de chances de y répondre et les place dans le contexte du modèle. La tâche passe de « se remémorer ce fait » à « lire ce passage et l’expliquer correctement », ce qui correspond précisément au type de travail que les modèles linguistiques accomplissent avec fiabilité.

Le concept est simple, mais le faire fonctionner correctement ne l’est pas. Récupérer les éléments appropriés, les maintenir cohérents et répondre suffisamment rapidement pour que personne ne reste bloqué en attendant une réponse impliquent des décisions qui sont facilement sous-estimées. Le système décrit ici est délibérément à un stade de base : récupérer les informations, puis répondre. Des extensions plus ambitieuses, telles que des graphes de connaissances ou un agent capable d’utiliser des outils, s’appuient précisément sur ces éléments de base, et elles ne fonctionnent que si cette base est solide.

Markdown comme source de vérité

Toutes les connaissances auxquelles le système répond se trouvent dans des fichiers Markdown. Ce choix est pratique plutôt que esthétique. Markdown offre suffisamment de structure pour préserver le sens, y compris les titres, la hiérarchie, les listes et les tableaux, tout en restant du texte brut qui peut être fragmenté et intégré sans avoir à gérer un format de marquage plus lourd.

Les fichiers sont rédigés de la manière dont les gens écrivent naturellement des documents techniques : un titre pour chaque sujet, des sous-titres pour les détails qui y sont associés, des tableaux lorsque la structure est importante, et des captures d’écran là où une image explique plus rapidement que du texte. Aucun aspect du style d’écriture n’est adapté pour s’adapter au processus de traitement. Cela est important, car la documentation qui doit être rédigée pour les systèmes de recherche a tendance à ne pas être écrite du tout.

Éviter les captures d’écran dans le chemin d’incorporation

Les captures d’écran méritent une décision de conception propre. Dans la documentation des applications, elles ne sont pas simplement décoratives : une grande partie des questions de type « comment faire » concernent en réalité le choix du bouton ou du menu à utiliser, et le texte seul ne suffit pas pour y répondre efficacement.

Au lieu d’incorporer des images directement dans les fichiers de documentation, celles-ci sont hébergées séparément et chaque fichier Markdown fait référence à elles via une URL. Le lien n’est qu’un texte, il est donc transmis lors du découpage, de l’incorporation et du récupération comme n’importe quel autre texte. Lorsqu’un bloc récupéré contenant un tel lien se retrouve dans une réponse, l’interface affiche la URL et charge l’image au moment de l’affichage. L’utilisateur voit la documentation telle qu’elle a été écrite, y compris les images, tandis que le processus d’ingestion et de récupération ne traite jamais du tout les données d’image.

Ce principe mérite d’être nommé : conserver le contenu sous forme de texte à chaque étape où seul du texte est nécessaire, et gérer les médias plus complexes au dernier moment possible, c’est-à-dire juste avant qu’ils ne soient affichés à une personne.

Diffusion en continu de la réponse au fur et à mesure de sa génération

Cette même logique du « dernier pas » s’applique à la fourniture des réponses. Une fois la génération commencée, il n’y a aucune raison de faire attendre l’utilisateur jusqu’à ce que la réponse soit complète. Les tokens reviennent par le biais d’une connexion persistante au fur et à mesure que le modèle les génère, de sorte que la réponse se construit mot par mot, un peu comme si quelqu’un tapait une explication. Associé aux images qui s’chargent de manière asynchrone au fur et à mesure que leurs URL apparaissent dans le flux, l’expérience ressemble à une conversation, même si en réalité il s’agit avant tout d’une requête de base de données suivie de la génération.

Chunking hiérarchique : respecter la structure du document

Au préalable de toute recherche, la documentation doit être divisée en unités qui peuvent chacune être intégrées et récupérées indépendamment. C’est à ce stade que de nombreux systèmes RAG prennent des raccourcis discrètement, et les conséquences sont facilement négligées.

Pourquoi le découpage à taille fixe échoue discrètement

L’approche naïve est mécanique : on choisit un nombre de tokens, on divise le document en segments d’environ cette taille, et c’est tout. Elle est rapide à mettre en place, mais elle commet des erreurs sans jamais générer d’erreur. Une fenêtre de taille fixe ne prend pas en compte les limites des phrases, et encore moins celles de nature conceptuelle. Elle divise volontiers une procédure numérotée en deux, isole une table du titre qui lui donne son sens, ou laisse une définition dans un segment tandis que son exemple se retrouve dans un autre.

Ces segments ont la longueur correcte mais la forme inadaptée, et la qualité de la récupération dépend de cette forme. Si le texte récupéré ne contient pas une pensée complète, aucune manipulation minutieuse ultérieure ne pourra la corriger. Le modèle raisonne à partir d’un fragment, et la réponse en reflète les limites.

Lire plutôt l’arbre des titres

Le découpage hiérarchique part du constat que la documentation possède déjà une structure, et que cette structure constitue un atout. Les documents techniques bien rédigés forment un arbre de titres : des sections principales, des sous-sections imbriquées en dessous d’elles, ainsi que du texte principal associé à chaque niveau. Au lieu d’ignorer cet arbre, l’outil de découpage le parcourt :

  • Chaque section principale devient un bloc à part entière.
  • Chaque sous-section devient également un bloc distinct, mais qui indique sa section parente.
  • Si une sous-section est suffisamment courte pour être lue plus facilement avec sa section parente, les deux sont fusionnées en un seul bloc combiné, plutôt que de produire un fragment minuscule dépourvu de contexte.
  • Le contenu qui ne relève d’aucun titre, comme les introductions, les paragraphes isolés ou les notes, est tout de même capturé en tant que type de bloc distinct, au lieu d’être ignoré ou forcé de manière artificielle dans un bloc voisin.

Métadonnées qui rendent possibles les étapes ultérieures

Chaque bloc contient plus que son texte uniquement :

  • Un fil d’Ariane : la chaîne des titres hiérarchiques. Un bloc concernant une seule option de configuration sait toujours qu’il fait partie d’un flux de travail plus large, même lorsqu’il est récupéré isolément.
  • Une étiquette de type : permettant de distinguer les sections de niveau supérieur, les détails imbriqués, les blocs parents et enfants fusionnés ainsi que des notes indépendantes.
  • Un identifiant stable : reliant le bloc à son origine précise dans le fichier source.

Ces métadonnées ne sont pas décoratives. Ce sont elles qui rendent possibles le réclassement, la reconstitution des sections divisées et des réponses cohérentes et traçables par la suite. Une liste plate de blocs de texte de taille égale ne peut pas assurer tout cela ; un bloc qui connaît sa place dans l’arborescence, lui, le peut.

Le compromis fondamental consiste à suivre la structure déjà existante dans le document plutôt que d’en imposer une arbitraire. Cela porte rapidement ses fruits : les segments lus ressemblent à des pensées complètes parce qu’ils le sont réellement, structurés exactement comme l’a fait leur auteur dans la documentation.

Une deuxième étape adaptative pour limiter le nombre de tokens d’incorporation

La segmentation hiérarchique assure une bonne forme aux segments, mais elle ignore une contrainte stricte : le modèle d’incorporation ne peut traiter qu’un nombre limité de tokens par entrée. La structure d’un document ne tient pas compte de cette limite. Une FAQ longue, un tableau de référence étendu ou une sous-section particulièrement volumineuse peuvent constituer des segments parfaitement cohérents tout en dépassant la capacité d’accueil du modèle.

Cette défaillance est dangereusement silencieuse. Selon le client, un bloc trop volumineux est soit rejeté, soit tronqué en silence avant son intégration, et dans les deux cas le contenu disparaît sans que personne ne s’en aperçoive au moment de l’ingestion. Le troncement est la conséquence la plus insidieuse, car le bloc existe toujours et est bien récupéré ; son vecteur ne représente simplement plus le texte que l’on croyait.

La solution consiste à effectuer une deuxième étape après celle hiérarchique. Chaque bloc est évalué par rapport à une limite de sécurité située bien en dessous du maximum réel du modèle, ce qui crée une marge de sécurité plutôt qu’un bord abrupt. Le nombre de tokens varie d’un tokeniseur à l’autre, et une marge absorbe cette incertitude. Les blocs inférieurs à cette limite passent inchangés. Ceux qui la dépassent sont divisés davantage, et chaque partie résultante est :

  • étiquetée explicitement comme faisant partie d’un tout plus grand, afin que les étapes ultérieures puissent retrouver ses éléments associés
  • Étant donné une petite superposition de texte avec le fragment voisin, personne ne lisant ce texte, qu’il s’agisse d’un humain ou d’un modèle, ne rencontre une coupure brutale et dépourvue de contexte au milieu d’une pensée
  • Déterminer précisément où effectuer la coupure, et comprendre pourquoi une segmentation naïve n’est pas suffisante même dans ce cas, constitue en soi un sujet de conception important. Fondamentalement, l’idée essentielle est que la phase hiérarchique garantit la forme et la phase adaptative assure l’adaptation, de sorte que une bonne forme ne vous fait jamais perdre du contenu.

    Stockage des vecteurs avec leurs métadonnées

    Les blocs intégrés nécessitent un système conçu pour répondre à une seule question : parmi tous ces vecteurs, lesquels sont les plus proches de ce nouveau vecteur, et ce rapidement à grande échelle. Les bases de données relationnelles ne sont pas conçues pour cette requête, c’est pourquoi un stockage de vecteurs spécifiquement développé prend en charge cette tâche.

    La base de données fonctionne à l’intérieur d’un conteneur plutôt que d’être installée directement sur l’hôte. Les raisons sont pratiques : une instance conteneurisée est facile à démarrer, à supprimer ou à déplacer vers un autre ordinateur, et elle ne crée aucune dépendance supplémentaire sur le système hôte.

    Parallèlement à chaque vecteur, le stockage conserve un en-tête de métadonnées complet contenant le texte du chunk, des informations de navigation, son type ainsi que son identifiant source. Chaque résultat de similarité arrive donc accompagné de tout le contexte nécessaire pour être utilisé immédiatement, et non pas sous forme de vecteur anonyme. C’est la combinaison d’une recherche de similarité rapide et de métadonnées riches associées à chaque résultat qui permet au filtrage, au réclassement et à la reconstitution des résultats de fonctionner sans avoir recours à une nouvelle recherche dans une autre source fiable.

    Le cycle de vie de la demande : de la question à la réponse en flux

    C’est ici que le système effectue son véritable travail, il est donc utile de le décrire avec suffisamment de précision afin que la logique puisse être réutilisée.

    Séparation de la soumission et du streaming

    L’API expose deux points d’entrée avec des tâches distinctes. Le premier reçoit la question et renvoie immédiatement un identifiant de conversation. Le second est un point d’entrée de streaming auquel le frontend se connecte à l’aide de cet identifiant. Accepter une question et fournir une réponse sont des responsabilités différentes, et c’est en les séparant que la réponse peut être transmise progressivement, plutôt que de maintenir une seule requête ouverte jusqu’à ce qu’une réponse monolithique soit prête. Cela permet également au client d’avoir un moyen clair pour se reconnecter ou de relier les journaux d’une conversation spécifique.

    Entre ces deux points d’entrée, les étapes suivantes s’exécutent dans l’ordre.

    Étape 1 : intégrer la question avec le modèle d’ingestion

    Le texte de l’utilisateur est incorporé à l’aide du même modèle utilisé lors de son ingestion. Il ne s’agit pas d’un détail qui puisse être modifié. Les comparaisons de similarité n’ont de sens que lorsque le vecteur de requête et les vecteurs stockés appartiennent au même espace vectoriel. Si le modèle d’incorporation change un jour, chaque morceau stocké doit être réincorporé, car les vecteurs provenant de deux modèles différents ne sont pas comparables, même s’ils décrivent le même texte.

    Étape 2 : lancer un large filet

    Le vecteur de requête est comparé aux vecteurs des morceaux stockés, et environ les douze résultats les plus proches sont obtenus. La mesure utilisée est la similarité cosinus, qui mesure essentiellement l’angle entre deux vecteurs : plus l’angle est petit, plus les significations sont similaires. Cette étape est délibérément généreuse ; son but est d’assurer que rien de pertinent ne soit exclu avant le début du véritable processus de sélection.

    Étape 3 : affiner les candidats à travers trois filtres

    Trois filtres s’appliquent ensuite en séquence pour réduire ces une douzaine de candidats aux quelques-uns qui sont vraiment pertinents :

    1. Un seuil de similarité. Les candidats ayant un score inférieur au minimum sont éliminés. Il s’agit de fragments qui ont fait partie de la liste uniquement parce qu’il n’y avait rien de mieux, et leur maintien ajouterait du bruit inutile.
    2. Un passage de réclassement. Les candidats restants sont réévalués par un modèle qui fonctionne différemment de la première recherche. Au lieu d’incorporer séparément la question et chaque fragment puis de comparer les vecteurs, ce modèle prend la question et un fragment en tant qu’entrée conjointe pour déterminer si ce fragment répond réellement à la question. Cela permet d’éliminer certains faux positifs : des passages qui se trouvent près de la requête dans l’espace vectoriel mais qui ne contiennent pas en réalité la réponse lorsqu’on les lit à côté d’elle.
  • Un plafond strict et un plancher rigide. Après le réclassement, un seuil beaucoup plus strict élimine ce qui reste, et ce qui survit est limité à un maximum fixe et faible.
  • Chaque étape a une fonction précise : la première protège le résultat contre les bruits, la deuxième assure la précision, et la troisième impose une limite stricte quant à la quantité de contexte qui parvient au modèle. Ce qui reste est le meilleur matériel disponible, et non simplement le premier élément d’une longue liste.

    La raison de cet ordre réside dans le coût. Le réclasseur par lecture en paires est bien plus précis que la comparaison vectorielle, mais aussi beaucoup plus coûteux, car il doit traiter chaque paire de fragments de question ensemble. Le faire fonctionner sur une douzaine de candidats préfiltrés plutôt que sur l’ensemble du corpus permet d’obtenir sa précision sans en assumer le coût total.

    Étape 4 : restaurer les sections séparées

    Certains fragments survivants ne sont que des parties d’une section que le processus adaptatif a dû diviser. Si le modèle reçoit, disons, la deuxième des trois parties sans aucun indice indiquant l’existence des autres, sa réponse se fonde sur des informations incomplètes. Par conséquent, avant que la demande ne soit assemblée, chaque fragment survivant est vérifié pour détecter ses « frères » et sœurs, c’est-à-dire les autres parties de la même section originale. Lorsqu’ils existent, ils sont récupérés et disposés dans l’ordre, chacun étant accompagné de son indicateur de position. Le modèle lit alors toute la section plutôt qu’un simple extrait d’elle. C’est précisément là que les étiquettes des parties et les identifiants stables ajoutés au moment de l’ingestion se révèlent utiles.

    Étape 5 : assembler la demande

    Tous les fragments récupérés, ainsi que leurs éléments correspondants restaurés, forment le contexte de référence. La question de l’utilisateur est transmise en même temps, et un prompt système, décrit dans la section suivante, indique au modèle le rôle qu’il doit jouer et comment utiliser ce matériel.

    Étape 6 : diffuser la réponse

    Le texte généré est renvoyé via le point d’entrée en flux auquel le client s’était connecté initialement, sous forme de petits lots plutôt que d’une seule réponse bloquante. L’utilisateur voit la réponse se former en temps réel.

    En résumé : vectoriser la requête, effectuer des recherches étendues, filtrer par trois étapes, compléter les parties manquantes, fournir un prompt au modèle et diffuser sa sortie. Aucune étape n’est particulièrement complexe. La qualité provient de transitions fluides entre les étapes et du fait que chaque étape a une seule responsabilité bien définie.

    Conception du prompt : format, modèle et ton

    Lorsque la demande est assemblée, les problèmes difficiles de récupération des informations ont été résolus : les bons fragments ont été trouvés, filtrés et réassemblés. Ce qui reste est tout aussi sujet à erreur : il s’agit de présenter ce matériel de manière à ce que le modèle produise une bonne réponse, et non simplement une réponse techniquement correcte.

    Rédiger la demande en Markdown

    Le prompt lui-même est rédigé en Markdown. La raison en est encore pratique : les modèles ont été exposés à d’immenses quantités de Markdown dans des documents, des fichiers README et des écrits techniques ; par conséquent, des instructions dans un format familier sont interprétées de manière plus fiable que des instructions dans une structure personnalisée qu’il faut déchiffrer. Les titres séparent les sections du prompt, le contexte récupéré est clairement isolé des instructions environnantes, et tout contenu reconstitué à partir de parties séparées porte un marqueur visible, permettant ainsi au modèle de distinguer « une idée continue assemblée à partir de fragments » du texte récupéré ordinaire.

    Choisir un modèle plus petit et plus rapide pour la synthèse

    C’est une tâche de synthèse. Lorsque le contexte est déjà disponible, un modèle compact offre une qualité de réponse proche celle d’un modèle bien plus grand, tout en répondant plus rapidement et à moindre coût. La capacité de raisonnement d’un modèle plus grand mérite d’être prise en compte lorsque le modèle doit résoudre un problème. Elle est beaucoup moins utile lorsque la réponse est déjà disponible et que la tâche consiste à l’expliquer correctement. Ce compromis dépend de la qualité du contexte : plus le contexte est faible, plus l’aptitude d’un modèle plus grand à gérer l’ambiguïté devient importante, ce qui constitue une raison supplémentaire d’investir dans les étapes de filtrage.

    Anatomie intentionnelle d’une instruction

    L’instruction système suit une structure fixe plutôt que d’être improvisée :

    • Rôle. Elle commence par indiquer au modèle quel type d’assistant il est et dans quel domaine il intervient, de sorte que le ton et les hypothèses sont définis dès la première ligne.
  • Tâche. Elle définit explicitement la mission : lire le contexte fourni et répondre strictement à partir de celui-ci, sans recourir aux connaissances générales ou aux suppositions, même lorsque la réponse semble évidente.
  • Confiance et lacunes. Un bref préambule indique à quel degré de confiance les réponses doivent être formulées et que faire lorsque le contexte ne contient vraiment pas la réponse : il faut l’indiquer clairement plutôt que d’inventer quelque chose.
  • Ton et style. Le degré de formalité, la longueur typique des réponses ainsi que la décision de commencer par la réponse directe ou de l’élaborer progressivement sont tous spécifiés, sans être laissés à l’interprétation.
  • Rien n’est laissé à l’interprétation du modèle concernant l’apparence d’un assistant utile. Chaque comportement est décrit en détail, tout comme lors de l’onboarding d’un nouveau collègue, où les normes de communication de l’équipe sont expliquées avant toute tâche lui étant confiée.

    Le résultat est un assistant qui répond avec assurance lorsqu’il dispose de bases solides, admet clairement le contraire et maintient une voix cohérente à travers chaque conversation. Cette cohérence ne provient pas du modèle lui-même, mais de la formulation de l’instruction qui le guide.

    Affectation par intention : blocs réguliers et blocs d’écran

    Les questions qui se ressemblent peuvent demander des choses très différentes. Une question comme « Comment le prix est-il calculé dans ce flux de travail ? » est conceptuelle et exige une explication. Une autre telle que « Quel champ sur cet écran indique le prix ? » est orientée vers la navigation et cherche un champ, un bouton ou une zone spécifique de l’interface. Bien qu’elles puissent toutes deux tirer des informations d’une même partie de la documentation, les réponses idéales n’ont presque rien en commun. Les traiter de la même manière conduit soit à un exposé conceptuel pour quelqu’un qui n’avait besoin que de l’emplacement, soit, pire encore, à une description abstraite d’un élément de l’interface alors que l’utilisateur avait besoin de son emplacement exact.

    Séparer les connaissances au moment de l’acquisition

    La distinction est établie au niveau des blocs de contenu, et non seulement au moment de la réponse. En plus des blocs de documentation en prose classiques, une deuxième catégorie contient du contenu qui décrit les écrans et leurs champs : comment chaque champ s’appelle, à quoi il sert et où il se situe par rapport aux autres champs sur le même écran. Ces éléments ne sont pas étiquetés a posteriori. La classification a lieu dès que le contenu est ingéré, de sorte qu’au moment où une question arrive, il existe deux ensembles de connaissances clairement séparés plutôt qu’un amas indifférencié.

    Choix de l’approche de réponse au moment de la requête

    Lorsqu’une question arrive, on évalue à quel type de bloc elle demande réellement accès : une documentation conceptuelle ou des détails spécifiques sur un écran et ses champs. Cette classification influence les blocs qui sont priorisés lors de la récupération des informations ainsi que le type de réponse que le modèle reçoit :

    • Une question axée sur l’écran reçoit un prompt conçu pour une précision maximale concernant les éléments de l’interface : littéral, spécifique et axé exactement sur le lieu et l’objet en question.
    • Une question conceptuelle reçoit un prompt conçu pour une explication : plus large et ouvert à la connexion entre les idées.

    Le processus de récupération des informations et le modèle de génération sont identiques dans les deux cas ; seule la manière de répondre change, choisie en fonction des besoins de la question plutôt que d’imposer une réponse via un modèle générique unique.

    Cela est plus important qu’il n’y paraît. Un assistant qui utilise un seul style de réponse finit par sonner générique, quel que soit la qualité de sa recherche. C’est en reconnaissant l’intention, et non seulement le sujet, que l’assistant donne l’impression de prêter attention à ce qui a réellement été demandé. Si vous adoptez cette approche, prévoyez une solution de secours pour les questions ambiguës, par exemple en optant pour une posture conceptuelle et en incluant tous les éléments d’écran fortement correspondants, afin qu’une mauvaise classification se produise de manière discrète plutôt que d’engendrer un style de réponse incorrect.

    Gérer la mémoire

    Un retrait et une génération minutieux sont inutiles si le service épuise progressivement sa mémoire. Un processus qui insère du texte, conserve des fragments en mémoire, assemble des prompts et génère des résultats, de manière répétée et parfois simultanée, crée progressivement une pression mémoire sans que rien ne la gère activement. Les objets nécessaires uniquement pour une requête ont tendance à persister plus longtemps que nécessaire lorsque personne ne s’occupe de les nettoyer.

    Ainsi, le nettoyage n’est pas laissé au hasard. Aux frontières naturelles, comme la fin d’une requête ou l’achèvement d’un lot de traitement, la mémoire est explicitement récupérée plutôt que de compter sur son libération spontanée. En pratique, cela signifie supprimer les références aux gros objets intermédiaires, vider les caches par requête et, pour les modèles hébergés localement, libérer toute mémoire que le système d’exécution retient encore.

    C’est un travail peu glamour qui ne figure jamais dans une démo et n’est remarqué que lorsqu’il fait défaut : un service dont les performances se détériorent au fil d’un temps de fonctionnement prolongé, plutôt que de répondre à sa millième requête aussi rapidement qu’à la première. Le succès dépend moins d’une ingénierie astucieuse que de la discipline, en traitant la mémoire comme quelque chose que chaque étape gère elle-même et non comme quelque chose que le système d’exécution s’occupera forcément. Surveiller l’utilisation de la mémoire au cours d’un test de charge prolongé, et non seulement lors d’un bref benchmark, est le moyen le plus simple de confirmer que cette discipline fonctionne bien.

    Points clés

    La base décrite ici constitue un pipeline RAG complet et solide : un découpage tenant compte de la structure, des embeddings sécurisés pour les tokens, un filtrage en plusieurs étapes, une réassemblage des sections séparées, des instructions bien structurées qui incitent le modèle à être honnête sur ses connaissances, ainsi qu’une livraison en temps réel. Les décisions les plus importantes sont les suivantes :

    • Gardez les documents sous un format de texte brut structuré et ne traitez les images et autres médias qu’au moment de l’affichage.
    • Découpez le contenu en fonction de la structure des titres, et ajoutez à chaque partie des pistes de navigation, des étiquettes de type ainsi que des identifiants stables.
    • Ajoutez une deuxième étape qui respecte la limite de tokens du modèle d’incorporation en intégrant une marge de sécurité, des parties étiquetées et de petits chevauchements.
    • Incorporez toujours les requêtes avec le même modèle utilisé lors de l’ingestion, et réincorporez tout lorsque ce modèle change.
    • Récupérez un grand nombre de résultats, puis affinez-les à l’aide d’un seuil de similarité, d’un réordonneur par lecture en paire et d’une limite finale stricte.
    • Reconstituez les sections séparées avant de formuler la demande, afin que le modèle ne doive pas raisonner sur un fragment non marqué.
    • Laissez un modèle plus petit et plus rapide effectuer la synthèse une fois que la récupération des données a accompli le travail difficile, en spécifiant explicitement son rôle, sa tâche, la gestion des lacunes et le ton souhaité.
  • Classifiez l’intention, et non seulement le sujet, et attribuez à chaque type de question sa propre approche de réponse.
  • Récupérez la mémoire sur demande et aux limites des lots afin que le service reste aussi rapide après de longues heures d’activité qu’à son démarrage.
  • Chacune de ces options est modeste en soi. Ensemble, elles forment un système dont les réponses sont fondées, cohérentes et rapides, et elles vous fournissent une base stable pour des techniques de récupération plus ambitieuses par la suite.

    Lectures complémentaires