用 LangChain 搭建基於 Notion 文檔的 RAG 應用

如何通過語言模型查詢 Notion 文檔?LangChain 和 Milvus 缺一不可。

在整個過程中,我們會將 LangChain 作爲框架,Milvus 作爲相似性搜索引擎,用二者搭建一個基本的檢索增強生成(RAG)應用。在之前的文章中,我們已經介紹過 LangChain 中的“自查詢”(Self-querying)。本質上,LangChain 中的自查詢功能就是構建一個基本的 RAG 架構,如圖所示:

在 LangChain 中處理 Notion 文檔共包含三個步驟:獲取、存儲和查詢文檔。獲取是指獲取 Notion 文檔並將內容加載到內存中。存儲步驟包括啓動向量數據庫(Milvus)、將文檔轉化爲向量、將文檔向量存儲至向量數據庫中。查詢部分包括針對 Notion 文檔進行提問。本文將帶大家一一拆解這三個步驟,代碼請參考 colab notebook

01.獲取 Notion 文檔

用 LangChain 的 NotionDirectoryLoader將文檔加載到內存中。我們提供文檔的路徑並調用load 函數來獲取 Notion 文檔。加載完畢後,可以得到 Notion 文檔的 Markdown 文件。本例中我們以一個 Markdown 文件示意。

接下來,用 LangChain 的 markdown 標題文本分割器。我們向其提供一個分割符列表,然後傳入之前命名的 md_file 來獲取分割內容。在實際定義headers_to_split_on列表時,請使用自己 Notion 文檔的標題。

# Load Notion page as a markdownfile filefrom 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 pagefrom 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)

分割任務並檢查分割結果。用 LangChain 的 RecursiveCharacterTextSplitter,使用一些不同的字符來進行分割。四個默認的檢查字符是換行符、雙換行符、空格或無空格。也可以選擇傳入自己的 separators 參數。

將 Notion文檔進行分塊時,我們還需要定義兩個關鍵超參數——分塊大小(chunk size)和分塊重疊(chunk overlap)。本例中,分塊大小爲 64,重疊爲 8。隨後,我們就可以調用 split_documents 函數將所有文檔進行分割。

# 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

下圖展示了部分分割的 document 對象,其中包含了頁面內容和元數據。元數據顯示了內容是從哪個章節中提取出來的。

02.存儲 Notion 文檔

所有文檔加載和分割完畢後,就需要存儲這些文檔塊。首先,在 notebook 中直接運行向量數據庫 Milvus Lite,隨後導入所需的 LangChain 模塊——Milvus 和 OpenAI Embeddings。

用 LangChain 的 Milvus 模塊爲文檔塊創建 Collection。這個步驟中我們需要傳入的參數包括:文檔列表、使用的 Embedding 模型、連接參數、以及 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")

03.查詢 Notion 文檔

現在可以開始查詢文檔了。開始前,我們需要從 LangChain 中再導入三個模塊:

  • OpenAI:用於訪問GPT。

  • SelfQueryRetriever:用於搭建基本的 RAG 應用。

  • Attribute info:用於傳入元數據的。

首先,我們定義元數據。隨後,需要給自查詢檢索器提供文檔的描述。本例中,描述即爲“文檔的主要部分”。在我們實例化自查詢檢索器前,現將 GPT 的溫度(Temperature)設置爲 0,並賦值給一個名爲 llm 的變量。有了 LLM、向量數據庫、文檔描述和元數據字段後,我們就完成了自查詢檢索器定義。

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?")

以下例子中我們提出了一個問題:“一名優秀工程師有哪些品質?”(What makes a distinguished engineer?)

響應如下圖所示。我們獲得了與提問在語義上最相似的文檔片段。但不難發現,其回答也僅僅只是語義上相似,並非完全正確。

本教程介紹瞭如何加載並解析 Notion 文檔,並搭建一個基本的 RAG 應用查詢 Notion 文檔。我們使用到了 LangChain 作爲框架,Milvus 作爲向量數據庫用於相似性搜索。如果想要進行深入的探索,建議大家調整分塊大小和重疊等參數,檢查不同的參數值是如何影響查詢結果的。

所謂分塊(Chunking)是構建檢索增強型生成(RAG應用程序中最具挑戰性的問題。具體的介紹和操作可參考《在 LangChain 嘗試了 N 種可能後,我發現了分塊的奧義!》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章