Neo4j 概述 本教程涵蓋如何在 LangChain 中使用 Neo4j。 Neo4j 是一個由向量儲存支援的圖形資料庫,可以部署在本地或雲端。 要充分利用 Neo4j,您需要學習 Cypher,這是一種聲明式查詢語言。 本教程將帶您了解如何使用 Neo4j 進行 CRUD 操作:儲存、更新、刪除 文件,以及執行 基於相似度的檢索。 目錄 概述 環境設置 什麼是 Neo4j? 準備數據 設置 Neo4j 文件管理器 參考資料 Cypher Neo4j Docker 安裝 Neo4j 官方安裝指南 Neo4j Python SDK 文件 Neo4j 文件 Langchain Neo4j 文件 環境設置 設置環境。您可以參考 環境設置 以獲取更多詳細資訊。 [注意] langchain-opentutorial 是一個提供易於使用的環境設置、實用函數和教程工具的套件。 您可以查看 langchain-opentutorial 以獲取更多詳細資訊。 %%capture --no-stderr %pip install langchain-opentutorial # 安裝必要的套件 %pip install -qU neo4j # 安裝所需套件 from langchain_opentutorial import package package.install( [ "langsmith", "langchain", "langchain_core", "langchain_community", "langchain_openai", "neo4j", "nltk", ], verbose=False, upgrade=False, ) # 設置環境變數 from langchain_opentutorial import set_env set_env( { "OPENAI_API_KEY": "您的 OPENAI API KEY", "LANGCHAIN_API_KEY": "您的 LangChain API KEY", "LANGCHAIN_TRACING_V2": "true", "LANGCHAIN_ENDPOINT": "https://api.smith.langchain.com", "LANGCHAIN_PROJECT": "Neo4j", "NEO4J_URI": "您的 Neo4j Aura URI", "NEO4J_USERNAME": "您的 Neo4j Aura 用戶名", "NEO4J_PASSWORD": "您的 Neo4j Aura 密碼", } ) 您也可以在 .env 檔案中設置 API 金鑰(如 OPENAI_API_KEY)並載入它們。 [注意] 如果您已經在前面的步驟中設置了所需的 API 金鑰,則無需執行此操作。 from dotenv import load_dotenv load_dotenv(override=True) 設置 Neo4j 我們有兩個選項開始:雲端或本地部署。 在本教程中,我們將使用由 Neo4j 提供的名為 Aura 的雲端服務。 我們還將描述如何使用 Docker 部署 Neo4j。 開始使用 Aura 您可以在 Neo4j 官方網站上建立新的 Neo4j Aura 帳戶。 訪問網站並點擊右上角的「Get Started」免費按鈕。 登入後,您將看到一個 Create instance 按鈕,之後您將看到您的用戶名和密碼。 要獲取您的 API 金鑰,點擊 Download and continue 下載包含連接您的 NEO4j Aura 的 API 金鑰的 .txt 檔案。 開始使用 Docker 以下是如何使用 Docker 運行 Neo4j 的說明。 要運行 Neo4j 容器,使用以下命令。 docker run \ -itd \ --publish=7474:7474 --publish=7687:7687 \ --volume=$HOME/neo4j/data:/data \ --env=NEO4J_AUTH=none \ --name neo4j \ neo4j 您可以訪問 Neo4j Docker 安裝 參考資料以查看更詳細的資訊。 [注意] Neo4j 還支援在 macOS、Windows 和 Linux 上的原生部署。訪問 Neo4j 官方安裝指南 參考資料以獲取更多詳細資訊。 Neo4j 社群版 僅支援一個資料庫。 什麼是 Neo4j? Neo4j 是一個原生圖形資料庫,這意味著它將數據表示為節點和邊。 節點 label:標籤,用於表示節點在領域中的角色。 property:鍵值對,例如 name-John。 邊 表示兩個節點之間的關係。 有方向性,意味著它有起始節點和結束節點。 property:像節點一樣,邊也可以有屬性。 NoSQL Neo4j 不需要預先定義的架構,允許靈活的數據建模。 Cypher Neo4j 使用 Cypher,一種聲明式查詢語言,來與資料庫互動。 Cypher 表達式類似於人類思考關係的方式。 準備數據 本節將指導您完成 數據準備過程。 本節包括以下組件: 數據介紹 預處理數據 介紹數據 在本教程中,我們將使用童話故事 📗 小王子 的 PDF 格式作為我們的數據。 此材料符合 Apache 2.0 許可證。 數據以從原始 PDF 轉換而來的文本 (.txt) 格式使用。 您可以在下面的連結查看數據。 數據連結 預處理數據 在本教程部分中,我們將預處理《小王子》的文本數據,並將其轉換為帶有元數據的 LangChain Document 物件列表。 每個文件塊將在元數據中包含一個從每個部分的第一行提取的 title 欄位。 from langchain.schema import Document from langchain.text_splitter import RecursiveCharacterTextSplitter import re from typing import List def preprocessing_data(content: str) -> List[Document]: # 1. 以雙換行符分割文本以分離部分 blocks = content.split("\n\n") # 2. 初始化文本分割器 text_splitter = RecursiveCharacterTextSplitter( chunk_size=500, # 每個塊的最大字符數 chunk_overlap=50, # 塊之間的重疊以保持上下文 separators=["\n\n", "\n", " "], # 分割的優先順序 ) documents = [] # 3. 遍歷每個部分 for block in blocks: lines = block.strip().splitlines() if not lines: continue # 使用方括號 [ ] 從第一行提取標題 first_line = lines[0] title_match = re.search(r"\[(.*?)\]", first_line) title = title_match.group(1).strip() if title_match else None # 從內容中移除標題行 body = "\n".join(lines[1:]).strip() if not body: continue # 4. 使用文本分割器對部分進行分塊 chunks = text_splitter.split_text(body) # 5. 為每個塊創建帶有相同標題元數據的 LangChain Document for chunk in chunks: documents.append(Document(page_content=chunk, metadata={"title": title})) print(f"生成了 {len(documents)} 個分塊文件。") return documents # 載入整個文本檔案 with open("./data/the_little_prince.txt", "r", encoding="utf-8") as f: content = f.read() # 預處理數據 docs = preprocessing_data(content=content) 設置 Neo4j 本部分將帶您完成 Neo4j 的初始設置。 本節包括以下組件: 載入嵌入模型 載入 Neo4j 客戶端 創建索引 載入嵌入模型 在 載入嵌入模型 部分,您將學習如何載入嵌入模型。 本教程使用 OpenAI 的 API-Key 來載入模型。 💡 如果您希望使用其他嵌入模型,請參閱以下說明。 嵌入模型 import os from langchain_openai import OpenAIEmbeddings embedding = OpenAIEmbeddings(model="text-embedding-3-large") 載入 Neo4j 客戶端 在 載入 Neo4j 客戶端 部分,我們介紹如何使用 Neo4j 的 Python SDK 載入 資料庫客戶端物件。 Neo4j Python SDK 文件 import time import neo4j # 創建資料庫客戶端物件函數 def get_db_client(uri, username, password): """ 初始化並返回一個 VectorStore 客戶端實例。 此函數從環境變數或預設值載入配置(例如 API 金鑰、主機) 並創建一個客戶端物件來與 Neo4j Python SDK 互動。 Returns: client:ClientType - Neo4j 客戶端的實例。 Raises: ValueError: 如果缺少所需配置。 """ client = neo4j.GraphDatabase.driver(uri=uri, auth=(username, password)) return client # 獲取資料庫客戶端物件 uri = os.getenv("NEO4J_URI") username = os.getenv("NEO4J_USERNAME") password = os.getenv("NEO4J_PASSWORD") client = get_db_client(uri, username, password) 創建索引 如果您成功連接到 Neo4j Aura,一些基本索引已經創建。 但是,在本教程中,我們將使用 Neo4jIndexManager 類別創建一個新索引。 from utils.neo4j import Neo4jIndexManager # 創建 IndexManager 物件 indexManger = Neo4jIndexManager(client) # 創建新索引 index_name = "tutorial_index" node_label = "tutorial_node" # 創建新索引 try: tutorial_index = indexManger.create_index( embedding, index_name=index_name, metric="cosine", node_label=node_label ) except Exception as e: print("索引創建失敗,原因是") print(type(e)) print(str(e)) 文件管理器 為了支援 Langchain-Opentutorial,我們為 VectorDBs 實現了一套自定義的 CRUD 功能。 包括以下操作: upsert:更新現有文件或如果不存在則插入 upsert_parallel:為大規模數據並行執行 upsert similarity_search:基於嵌入搜索相似文件 delete:根據篩選條件刪除文件 這些功能中的每一個都被實現為特定於每個 VectorDB 的類別方法。 在本教程中,您可以輕鬆利用這些方法與您的 VectorDB 互動。 我們計劃通過在未來添加更多常見操作來持續擴展功能。 創建實例 首先,我們創建一個 Neo4j 輔助類別的實例來使用其 CRUD 功能。 此類別使用 Neo4j Python SDK 客戶端實例、索引名稱 和 嵌入模型實例 進行初始化,這些都在前面的部分中定義。 from utils.neo4j import Neo4jDocumentManager crud_manager = Neo4jDocumentManager( client=client, index_name="tutorial_index", embedding=embedding ) 現在您可以使用 crud_manager 實例執行以下 CRUD 操作。 這些實例讓您可以輕鬆管理 Neo4j 中的文件。 Upsert 文件 更新 現有文件或如果不存在則 插入 ✅ 參數 texts:Iterable[str] – 要插入/更新的文本內容列表。 metadatas:Optional[List[Dict]] – 每個文本的元數據字典列表(可選)。 ids:Optional[List[str]] – 文件的自定義 ID。如果未提供,將自動生成 ID。 **kwargs:底層向量存儲的額外參數。 🔄 返回 None from uuid import uuid4 # 為每個文件創建 ID ids = [str(uuid4()) for _ in docs] args = { "texts": [doc.page_content for doc in docs[:2]], "metadatas": [doc.metadata for doc in docs[:2]], "ids": ids[:2], # 如有需要,添加額外參數 } crud_manager.upsert(**args) Upsert 並行文件 為大規模數據 並行 執行 upsert ✅ 參數 texts:Iterable[str] – 要插入/更新的文本內容列表。 metadatas:Optional[List[Dict]] – 每個文本的元數據字典列表(可選)。 ids:Optional[List[str]] – 文件的自定義 ID。如果未提供,將自動生成 ID。 batch_size:int – 每批文件數量(預設:32)。 workers:int – 並行工作者數量(預設:10)。 **kwargs:底層向量存儲的額外參數。 🔄 返回 None from uuid import uuid4 args = { "texts": [doc.page_content for doc in docs], "metadatas": [doc.metadata for doc in docs], "ids": ids, # 如有需要,添加額外參數 } crud_manager.upsert_parallel(**args) 相似度搜索 基於 嵌入 搜索 相似文件。 此方法使用 **"余弦相似度"**。 ✅ 參數 query:str – 用於相似度搜索的文本查詢。 k:int – 要返回的頂級結果數量(預設:10)。 **kwargs:額外的搜索選項(例如篩選器)。 🔄 返回 results:List[Document] – 按相似度排名的 LangChain Document 物件列表。 # 按查詢搜索 results = crud_manager.search(query="真正重要的東西是肉眼看不見的。", k=3) for idx, result in enumerate(results): print(f"排名 {idx+1}") print(f"內容:{result['text']}") print(f"元數據:{result['metadata']}") print(f"相似度分數:{result['score']}") print() 刪除文件 根據篩選條件刪除文件 ✅ 參數 ids:Optional[List[str]] – 要刪除的文件 ID 列表。如果為 None,則根據篩選器刪除。 filters:Optional[Dict] – 指定篩選條件的字典(例如元數據匹配)。 **kwargs:任何額外參數。 🔄 返回 Boolean # 按 ID 刪除 ids = ids[:5] # 您要刪除的 'ids' 值 crud_manager.delete(ids=ids) # 按 ID 和篩選器刪除 filters = {"page": 6} crud_manager.delete(filters={"title": "chapter 6"}) # 刪除全部 crud_manager.delete()