Geração Aumentada por Recuperação em documentos do Notion via LangChain
Este artigo foi publicado originalmente em The Sequence e é republicado aqui com permissão.
Você tem documentos do Notion que deseja pedir a um modelo de linguagem para consultar por você? Vamos criar um aplicativo básico do tipo geração aumentada por recuperação (RAG) usando LangChain e Milvus. Usamos LangChain como estrutura operacional e Milvus como mecanismo de similaridade. Você pode encontrar o notebook deste blog no colab.
Neste tutorial, passamos pelo seguinte:
Revisão de Self Querying do LangChain
Trabalhando com documentos do Notion no LangChain
Ingerindo seus documentos do Notion
Armazenando seus documentos do Notion
Consultando seus documentos do Notion
Resumo de consulta a documentos do Notion com LangChain e Milvus
Revisão de self querying do LangChain
Recentemente abordamos como usar o LangChain para consultar um banco de dados vetorial, uma introdução ao que o LangChain chama de “self-querying”. Nos bastidores, a funcionalidade de self-querying no LangChain está construindo uma arquitetura RAG básica como a mostrada abaixo.
Trabalhando com documentos do Notion no LangChain
Vou dividir isso em três etapas: ingestão, armazenamento e consulta. A ingestão cobre obter seus documentos do Notion e carregar o conteúdo na memória. O armazenamento cobre iniciar um banco de dados vetorial (Milvus), vetorizar os documentos, colocá-los no banco de dados vetorial, e a consulta cobre fazer uma pergunta sobre seus documentos do Notion.
Ingerindo seus documentos do Notion
Usamos o NotionDirectoryLoader do LangChain para carregar os documentos na memória. Fornecemos o caminho para nossos documentos e chamamos a função load para obtê-los. Depois que os documentos são carregados na memória, pegamos o arquivo markdown, neste caso, apenas um.
Em seguida, usamos o divisor de texto por cabeçalhos markdown do LangChain. Fornecemos a ele uma lista de divisores para dividir e então passamos o md_file nomeado anteriormente para obter nossas divisões. Ao definir sua lista headers_to_split_on, certifique-se de usar os cabeçalhos que você usa no seu documento do Notion, não apenas os exemplos que forneci.
# 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)
No código abaixo, realizamos e examinamos nossas divisões. Usamos o RecursiveCharacterTextSplitter do LangChain, que testa alguns caracteres diferentes para dividir. Os quatro caracteres padrão a verificar são uma nova linha, uma nova linha dupla, um espaço ou nenhum espaço. Você também pode optar por passar os seus próprios com um parâmetro separators, que não usamos desta vez.
Os dois hiperparâmetros essenciais a definir ao dividir seu documento do Notion em chunks são o tamanho do chunk e a sobreposição do chunk. Para este exemplo, usamos um tamanho de chunk de 64 e uma sobreposição de 8. No futuro, abordaremos o teste desses valores e a descoberta de bons valores. Depois de definir o divisor de texto, chamamos suas funções split_documents para obter todas as divisões de Document.
# Define our text splitter
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
A imagem abaixo mostra alguns objetos Document da divisão acima. Observe que ela inclui o conteúdo da página e os metadados, que incluem a seção da qual o conteúdo foi extraído.
Armazenando seus documentos do Notion
Com todos os documentos carregados e divididos, é hora de armazenar essas divisões. Primeiro, iniciamos nosso banco de dados vetorial diretamente em nosso notebook usando Milvus Lite. Também precisamos obter os módulos necessários do LangChain - Milvus e OpenAIEmbeddings.
Depois das importações e de colocar o banco de dados vetorial em execução, usamos o módulo Milvus do LangChain para criar uma coleção a partir dos nossos documentos. Precisamos passar a lista de documentos, os embeddings a serem usados, os parâmetros de conexão e (opcionalmente) um nome de coleção.
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")
Consultando seus documentos do Notion
Tudo está configurado e pronto para consultas. Para esta seção, precisamos de mais três importações do LangChain - OpenAI para acessar o GPT, o SelfQueryRetriever para criar nosso RAG básico e o objeto “Attribute info” para passar os metadados. Para começar, definimos alguns metadados. Para este exemplo, apenas as seções que estamos usando até agora.
Também damos ao self-query retriever uma descrição dos documentos. Neste caso, simplesmente “principais seções do documento”. Logo antes de instanciarmos nosso self-query retriever, definimos uma versão do GPT com temperatura 0 para uma variável llm. Com o LLM, o banco de dados vetorial, a descrição do documento e os campos de metadados prontos, definimos o self-query retriever.
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?")
Meu exemplo escolhido é “O que faz um engenheiro distinto?” Pela resposta na imagem abaixo, podemos ver os chunks mais semanticamente semelhantes retornados. Como podemos ver, só porque são as respostas mais semanticamente semelhantes não significa que sejam as corretas. Em artigos futuros, abordaremos como experimentar com chunking e outras técnicas para melhorar nossas respostas.
Resumo de consultas a documentos do Notion no LangChain
Neste tutorial, abordamos como carregar e analisar um documento do Notion em seções para consultar em uma arquitetura RAG básica. Usamos o LangChain como framework de orquestração e o Milvus como nosso banco de dados vetorial. O LangChain junta as peças, e o Milvus impulsiona a busca por similaridade.
Para levar este tutorial adiante, há muitas coisas que podemos testar. Exemplos de dois hiperparâmetros a verificar são o tamanho do chunk e o tamanho da sobreposição entre chunks. Podemos usá-los para ajustar nossas respostas e sua aparência. Além do ajuste, também precisamos avaliar as respostas.
Em tutoriais futuros, veremos diferentes estratégias de chunking. Não apenas isso, mas também analisaremos mais profundamente embeddings, estratégias de divisão e avaliação.
Continue lendo
Stop Building AI Data Infra for the Wrong Stage
Learn how AI data infrastructure should evolve from prototype to enterprise scale, and when Vector Lakebase becomes the right architecture for AI apps.

Will Amazon S3 Vectors Kill Vector Databases—or Save Them?
AWS S3 Vectors aims for 90% cost savings for vector storage. But will it kill vectordbs like Milvus? A deep dive into costs, limits, and the future of tiered storage.

Similarity Metrics for Vector Search
Exploring five similarity metrics for vector search: L2 or Euclidean distance, cosine distance, inner product, and hamming distance.



