Metadata-Version: 2.4
Name: openai-redis-vectorstore
Version: 0.3.0
Summary: 基于RedisStack向量数据库，集成embeddings和rerank模型，支持二阶段召回，支持添加和删除等管理功能。
Author: rRR0VrFP
Maintainer: rRR0VrFP
License: Apache License, Version 2.0
Project-URL: Homepage, https://gitee.com/rRR0VrFP/openai-redis-vectorstore
Keywords: openai-redis-vectorstore
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: PyYAML
Requires-Dist: httpx
Requires-Dist: pydantic
Requires-Dist: python-environment-settings
Requires-Dist: redis
Requires-Dist: zenutils
Requires-Dist: redisvl>=0.27.1
Dynamic: license-file

# openai-redis-vectorstore

基于RedisStack向量数据库，集成embeddings和rerank模型，支持二阶段召回，支持添加和删除等管理功能。

## 安装

```shell
pip install openai-redis-vectorstore
```

## 依赖说明

- 使用`python-environment-settings`管理配置项。详见该项目的参考文档。
- 使用`redisvl`实现向量检索，通过`httpx`以裸 HTTP 方式调用 OpenAI 兼容的`embeddings`与`rerank`接口，不依赖`openai` SDK。

## 配置项说明

- OPENAI_REDIS_VECTORSTORE_REDIS_STACK_URL: redis-stack服务器地址。如：redis://localhost:6379/0
    - 要使用redis-stack向量功能，必须是0号库（否则会报`redis.exceptions.ResponseError: Cannot create index on db != 0`错误）
    - （配置项别名）
    - REDIS_STACK_URL
    - REDIS_URL
    - REDIS

- OPENAI_BASE_URL: embeddings/rerank通用服务地址。如：http://localhost/v1
    - （配置项别名）LLM_BASE_URL、BASE_URL
- OPENAI_API_KEY: embeddings/rerank通用访问密钥。
    - （配置项别名）LLM_API_KEY、API_KEY
- OPENAI_EMBEDDINGS_BASE_URL: embeddings专用服务地址。优先于`OPENAI_BASE_URL`，未配置时回落到`OPENAI_BASE_URL`。
    - （配置项别名）EMBEDDINGS_BASE_URL
- OPENAI_EMBEDDINGS_API_KEY: embeddings专用访问密钥。优先于`OPENAI_API_KEY`，未配置时回落到`OPENAI_API_KEY`。
    - （配置项别名）EMBEDDINGS_API_KEY
- OPENAI_EMBEDDINGS_MODEL: embeddings模型名称。默认`bge-m3`。
    - （配置项别名）OPENAI_EMBEDDINGS_MODEL_NAME、EMBEDDINGS_MODEL、EMBEDDINGS_MODEL_NAME
- OPENAI_EMBEDDINGS_MAX_SIZE: embeddings单次可处理的文本长度上限（字符数），非向量维度。默认`1024`。
    - （配置项别名）EMBEDDINGS_MAX_SIZE
- OPENAI_EMBEDDINGS_DIMS: embeddings向量维度（建索引用），由所选模型决定。默认`1024`。
    - （配置项别名）EMBEDDINGS_DIMS
- OPENAI_RERANK_BASE_URL: rerank专用服务地址。优先于`OPENAI_BASE_URL`，未配置时回落到`OPENAI_BASE_URL`。
    - （配置项别名）RERANK_BASE_URL
- OPENAI_RERANK_API_KEY: rerank专用访问密钥。优先于`OPENAI_API_KEY`，未配置时回落到`OPENAI_API_KEY`。
    - （配置项别名）RERANK_API_KEY
- OPENAI_RERANK_MODEL: rerank模型名称。默认`bge-reranker-v2-m3`。
    - （配置项别名）OPENAI_RERANK_MODEL_NAME、RERANK_MODEL、RERANK_MODEL_NAME
