Accueil / Articles / Comparaison de RAG, de RAG sans vecteurs et de GraphRAG

Comparaison de RAG, de RAG sans vecteurs et de GraphRAG

RAG vectoriel classique, recherche sans vecteurs lexicaux et GraphRAG : segmentation en chunks, embeddings, BM25, graphes à plusieurs sauts, et les cas où chaque méthode s’avère utile.

1834 mots

Comment les modèles de langage d’aujourd’hui répondent à partir de matériel qui n’a jamais figuré dans leur formation.

RAG en une phrase

Retrieval Augmented Generation signifie exactement ce que son acronyme indique. D’abord, on récupère du matériel lié à la question de l’utilisateur. Ensuite, un modèle rédige une réponse en utilisant ce matériel ainsi que l’instruction fournie. Le processus consiste d’abord en la récupération des informations, puis en leur transformation en réponse.

Pourquoi se donner cette peine ?

Les modèles ne connaissent que ce qui figurait dans leur corpus de formation. Tout le reste leur est invisible.

Imaginez un livre rare de 3 000 pages qui apparaît à peine en ligne et n’a jamais fait partie d’aucun corpus de formation. Si vous posez une question à un modèle à son sujet, vous obtiendrez des réponses vides. Même en utilisant des outils de collecte de données, vous n’obtiendrez rien — il n’y a rien à extraire.

RAG comble cette lacune. Chargez le PDF dans le pipeline. Au moment de la requête, récupérez les sections les plus pertinentes et ajoutez-les en contexte. La tâche du modèle se réduit alors à : répondre à partir de ce texte, en utilisant ses compétences linguistiques pour structurer la réponse.

Aucune fine-tuning nécessaire. Il suffit du bon contexte au bon moment.

Squelette du pipeline

L’indexation vient en premier, et elle commence par le découpage.

Stratégies de découpage

Découpages courants :

Pour page. Une page → un chunk (3 000 pages → 3 000 chunks). Idéal lorsque l’on souhaite des résultats précis et ciblés.

Pour paragraphe. Des coupes plus fines aux limites des paragraphes. Cela peut donner un rendu plus net, mais les tailles varient fortement (200 tokens à côté de 2 000). Des longueurs inégales entraînent des embeddings inégaux et nuisent discrètement à la récupération d’informations.

Fenêtres fixes. Des budgets de tokens constants — souvent 512 — quel que soit l’endroit où la coupe a lieu. Des tailles uniformes produisent des embeddings plus comparables. En cas d’incertitude, la plupart des équipes optent pour cette méthode.

from langchain_text_splitters import CharacterTextSplitter
from langchain_core.documents import Document

def perform_fixed_size_chunking(document, chunk_size=1000, chunk_overlap=200😞
    """
    Performs fixed-size chunking on a document with specified overlap.

    Args:
        document (str): The text document to process
        chunk_size (int): The target size of each chunk in characters
        chunk_overlap (int): The number of characters of overlap between chunks

    Returns:
        list: The chunked documents with metadata
    """
    # Create the text splitter with optimal parameters
    text_splitter = CharacterTextSplitter(
        separator="\n\n",
        chunk_size=chunk_size,
        chunk_overlap=chunk_overlap,
        length_function=len
    )

    # Split the text into chunks
    chunks = text_splitter.split_text(document)
    print(f"Document split into {len(chunks)} chunks")

    # Convert to Document objects with metadata
    documents = []
    for i, chunk in enumerate(chunks):
        doc = Document(
            page_content=chunk,
            metadata={
                "chunk_id": i,
                "total_chunks": len(chunks),
                "chunk_size": len(chunk),
                "chunk_type": "fixed-size"
            }
        )
        documents.append(doc)

    return documents

# Example usage
if __name__ == "__main__":

    # Create the dummy document
    document = create_dummy_document()

    # Process with fixed-size chunking
    chunked_docs = perform_fixed_size_chunking(
        document,
        chunk_size=1000,
        chunk_overlap=200
    )

    # Display results
    print("\n----- CHUNKING RESULTS -----")
    print(f"Total chunks: {len(chunked_docs)}")

    # Print an example chunk
    print("\n----- EXAMPLE CHUNK -----")
    middle_chunk_idx = len(chunked_docs) // 2
    example_chunk = chunked_docs[middle_chunk_idx]
    print(f"Chunk {middle_chunk_idx} content ({len(example_chunk.page_content)} characters):")
    print("-" * 40)
    print(example_chunk.page_content)
    print("-" * 40)
    print(f"Metadata: {example_chunk.metadata}")

    # For integration with Databricks Vector Search
    print("\nThese documents are ready for embedding and storage in Databricks Vector Search")
    print("Example next steps:")
    print("1. Create embeddings using the Databricks embedding endpoint")
    print("2. Store documents and embeddings in Delta table")
    print("3. Create Vector Search index for retrieval")

Embeddings

Un embedding transforme du texte (mot, phrase, page) en un vecteur dense et à haute dimension qui encode le sens, de sorte que des idées similaires se regroupent.

Quatre phases au sein de RAG

  1. Côté corpus. Embedder chaque morceau ; stocker l’index + le vecteur.
  2. Côté requête. Embedder l’instruction de l’utilisateur afin que la demande dispose d’une empreinte sémantique.
  3. Récherche. Chercher dans le stockage les voisins les plus proches — généralement par similarité cosinus.
  • Augmentation. Joignez les extraits récupérés comme contexte supplémentaire ; le modèle répond en s’appuyant à la fois sur l’instruction et sur ce contexte.
  • Où se trouvent les vecteurs

    Ce sont des entrepôts spécialisés dans les embeddings, et non des bases de données relationnelles ou d’objets générales. AlloyDB, Pinecone et Qdrant sont courants ; de nombreuses équipes utilisent pgvector avec PostgreSQL.

    Où le RAG vectoriel classique rencontre des difficultés

    1. Les coupes peuvent ignorer le sens. Les fenêtres relatives peuvent être séparées ; une plus grande superposition aide parfois, mais ce n’est pas une solution universelle.
    2. La similarité peut manquer les paraphrases — « les ventes ont diminué » et « l’entreprise est en déclin » ne se trouvent pas nécessairement à proximité dans l’espace vectoriel.
    3. Les faits multi-étapes ne fonctionnent pas lorsque la cause et l’effet se trouvent dans des segments différents et qu’un seul est récupéré.
    4. La création, le stockage, l’indexation et la réindexation des embeddings sont coûteux.

    De plus : une base de données vectorielle n’est pas synonyme de RAG. C’est un seul type d’arrière-plan de recherche. Le RAG sans vecteurs conserve le principe « récupérer puis générer » tout en éliminant la recherche par embedding.

    Pourquoi éviter les vecteurs ? Le coût élevé de création/reindexation des embeddings, un comportement de correspondance exacte insuffisant pour les IDs, les nombres ou les codes d’erreur, ainsi que des infrastructures supplémentaires nécessaires à leur fonctionnement.

    Le RAG sans vecteurs constitue une famille de méthodes et non une solution unique :

    1. Recherche lexicale. BM25, Postgres tsvector, Elasticsearch — les termes exacts sont préférables aux sémantiques floues pour les SKUs, les citations et les lignes de journal.
    from rank_bm25 import BM25Okapi
    
    def vectorless_retrieve(query, corpus_chunks, top_k=3):
        """
        Lexical retrieval over raw text chunks - no embeddings, no vector DB.
        """
        tokenized_corpus = [chunk.lower().split() for chunk in corpus_chunks]
        bm25 = BM25Okapi(tokenized_corpus)
    
        tokenized_query = query.lower().split()
        scores = bm25.get_scores(tokenized_query)
    
        ranked = sorted(zip(corpus_chunks, scores), key=lambda x: x[1], reverse=True)
        return [chunk for chunk, score in ranked[:top_k]]
    
    1. Récupération par agents/outils. Pas d’index préalable ; le modèle analyse, appelle des API de recherche ou ouvre des sections sur demande, tout comme un agent de codage parcourt un dépôt. Recherche en temps réel basée sur le raisonnement.
  • Remplissage par contexte long. Grâce à de très grands fenêtres de contexte, de petits corpus peuvent être inclus dans la requête. Ce n’est pas une récupération classique, mais les résultats sont similaires pour des corpus modestes.
  • Ré-évaluation hybride. Une liste courte de mots-clés peu coûteuse, suivie d’un modèle qui réordonne les éléments en fonction de leur pertinence — vitesse grâce aux mots-clés avec une certaine nuance sémantique, sans index d’embedding complet au préalable.
  • Limites des approches sans vecteurs

    Les mots-clés échouent encore à gérer les paraphrases — parfois même pire que les embeddings. Les boucles agentives ajoutent du retard et davantage de tokens par requête. De très grands corpus privilégient toujours un index vectoriel bien conçu. Les approches sans vecteurs ont tendance à l’emporter à petite ou moyenne échelle, ou lorsque la précision prime sur la flexibilité.

    GraphRAG

    Le RAG classique peut séparer la cause de l’effet au sein des différents fragments. GraphRAG remplace les embeddings par des mots-clés ou un contexte long. Aucun de ces deux approches ne modélise la relation entre les idées. GraphRAG vise à combler cette lacune.

    Au lieu de se demander « quel fragment est le plus proche ? », demandez plutôt « comment ces concepts sont-ils liés ? » La réponse est un graphe de connaissances.

    Indexation — développer le graphe

    Omettez d’abord les fragments ou embeddings. Faites passer le document par un modèle qui extrait des entités et des relations. Résultat : des nœuds et des arêtes.

    Dans ce long livre théorique, vous pourriez obtenir des nœuds tels que Marx, Engels, Le Capital, Le Manifeste communiste, la plus-value, le matérialisme dialectique, ainsi que des arêtes comme « a écrit », « a co-écrit », « introduit un concept ».

    Les entités deviennent des nœuds ; les relations deviennent des arêtes. Vous cartographiez le sens, et non vous divisez les pages.

    Groupez les nœuds étroitement liés en communautés (Leiden est une méthode populaire), puis résumez chaque communauté lors d’un autre passage du modèle.

    Opérez à deux niveaux :

    • Nœuds pour des faits spécifiques et des liens directs
    • Communautés pour les thèmes et les résumés

    Questions précises → nœuds. Questions générales sur les « idées fondamentales » → résumés communautaires. Un index, deux modes de récupération.

    Interrogation — parcourir le graphe

    Question : « Qui a co-écrit avec Marx, et qu’ont-ils écrit ensemble ? »

    RAG classique rencontre des difficultés : la co-auteurship se trouve dans un seul bloc de texte, tandis que les œuvres concernées sont à des centaines de pages de distance ; on espère alors que les embeddings seront compatibles.

    RAG basé sur le graphe procède ainsi :

    1. Détecter Marx en tant qu’ancrage
    2. Charger le nœud et les arêtes de Marx
    3. Suivre la relation co-écrit → Engels
    4. Suivre les œuvres écrites par Engels → Le Manifeste, La Condition de la classe ouvrière, œuvres connexes

    Les arêtes directionnelles maintiennent les relations intactes. Les causes et effets, autrefois séparés dans le RAG classique, deviennent des nœuds reliés. Ce schéma à plusieurs sauts est difficile à gérer correctement tant pour le RAG basique que pour celui sans vecteurs.

    Assembler les nœuds, les arêtes et les résumés communautaires pour former un contexte ; générer le résultat comme d’habitude.

    from graphrag import GraphRAGPipeline
    
    # Indexing — runs once
    pipeline = GraphRAGPipeline(llm="claude-3", graph_store="neo4j")
    pipeline.index(documents=["book.pdf"])
    # Under the hood: entity extraction → graph build → community detection → summaries
    
    # Querying
    result = pipeline.query(
        "Who co-wrote with Marx and what did they write together?",
        mode="global"   # uses community summaries for broad questions
        # mode="local"  # uses node-level traversal for specific facts
    )
    print(result.answer)
    print(result.sources)   # returns actual nodes + edges used, fully traceable
    

    mode n’est pas purement esthétique. Les implémentations (y compris la pile open source de Microsoft) distinguent les approches globales des approches locales, car il s’agit de stratégies différentes.

    Coûts de GraphRAG

    L’extraction est coûteuse. Il faut lire l’ensemble du document ; les livres longs consomment beaucoup de tokens ; les références implicites (“comme discuté précédemment”) ne deviennent pas nécessairement des arêtes.

    La qualité du graphe limite la qualité du résultat. Une extraction médiocre → un graphe de mauvaise qualité → une récupération inefficace. Les correctifs impliquent souvent de relire tout le contenu, ce qui est problématique à grande échelle.

    En pratique, choisissez le système de récupération en fonction du type de requête plutôt que d’imposer une seule méthode pour toutes les questions.

    Résumé : utilisez le RAG classique pour les recherches sémantiques, une méthode sans vecteurs lorsque vous avez besoin de précision ou d’une infrastructure réduite, et Graph RAG lorsque les relations entre éléments sont essentielles.

    Choisir un style de récupération sans dogme

    Une séquence de décision utile se présente comme suit.

    Démarrez avec le RAG vectoriel classique lorsque votre corpus est volumineux, que les langues varient beaucoup et que vous avez généralement besoin de voisins sémantiques approximatifs. Investissez dès le départ dans la qualité du découpage en chunks et dans les processus de mise à jour des embeddings ; ce sont ces éléments qui déterminent principalement la qualité.

    Préférez les techniques non vectorielles lorsque des identifiants exacts sont plus importants que la paraphrase, lorsque le corpus est suffisamment petit pour permettre une recherche lexicale ou un contexte long, ou lorsque vous refusez de supporter les coûts liés à l’infrastructure d’embeddings. BM25 et ses variantes ne sont pas « démodés » : ce sont les outils idéaux pour les SKU, les citations et les chaînes d’erreurs.

    Optez pour le Graph RAG lorsque la question concernant le produit est relationnelle : qui est en lien avec qui, quel concept a introduit quelle idée, quelle communauté résume un thème. Prévoyez des coûts d’indexation plus élevés et considérez la qualité de l’extraction comme une exigence prioritaire.

    De nombreuses équipes optent pour une approche hybride : première passe lexicale, deuxième passe vectorielle, et utilisation de graphes pour un sous-ensemble de domaines à plusieurs étapes. Il ne s’agit pas de choisir une méthode en particulier, mais plutôt d’adapter l’outil de recherche au mode de défaillance que l’on observe réellement.

    Notes opérationnelles que les démonstrations omettent

    Les calendriers de réindexation sont importants. Des embeddings obsolètes détériorent silencieusement les performances du RAG vectoriel, même lorsque le modèle reste inchangé. Les paramètres de chevauchement sont cruciaux lorsque des fenêtres adjacentes partagent un sens similaire. Les filtres de métadonnées sont essentiels lorsque les différents utilisateurs ne doivent absolument pas voir les données des autres. Les ensembles d’évaluation sont importants lorsque l’on prétend obtenir de meilleurs résultats sans étiquettes.

    Pour Graph RAG, il convient de prévoir des tentatives répétées d’extraction, des mises à jour partielles du graphes ainsi que une résumation communautaire en cas de modification des documents. Pour la recherche agente, il faut planifier des budgets d’outils et des politiques de délai d’exécution afin qu’un modèle curieux ne consomme pas inutilement tous les tokens disponibles pour une seule requête.

    Aucune de ces notes n’est glamour. Ce sont elles qui font la différence entre un diagramme et un système capable de résister à un mois d’utilisation réelle.