Redis 向量存储
本笔记本介绍如何开始使用 Redis 向量存储。
Redis 是一个流行的开源内存数据结构存储,可以用作数据库、缓存、消息代理和队列。它现在包括向量相似性搜索功能,使其适合用作向量存储。
什么是 Redis?
大多数开发人员都熟悉 Redis
。从本质上讲,Redis
是一个属于键值家族的 NoSQL 数据库,可以用作缓存、消息代理、流处理和主数据库。开发人员选择 Redis
是因为它速度快,拥有庞大的客户端库生态系统,并且多年来已被主要企业部署。
在这些传统用例的基础上,Redis
提供了额外的功能,如搜索和查询能力,允许用户在 Redis
中创建二级索引结构。这使得 Redis
能 够以缓存的速度成为一个向量数据库。
Redis 作为向量数据库
Redis
使用压缩的倒排索引进行快速索引,内存占用低。它还支持许多高级功能,例如:
- 在 Redis 哈希和
JSON
中索引多个字段 - 向量相似性搜索(使用
HNSW
(近似最近邻)或FLAT
(KNN)) - 向量范围搜索(例如,查找查询向量半径内的所有向量)
- 增量索引,无性能损失
- 文档排名(使用 tf-idf,可选用户提供的权重)
- 字段加权
- 复杂布尔查询,使用
AND
、OR
和NOT
操作符 - 前缀匹配、模糊匹配和精确短语查询
- 支持 双重隐喻音匹配
- 自动完成建议(带模糊前缀建议)
- 基于词干的查询扩展,支持 多种语言(使用 Snowball)
- 支持中文分词和查询(使用 Friso)
- 数值过滤和范围
- 使用 Redis 地理空间索引的地理空间搜索
- 强大的聚合引擎
- 支持所有
utf-8
编码的文本 - 检索完整文档、选定字段或仅文档 ID
- 排序结果(例如,按创建日期)
客户端
由于 Redis
不仅仅是一个向量数据库,因此通常会有使用 Redis
客户端的用例,除了 LangChain
集成。您可以使用任何标准的 Redis
客户端库来运行搜索和查询命令,但使用一个封装了搜索和查询 API 的库是最简单的。以下是一些示例,但您可以在 这里 找到更多客户端库。
项目 | 语言 | 许可证 | 作者 | 星标 |
---|---|---|---|---|
jedis | Java | MIT | Redis | |
redisvl | Python | MIT | Redis | |
redis-py | Python | MIT | Redis | |
node-redis | Node.js | MIT | Redis | |
nredisstack | .NET | MIT | Redis |
部署选项
有多种方式可以部署Redis与RediSearch。最简单的入门方法是使用Docker,但还有许多潜在的部署选项,例如
- Redis Cloud
- Docker (Redis Stack)
- 云市场: AWS Marketplace, Google Marketplace, 或 Azure Marketplace
- 本地部署: Redis Enterprise Software
- Kubernetes: Redis Enterprise Software on Kubernetes
Redis连接Url模式
有效的Redis Url模式有:
redis://
- 连接到Redis独立版,未加密rediss://
- 连接到独立的Redis,使用TLS加密redis+sentinel://
- 通过Redis Sentinel连接到Redis服务器,不加密rediss+sentinel://
- 通过Redis Sentinel连接到Redis服务器,两个连接均使用TLS加密
有关其他连接参数的更多信息,请参阅redis-py文档。
设置
要使用RedisVectorStore,您需要安装langchain-redis
第三方库,以及本笔记本中使用的其他库。
%pip install -qU langchain-redis langchain-huggingface sentence-transformers scikit-learn
Note: you may need to restart the kernel to use updated packages.
凭证
Redis 连接凭证作为 Redis 连接 URL 的一部分传递。Redis 连接 URL 是多功能的,可以适应各种 Redis 服务器拓扑和认证方法。这些 URL 遵循特定格式,包括连接协议、认证细节、主机、端口和数据库信息。 Redis 连接 URL 的基本结构是:
[protocol]://[auth]@[host]:[port]/[database]
其中:
- 协议可以是 redis(标准连接)、rediss(SSL/TLS 连接)或 redis+sentinel(哨兵连接)。
- auth 包括用户名和密码(如果适用)。
- host 是 Redis 服务器的主机名或 IP 地址。
- port 是 Redis 服务器的端口。
- database 是 Redis 数据库编号。
Redis 连接 URL 支持各种配置,包括:
- 独立的 Redis 服务器(有或没有认证)
- Redis Sentinel 设置
- SSL/TLS 加密连接
- 不同的认证方法(仅密码或用户名-密码)
以下是不同配置的 Redis 连接 URL 示例:
# connection to redis standalone at localhost, db 0, no password
redis_url = "redis://localhost:6379"
# connection to host "redis" port 7379 with db 2 and password "secret" (old style authentication scheme without username / pre 6.x)
redis_url = "redis://:secret@redis:7379/2"
# connection to host redis on default port with user "joe", pass "secret" using redis version 6+ ACLs
redis_url = "redis://joe:secret@redis/0"
# connection to sentinel at localhost with default group mymaster and db 0, no password
redis_url = "redis+sentinel://localhost:26379"
# connection to sentinel at host redis with default port 26379 and user "joe" with password "secret" with default group mymaster and db 0
redis_url = "redis+sentinel://joe:secret@redis"
# connection to sentinel, no auth with sentinel monitoring group "zone-1" and database 2
redis_url = "redis+sentinel://redis:26379/zone-1/2"
# connection to redis standalone at localhost, db 0, no password but with TLS support
redis_url = "rediss://localhost:6379"
# connection to redis sentinel at localhost and default port, db 0, no password
# but with TLS support for booth Sentinel and Redis server
redis_url = "rediss+sentinel://localhost"
使用 Docker 启动 Redis 实例
要在 LangChain 中使用 Redis,您需要一个正在运行的 Redis 实例。您可以使用 Docker 启动一个:
docker run -d -p 6379:6379 redis/redis-stack:latest
在这个示例中,我们将使用本地 Redis 实例。如果您使用的是远程实例,您需要相应地修改 Redis URL。
import os
REDIS_URL = os.getenv("REDIS_URL", "redis://localhost:6379")
print(f"Connecting to Redis at: {REDIS_URL}")
Connecting to Redis at: redis://redis:6379
如果您想自动跟踪模型调用,您还可以通过取消注释下面的内容来设置您的 LangSmith API 密钥:
# os.environ["LANGSMITH_API_KEY"] = getpass.getpass("Enter your LangSmith API key: ")
# os.environ["LANGSMITH_TRACING"] = "true"
让我们通过 ping 它来检查 Redis 是否正常运行:
import redis
redis_client = redis.from_url(REDIS_URL)
redis_client.ping()
True
示例数据
20个新闻组数据集包含大约18000条关于20个主题的新闻组帖子。我们将使用一个子集进行演示,并专注于两个类别:'alt.atheism' 和 'sci.space':
<!--IMPORTS:[{"imported": "Document", "source": "langchain.docstore.document", "docs": "https://python.langchain.com/api_reference/core/documents/langchain_core.documents.base.Document.html", "title": "Redis Vector Store"}]-->
from langchain.docstore.document import Document
from sklearn.datasets import fetch_20newsgroups
categories = ["alt.atheism", "sci.space"]
newsgroups = fetch_20newsgroups(
subset="train", categories=categories, shuffle=True, random_state=42
)
# Use only the first 250 documents
texts = newsgroups.data[:250]
metadata = [
{"category": newsgroups.target_names[target]} for target in newsgroups.target[:250]
]
len(texts)
250
初始化
RedisVectorStore 实例可以通过几种方式初始化:
RedisVectorStore.__init__
- 直接初始化RedisVectorStore.from_texts
- 从文本列表初始化(可选带元数据)RedisVectorStore.from_documents
- 从一组langchain_core.documents.Document
对象初始化RedisVectorStore.from_existing_index
- 从现有的 Redis 索引初始化
下面我们将使用 RedisVectorStore.__init__
方法,使用一个 RedisConfig
实例。
- OpenAI
- HuggingFace
- Fake Embedding
pip install -qU langchain-openai
import getpass
os.environ["OPENAI_API_KEY"] = getpass.getpass()
from langchain_openai import OpenAIEmbeddings
embeddings = OpenAIEmbeddings(model="text-embedding-3-large")
pip install -qU langchain-huggingface
from langchain_huggingface import HuggingFaceEmbeddings
embeddings = HuggingFaceEmbeddings(model="sentence-transformers/all-mpnet-base-v2")
pip install -qU langchain-core
from langchain_core.embeddings import FakeEmbeddings
embeddings = FakeEmbeddings(size=4096)
我们将使用 SentenceTransformer 模型来创建嵌入。该模型在本地运行,不需要 API 密钥。
from langchain_redis import RedisConfig, RedisVectorStore
config = RedisConfig(
index_name="newsgroups",
redis_url=REDIS_URL,
metadata_schema=[
{"name": "category", "type": "tag"},
],
)
vector_store = RedisVectorStore(embeddings, config=config)
管理向量存储
向向量存储添加项目
ids = vector_store.add_texts(texts, metadata)
print(ids[0:10])
['newsgroups:f1e788ee61fe410daa8ef941dd166223', 'newsgroups:80b39032181f4299a359a9aaed6e2401', 'newsgroups:99a3efc1883647afba53d115b49e6e92', 'newsgroups:503a6c07cd71418eb71e11b42589efd7', 'newsgroups:7351210e32d1427bbb3c7426cf93a44f', 'newsgroups:4e79fdf67abe471b8ee98ba0e8a1a055', 'newsgroups:03559a1d574e4f9ca0479d7b3891402e', 'newsgroups:9a1c2a7879b8409a805db72feac03580', 'newsgroups:3578a1e129f5435f9743cf803413f37a', 'newsgroups:9f68baf4d6b04f1683d6b871ce8ad92d']
让我们检查第一个文档:
texts[0], metadata[0]
('From: bil@okcforum.osrhe.edu (Bill Conner)\nSubject: Re: Not the Omni!\nNntp-Posting-Host: okcforum.osrhe.edu\nOrganization: Okcforum Unix Users Group\nX-Newsreader: TIN [version 1.1 PL6]\nLines: 18\n\nCharley Wingate (mangoe@cs.umd.edu) wrote:\n: \n: >> Please enlighten me. How is omnipotence contradictory?\n: \n: >By definition, all that can occur in the universe is governed by the rules\n: >of nature. Thus god cannot break them. Anything that god does must be allowed\n: >in the rules somewhere. Therefore, omnipotence CANNOT exist! It contradicts\n: >the rules of nature.\n: \n: Obviously, an omnipotent god can change the rules.\n\nWhen you say, "By definition", what exactly is being defined;\ncertainly not omnipotence. You seem to be saying that the "rules of\nnature" are pre-existant somehow, that they not only define nature but\nactually cause it. If that\'s what you mean I\'d like to hear your\nfurther thoughts on the question.\n\nBill\n',
{'category': 'alt.atheism'})
从向量存储删除项目
# Delete documents by passing one or more keys/ids
vector_store.index.drop_keys(ids[0])
1