ObjectBox 向量存储演示
本笔记本将演示如何使用 ObjectBox 作为 LlamaIndex 的高效设备端向量存储。我们将考虑一个简单的 RAG 应用场景:给定一个文档,用户可以用自然语言提问并从大型语言模型获取相关答案。RAG 流水线将按以下维度进行配置:
- 一个内置的
SimpleDirectoryReader读取器,来自 LlamaIndex - 一个内置的
SentenceSplitter节点解析器,来自 LlamaIndex - 来自HuggingFace作为嵌入提供者的模型
- ObjectBox 作为 NoSQL 和向量数据库
- 谷歌的 Gemini 作为远程 LLM 服务
我们安装 HuggingFace 和 Gemini 的集成,以便与 LlamaIndex 一起使用
!pip install llama_index_vector_stores_objectbox --quiet!pip install llama-index --quiet!pip install llama-index-embeddings-huggingface --quiet!pip install llama-index-llms-gemini --quiet[?25l [90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━[0m [32m0.0/1.6 MB[0m [31m?[0m eta [36m-:--:--[0m[2K [91m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━[0m[90m╺[0m[90m━━[0m [32m1.5/1.6 MB[0m [31m40.2 MB/s[0m 预计剩余时间 [36m0:00:01[0m [2K [90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━[0m [32m1.6/1.6 MB[0m [31m25.4 MB/s[0m 预计剩余时间 [36m0:00:00[0m [2K [90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━[0m [32m4.0/4.0 MB[0m [31m44.0 MB/s[0m 预计剩余时间 [36m0:00:00[0m [2K [90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━[0m [32m1.5/1.5 MB[0m [31m38.9 MB/s[0m 预计剩余时间 [36m0:00:00[0m [2K [90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━[0m [32m1.1/1.1 MB[0m [31m37.5 MB/s[0m 预计剩余时间 [36m0:00:00[0m [2K [90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━[0m [32m76.4/76.4 kB[0m [31m5.2 MB/s[0m 预计剩余时间 [36m0:00:00[0m [2K [90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━[0m [32m77.9/77.9 kB[0m [31m4.9 MB/s[0m 预计剩余时间 [36m0:00:00[0m [2K [90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━[0m [32m49.3/49.3 kB[0m [31m3.1 MB/s[0m 预计剩余时间 [36m0:00:00[0m [2K [90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━[0m [32m58.3/58.3 kB[0m [31m3.4 MB/s[0m 预计剩余时间 [36m0:00:00[0m [?25h
!mkdir -p 'data/paul_graham/'!wget 'https://raw.githubusercontent.com/run-llama/llama_index/main/docs/examples/data/paul_graham/paul_graham_essay.txt' -O 'data/paul_graham/paul_graham_essay.txt'3) 为RAG设置一个LLM (Gemini)
Section titled “3) Setup a LLM for RAG (Gemini)”我们使用 Google Gemini 的云端 API 作为大型语言模型。您可以从控制台获取 API 密钥。
from llama_index.llms.gemini import Geminiimport getpass
gemini_key_api = getpass.getpass("Gemini API Key: ")gemini_llm = Gemini(api_key=gemini_key_api)4) 为RAG设置嵌入模型(HuggingFace bge-small-en-v1.5)
Section titled “4) Setup an embedding model for RAG (HuggingFace bge-small-en-v1.5)”HuggingFace 托管了多种嵌入模型,可以从 MTEB 排行榜中查看。
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
hf_embedding = HuggingFaceEmbedding(model_name="BAAI/bge-base-en-v1.5")embedding_dim = 384在RAG流程中,第一步是读取给定的文档。我们使用SimpleDirectoryReader,它通过检查目录中的文件扩展名来选择最佳的文件读取器。
接下来,我们从文档中通过SimpleDirectoryReader读取的内容生成文本块(文本子序列)。SentenceSplitter是一种文本分割器,它能在将文本分割成大小为chunk_size的块时保持句子边界完整。
from llama_index.core import SimpleDirectoryReaderfrom llama_index.core.node_parser import SentenceSplitter
reader = SimpleDirectoryReader("./data/paul_graham")documents = reader.load_data()
node_parser = SentenceSplitter(chunk_size=512, chunk_overlap=20)nodes = node_parser.get_nodes_from_documents(documents)6) 配置 ObjectBoxVectorStore
Section titled “6) Configure ObjectBoxVectorStore”ObjectBoxVectorStore 可以通过以下几个选项进行初始化:
embedding_dim(必填): 向量数据库将存储的嵌入维度distance_type: 从COSINE、DOT_PRODUCT、DOT_PRODUCT_NON_NORMALIZED和EUCLIDEAN中选择db_directory: 应创建.mdbObjectBox 数据库文件的目录路径clear_db: 如果数据库文件存在于db_directory上,则删除现有数据库文件do_log: 启用来自ObjectBox集成的日志记录
from llama_index.vector_stores.objectbox import ObjectBoxVectorStorefrom llama_index.core import StorageContext, VectorStoreIndex, Settingsfrom objectbox import VectorDistanceType
vector_store = ObjectBoxVectorStore( embedding_dim, distance_type=VectorDistanceType.COSINE, db_directory="obx_data", clear_db=False, do_log=True,)
storage_context = StorageContext.from_defaults(vector_store=vector_store)
Settings.llm = gemini_llmSettings.embed_model = hf_embedding
index = VectorStoreIndex(nodes=nodes, storage_context=storage_context)query_engine = index.as_query_engine()response = query_engine.query("Who is Paul Graham?")print(response)可选:将 ObjectBoxVectorStore 配置为检索器
Section titled “Optional: Configuring ObjectBoxVectorStore as a retriever”一个LlamaIndex 检索器负责根据查询从向量数据库中获取相似的文本块。
retriever = index.as_retriever()response = retriever.retrieve("What did the author do growing up?")
for node in response: print("Retrieved chunk text:\n", node.node.get_text()) print("Retrieved chunk metadata:\n", node.node.get_metadata_str()) print("\n\n\n")可选:使用 delete_nodes 移除与单个查询关联的数据块
Section titled “Optional: Removing chunks associated with a single query using delete_nodes”我们可以使用 ObjectBoxVectorStore.delete_nodes 方法从向量数据库中移除数据块(节点),只需提供一个包含节点ID的列表作为参数。
response = retriever.retrieve("What did the author do growing up?")
node_ids = []for node in response: node_ids.append(node.node_id)print(f"Nodes to be removed: {node_ids}")
print(f"No. of vectors before deletion: {vector_store.count()}")vector_store.delete_nodes(node_ids)print(f"No. of vectors after deletion: {vector_store.count()}")ObjectBoxVectorStore.delete 方法可用于移除与单个文档相关的块(节点),该文档的 id_ 作为参数提供。
document = documents[0]print(f"Document to be deleted {document.id_}")
print(f"No. of vectors before deletion: {vector_store.count()}")vector_store.delete(document.id_)print(f"No. of vectors after document deletion: {vector_store.count()}")