
Jerry Liu • 2023-09-27
Timescale Vector x LlamaIndex:让 PostgreSQL 成为 AI 应用更好的向量数据库
作者:Avthar Sewrathan, Matvey Arye, Jerry Liu, Yi Ding
隆重推出 Timescale Vector 与 LlamaIndex 的集成。Timescale Vector 使 LlamaIndex 开发者能够使用 PostgreSQL 作为向量数据库构建更好的 AI 应用:它提供了更快的向量相似性搜索、高效的基于时间的搜索过滤,以及单一易用云 PostgreSQL 数据库的操作简便性,该数据库不仅适用于向量嵌入,也适用于 AI 应用的关系型和时间序列数据。
根据Stack Overflow 2023 开发者调查,PostgreSQL 是世界上最受欢迎的数据库。原因也很充分:它经过三十多年的生产环境实践磨练,健壮可靠,并拥有丰富的工具、驱动程序和连接器生态系统。
虽然 pgvector(PostgreSQL 上的向量数据开源扩展)是一个很棒的扩展(其所有功能都作为 Timescale Vector 的一部分提供),但它只是为在 PostgreSQL 上构建 AI 应用的开发者提供生产级体验的一块拼图。在与众多敏捷初创公司和成熟行业巨头的开发者交流后,我们看到需要增强 pgvector,以满足构建 AI 应用开发者的性能和操作需求。
总结:Timescale Vector 如何帮助您使用 LlamaIndex 构建更好的 AI 应用
- 在数百万向量上实现更快的相似性搜索: 受 DiskANN 算法启发,引入了新的搜索索引,Timescale Vector 在约 99% 的召回率下实现了比专用数据库快 3 倍的搜索速度,并且在包含一百万个 OpenAI 嵌入的数据集上,性能比所有现有的 PostgreSQL 搜索索引快 39.39% 到 1,590.33%。此外,启用乘积量化 (product quantization) 可实现比 pgvector 小 10 倍的索引空间节省。Timescale Vector 还提供 pgvector 的分层可导航小世界 (HNSW) 和倒排文件平面 (IVFFlat) 索引算法。
- 使用基于时间的过滤实现高效相似性搜索: Timescale Vector 优化了基于时间的向量搜索查询,利用Timescale 超表的自动时间分区和索引功能,高效查找最近的嵌入、按时间范围或文档年龄限制向量搜索,并轻松存储和检索大型语言模型 (LLM) 响应和聊天历史。基于时间语义搜索还使您能够将检索增强生成 (RAG) 与基于时间的上下文检索结合使用,从而为用户提供更有用的 LLM 响应。
- 简化的 AI 基础设施堆栈: 通过将向量嵌入、关系型数据和时间序列数据组合到一个 PostgreSQL 数据库中,Timescale Vector 消除了大规模管理多个数据库系统带来的操作复杂性。
- 简化的元数据处理和多属性过滤: 开发者可以利用所有 PostgreSQL 数据类型来存储和过滤元数据,并将向量搜索结果与关系型数据进行 JOIN 操作,以获得更具上下文相关性的响应。在未来的版本中,Timescale Vector 将进一步优化丰富多属性过滤,在基于元数据过滤时实现更快的相似性搜索。
除了这些针对向量工作负载的创新之外,Timescale Vector 还提供了一个强大、生产就绪的云 PostgreSQL 平台,具有灵活的定价、企业级安全性以及免费的专家支持。
在本文的其余部分,我们将更深入地(附带代码!)探讨 Timescale Vector 为希望使用 PostgreSQL 作为向量数据库并结合 LlamaIndex 的开发者提供的独特功能
- 使用 DiskANN、HNSW 和 IVFFlat 索引类型实现更快的相似性搜索。
- 按时间过滤向量时实现高效相似性搜索。
- 使用基于时间的上下文检索增强生成 (RAG)。
(如果您想直接查看代码,请查阅本教程)。
🎉 LlamaIndex 用户可免费获得 3 个月 Timescale Vector 使用权
我们为 LlamaIndex 用户提供 Timescale Vector 的延长 90 天试用期。这使得在 Timescale Vector 上测试和开发您的应用变得简单,因为在试用期间创建的任何云 PostgreSQL 数据库都不会向您收费。立即免费试用 Timescale Vector。
PostgreSQL 中更快的向量相似性搜索
Timescale Vector 加快了在大型向量数据集上的近似最近邻 (ANN) 搜索速度,通过引入受DiskANN 算法启发的先进 ANN 索引来增强 pgvector。Timescale Vector 还提供 pgvector 的 HNSW 和 IVFFlat 索引算法,为开发者提供了根据其用例选择合适索引的灵活性。
我们使用ANN 基准测试套件进行的性能基准测试表明,Timescale Vector 在约 99% 召回率下,搜索速度比所有现有 PostgreSQL 搜索索引快 39.43% 到 1,590.33%;在包含一百万个 OpenAI 嵌入的数据集上,在约 99% 召回率下,搜索速度比专用向量数据库快 3 倍。您可以在此处阅读更多关于性能基准测试方法、比较的数据库和结果的信息。

