如何高效地在Redis向量数据库中存储和加载自定义嵌入

如何高效地在Redis向量数据库中存储和加载自定义嵌入

本文详细介绍了如何利用Langchain库在redis向量数据库中存储和检索自定义文本嵌入。我们将从加载本地文本文件、进行文档切分,到生成嵌入并将其持久化到redis,最终执行相似性搜索,提供一个完整的操作指南。内容涵盖关键代码示例、不同嵌入模型的选择,以及关于Redis中嵌入数据生命周期(TTL)的考量,旨在帮助开发者构建高效的向量搜索应用。

1. 引言与背景

在构建基于大型语言模型(LLM)的应用时,将自定义数据(如文档、文章、用户评论等)转化为可搜索的向量嵌入,并存储在向量数据库中,是实现知识检索和问答系统的核心环节。Redis作为一个高性能的内存数据库,结合其向量搜索模块(Redis Stack),能够提供快速、可扩展的向量存储和检索能力。Langchain作为LLM应用开发的强大框架,为与各种向量数据库集成提供了便捷的接口

本教程将专注于解决一个常见需求:如何将本地文本文件中的数据加载、处理、生成嵌入,并有效地存储到Redis中,以便后续进行高效的相似性搜索。

2. 环境准备与依赖安装

在开始之前,请确保您已安装必要的python库,并且Redis服务器已启动并运行。

pip install langchain openai redis-py tiktoken # 如果需要使用SentenceTransformer,请安装: # pip install sentence-transformers

确保您的Redis服务器已启动,并且安装了Redis Stack(包含RediSearch模块),以便支持向量索引功能。默认情况下,Langchain将尝试连接 redis://localhost:6379。

3. 加载与处理自定义文本数据

要将自定义文本数据导入Redis,首先需要加载这些数据并对其进行预处理。Langchain提供了多种文档加载器(Document Loaders)和文本切分器(Text Splitters),以适应不同的数据源和处理需求。

3.1 加载文本文件

对于本地文本文件,TextLoader 是一个简单而有效的选择。它能将整个文件的内容加载为一个Langchain Document 对象

假设您有一个名为 union.txt 的文本文件,内容如下:

# union.txt Langchain是一个用于开发由语言模型驱动的应用程序的框架。 它提供了一套工具、组件和接口,旨在简化LLM应用的开发流程。 Langchain支持多种模型、数据源和工具的集成。 Redis是一个开源的内存数据结构存储,可用作数据库、缓存和消息代理。 它支持多种数据结构,如字符串、哈希、列表、集合、有序集合等。 Redis Stack是Redis的扩展,包含了RediSearch等模块,提供了向量搜索能力。 向量数据库是专门用于存储、管理和查询向量嵌入的数据库。 它们通常支持高效的相似性搜索算法,如最近邻搜索(Nearest Neighbor Search)。 在构建RAG(Retrieval-Augmented Generation)系统时,向量数据库至关重要。

使用 TextLoader 加载文件:

from langchain.document_loaders import TextLoader  # 假设 union.txt 位于脚本同级目录 loader = TextLoader("union.txt", encoding="utf-8") documents = loader.load()  print(f"加载的文档数量: {len(documents)}") print(f"第一个文档内容预览: {documents[0].page_content[:100]}...")

3.2 切分文档

大型文档通常需要被切分成更小的块(chunks),以便更好地进行嵌入和搜索。较小的块有助于提高搜索的精确性,并避免超出嵌入模型或LLM的上下文窗口限制。CharacterTextSplitter 是一个常用的文本切分器。

from langchain.text_splitter import CharacterTextSplitter  # 定义切分器: # chunk_size: 每个块的最大字符数 # chunk_overlap: 块之间重叠的字符数,有助于保留上下文 text_splitter = CharacterTextSplitter(chunk_size=200, chunk_overlap=20) docs = text_splitter.split_documents(documents)  print(f"切分后的文档块数量: {len(docs)}") for i, doc in enumerate(docs[:3]): # 打印前3个文档块     print(f"n--- 文档块 {i+1} ---")     print(doc.page_content)     print(f"元数据: {doc.metadata}")

注意事项:

  • chunk_size 和 chunk_overlap 的选择对搜索结果有显著影响。应根据您的数据特性和应用需求进行调整。
  • Langchain还提供了其他切分器,如 RecursiveCharacterTextSplitter,它能更智能地根据段落、句子等结构进行切分。

4. 生成嵌入并存储到Redis

在文档切分完成后,下一步是使用嵌入模型将这些文本块转换为向量嵌入,并将其存储到Redis向量数据库中。

4.1 选择嵌入模型

Langchain支持多种嵌入模型,包括OpenAI Embeddings、Sentence Transformers等。您可以根据需求选择合适的模型。

  • OpenAIEmbeddings: 适用于需要与OpenAI生态系统集成的场景,通常提供高质量的嵌入。需要配置OpenAI API密钥。
  • SentenceTransformerEmbeddings: 适用于本地运行或对成本敏感的场景,提供了多种预训练模型。

本教程以 OpenAIEmbeddings 为例:

from langchain.embeddings import OpenAIEmbeddings from langchain.vectorstores.redis import Redis  # 初始化嵌入模型 # 确保已设置 OPENAI_API_KEY 环境变量 embeddings = OpenAIEmbeddings()  # 或者使用 SentenceTransformerEmbeddings (如果已安装 sentence-transformers) # from langchain.embeddings.sentence_transformer import SentenceTransformerEmbeddings # embeddings = SentenceTransformerEmbeddings(model_name="all-MiniLM-L6-v2")

4.2 存储到Redis

Redis.from_documents 方法是Langchain与Redis集成的核心,它负责将处理好的文档块、选定的嵌入模型和Redis连接信息结合起来,自动完成嵌入生成和数据存储。

# 假设 Redis 服务器运行在 localhost:6379 redis_url = "redis://localhost:6379" index_name = "my_custom_embeddings" # 为您的嵌入数据指定一个索引名称  # 从文档生成嵌入并存储到Redis vectorstore = Redis.from_documents(     docs,     embeddings,     redis_url=redis_url,     index_name=index_name, )  print(f"成功将 {len(docs)} 个文档块及其嵌入存储到Redis索引 '{index_name}' 中。")

执行上述代码后,您的文本块及其对应的向量嵌入就会被索引并存储在Redis中。

5. 执行相似性搜索

一旦数据存储在Redis中,您就可以使用 similarity_search 或 similarity_search_with_score 方法来查询与给定文本最相似的文档。

# 使用之前创建的 vectorstore 对象进行搜索 query_text = "什么是向量数据库?"  # 执行相似性搜索,返回最相似的文档 results = vectorstore.similarity_search(query_text) print(f"n--- 相似性搜索结果 (Top 4) ---") for i, doc in enumerate(results[:4]):     print(f"文档 {i+1}:")     print(f"  内容: {doc.page_content}")     print(f"  元数据: {doc.metadata}")     print("-" * 20)  # 执行相似性搜索并返回分数(分数越低表示越相似,通常是距离度量) results_with_score = vectorstore.similarity_search_with_score(query_text) print(f"n--- 相似性搜索结果带分数 (Top 4) ---") for i, (doc, score) in enumerate(results_with_score[:4]):     print(f"文档 {i+1}:")     print(f"  内容: {doc.page_content}")     print(f"  分数: {score}")     print(f"  元数据: {doc.metadata}")     print("-" * 20)

6. 关于Redis中嵌入的TTL(Time-To-Live)

用户经常会关心存储在Redis中的数据生命周期(TTL)。在Langchain的Redis向量存储实现中,直接通过 from_documents 方法为每个单独的嵌入设置TTL并不直接支持。然而,Redis本身支持对键设置TTL。

  • Redis Vector Store的TTL参数: Redis 类的构造函数中有一个 ttl 参数,但它主要用于设置整个索引的过期时间,而不是每个文档块的单独过期时间。如果设置,整个索引会在指定时间后过期。
  • 手动管理TTL: 如果您需要对每个嵌入(或其对应的Redis键)进行精细的TTL控制,您可能需要:
    1. 在将数据写入Redis之前,手动为每个键生成唯一的键名。
    2. 使用Redis客户端库(如 redis-py)直接操作Redis,在存储向量和元数据时,通过 EXPIRE 或 SETEX 命令为每个键设置TTL。
    3. Langchain的 Redis.add_documents 方法在内部调用了Redis的FT.ADD命令,该命令目前不直接支持为单个文档设置TTL。

对于大多数向量搜索应用,通常不会为每个嵌入设置短期TTL,因为嵌入数据通常是相对稳定的知识库。如果数据需要定期更新或过期,更常见的做法是重新构建索引或使用外部机制来管理数据的生命周期。

7. 总结与最佳实践

本教程详细演示了如何使用Langchain将自定义文本数据加载、切分、嵌入并存储到Redis向量数据库中,并执行相似性搜索。

关键步骤回顾:

  1. 加载数据: 使用 TextLoader 等加载器读取原始文本。
  2. 切分文档: 利用 CharacterTextSplitter 等工具将文档切分成适当大小的块。
  3. 选择嵌入模型: 根据需求选择 OpenAIEmbeddings 或 SentenceTransformerEmbeddings 等。
  4. 存储与索引: 使用 Redis.from_documents 将处理后的文档和嵌入存储到Redis。
  5. 执行搜索: 通过 similarity_search 或 similarity_search_with_score 查询相似文档。

最佳实践:

  • 优化切分策略: 实验不同的 chunk_size 和 chunk_overlap 值,以找到最适合您数据和查询模式的配置。
  • 选择合适的嵌入模型: 考虑模型性能、成本和部署环境。
  • 监控Redis性能: 确保Redis实例有足够的内存和CPU资源来处理向量数据和查询负载。
  • 索引命名规范: 使用有意义的 index_name,便于管理多个向量索引。

通过掌握这些技术,您可以有效地利用Redis作为强大的向量数据库,为您的LLM应用提供高效、灵活的知识检索能力。

© 版权声明
THE END
喜欢就支持一下吧
点赞10 分享