- OPENAI_RERANK_MAX_SIZE: rerank单次可处理的文本长度上限（字符数），非向量维度。默认`1024`。
    - （配置项别名）RERANK_MAX_SIZE

## 使用

> 以下示例默认相关模型服务已按[配置项说明](#配置项说明)正确配置，且依赖`redis-stack`已启动。

### 将文本插入到向量数据库

*代码：*

```python
import uuid

from openai_redis_vectorstore import RedisVectorStore

index_name = str(uuid.uuid4())
page_id = str(uuid.uuid4())
rvs = RedisVectorStore(index_name=index_name)

# 插入单条文本
uid = rvs.insert("hello", metadata={"id": 1}, page_id=page_id)
assert uid == f"{index_name}:{page_id}"
```

*说明：*

- `metadata`：内容在业务系统中的属性集，`id=1`表示内容在业务系统中的唯一码。
- `uid`：内容在向量数据库中的唯一码，一般为`<index_name>:<page_id>`。可以根据`uid`从向量数据库中删除或查询相应内容。

### 批量插入文本

*代码：*

```python
import uuid

from openai_redis_vectorstore import RedisVectorStore

index_name = str(uuid.uuid4())
rvs = RedisVectorStore(index_name=index_name)

# 批量插入多段文本
uids = rvs.insert_many(
    ["开会了", "再见", "你好"],
    metadatas=[
        {"id": 1},
        {"id": 2},
        {"id": 3},
    ],
)
assert len(uids) == 3
```

### 关联知识库/文档/分类（用于后续过滤）

*代码：*

```python
import uuid

from openai_redis_vectorstore import RedisVectorStore

index_name = str(uuid.uuid4())
rvs = RedisVectorStore(index_name=index_name)

rvs.insert("hello", kb_id="k1", doc_id="d1", category="c1")
rvs.insert("hi", kb_id="k2", doc_id="d2", category="c2")
```

*说明：*

- `kb_id`：知识库唯一码。
- `doc_id`：文本内容关联文档唯一码。
- `category`：文本内容分类。
- 三者均可作为搜索时的过滤条件，详见下文"带过滤条件的检索"。

### 相似度检索（带得分）

*代码：*

```python
import uuid

from openai_redis_vectorstore import RedisVectorStore

index_name = str(uuid.uuid4())
rvs = RedisVectorStore(index_name=index_name)
rvs.insert("hello", kb_id="k1", category="c1")
rvs.insert("hi", kb_id="k2", category="c2")

docs = rvs.similarity_search_with_relevance_scores("hello", k=4)
for doc in docs:
    print(doc.vs_page_content, doc.vs_embeddings_score)
```

*说明：*

- 返回结果按`vs_embeddings_score`（归一化到`[0,1]`，越大越相似）降序排列。
- `Document`常用字段：
  - `vs_uid`：向量数据库唯一码。
  - `vs_page_content`：检索命中的文本内容。
  - `vs_embeddings_score`：embeddings相似度得分。
  - `vs_index_name`：命中的索引名。

### 带过滤条件的检索（kb_id / category）

*代码：*

```python
import uuid

from openai_redis_vectorstore.base import RedisVectorStore

index_name = str(uuid.uuid4())
rvs = RedisVectorStore(index_name=index_name)
rvs.insert("hello", kb_id="k1", category="c1")
rvs.insert("hi", kb_id="k2", category="c2")

# 在指定的知识库与分类范围内检索
docs = rvs.similarity_search_with_relevance_scores(
    "hello",
    kb_ids=["k1", "k2"],
    category="c2",
)
assert len(docs) == 1
assert docs[0].vs_page_content == "hi"
```

> 提示：`kb_id`/`kb_ids`、`category`/`categories`可单独或组合使用；也可通过`filter=`参数传入redisvl的过滤表达式（如`Tag("category") == "c2"`）。

### 二阶段召回（embeddings + rerank）

*代码：*

```python
import uuid

from openai_redis_vectorstore.base import RedisVectorStore

index_name1 = str(uuid.uuid4())
index_name2 = str(uuid.uuid4())
rvs = RedisVectorStore()

# 向1号逻辑库中插入3条数据
rvs.insert_many(
    ["开会了", "再见", "你好"],
    metadatas=[{"id": 1}, {"id": 2}, {"id": 3}],
    index_name=index_name1,
)

# 向2号逻辑库中插入3条数据
rvs.insert_many(
    ["开会去", "好的", "谢谢"],
    metadatas=[{"id": 1}, {"id": 2}, {"id": 3}],
    index_name=index_name2,
)

# 二阶段召回：先用embeddings粗召回，再用rerank精排
# 支持跨多个索引汇总结果
docs = rvs.similarity_search_and_rerank(
    query="开会",
    index_names=[index_name1, index_name2],
    embeddings_score_threshold=0.65,
    rerank_score_threshold=0.85,
)
assert len(docs) == 2
doc1 = docs[0]
doc2 = docs[1]
assert doc1.vs_index_name in [index_name1, index_name2]
assert doc2.vs_index_name in [index_name1, index_name2]
assert doc1.vs_rerank_score > doc2.vs_rerank_score
```

*说明：*

- `embeddings_score_threshold`：embeddings粗召回的相似度阈值（`[0,1]`）。
- `rerank_score_threshold`：rerank精排的相似度阈值（`[0,1]`）。
- `vs_rerank_score`：rerank后的排序得分。
- `k`：最终返回条数；`scale`：粗召回时按`k * scale`放大候选集再精排。

### 查询单条记录

*代码：*

```python
import uuid

from openai_redis_vectorstore import RedisVectorStore

index_name = str(uuid.uuid4())
rvs = RedisVectorStore(index_name=index_name)
uid = rvs.insert("hello")

item = rvs.get_item(uid)
print(item)
```

### 删除记录

*代码：*

```python
import uuid

from openai_redis_vectorstore import RedisVectorStore

index_name = str(uuid.uuid4())
rvs = RedisVectorStore(index_name=index_name)
uid1 = rvs.insert("hello")
uid2 = rvs.insert("hi")

# 删除单条
rvs.delete(uid1)
# 批量删除
rvs.delete_many([uid2])
```

### 清空索引

*代码：*

```python
import uuid

from openai_redis_vectorstore import RedisVectorStore

index_name = str(uuid.uuid4())
rvs = RedisVectorStore(index_name=index_name)
rvs.insert("hello")

# 清空指定索引（默认清空实例初始化时的索引）
rvs.flush(index_name=index_name)
```

## 版本记录

### v0.1.0

- 版本首发。

### v0.1.1

- 修改：搜索一个空向量库时，只在日志中记录WARNING信息并返回空数组。

### v0.1.2

- 修改：`openai_redis_vectorstore.schemas.Document`增加`content`字段。

### v0.1.3

- 修正：查询结果page_content字段没有做反序列化的问题。

### v0.2.0

- 优化：默认配置项与`embeddings/rerank`相关配置项保持一致。
- 优化：允许`embeddings/rerank`使用不同的`base_url`和`api_key`。
- 变更（不兼容）：使用`langchain_redis`取代`langchain_community.vectorstores.redis`。

### v0.2.1

- 新增：支持`kb_id`，`category`过滤条件。

### 0.2.2

- 修正：`flush`没有删除索引的问题。

### v0.3.0

- 变更（不兼容）：使用`redisvl`取代`langchain_redis`，移除`langchain`相关依赖。
- 变更（不兼容）：`embeddings`与`rerank`改为通过`httpx`以裸 HTTP 方式调用 OpenAI 兼容接口，移除`openai` SDK 依赖。
- 变更（不兼容）：`score`归一化为`[0,1]`并手动`threshold`过滤。
- 新增：单元测试及覆盖率配置。
