Génération augmentée par récupération sur les docs Notion via LangChain
Cet article a été initialement publié dans The Sequence et est republié ici avec autorisation.
Avez-vous des documents Notion que vous souhaitez demander à un modèle de langage d’interroger pour vous ? Construisons une application basique de type génération augmentée par récupération (RAG) à l’aide de LangChain et de Milvus. Nous utilisons LangChain comme cadre opérationnel et Milvus comme moteur de similarité. Vous pouvez trouver le notebook de ce blog sur colab.
Dans ce tutoriel, nous abordons les points suivants :
Revue de l’auto-interrogation LangChain
Travailler avec des documents Notion dans LangChain
Ingérer vos documents Notion
Stocker vos documents Notion
Interroger vos documents Notion
Résumé de l’interrogation de documents Notion avec LangChain et Milvus
Revue de l’auto-interrogation LangChain
Nous avons récemment couvert comment utiliser LangChain pour interroger une base de données vectorielle, une introduction à ce que LangChain appelle « l’auto-interrogation ». En coulisses, la fonctionnalité d’auto-interrogation de LangChain construit une architecture RAG basique comme celle illustrée ci-dessous.
Travailler avec des documents Notion dans LangChain
Je vais diviser cela en trois étapes : l’ingestion, le stockage et l’interrogation. L’ingestion couvre la récupération de vos documents Notion et le chargement du contenu en mémoire. Le stockage couvre le lancement d’une base de données vectorielle (Milvus), la vectorisation des documents, leur insertion dans la base de données vectorielle, et l’interrogation couvre le fait de poser une question sur vos documents Notion.
Ingérer vos documents Notion
Nous utilisons le NotionDirectoryLoader de LangChain pour charger les documents en mémoire. Nous fournissons le chemin vers nos documents et appelons la fonction load pour les obtenir. Une fois les documents chargés en mémoire, nous récupérons le fichier markdown, dans ce cas, un seul.
Ensuite, nous utilisons le séparateur de texte par en-têtes markdown de LangChain. Nous lui fournissons une liste de séparateurs sur lesquels effectuer le découpage, puis nous passons le md_file précédemment nommé pour obtenir nos segments. Lorsque vous définissez votre liste headers_to_split_on, assurez-vous d’utiliser les en-têtes que vous utilisez dans votre document Notion, et pas seulement les exemples que j’ai fournis.
# Load Notion page as a markdownfile file
from langchain.document_loaders import NotionDirectoryLoader
path='./notion_docs'
loader = NotionDirectoryLoader(path)
docs = loader.load()
md_file=docs[0].page_content
# Let's create groups based on the section headers in our page
from langchain.text_splitter import MarkdownHeaderTextSplitter
headers_to_split_on = [
("##", "Section"),
]
markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
md_header_splits = markdown_splitter.split_text(md_file)
Dans le code ci-dessous, nous effectuons et examinons nos découpages. Nous utilisons le RecursiveCharacterTextSplitter de LangChain, qui teste différents caractères sur lesquels découper. Les quatre caractères par défaut à vérifier sont un saut de ligne, un double saut de ligne, une espace ou aucune espace. Vous pouvez également choisir de fournir les vôtres avec un paramètre separators, que nous n’avons pas utilisé cette fois-ci.
Les deux hyperparamètres essentiels à définir lors du découpage de votre document Notion sont la taille du fragment et le chevauchement des fragments. Pour cet exemple, nous utilisons une taille de fragment de 64 et un chevauchement de 8. À l’avenir, nous couvrirons le test de ces valeurs et la recherche de bonnes valeurs. Une fois le séparateur de texte défini, nous appelons ses fonctions split_documents pour obtenir tous nos découpages Document.
# Définir notre diviseur de texte
from langchain.text_splitter import RecursiveCharacterTextSplitter
chunk_size = 64
chunk_overlap = 8
text_splitter = RecursiveCharacterTextSplitter(chunk_size=chunk_size, chunk_overlap=chunk_overlap)
all_splits = text_splitter.split_documents(md_header_splits)
all_splits
L’image ci-dessous montre quelques objets Document issus de la division ci-dessus. Notez qu’elle inclut le contenu de la page et les métadonnées, qui comprennent la section dont le contenu est extrait.
Stocker vos documents Notion
Une fois tous les documents chargés et divisés, il est temps de stocker ces fragments. Tout d’abord, nous lançons notre base de données vectorielle directement dans notre notebook à l’aide de Milvus Lite. Nous devons également obtenir les modules LangChain nécessaires - Milvus et OpenAIEmbeddings.
Après les imports et la mise en place de la base de données vectorielle, nous utilisons le module Milvus de LangChain pour créer une collection à partir de nos documents. Nous devons lui transmettre la liste de documents, les embeddings à utiliser, les paramètres de connexion et (facultativement) un nom de collection.
from milvus import default_server
default_server.start()
from langchain.vectorstores import Milvus
from langchain.embeddings import OpenAIEmbeddings
vectordb = Milvus.from_documents(documents=all_splits,
embedding=OpenAIEmbeddings(),
connection_args={"host": "127.0.0.1", "port": default_server.listen_port},
collection_name="EngineeringNotionDoc")
Interroger vos documents Notion
Tout est configuré et prêt pour les requêtes. Pour cette section, nous avons besoin de trois imports supplémentaires depuis LangChain - OpenAI pour accéder à GPT, le SelfQueryRetriever pour créer notre RAG de base, et l’objet « Attribute info » pour transmettre les métadonnées. Pour commencer, nous définissons quelques métadonnées. Pour cet exemple, seulement les sections que nous avons utilisées jusqu’à présent.
Nous donnons également au récupérateur auto-interrogeant une description des documents. Dans ce cas, simplement « principales sections du document ». Juste avant d’instancier notre récupérateur auto-interrogeant, nous définissons une version de GPT avec une température de 0 dans une variable llm. Avec le LLM, la base de données vectorielle, la description du document et les champs de métadonnées prêts, nous définissons le récupérateur auto-interrogeant.
from langchain.llms import OpenAI
from langchain.retrievers.self_query.base import SelfQueryRetriever
from langchain.chains.query_constructor.base import AttributeInfo
metadata_fields_info = [
AttributeInfo(
name="Section",
description="Part of the document that the text comes from",
type="string or list[string]"
),
]
document_content_description = "Major sections of the document"
llm = OpenAI(temperature=0)
retriever = SelfQueryRetriever.from_llm(llm, vectordb, document_content_description, metadata_fields_info, verbose=True)
retriever.get_relevant_documents("What makes a distinguished engineer?")
Mon exemple choisi est « Qu’est-ce qui fait un ingénieur distingué ? » Dans la réponse de l’image ci-dessous, nous pouvons voir les fragments les plus similaires sémantiquement qui ont été renvoyés. Comme nous pouvons le constater, ce n’est pas parce qu’ils sont les réponses les plus similaires sémantiquement que ce sont les bonnes. Dans de prochains articles, nous verrons comment expérimenter avec le découpage et d’autres techniques pour améliorer nos réponses.
Résumé de l’interrogation de documents Notion dans LangChain
Dans ce tutoriel, nous avons vu comment charger et analyser un document Notion en sections à interroger dans une architecture RAG de base. Nous avons utilisé LangChain comme framework d’orchestration et Milvus comme base de données vectorielle. LangChain assemble les éléments, et Milvus pilote la recherche de similarité.
Pour aller plus loin avec ce tutoriel, il y a de nombreuses choses que nous pouvons tester. Deux exemples d’hyperparamètres à vérifier sont la taille des fragments et la taille du chevauchement entre les fragments. Nous pouvons les utiliser pour ajuster nos réponses et leur apparence. En plus de l’ajustement, nous devons également évaluer les réponses.
Dans les futurs tutoriels, nous examinerons différentes stratégies de découpage en blocs. Non seulement cela, mais nous approfondirons également les embeddings, les stratégies de fractionnement et l’évaluation.
Continuer à lire

Zilliz Cloud On-Demand Compute: Pay Only for What You Use
The customer case behind Zilliz Cloud On-Demand: how a $10K vector search bill came down to under $500, and the engineering changes that made it possible.

How to Improve Retrieval Quality for Japanese Text with Sudachi, Milvus/Zilliz, and AWS Bedrock
Learn how Sudachi normalization and Milvus/Zilliz hybrid search improve Japanese RAG accuracy with BM25 + vector fusion, AWS Bedrock embeddings, and practical code examples.

Why Context Engineering Is Becoming the Full Stack of AI Agents
Discover how context engineering unifies prompts, RAG, and tools to build smarter, production-ready AI agents powered by Milvus.



