加载数据(提取)
在我们选择的 LLM 对我们的原始数据进行处理之前,我们首先需要对原始数据进行简单处理并加载原始数据。这个过程类似于 ML 世界中的数据清理/特征工程流程,或传统数据设置中的 ETL 管道。
数据提取流程通常由三个主要阶段组成:
- 加载数据
- 转换数据
- 索引并存储数据
我们将在后续章节中介绍索引/存储。在本小节中,我们主要讨论加载器和转换过程。
加载器
在我们选择的 LLM 对我们的原始数据进行处理之前,我们需要加载原始数据。LlamaIndex 执行此操作的方式是通过 数据连接器(也称为 Reader )。数据连接器 从不同的数据源提提取数据并将数据格式化为 Document 对象。Document 是数据和有关该数据的元数据的集合。
使用 SimpleDirectoryReader 加载
最容易使用的加载器是 SimpleDirectoryReader,它可以从给定目录中的 每个文件 创建文档 Document 对象。它内置于 LlamaIndex 中,可以读取各种格式,包括 Markdown、PDF、Word 文档、PowerPoint 演示文稿、图像、音频和视频。
from llama_index.core import SimpleDirectoryReader
documents = SimpleDirectoryReader("./data").load_data()
使用 LlamaHub 的阅读器
由于获取数据的地方太多,因此并非所有数据都内置。相反,您可以从我们的数据连接器注册中心 LlamaHub 下载它们。
在此示例中,LlamaIndex 下载并安装名为 DatabaseReader 的连接器,该连接器针对 SQL 数据库运行查询并将结果的每一行返回为Document:
from llama_index.core import download_loader
from llama_index.readers.database import DatabaseReader
reader = DatabaseReader(
scheme=os.getenv("DB_SCHEME"),
host=os.getenv("DB_HOST"),
port=os.getenv("DB_PORT"),
user=os.getenv("DB_USER"),
password=os.getenv("DB_PASS"),
dbname=os.getenv("DB_NAME"),
)
query = "SELECT * FROM users"
documents = reader.load_data(query=query)
LlamaHub上有数百种连接器可供使用!
直接创建文档
除了使用加载器之外,您还可以直接使用 Document。
数据转换
加载数据后,您需要处理和转换数据,然后将其放入存储系统。这些转换包括 分块、提取元数据 和 对每个块提取embedding。这是确保 LLM 能够检索和最佳使用数据所必需的。
转换输入/输出是Node对象(一个 Document 是对应 Node 的子类)。转换也可以堆叠和重新排序。
我们有用于转换文档的高级 API 和低级 API。
高级转换 API
索引有一个 .from_documents() 方法,它接受一个 Document 对象数组,并正确地解析和分块它们。但是,有时你会希望更好地控制文档的拆分方式。
from llama_index.core import VectorStoreIndex
vector_index = VectorStoreIndex.from_documents(documents)
vector_index.as_query_engine()
在底层,这会将您的文档拆分为节点对象,这些对象类似于文档(它们包含文本和元数据)但与其父文档有关系。
如果您想要自定义核心组件,比如文本分割器,通过这个抽象,您可以传入自定义 transformations 列表或应用到全局 Settings:
from llama_index.core.node_parser import SentenceSplitter
text_splitter = SentenceSplitter(chunk_size=512, chunk_overlap=10)
# global
from llama_index.core import Settings
Settings.text_splitter = text_splitter
# per-index
index = VectorStoreIndex.from_documents(
documents, transformations=[text_splitter]
)
低级转换 API
您还可以显式定义这些步骤。
您可以通过使用我们的转换模块(文本分割器、元数据提取器等)作为独立组件来实现这一点,或者在我们的声明性转换流程接口中组合它们。
让我们来看看下面的步骤。
将文档拆分成节点
处理文档的关键步骤是将它们拆分为 “块”/Node 对象。关键思想是将数据处理成可以检索/提供给 LLM 的小块。
LlamaIndex 支持各种文本分割器,从基于段落/句子/标记的分割器到基于文件的分割器,如 HTML、JSON。
它们可以单独使用,也可以作为摄取管道的一部分使用。
from llama_index.core import SimpleDirectoryReader
from llama_index.core.ingestion import IngestionPipeline
from llama_index.core.node_parser import TokenTextSplitter
documents = SimpleDirectoryReader("./data").load_data()
pipeline = IngestionPipeline(transformations=[TokenTextSplitter(), ...])
nodes = pipeline.run(documents=documents)
添加元数据
我们还可以选择将元数据添加到文档和节点。这可以手动完成,也可以使用自动元数据提取器完成。
下面的指南介绍了 1)如何自定义文档和 2)如何自定义节点。
document = Document(
text="text",
metadata={"filename": "<doc_file_name>", "category": "<category>"},
)
添加嵌入embedding
要将节点插入向量索引,它应该具有嵌入embedding。有关更多详细信息,请参阅我们的提取管道或嵌入指南。
直接创建并传递节点
如果我们愿意,您可以直接创建节点并将节点列表直接传递给索引器: