AccueilClientsExpertisesOpen SourceBlogContactEstimer

13 mai 2025

Comment fonctionne un RAG ?

11 minutes de lecture

Comment fonctionne un RAG ?
🇺🇸 This post is also available in english

Ces dernières années ont été marquées par la démocratisation des LLMs (Large Language Models), ces modèles de langage qui permettent de générer des textes sur la base de données générales. Aujourd'hui, l'intérêt qui leur est porté dépasse largement les frontières de la tech et des mathématiques. Les applications de ces modèles sont nombreuses et variées, allant de la génération de texte à la compréhension de la langue naturelle en passant par la traduction. En somme, des outils puissants qui ne cessent de se développer et de s'améliorer, mais qui souffrent encore de certaines limitations que nous allons aborder dans cet article.

Introduction

Avec la démocratisation de ces outils, beaucoup de personnes ont pu expérimenter et utiliser les modèles de langage "génériques", je précise génériques car ces LLMs sont entraînés sur des données générales et ne sont donc pas spécialisés dans un domaine précis. Ce qui peut rapidement devenir un frein lorsque l'on souhaite générer des réponses précises et contextuelles. Les structures souhaitant exploiter les LLMs pour leurs besoins ont donc dû trouver des solutions pour améliorer la qualité des réponses générées. Deux solutions sont apparues :

  • Fine-Tuning sur un modèle générique
  • RAG (Retrieval-Augmented Generation)

Dans cet article, nous allons nous concentrer sur les inconvénients de la première solution, le Fine-Tuning, et nous concentrerons sur la seconde solution, le RAG, et comment il peut être utilisé pour améliorer la qualité des réponses générées par les LLMs et comment le mettre en place. Nous allons expliquer de manière détaillée comment fonctionne un RAG et comment le mettre en place de manière générale, il existe cependant de nombreuses variantes de ce système qui vont dépendre des modèles de langage et/ou des plateformes utilisées. L'idée est donc de comprendre les principes généraux et de pouvoir les appliquer à des cas concrets.

TL;DR

Un RAG est un système qui va nous permettre d'enrichir nos questions envoyées à un LLM avec des informations contextuelles. Pour ce faire, il utilise l'embedding (ou plongement vectoriel) des documents et une base de données vectorielle pour stocker ces vecteurs. Cela nécessite de mettre en place et de remplir, en amont, la base de données vectorielle avec nos documents. Enfin, lors de l'envoi d'une question au LLM, celui-ci recevra non seulement la question originale mais également le contexte des documents les plus proches de la question grâce à la recherche vectorielle ou similarity search.

RAG

Fine-Tuning

Les LLMs sont des modèles de langage qui sont entraînés sur des données générales, ce qui leur permet de générer des réponses très performantes. Cependant, il est possible de les entraîner sur des données spécifiques à un domaine, ce qui leur permet de générer des réponses plus précises et contextuelles, on peut voir cela comme un sur-entraînement du modèle sur les données spécifiques. Cela peut être une solution efficace mais qui comporte des inconvénients :

  • L'entraînement peut être coûteux en temps et en ressources
  • L'entraînement se fait sur des données statiques, il est donc nécessaire de mettre à jour le modèle régulièrement pour maintenir les connaissances à jour
  • Il est nécessaire de gérer les données d'entraînement, ce qui peut être une tâche complexe et fastidieuse

Évidemment, cette solution n'est pas sans intérêt, elle permet dans certains usages de générer des réponses très performantes. Mais ici nous allons nous intéresser à un usage plutôt orienté Question/Réponse qui sera censé répondre de manière précise et contextuelle à une question. Pour cela, nous allons nous tourner et approfondir une autre solution: le RAG.

Pour des besoins spécifiques, il est possible de combiner les deux solutions. En utilisant dans un RAG un modèle pré-entraîné.

Retrieval-Augmented Generation

Un RAG pour Retrieval-Augmented Generation est un système qui va nous permettre d'enrichir nos questions envoyées à un LLM avec des informations contextuelles. Pour ce faire, il utilise l'embedding (ou plongement vectoriel) des documents et une base de données vectorielle pour stocker ces vecteurs. Cela nécessite de mettre en place et de remplir, en amont, la base de données vectorielle avec nos documents. Enfin, lors de l'envoi d'une question au LLM, celui-ci recevra non seulement la question originale mais également le contexte des documents les plus proches de la question grâce à la recherche vectorielle ou similarity search.

Comment fonctionne un RAG ?

Comme nous l'avons vu précédemment, une étape importante d'un RAG est de mettre en place et de remplir la base de données vectorielle avec nos données. Cette étape est primordiale pour le bon fonctionnement du RAG. Nous allons revenir en détails sur les différentes manières de créer ces embeddings.

Une fois que ces embeddings sont créés, lors de l'envoi d'une question au LLM, celle-ci sera vectorisée et comparée aux vecteurs des documents présents dans la base de données vectorielle. Cette comparaison permettra de récupérer les quelques morceaux de documents les plus pertinents et de les envoyer au LLM en plus de la question originale.

