在本教程中,我们将向您展示如何构建一个数据摄入管道,将数据摄入到一个向量数据库中。
我们将使用Pinecone作为向量数据库。
我们将展示如何完成以下操作:
在本教程中,您将需要一个pinecone.io的API密钥。您可以免费注册以获得Starter账户。
如果您创建了Starter账户,可以随意为您的应用程序命名。
一旦您拥有了账户,请转到Pinecone控制台中的“API密钥”。您可以使用默认密钥,也可以为本教程创建一个新的密钥。
保存您的API密钥及其环境(免费账户为gcp_starter
)。您将在下面需要它们。
如果您在colab上打开这个笔记本,您可能需要安装LlamaIndex 🦙。
%pip install llama-index-embeddings-openai
%pip install llama-index-vector-stores-pinecone
%pip install llama-index-llms-openai
!pip install llama-index
在本教程中,您将需要一个OpenAI的API密钥。登录到您的platform.openai.com账户,点击右上角的个人资料图片,然后从菜单中选择“API密钥”。为本教程创建一个API密钥并保存好。您将在下面用到它。
首先,我们添加我们的依赖项。
!pip -q install python-dotenv pinecone-client llama-index pymupdf
dotenv_path = ( "env" # Google Colabs不允许你打开一个 .env 文件,但你可以设置)with open(dotenv_path, "w") as f: f.write('PINECONE_API_KEY="<你的api密钥>"\n') f.write('PINECONE_ENVIRONMENT="gcp-starter"\n') f.write('OPENAI_API_KEY="<你的api密钥>"\n')
请在我们创建的文件中设置您的OpenAI API密钥、Pinecone API密钥和环境。
import os
from dotenv import load_dotenv
load_dotenv(dotenv_path=dotenv_path)
我们构建一个空的Pinecone索引,并定义必要的LlamaIndex包装器/抽象,以便我们可以开始将数据加载到Pinecone中。
注意:不要将API密钥保存在代码中,也不要将pinecone_env添加到您的存储库中!
import pinecone
api_key = os.environ["PINECONE_API_KEY"]
environment = os.environ["PINECONE_ENVIRONMENT"]
pinecone.init(api_key=api_key, environment=environment)
index_name = "llamaindex-rag-fs"
# [可选] 在重新运行教程之前删除索引。# pinecone.delete_index(index_name)
# dimensions are for text-embedding-ada-002pinecone.create_index( index_name, dimension=1536, metric="euclidean", pod_type="p1")
pinecone_index = pinecone.Index(index_name)
# [可选] 删除索引中的内容 - 在免费账户上无法使用pinecone_index.delete(deleteAll=True)
简单的包装抽象,用于在LlamaIndex中使用。包装在StorageContext中,以便我们可以轻松地加载节点。
from llama_index.vector_stores.pinecone import PineconeVectorStore
vector_store = PineconeVectorStore(pinecone_index=pinecone_index)
我们将展示如何构建一个数据摄取管道,就像在介绍中提到的那样。
请注意,步骤(2)和(3)可以通过我们的NodeParser
抽象来处理,它可以处理拆分和节点创建。
在本教程中,我们将向您展示如何手动创建这些对象。
!mkdir data
!wget --user-agent "Mozilla" "https://arxiv.org/pdf/2307.09288.pdf" -O "data/llama2.pdf"
--2023-10-13 01:45:14-- https://arxiv.org/pdf/2307.09288.pdf Resolving arxiv.org (arxiv.org)... 128.84.21.199 Connecting to arxiv.org (arxiv.org)|128.84.21.199|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 13661300 (13M) [application/pdf] Saving to: ‘data/llama2.pdf’ data/llama2.pdf 100%[===================>] 13.03M 7.59MB/s in 1.7s 2023-10-13 01:45:16 (7.59 MB/s) - ‘data/llama2.pdf’ saved [13661300/13661300]
import fitz
file_path = "./data/llama2.pdf"
doc = fitz.open(file_path)
在这里,我们导入我们的 SentenceSplitter
来将文档文本分割成更小的块,同时尽量保留段落/句子的结构。
from llama_index.core.node_parser import SentenceSplitter
text_parser = SentenceSplitter( chunk_size=1024, # 分隔符=" ",)
text_chunks = []# 保持与源文档索引的关系,以帮助在(3)中注入文档元数据doc_idxs = []for doc_idx, page in enumerate(doc): page_text = page.get_text("text") cur_text_chunks = text_parser.split_text(page_text) text_chunks.extend(cur_text_chunks) doc_idxs.extend([doc_idx] * len(cur_text_chunks))
我们将每个文本块转换为一个TextNode
对象,这是LlamaIndex中的一个低级数据抽象,它存储内容,同时也允许定义元数据和与其他节点的关系。
我们将文档中的元数据注入到每个节点中。
这本质上复制了我们的SentenceSplitter
中的逻辑。
from llama_index.core.schema import TextNode
nodes = []
for idx, text_chunk in enumerate(text_chunks):
node = TextNode(
text=text_chunk,
)
src_doc_idx = doc_idxs[idx]
src_page = doc[src_doc_idx]
nodes.append(node)
print(nodes[0].metadata)
# 打印一个示例节点print(nodes[0].get_content(metadata_mode="all"))
from llama_index.core.extractors import (
QuestionsAnsweredExtractor,
TitleExtractor,
)
from llama_index.core.ingestion import IngestionPipeline
from llama_index.llms.openai import OpenAI
llm = OpenAI(model="gpt-3.5-turbo")
extractors = [
TitleExtractor(nodes=5, llm=llm),
QuestionsAnsweredExtractor(questions=3, llm=llm),
]
pipeline = IngestionPipeline(
transformations=extractors,
)
nodes = await pipeline.arun(nodes=nodes, in_place=False)
print(nodes[0].metadata)
from llama_index.embeddings.openai import OpenAIEmbedding
embed_model = OpenAIEmbedding()
for node in nodes:
node_embedding = embed_model.get_text_embedding(
node.get_content(metadata_mode="all")
)
node.embedding = node_embedding
现在我们将这些节点插入到我们的 PineconeVectorStore
中。
注意:我们跳过了 VectorStoreIndex
抽象层,这是一个处理摄入的更高级抽象层。我们将在下一节中使用 VectorStoreIndex
来快速检索/查询。
vector_store.add(nodes)
现在我们的数据摄入已经完成,我们可以从这个向量存储中检索和查询数据。
注意:在这里我们可以使用高级的VectorStoreIndex
抽象。请查看下一节,了解如何在较低级别定义检索!
from llama_index.core import VectorStoreIndex
from llama_index.core import StorageContext
index = VectorStoreIndex.from_vector_store(vector_store)
query_engine = index.as_query_engine()
query_str = "Can you tell me about the key concepts for safety finetuning"
response = query_engine.query(query_str)
print(str(response))