在 LlamaIndex 中使用 Timescale Vector 的 DiskANN、HNSW 或 IVFFLAT 索引非常简单。
只需创建一个 Timescale Vector 向量存储,并添加您想要查询的数据节点,如下所示
from llama_index.vector_stores import TimescaleVectorStore
# Create a timescale vector store with specified params
ts_vector_store = TimescaleVectorStore.from_params(
service_url=TIMESCALE_SERVICE_URL,
table_name="your_table_name",
time_partition_interval= timedelta(days=7),
)
ts_vector_store.add(nodes)
然后运行
# Create a timescale vector index (DiskANN)
ts_vector_store.create_index()
这将使用默认参数创建一个 Timescale Vector 索引。
需要指出的是,“索引”这个术语有点多义。对于许多向量存储来说,索引是存储您数据的东西(在关系型数据库中通常称为表),但在 PostgreSQL 世界中,索引是加速搜索的东西,我们这里使用的是后者的含义。
我们还可以在 create_index
命令中指定索引创建的具体参数,如下所示
# create new timescale vector index (DiskANN) with specified parameters
ts_vector_store.create_index("tsv", max_alpha=1.0, num_neighbors=50)
Timescale Vector 新的 DiskANN 启发式向量搜索索引的优势包括以下几点
- 在 PostgreSQL 中以 99% 的准确率实现更快的向量搜索。
- 针对磁盘运行进行优化,而不仅仅是内存使用。
- 与 PostgreSQL 兼容的量化优化,减小向量大小并因此缩小索引大小(在某些情况下缩小10 倍!)并加速搜索。
- 高效的混合搜索或过滤附加维度。
有关 Timescale Vector 新索引如何工作的更多信息,请参阅这篇博客文章。
Pgvector 作为 Timescale Vector 的一部分打包,因此您也可以在 LlamaIndex 应用中访问 pgvector 的 HNSW 和 IVFFLAT 索引算法。从 LlamaIndex 应用代码中方便地创建 ANN 搜索索引的能力,使得创建不同的索引并比较其性能变得容易。
# Create an HNSW index
# Note: You don't need to specify m and ef_construction parameters as we set smart defaults.
ts_vector_store.create_index("hnsw", m=16, ef_construction=64)
# Create an IVFFLAT index
# Note: You don't need to specify num_lists and num_records parameters as we set smart defaults.
ts_vector_store.create_index("ivfflat", num_lists=20, num_records=1000)
为您的 LlamaIndex AI 应用添加高效的基于时间搜索功能
Timescale Vector 优化了基于时间的向量搜索,利用Timescale 超表的自动时间分区和索引功能,高效地按时间和相似性搜索向量。
时间通常是向量嵌入的重要元数据组成部分。嵌入来源,如文档、图片和网页,通常与它们关联着一个时间戳,例如创建日期、发布日期或最后更新日期等等。
我们可以利用向量嵌入集合中的时间元数据来丰富搜索结果的质量和适用性,通过检索不仅在语义上相似,而且与特定时间范围相关的向量。
以下是一些基于时间检索向量可以改进您的 LlamaIndex 应用的示例
- 查找最近的嵌入: 查找与查询向量语义上最相似的最新嵌入。例如,查找与选举相关的最新新闻、文档或社交媒体帖子。
- 在时间范围内搜索: 将相似性搜索限制在相关时间范围内的向量。例如,询问关于知识库的基于时间的问题(“2023 年 1 月至 3 月之间添加了哪些新功能?”)。
- 聊天历史: 存储和检索 LLM 响应历史。例如,聊天机器人聊天历史。
让我们看看在一个 Git 日志数据集上执行基于时间搜索的示例。在 Git 日志中,每个条目都有一个时间戳、一个作者以及关于提交的一些信息。
为了说明如何使用 TimescaleVector 的基于时间向量搜索功能,我们将询问关于 TimescaleDB Git 日志历史的问题。每个 Git 提交条目都有与之关联的时间戳、消息以及其他元数据(例如,作者)。
我们将说明如何创建带有基于时间的 UUID 的节点,以及如何使用 Timescale Vector 向量存储运行带有时间范围过滤器的相似性搜索。
从 Git 日志中的每个提交创建节点
首先,我们使用 Pandas 从演示 CSV 文件加载 Git 日志条目
import pandas as pd
from pathlib import Path
# Read the CSV file into a DataFrame
file_path = Path("../data/csv/commit_history.csv")
df = pd.read_csv(file_path)
接下来,我们将为 Git 日志数据集中的每个提交创建 TextNode
类型的节点,提取相关信息并分别将其分配给节点的文本和元数据。
from llama_index.schema import TextNode, NodeRelationship, RelatedNodeInfo
# Create a Node object from a single row of data
def create_node(row):
record = row.to_dict()
record_name = split_name(record["author"])
record_content = str(record["date"]) + " " + record_name + " " + str(record["change summary"]) + " " + str(record["change details"])
node = TextNode(
id_=create_uuid(record["date"]),
text= record_content,
metadata={
'commit': record["commit"],
'author': record_name,
'date': create_date(record["date"]),
}
)
return node
nodes = [create_node(row) for _, row in df.iterrows()]
注意: 上面的代码引用了两个辅助函数(split_name()
和 create_date()
)来获取正确格式的数据,为了简洁起见,我们省略了它们。完整代码包含在本文末尾资源部分链接的教程中。
根据每个 Git 提交的日期为每个节点创建 UUID
我们将仔细看看我们用来创建每个节点 id_
的辅助函数。在 LlamaIndex 中进行基于时间的搜索时,Timescale Vector 使用 UUID v1 的“datetime”部分将向量放入正确的时间分区。Timescale Vector 的 Python 客户端库提供了一个名为 uuid_from_time
的易用函数,可以从 Python DateTime 对象创建 UUID v1,然后我们将它用作 TextNodes 的 id
。
from timescale_vector import client
# Function to take in a date string in the past and return a uuid v1
def create_uuid(date_string: str):
if date_string is None:
return None
time_format = '%a %b %d %H:%M:%S %Y %z'
datetime_obj = datetime.strptime(date_string, time_format)
uuid = client.uuid_from_time(datetime_obj)
return str(uuid)
由于我们处理的是过去的时间戳,因此我们利用 uuid_from_time
函数来帮助为每个节点生成正确的 UUID。如果您想要与您的节点(或文档)关联当前日期和时间以进行基于时间的搜索,则可以跳过此步骤。默认情况下,当节点添加到 Timescale Vector 的表中时,将自动生成与当前日期和时间关联的 UUID。
让我们看看一个节点的内容
print(nodes[0].get_content(metadata_mode="all"))
commit: 44e41c12ab25e36c202f58e068ced262eadc8d16
author: Lakshmi Narayanan Sreethar
date: 2023-09-5 21:03:21+0850
Tue Sep 5 21:03:21 2023 +0530 Lakshmi Narayanan Sreethar Fix segfault in set_integer_now_func When an invalid function oid is passed to set_integer_now_func, it finds out that the function oid is invalid but before throwing the error, it calls ReleaseSysCache on an invalid tuple causing a segfault. Fixed that by removing the invalid call to ReleaseSysCache. Fixes #6037
为每个节点的文本创建向量嵌入
接下来,我们将为每个节点的内容创建向量嵌入,以便我们可以在与每个节点相关的文本上执行相似性搜索。我们将使用 OpenAIEmbedding
模型来创建嵌入。
# Create embeddings for nodes
from llama_index.embeddings import OpenAIEmbedding
embedding_model = OpenAIEmbedding()
for node in nodes:
node_embedding = embedding_model.get_text_embedding(
node.get_content(metadata_mode="all")
)
node.embedding = node_embedding
将节点加载到 Timescale Vector 向量存储中
接下来,我们将创建一个 TimescaleVectorStore
实例,并将我们创建的节点添加到其中。
# Create a timescale vector store and add the newly created nodes to it
ts_vector_store = TimescaleVectorStore.from_params(
service_url=TIMESCALE_SERVICE_URL,
table_name="li_commit_history",
time_partition_interval= timedelta(days=7),
)
ts_vector_store.add(nodes)
为了利用 Timescale Vector 高效的基于时间搜索功能,我们在实例化 Timescale Vector 向量存储时需要指定 time_partition_interval
参数。该参数表示按时间对数据进行分区的每个间隔长度。每个分区将包含落在指定时间长度内的数据。
在上面的示例中,为了简单起见,我们使用了七天,但您可以根据您的应用所使用的查询选择任何合理的值——例如,如果您经常查询最近的向量,您可能希望使用较小的时间间隔,例如一天;或者如果您查询跨越十年时间段的向量,那么您可能希望使用较大的时间间隔,例如六个月或一年。作为一般规则,常见的查询应该只触及少数几个分区,同时您的完整数据集应该适合 1000 个分区以内,但不要太纠结——系统对这个值不是很敏感。
使用时间过滤器进行相似性搜索
现在,我们已经将包含向量嵌入数据和元数据的节点加载到 Timescale Vector 向量存储中,并在存储向量和元数据的表上启用了自动时间分区,我们可以按照以下方式使用基于时间的过滤器查询我们的向量存储
# Query the vector database
vector_store_query = VectorStoreQuery(query_embedding = query_embedding, similarity_top_k=5)
# Time filter variables for query
start_dt = datetime(2023, 8, 1, 22, 10, 35) # Start date = 1 August 2023, 22:10:35
end_dt = datetime(2023, 8, 30, 22, 10, 35) # End date = 30 August 2023, 22:10:35
# return most similar vectors to query between start date and end date date range
# returns a VectorStoreQueryResult object
query_result = ts_vector_store.query(vector_store_query, start_date = start_dt, end_date = end_dt)
让我们看看查询返回的节点的日期和内容
# for each node in the query result, print the node metadata date
for node in query_result.nodes:
print("-" * 80)
print(node.metadata["date"])
print(node.get_content(metadata_mode="all"))
--------------------------------------------------------------------------------
2023-08-3 14:30:23+0500
commit: 7aeed663b9c0f337b530fd6cad47704a51a9b2ec
author: Dmitry Simonenko
date: 2023-08-3 14:30:23+0500
Thu Aug 3 14:30:23 2023 +0300 Dmitry Simonenko Feature flags for TimescaleDB features This PR adds..
--------------------------------------------------------------------------------
2023-08-29 18:13:24+0320
commit: e4facda540286b0affba47ccc63959fefe2a7b26
author: Sven Klemm
date: 2023-08-29 18:13:24+0320
Tue Aug 29 18:13:24 2023 +0200 Sven Klemm Add compatibility layer for _timescaledb_internal functions With timescaledb 2.12 all the functions present in _timescaledb_internal were…
--------------------------------------------------------------------------------
2023-08-22 12:01:19+0320
commit: cf04496e4b4237440274eb25e4e02472fc4e06fc
author: Sven Klemm
date: 2023-08-22 12:01:19+0320
Tue Aug 22 12:01:19 2023 +0200 Sven Klemm Move utility functions to _timescaledb_functions schema To increase schema security we do not want to mix…
--------------------------------------------------------------------------------
2023-08-29 10:49:47+0320
commit: a9751ccd5eb030026d7b975d22753f5964972389
author: Sven Klemm
date: 2023-08-29 10:49:47+0320
Tue Aug 29 10:49:47 2023 +0200 Sven Klemm Move partitioning functions to _timescaledb_functions schema To increase schema security…
--------------------------------------------------------------------------------
2023-08-9 15:26:03+0500
commit: 44eab9cf9bef34274c88efd37a750eaa74cd8044
author: Konstantina Skovola
date: 2023-08-9 15:26:03+0500
Wed Aug 9 15:26:03 2023 +0300 Konstantina Skovola Release 2.11.2 This release contains bug fixes since the 2.11.1 release…
成功!请注意,结果中只包含时间戳在指定起始日期(2023 年 8 月 1 日)和结束日期(2023 年 8 月 30 日)范围内的向量。
以下是关于为什么 Timescale Vector 的基于时间分区能加速带有基于时间过滤器的 ANN 查询的一些直观解释。
Timescale Vector 按时间对数据进行分区,并在每个分区上单独创建 ANN 索引。然后,在搜索过程中,我们执行一个三步过程
- 第 1 步:过滤掉不符合时间谓词的分区。
- 第 2 步:在所有匹配的分区上执行相似性搜索。
- 第 3 步:合并步骤 2 中每个分区的所有结果,重新排序,并按时间过滤结果。
Timescale Vector 利用TimescaleDB 的超表,超表根据时间戳自动对向量和关联的元数据进行分区。这使得可以通过查询向量的相似性和时间对向量进行高效查询,因为查询时间窗口之外的分区会被忽略,通过一次性过滤掉大片数据,从而大大提高了搜索效率。
在 TimescaleVectorStore 上执行向量相似性搜索时,除了指定搜索的起始日期和结束日期外,我们还可以指定一个带有给定起始日期和后续时间增量的过滤器
# return most similar vectors to query from start date and a time delta later
query_result = ts_vector_store.query(vector_store_query, start_date = start_dt, time_delta = td)
我们可以指定一个在给定结束日期和之前时间增量内的时间过滤器。这种语法对于过滤搜索结果以包含在特定截止日期之前的向量非常有用。
# return most similar vectors to query from end date and a time delta earlier
query_result = ts_vector_store.query(vector_store_query, end_date = end_dt, time_delta = td)
使用 Timescale Vector 在 LlamaIndex 应用中通过基于时间的上下文检索增强生成 (RAG)
让我们将所有内容整合起来,看看如何使用 TimescaleVectorStore 来支持在我们上面检查过的 Git 日志数据集上的 RAG。
为此,我们可以将 TimescaleVectorStore 用作QueryEngine。创建查询引擎时,我们使用 TimescaleVector 的时间过滤器,通过将时间过滤器参数作为 vector_store_kwargs
传递,将搜索限制在相关的时间范围内。
from llama_index import VectorStoreIndex
from llama_index.storage import StorageContext
index = VectorStoreIndex.from_vector_store(ts_vector_store)
query_engine = index.as_query_engine(vector_store_kwargs = ({"start_date": start_dt, "end_date":end_dt}))
query_str = "What's new with TimescaleDB functions? When were these changes made and by whom?"
response = query_engine.query(query_str)
print(str(response))
我们向 LLM 询问了一个关于我们的 Git 日志的问题,即“TimescaleDB 函数有什么新变化?这些变化是什么时候由谁做出的?”
这是我们得到的响应,它综合了在 Timescale VectorStore 上使用基于时间过滤进行语义搜索返回的节点
TimescaleDB functions have undergone changes recently. These changes include the addition of several GUCs (Global User Configuration) that allow for enabling or disabling major TimescaleDB features. Additionally, a compatibility layer has been added for the "_timescaledb_internal" functions, which were moved into the "_timescaledb_functions" schema to enhance schema security. These changes were made by Dmitry Simonenko and Sven Klemm. The specific dates of these changes are August 3, 2023, and August 29, 2023, respectively.
这是一个强大概念的简单示例——在您的 RAG 应用中使用基于时间的上下文检索可以帮助为您的用户提供更相关的答案。这种基于时间的上下文检索对任何具有自然语言和时间组成部分的数据集都很有帮助。Timescale Vector 凭借其高效的基于时间相似性搜索能力,独特地实现了这一点,而且由于 Timescale Vector 集成,在您的 LlamaIndex 应用中利用它也非常容易。
资源和后续步骤
现在您已经了解了 Timescale Vector 如何帮助您使用 PostgreSQL 支持更好的 AI 应用,接下来轮到您深入实践了。请按照下面的资源列表中的教程或阅读博客文章,迈出您学习旅程的下一步
- 上手教程: 学习如何使用真实数据集在 LlamaIndex 中使用 Timescale Vector。您将学习如何将 Timescale Vector 用作 Vectorstore、Retriever 和 QueryEngine,并对向量执行基于时间的相似性搜索。
- Timescale Vector 解释: 了解 Timescale Vector 的内部原理。
- Timescale Vector 网站: 了解更多关于 Timescale Vector 和 Timescale AI 发布周的信息。
🎉 温馨提示:LlamaIndex 用户可免费使用 Timescale Vector 90 天
我们为 LlamaIndex 用户提供 Timescale Vector 的延长 90 天试用期。这使得在 Timescale Vector 上测试和开发您的应用变得简单,因为在试用期间创建的任何云 PostgreSQL 数据库都不会向您收费。立即免费试用 Timescale Vector。