Il suffira alors au LLM de générer une réponse en s'appuyant sur le contexte fourni en entrée.

Travail préliminaire

Les données d'entrée

Une partie importante d'un RAG est la qualité des données utilisées pour créer les embeddings. En effet, plus les données sont pertinentes et contextuelles, plus la réponse générée par le LLM sera précise.

L'origine et le format des données d'entrée sont très variés. En effet, il peut s'agir de documents textuels, d'images, de pdf, de tableurs, et même de données extraites directement d'une base de données. Il est donc important de pouvoir les traiter de manière uniforme et de pouvoir les convertir en embeddings. La seule contrainte est que les données doivent être convertibles en texte.

Dans certains cas, il peut être nécessaire de convertir les données en texte. Par exemple, si les données sont des images, il est possible de les convertir en texte en utilisant des modèles de vision par ordinateur, ce qui permettra d'obtenir une description textuelle du contenu de l'image. Pour les PDFs, il est possible d'utiliser des modèles d'OCR (Optical Character Recognition) pour extraire le texte comme Mistral OCR par exemple.

Partitionnement ou chunking

Le partitionnement ou "chunking" est l'étape qui consiste à découper nos documents en morceaux plus petits afin de les stocker dans notre base de données vectorielle. Cette étape est cruciale car elle influence directement la performance de notre RAG.

Partitionnement

Taille des chunks

La taille des chunks est un paramètre important à considérer lors de la mise en place d'un RAG. Il faut souvent expérimenter avec différentes tailles et différents niveaux de chevauchement pour trouver la configuration optimale. Les chunks plus petits permettent généralement une recherche plus précise, car ils contiennent moins de texte de remplissage qui pourrait diluer la représentation sémantique. Cela aide le système RAG à identifier et extraire plus efficacement les informations pertinentes. Toutefois, cette précision s'accompagne d'un coût : les chunks plus petits augmentent le temps de traitement et les ressources nécessaires.

Méthodes de découpage

Bien que la méthode la plus simple soit de découper le texte par caractère, d'autres options existent selon le cas d'utilisation et la structure du document :

  • Par tokens : pour éviter de dépasser les limites de tokens dans les appels API
  • Par phrases ou paragraphes : pour maintenir la cohérence des chunks
  • Par en-têtes HTML ou propriétés JSON : pour respecter la structure du document
  • Par morceaux de code significatifs : si vous travaillez avec du code, il est souvent recommandé d'utiliser un analyseur d'arbre syntaxique abstrait (AST)

Embeddings

Maintenant que nous avons nos données partitionnées, nous pouvons les convertir en embeddings, c'est-à-dire en vecteurs qui vont représenter nos données et nous permettre de capturer leur signification sémantique. Pour ce faire, nous allons utiliser un modèle d'embedding comme mistral-embed de Mistral ou text-embedding-3-small de OpenAI par exemple.

Visualisation des embeddings

Pourquoi utiliser un modèle d'embedding plutôt que de générer des embeddings programmatiquement?

Il existe en effet de nombreuses manières programmatiques de générer des embeddings, mais l'utilisation d'un modèle d'embedding pré-entraîné permet de générer des embeddings de meilleure qualité car le modèle permet de mieux saisir le sens des données, l'intention et le contexte d'un chunk. Le vecteur généré sera donc plus pertinent.

Base de données vectorielle

La base de données vectorielle est un outil essentiel pour stocker et récupérer les embeddings. Elle va nous permettre de stocker les vecteurs et de les récupérer rapidement. Il existe plusieurs solutions pour mettre en place une base de données vectorielle, soit dédiées au RAG, soit intégrées à une pile logicielle existante si celle-ci le permet. Parmi les solutions les plus courantes, on peut citer Pinecone, Faiss, Annoy, HNSW ou encore Milvus.

Si votre projet utilise déjà une base de données PostgreSQL, il est possible d'utiliser l'extension pgvector pour stocker et récupérer les embeddings. Cela permet de bénéficier d'une base de données vectorielle nativement intégrée à votre projet et donc de créer des liens entre vos données et vos embeddings. Très utile lorsque l'on souhaite récupérer des informations relatives à des documents.

Base de données vectorielle

Voilà, nous avons maintenant nos données partitionnées, nos embeddings et notre base de données vectorielle. Nous pouvons maintenant passer à la phase d'interprétation des questions et de génération de réponses en créant un Assistant RAG.

Création d'un Assistant RAG

Cette phase d'utilisation d'un RAG va fortement dépendre de la plateforme utilisée et/ou du framework utilisé, dans certains cas, il sera possible de créer un Assistant RAG en mode autonome. Autrement, il sera nécessaire de créer un Assistant RAG en mode autonome en définissant les outils nécessaires et la manière de les utiliser explicitement. Dans les deux cas, il sera nécessaire de définir les outils nécessaires pour générer des réponses précises et contextuelles.

Définir les outils

Par outil, nous entendons les différentes fonctions qui vont être utilisées pour récupérer les données dans la base de données vectorielle et les utiliser pour générer des réponses précises et contextuelles.

Recherche vectorielle

La recherche vectorielle est la fonction qui va permettre de récupérer les données dans la base de données vectorielle en fonction de la question.

Dans un premier temps, il faudra transformer la question en vecteur grâce au modèle d'embedding que nous avons utilisé pour créer nos embeddings plus tôt. De cette manière, nous pourrons comparer le vecteur de la question à tous les vecteurs des documents présents dans la base de données vectorielle à l'aide d'une fonction de recherche vectorielle comme similarity avec l'extension pgvector de PostgreSQL.

Recherche vectorielle

Cette recherche vectorielle va nous permettre d'obtenir les documents les plus proches de la question, c'est-à-dire les documents qui ont le plus de ressemblance avec la question.

Pour l'exemple, nous allons utiliser la fonction similarity avec l'extension pgvector de PostgreSQL.

tool.ts
export const findRelevantContent = async (userQuery: string) => {
  const embedding = await generateEmbedding(userQuery);
  const vectorQuery = `[${embedding.join(",")}]`;
  const embeddings = await db.$queryRaw
  `
      SELECT
        embeddings.id,
        embeddings.content,
        documents.name,
        1 - (embedding <=> ${vectorQuery}::vector) as similarity
      FROM embeddings
      INNER JOIN documents ON embeddings.document_id = documents.id
      WHERE 1 - (embedding <=> ${vectorQuery}::vector) > .5
      ORDER BY similarity DESC
      LIMIT 5;
    `;
  return embeddings;
};

Ici on limite la recherche à 5 chunks ayant une similarité supérieure à 0.5. Cette valeur est arbitraire et peut être ajustée en fonction de la pertinence des résultats.

Prompt

Le prompt est le texte qui va être envoyé au LLM pour générer une réponse. Il va contenir les directives pour le LLM afin qu'il génère une réponse pertinente en lui précisant d'utiliser l'outil de recherche vectorielle pour récupérer les documents les plus proches de la question.

C'est dans cette partie qu'il faudra également préciser à l'assistant comment il doit utiliser le contexte des documents.

Pour l'exemple, nous allons utiliser le SDK de Vercel pour générer une réponse en lui précisant d'utiliser l'outil de recherche vectorielle pour récupérer les documents les plus proches de la question.

generate-answer.ts
export async function POST(req: Request) {
  const { messages } = await req.json();
  const result = await streamText({
    model: model,
    messages: convertToCoreMessages(messages),
    system: `You are a helpful assistant. Check your knowledge base before answering any questions.
    Only respond to questions using information from tool calls.
    If no relevant information is found in the tool calls, respond, "Sorry, I don't know."`,
    tools: {
      getInformation: tool({
        description: `get information from your knowledge base to answer questions.`,
        parameters: z.object({
          question: z.string().describe("the users question"),
        }),
        execute: async ({ question }) => findRelevantContent(question),
      }),
    },
    toolChoice: "required",
  });
  return result.toAIStreamResponse();
}

La manière d'imposer à l'assistant l'utilisation de l'outil de recherche vectorielle dépendra du SDK utilisé.

Conclusion

Vous l'aurez compris, le RAG est un outil puissant qui peut être utilisé pour générer des réponses précises et contextuelles et éviter tout problème d'hallucinations, fréquent lors de l'utilisation de LLMs sur des sujets complexes. En orientant et en contextualisant les questions envoyées au LLM, il est possible d'obtenir des réponses qui s'appuient réellement sur vos données.

Les cas d'utilisation sont nombreux, chez Premier Octet, nous avons déjà pu mettre en place des RAGs sur des projets de chatbots et de génération de réponses, pour des structures qui souhaitaient utiliser la puissance des LLMs tout en l'adaptant à leurs propres besoins et leurs données.

Si vous avez des retours d'expérience sur ces sujets-là, ou que vous souhaitez en savoir plus sur le RAG, n'hésitez pas à nous contacter.

Références

À découvrir également

DotJS 2025 : focus sur l’avenir de JavaScript et l’IA

07 Apr 2025

DotJS 2025 : focus sur l’avenir de JavaScript et l’IA

On était à la conférence DotJS 2025, on vous raconte tout

par

Baptiste

et

Colin

Document Picture-in-Picture, le PiP pour n’importe quel contenu HTML

02 Apr 2025

Document Picture-in-Picture, le PiP pour n’importe quel contenu HTML

Découvrez comment utiliser la Document Picture-in-Picture API pour afficher n’importe quel contenu HTML dans une fenêtre flottante toujours visible, et explorez des cas d’usage innovants qui réinventent l’expérience utilisateur sur le web.

par

Quentin

Comment simuler une réponse streamée avec le SDK Vercel AI

23 Oct 2024

Comment simuler une réponse streamée avec le SDK Vercel AI

Découvrez comment simuler une réponse streamée avec le SDK Vercel AI et useCompletion.

par

Baptiste

Premier Octet vous accompagne dans le développement de vos projets avec openai

En savoir plusNous contacter
18 avenue Parmentier
75011 Paris
+33 1 43 57 39 11
hello@premieroctet.com

Suivez nos aventures

GitHub
X
Flux RSS

Naviguez à vue