openai-simple-vectorstore
可扩展的多向量数据库接入库,内置 redis-search、milvus、milvus-lite、 pgvector、elasticsearch、sqlite-vec 与 qdrant 等后端,并提供统一的 embeddings / rerank 二阶段召回、插入、删除、刷新等管理接口。
- redis-search 后端与 openai-redis-vectorstore
行为兼容(相同的 uid 规则与 relevance score,生成的 key 布局一致),但索引
PREFIX由 上游的index_name改为index_name + ":",以隔离同名前缀的其它索引(见版本记录)。 - 通过工厂 + 注册表机制可低成本扩展其它向量数据库后端。
安装
需要 Python >= 3.10。作为业务方使用时,从 PyPI 安装本库并选择所需后端:
# 仅核心依赖(不含任何后端驱动,需配合下面的某个后端 extra 使用)
pip3 install openai-simple-vectorstore
# redis-search 后端(也是 create_vector_store() 的默认后端)
pip3 install "openai-simple-vectorstore[redis]"
# 仅 milvus 后端(依赖 pymilvus)
pip3 install "openai-simple-vectorstore[milvus]"
# 仅 milvus-lite 后端(嵌入式 milvus,无需单独部署,适合本地开发)
pip3 install "openai-simple-vectorstore[milvus-lite]"
# 仅 pgvector 后端(依赖 psycopg2-binary)
pip3 install "openai-simple-vectorstore[pgvector]"
# 仅 elasticsearch 后端
pip3 install "openai-simple-vectorstore[elasticsearch]"
# 仅 sqlite-vec 后端(嵌入式 SQLite,无需单独部署)
pip3 install "openai-simple-vectorstore[sqlite-vec]"
# 仅 qdrant 后端(依赖 qdrant-client)
pip3 install "openai-simple-vectorstore[qdrant]"
# 全部后端
pip3 install "openai-simple-vectorstore[all]"
在仓库源码目录内本地开发时使用
pip3 install -e ".[all]";离线安装本库自身依赖时, 先准备本地 wheel 目录,再执行pip3 install --no-index --find-links <wheel 目录> -r requirements.txt。
快速开始
选择 redis-search 后端(默认)
需先安装 redis 后端:
pip3 install "openai-simple-vectorstore[redis]"。
from openai_simple_vectorstore import create_vector_store
vs = create_vector_store() # 默认后端由 OPENAI_SIMPLE_VECTORSTORE_VECTOR_DB 决定
# 插入
uid = vs.insert("今天天气很好", kb_id="kb1", doc_id="doc1", page_id="p1")
# 二阶段召回(向量检索 + rerank 重排)
docs = vs.similarity_search_and_rerank(
query="今天天气怎么样",
index_name="default",
k=3,
)
for doc in docs:
print(doc.vs_page_content, doc.vs_embeddings_score, doc.vs_rerank_score)
# 删除 / 刷新
vs.delete(uid)
vs.flush("default")
选择 milvus 后端
from openai_simple_vectorstore import create_vector_store
vs = create_vector_store( # 或环境变量 OPENAI_SIMPLE_VECTORSTORE_VECTOR_DB=milvus
vector_db="milvus"
)
vs.insert("..." , kb_id="kb1", doc_id="doc1", page_id="p1")
docs = vs.similarity_search_and_rerank(query="...", index_name="default", k=3)
选择 milvus-lite 后端(嵌入式,本地开发)
milvus-lite 与 milvus 使用相同的 MilvusClient API,仅连接方式不同:milvus 通过
http://host:19530 连接服务端,milvus-lite 通过本地文件路径启动进程内嵌入式数据库,
无需部署 milvus 服务。
from openai_simple_vectorstore import create_vector_store
vs = create_vector_store(
vector_db="milvus-lite", # 或环境变量 OPENAI_SIMPLE_VECTORSTORE_VECTOR_DB=milvus-lite
milvus_lite_uri="./milvus_lite.db", # 默认 OPENAI_SIMPLE_VECTORSTORE_MILVUS_LITE_URI
)
vs.insert("..." , kb_id="kb1", doc_id="doc1", page_id="p1")
docs = vs.similarity_search_and_rerank(query="...", index_name="default", k=3)
选择 sqlite-vec 后端(嵌入式,本地开发)
sqlite-vec 是 SQLite 的向量搜索扩展,所有数据存在本地文件中,无需单独部署服务。 注意:需要 sqlite3 支持 loadable-extension(多数发行版 CPython 均支持)。
from openai_simple_vectorstore import create_vector_store
vs = create_vector_store(
vector_db="sqlite-vec", # 或环境变量 OPENAI_SIMPLE_VECTORSTORE_VECTOR_DB=sqlite-vec
sqlite_vec_uri="./sqlite_vec.db", # 默认 OPENAI_SIMPLE_VECTORSTORE_SQLITE_VEC_URI
)
vs.insert("..." , kb_id="kb1", doc_id="doc1", page_id="p1")
docs = vs.similarity_search_and_rerank(query="...", index_name="default", k=3)
选择 pgvector 后端(PostgreSQL 扩展)
需要 PostgreSQL 已安装 pgvector 扩展(程序启动时会尝试 CREATE EXTENSION IF NOT EXISTS vector):
from openai_simple_vectorstore import create_vector_store
vs = create_vector_store(
vector_db="pgvector", # 或环境变量 OPENAI_SIMPLE_VECTORSTORE_VECTOR_DB=pgvector
pgvector_url="postgresql://user:pass@localhost:5432/postgres", # 默认 OPENAI_SIMPLE_VECTORSTORE_PGVECTOR_URL
)
vs.insert("..." , kb_id="kb1", doc_id="doc1", page_id="p1")
docs = vs.similarity_search_and_rerank(query="...", index_name="default", k=3)
选择 elasticsearch 后端
需要 Elasticsearch 8.x(dense_vector + HNSW):
from openai_simple_vectorstore import create_vector_store
vs = create_vector_store(
vector_db="elasticsearch", # 或环境变量 OPENAI_SIMPLE_VECTORSTORE_VECTOR_DB=elasticsearch
es_url="http://localhost:9200",
)
vs.insert("..." , kb_id="kb1", doc_id="doc1", page_id="p1")
docs = vs.similarity_search_and_rerank(query="...", index_name="default", k=3)
选择 qdrant 后端
需要部署 Qdrant 服务(或使用 Qdrant Cloud)。使用原生 payload 过滤,无需像 milvus / sqlite-vec 那样对候选集做放大:
from openai_simple_vectorstore import create_vector_store
vs = create_vector_store(
vector_db="qdrant", # 或环境变量 OPENAI_SIMPLE_VECTORSTORE_VECTOR_DB=qdrant
qdrant_url="http://localhost:6333", # 默认 OPENAI_SIMPLE_VECTORSTORE_QDRANT_URL
qdrant_api_key=None, # 默认 OPENAI_SIMPLE_VECTORSTORE_QDRANT_API_KEY
# qdrant_prefer_grpc=True, # 默认 OPENAI_SIMPLE_VECTORSTORE_QDRANT_PREFER_GRPC
# qdrant_timeout=60, # 客户端请求超时(秒),默认 OPENAI_SIMPLE_VECTORSTORE_QDRANT_TIMEOUT
# qdrant_on_disk=False, # 总开关,默认 OPENAI_SIMPLE_VECTORSTORE_QDRANT_ON_DISK(False)
# qdrant_vector_on_disk=True, # 向量是否落盘,覆盖总开关
# qdrant_hnsw_on_disk=True, # HNSW 图是否落盘,覆盖总开关
# qdrant_payload_on_disk=True, # payload 索引是否落盘,覆盖总开关
)
vs.insert("..." , kb_id="kb1", doc_id="doc1", page_id="p1")
docs = vs.similarity_search_and_rerank(query="...", index_name="default", k=3)
qdrant 的向量、HNSW 图、payload 索引三部分是否落盘可分别配置:
qdrant_vector_on_disk / qdrant_hnsw_on_disk / qdrant_payload_on_disk
(对应环境变量 OPENAI_SIMPLE_VECTORSTORE_QDRANT_{VECTOR,HNSW,PAYLOAD}_ON_DISK)。
三者默认均为 False,即默认全部驻留内存,以获得最低检索延迟。qdrant_on_disk
是给三者兜底的总开关(默认 False):未单独设置的项会继承它,单独设置项优先。
落盘(mmap)可避免大图常驻内存,但检索时的随机读会变慢,建议搭配 NVMe SSD。
本地快速启动(podman):
podman run -d --name qdrant -p 6333:6333 -p 6334:6334 docker.io/qdrant/qdrant:latest
直接实例化(不依赖全局配置)
from openai_simple_vectorstore import RedisVectorStore
from openai_simple_vectorstore.base import Connection
from openai_simple_vectorstore.utils import YamlSerializer
vs = RedisVectorStore(
redis_stack_url="redis://localhost:6379/0",
embeddings_llm=Connection(base_url="http://localhost/v1", api_key="sk-xxx"),
rerank_llm=Connection(base_url="http://localhost/v1", api_key="sk-xxx"),
embeddings_model="bge-m3",
rerank_model="bge-reranker-v2-m3",
metadata_serializer=YamlSerializer(),
)
metadata_serializer决定业务自定义元数据(metadata/vs_metadata)持久化时的序列化方式, 默认YamlSerializer;也可传入JsonSerializer(均定义于openai_simple_vectorstore.utils)。
使用说明
VectorStore 对业务屏蔽后端差异,统一提供写入 / 检索 / 读取 / 删除 / 索引管理接口。
下面所有示例中的 vs 均指已创建的 VectorStore 实例。
索引名归一化(逻辑名 → 物理名)
公开接口接收的是逻辑索引名,库内会按当前实例的后端规则归一化为物理合法名,
调用方无需(也不应)自己替换字符。逻辑名可以包含 :、-、. 等字符:
# 逻辑名直接传给所有公开接口:写入 / 检索 / 读取 / 删除 / flush / schema / 多索引检索
vs.insert("今天天气很好", index_name="noah-ai-kb")
vs.insert("另一条", index_name="faq:kb")
# 排查或迁移时查看实际落到后端的物理名(按当前实例后端规则)
vs.get_index_name("noah-ai-kb") # qdrant/milvus: "noah_ai_kb";redis/es/pgvector/sqlite-vec: "noah-ai-kb"
vs.get_index_name("faq:kb") # 所有后端: "faq_kb"
vs.normalize_index_name("faq:kb") # 与 get_index_name 相同(显式传名时)
# 不需要实例时可用模块级函数;它只实现 qdrant/milvus 系列的完整归一化规则
from openai_simple_vectorstore.base import normalize_index_name
normalize_index_name("faq:kb") # "faq_kb"
normalize_index_name("noah-ai-kb") # "noah_ai_kb"
| 后端 | 规则 | 示例 |
|---|---|---|
qdrant / milvus / milvus-lite |
[^A-Za-z0-9_] → _;数字开头前置 _;空名回退 _index;截断到 255 |
noah-ai-kb → noah_ai_kb |
redis / elasticsearch / pgvector / sqlite-vec |
仅把 uid 分隔符 : 替换为 _,保留连字符等其余字符 |
faq:kb → faq_kb,noah-ai-kb 不变 |
注意事项:
- 统一使用逻辑名:不要自行
replace(":", "_")或按后端手写归一化,否则容易与写入侧 落到不同物理索引;需要展示/迁移时用vs.get_index_name(...)获取物理名即可。 - uid 前缀是归一化后的索引名:
vs.insert(..., index_name="faq:kb")返回faq_kb:p1;get_item/delete可原样传入该 uid。 - 与上层封装一致:规则与
django-vectorstore-indexed-model对齐,ORM 与本库直连 使用同一逻辑名时会命中同一物理索引。 - 0.3.3 升级提示:旧版本对含
:的索引名直接抛ValueError,现在改为归一化; qdrant / milvus 系列中旧版本以连字符等非[A-Za-z0-9_]字符建出的 collection 会按 新物理名查找。仍在使用的旧索引需按新名迁移数据,或显式rebuild_index重建。 - 自定义后端:在子类上声明
backend = "mydb";若其后端允许的字符集与默认规则不同, 一并覆盖normalize_index_name()。
写入
# 单条:返回 uid(格式 <index_name>:<page_id>)
uid = vs.insert(
"今天天气很好",
page_id="p1", # 缺省自动生成 uuid4
kb_id="kb1", # 缺省 "default"
doc_id="doc1", # 缺省 "default"
category="news", # 缺省 "default"
index_name="default",
metadata={"author": "alice", "score": 9.5}, # 业务自定义元数据,随记录持久化
)
# 批量:返回 uid 列表;texts / metadatas / page_ids 长度必须一致
uids = vs.insert_many(
["文本一", "文本二"],
metadatas=[{"author": "alice"}, {"author": "bob"}],
page_ids=["p1", "p2"],
kb_id="kb1",
index_name="default",
)
同一
page_id重复写入是覆盖(upsert 语义),不会产生重复记录;调用方传入的metadata/metadatas不会被就地修改。
元数据与序列化
核心字段(vs_kb_id / vs_doc_id / vs_page_id / vs_category)与业务自定义
metadata 一起持久化。get_item 返回平铺的元数据 dict;检索结果则把非标准字段
收进 Document.vs_metadata:
uid = vs.insert(
"今天天气很好",
page_id="p1",
kb_id="kb1",
index_name="faq:kb",
metadata={
"author": "alice",
"level": 2,
"tags": ["news", "weather"],
"extra": {"source": "crawler"}, # 可嵌套,随序列化器持久化
},
)
item = vs.get_item(uid)
item["vs_page_id"] # "p1"(核心字段平铺)
item["author"] # "alice"
item["extra"] # {"source": "crawler"}
doc = vs.similarity_search_with_relevance_scores("天气", index_name="faq:kb", k=1)[0]
doc.vs_metadata # {"author": "alice", "level": 2, "tags": [...], "extra": {...}}
-
非核心字段默认用
YamlSerializer(safe_dump/safe_load)序列化;可改用JsonSerializer,或实现openai_simple_vectorstore.utils.Serializer的dumps/loads自定义:from openai_simple_vectorstore.utils import JsonSerializer vs = create_vector_store(metadata_serializer=JsonSerializer())
-
自定义元数据默认只持久化、不可过滤;要作为
Filter目标,需在实例构造时用filterable_metadata_fields/declared_fields声明(见下节)。 -
反序列化失败时保留存储原值,不会让整条记录读取失败。
检索
# 仅向量检索:相关度 = 1 - distance,返回按相关度降序的 [Document, ...]
docs = vs.similarity_search_with_relevance_scores(
"今天天气怎么样",
index_name="default",
k=5,
kb_id="kb1", # 快捷过滤,等价 filters=[Filter("vs_kb_id", ["kb1"])]
categories=["news", "tech"], # 多值:命中其一即可
embeddings_score_threshold=0.3, # 低于阈值的结果直接丢弃(None 表示不过滤)
)
# 二阶段召回:先向量检索取 k*scale 条候选,再经 rerank 重排取前 k
docs = vs.similarity_search_and_rerank(
"今天天气怎么样",
index_name="default",
k=3,
scale=3, # 候选数 = k * scale,默认 2
kb_ids=["kb1", "kb2"],
rerank_score_threshold=0.5,
)
# 多索引联合检索(按 uid 去重、统一排序)
docs = vs.similarity_search_with_relevance_scores(
"查询", index_names=["idx_a", "idx_b"], k=10,
)
# 需要命中记录的原始向量时显式开启(默认 False,见下方字段说明)
docs = vs.similarity_search_with_relevance_scores(
"查询", index_name="default", k=5, include_embedding=True,
)
include_embedding默认False:搜索命中通常不需要原始向量,默认不取回可将 取数从 O(k) 次网络往返降到 0(qdrant 直接复用检索 payload);vs_embedding因 此为None。需要向量时传include_embedding=True恢复取回。该参数在similarity_search_with_relevance_scores与similarity_search_and_rerank上均可用。
多索引联合检索时:
index_names优先于index_name;每个名字都会按后端规则归一化。- 各索引结果按 uid 去重后统一按相关度降序排序,再取前
k条;uid 含索引名, 因此不同索引里的相同page_id视为不同记录。- query embedding 只与查询文本和 embeddings 配置有关,只计算一次并复用于所有索引, 避免重复请求。
- 某个索引不存在时按空结果处理,不影响其它索引。
读取 / 删除 / 清空
item = vs.get_item(uid) # 返回元数据 dict(含 embedding 字节);不存在返回 {}
count = vs.delete(uid) # 返回实际删除条数
count = vs.delete_many(uids) # 按 index_name 自动分组批量删除
uids = list(vs.iter_uids("default")) # 枚举该索引下全部 uid(<index_name>:<page_id>)
count = vs.count_uids("default") # 枚举计数(基于 iter_uids)
vs.flush("default") # 清空该索引数据(破坏性;是否删除底层索引/表随后端而异)
iter_uids(index_name=None)只读遍历索引下全部 uid,返回格式与insert一致 (<index_name>:<page_id>),不加载向量与内容、按后端分页拉取(scroll / scan /search_after/ keyset),因此不会像keys *那样一次性载入全量 key。索引不存在 时返回空迭代、不创建索引;index_name经get_index_name按后端规则归一化,传入 逻辑名即可命中写入侧同一物理索引。count_uids是通用实现,等价于len(list(iter_uids(...)))。
delete/delete_many返回实际删除条数。milvus standalone 的服务端delete_count会把请求中不存在的 id 也计入,因此本库以删除前实际存在的条数为准; milvus-lite 本身返回真实计数。
flush一律清空该索引下的数据(破坏性)。是否同时删除底层索引 / 表随后端而异: redis / milvus / milvus-lite / qdrant / sqlite-vec 会删除索引或表;elasticsearch 仅清空文档并保留 index 与 mapping;pgvector 删除共享表中该索引对应的行。
枚举 uid(一致性稽核 / 孤儿清理)
iter_uids 只读、分页地遍历索引下的全部 uid,不加载向量与正文,适合做全量稽核或
清理“业务侧已删除、向量库仍残留”的孤儿记录:
# 逐个遍历(生成器,内存占用与索引大小无关)
for uid in vs.iter_uids("faq:kb"):
print(uid) # 形如 "faq_kb:p1"(前缀是归一化后的索引名)
# 统计数量:通用实现等价于 len(list(iter_uids(...))),会全量扫描
total = vs.count_uids("faq:kb")
# 与业务侧仍有效的 page_id 集合比对,删除孤儿向量
valid_page_ids = {"p1", "p2"}
orphans = [
uid for uid in vs.iter_uids("faq:kb")
if uid.split(":", 1)[1] not in valid_page_ids
]
for uid in orphans:
vs.delete(uid)
注意事项:
- 索引不存在时返回空迭代,且不会创建索引;需要区分“索引不存在”与“索引为空”时,
先用
describe_index(index_name)查看exists。 count_uids不是原生计数,后端没有更廉价的计数接口时会遍历全部 uid; 超大索引请评估耗时。- 不是快照:遍历期间若有并发写入 / 删除,结果可能包含新增记录或漏掉已删除记录; 稽核 / 清理类任务建议在低峰或停写时执行。
- redis 基于
SCAN:可能重复返回同一个 key(Redis 允许,库不做去重), 需要唯一结果时在调用方自行set(...)去重。 - 第三方后端:未实现
_engine_for_index时抛NotImplementedError(与get_item一致);redis 这类“key 即完整 uid”的后端可直接覆盖iter_uids。
检索结果字段(Document)
similarity_search_* 返回 Document,可用 document_schema=YourModel 换成自定义模型:
| 字段 | 含义 |
|---|---|
vs_page_content |
原始文本内容 |
vs_embedding |
向量(float32 字节);默认不取回(None),传 include_embedding=True 才填充 |
vs_embeddings_score |
向量相关度 1 - distance,取值 [0, 1] |
vs_rerank_score |
rerank 分数(仅 similarity_search_and_rerank) |
vs_uid |
<index_name>:<page_id> |
vs_index_name |
命中的索引名 |
vs_kb_id / vs_doc_id / vs_page_id / vs_category |
核心元数据 |
vs_metadata |
业务自定义元数据 dict(如 {"author": ..., "score": ...}) |
每次检索可用 document_schema 换成自定义 Pydantic 模型,只取需要的字段:
from typing import Optional
from pydantic import BaseModel
class MyHit(BaseModel):
vs_uid: Optional[str] = None
vs_page_content: Optional[str] = None
vs_metadata: Optional[dict] = None
hits = vs.similarity_search_with_relevance_scores(
"查询", index_name="default", k=5, document_schema=MyHit,
)
document_schema是单次调用参数(不是实例构造参数),字段应为Document的子集。 省略vs_embeddings_score不影响排序(内部使用计算的分数);但二阶段召回similarity_search_and_rerank必须能取到vs_page_content,自定义模型缺该字段会 无法 rerank 并报错。
过滤
from openai_simple_vectorstore.base import Filter
# tag 精确过滤:values 内多值为 OR 命中
docs = vs.similarity_search_with_relevance_scores(
"查询", index_name="default", k=5,
filters=[Filter(field="author", values=["alice", "bob"])],
)
# 多个 Filter 之间为 AND;numeric 用 op="range" 传 [low, high]
docs = vs.similarity_search_and_rerank(
"查询", k=5,
filters=[
Filter(field="author", values=["alice"]),
Filter(field="score", values=[7, 10], op="range"),
],
)
# 也接受 dict(等价 tag 精确过滤,多键为 AND);dict 形式不支持 range
docs = vs.similarity_search_with_relevance_scores(
"查询", k=5, filters={"author": "alice", "lang": "zh"},
)
过滤字段必须先在实例上声明,未声明 / 非法取值(空值、
None、low > high)会明确报错, 而不是静默忽略。单个Filter/dict可直接传入(不必包成 list);字符串取值按 单个值处理,不会被逐字符拆分;集合取值会排序以保证结果可复现。
声明可过滤字段
业务自定义元数据默认会随记录持久化并在 vs_metadata 中返回;要作为过滤条件,必须显式声明:
from openai_simple_vectorstore.base import IndexField
# 简单方式:一批 tag 字段
vs = create_vector_store(filterable_metadata_fields=["author", "lang"])
# 精细方式(与上面二选一;一旦提供 declared_fields,则忽略 filterable_metadata_fields)
vs = create_vector_store(
declared_fields=[
IndexField(name="author", type="tag"), # 字符串等值
IndexField(name="tags", type="tag_multi"), # 多值集合命中
IndexField(name="score", type="numeric"), # 数值,支持 op="range"
IndexField(name="body", type="text"), # 全文
# 向量字段:dims 必须与 embeddings_dims / 模型输出一致,metric 只支持 cosine
IndexField(name="embedding", type="vector", dims=1024, metric="cosine"),
],
)
-
自定义核心字段名:构造时传
kb_id_field_name/doc_id_field_name/page_id_field_name/category_field_name,检索结果的Document标准字段会自动回填:vs = create_vector_store( kb_id_field_name="kb", doc_id_field_name="doc", page_id_field_name="page", category_field_name="category", ) uid = vs.insert("文本", page_id="p1", kb_id="kb1") doc = vs.similarity_search_with_relevance_scores("文本", k=1)[0] doc.vs_page_id # "p1"(由自定义的 page 字段回填到标准字段) doc.vs_kb_id # "kb1"
-
更多字段模型与各后端过滤能力见下方《字段模型与过滤语义》。
索引管理
# 建索引(幂等):fields 优先于实例声明;已存在时仅增量补字段,不重建
vs.create_index("default", fields=[IndexField(name="author", type="tag")])
# 查看归一化 schema 与是否存在
info = vs.describe_index("default")
print(info.exists, [f.name for f in info.fields])
# 漂移检测与重建(重建是破坏性的,只能显式调用)
diff = vs.compare_index_schema("default")
if diff.drifted or not diff.exists:
vs.rebuild_index("default") # force=True:无条件重建
# vs.rebuild_index("default", force=False) # 仅在检测到漂移时重建
# 原生过滤索引的检查与补建(缺字段时 qdrant 仅变慢,milvus/pgvector/sqlite-vec 由元数据兜底;redis/elasticsearch 可能报错或失配)
if vs.check_index_fields("default"):
vs.build_index_fields("default")
注意事项:
create_index是幂等的:已存在的索引不会重建,只做增量补字段;要重建必须显式 调用rebuild_index。rebuild_index(force=False)仅在索引已存在且检测到漂移时执行;索引不存在时返回exists=False的IndexDiff,不会创建。force=True(默认)在索引不存在时会抛RuntimeError。compare_index_schema读不到线上 schema 时返回drifted=False(表示“无法检测”, 而不是“无漂移”),并记录日志。check_index_fields只读,bool(status)为真表示存在待补建的missing字段;build_index_fields只补missing、不删除/重建已有字段,索引未创建时会先创建。rebuild_index是破坏性操作(删除并重建,丢失该索引下的数据),请先确认。
连接与模型参数
from openai_simple_vectorstore.base import Connection
vs = create_vector_store(
vector_db="redis",
redis_stack_url="redis://localhost:6379/0",
embeddings_llm=Connection(base_url="http://localhost/v1", api_key="sk-xxx"),
rerank_llm=Connection(base_url="http://localhost/v1", api_key="sk-xxx"),
embeddings_model="bge-m3",
rerank_model="bge-reranker-v2-m3",
embeddings_dims=1024, # 嵌入维度,需与模型输出一致(默认 1024)
embeddings_max_size=8192, # 单段文本上限,超出截断(默认 1024)
rerank_max_size=8192, # rerank 单段文本上限,超出截断(默认 1024)
embeddings_score_threshold=None, # None 表示不过滤,见下
rerank_score_threshold=None, # None 表示不过滤,见下
redis_timeout=60,
)
- 只传
llm时同时用于 embeddings 与 rerank;分别配置请用embeddings_llm/rerank_llm。 - 各远程后端的超时可用
redis_timeout/milvus_timeout/pgvector_timeout/es_timeout/qdrant_timeout覆盖(默认 60 秒)。 - 两个阈值语义相同:
None(默认)表示不过滤;给出数字时,低于该分数的结果被丢弃。 区别只在作用对象:embeddings_score_threshold作用于向量相关度1 - distance ∈ [0, 1](因此设0.0等同于不过滤),rerank_score_threshold作用于 rerank 模型分数 (取值范围由模型决定,如 bge-reranker 近似[0, 1])。 - 配置优先级:显式传参 > 环境变量 > 默认值。环境变量在
import openai_simple_vectorstore时读取,运行中修改需重新导入,或改为显式传参。 Connection的base_url会回退到OPENAI_BASE_URL,并统一规范化为http://host/v1/形式;api_key为空时不发送鉴权头。
后端对比速查
| 后端 | 部署形态 | 原生过滤索引 | iter_uids 分页 |
flush 行为 |
特别提示 |
|---|---|---|---|---|---|
redis |
外部 redis-stack | 是(tag / tag_multi / numeric) | SCAN |
删 key + FT 索引 | 必须带 RediSearch;tag_multi 单个标签不能含逗号 |
milvus |
外部服务 | 否(元数据侧兜底) | query_iterator 或 limit/offset |
删除 collection | delete 返回删除前实际存在条数 |
milvus-lite |
嵌入式文件 | 否(元数据侧兜底) | 同上 | 删除 collection | 需 pymilvus + milvus-lite,无需部署服务 |
pgvector |
PostgreSQL 扩展 | 否(JSONB 过滤) | keyset(page_id 游标) |
删除该索引对应的行 | 多个索引共享同一张表 |
elasticsearch |
外部服务 | 是(mapping) | search_after |
清空文档,保留 index / mapping | insert_many 非事务,失败时尽力回滚 |
sqlite-vec |
嵌入式文件 | 否(元数据侧兜底) | keyset(rowid 游标) |
删除虚拟表 | 需要 sqlite3 支持 loadable-extension |
qdrant |
外部服务 | 是(payload index) | scroll |
删除 collection | 向量/HNSW/payload 默认内存,按需开启 on_disk |
“原生过滤索引”为否的后端仍可过滤,只是由元数据 / JSON 侧兜底,性能与索引能力见 《字段模型与过滤语义》。
环境变量
| 变量 | 默认值 | 说明 |
|---|---|---|
OPENAI_SIMPLE_VECTORSTORE_VECTOR_DB |
redis |
后端:redis / redis-search / milvus / milvus-lite / pgvector / elasticsearch / sqlite-vec / qdrant |
OPENAI_SIMPLE_VECTORSTORE_REDIS_STACK_URL |
redis://localhost:6379/0 |
redis-stack 地址(redis 后端) |
OPENAI_SIMPLE_VECTORSTORE_REDIS_TIMEOUT |
60 |
redis 连接/读写超时秒数(别名 REDIS_TIMEOUT) |
OPENAI_SIMPLE_VECTORSTORE_MILVUS_URI |
http://localhost:19530 |
milvus 地址 |
OPENAI_SIMPLE_VECTORSTORE_MILVUS_TOKEN |
空 | milvus 鉴权 token |
OPENAI_SIMPLE_VECTORSTORE_MILVUS_CONNECTION_ALIAS |
default |
milvus 连接别名 |
OPENAI_SIMPLE_VECTORSTORE_MILVUS_TIMEOUT |
60 |
milvus RPC 超时秒数(别名 MILVUS_TIMEOUT) |
OPENAI_SIMPLE_VECTORSTORE_MILVUS_LITE_URI |
./milvus_lite.db |
milvus-lite 本地数据库文件路径 |
OPENAI_SIMPLE_VECTORSTORE_PGVECTOR_URL |
postgresql://localhost:5432/postgres |
pgvector 连接串(pgvector 后端) |
OPENAI_SIMPLE_VECTORSTORE_PGVECTOR_TIMEOUT |
60 |
pgvector 连接超时秒数(别名 PGVECTOR_TIMEOUT) |
OPENAI_SIMPLE_VECTORSTORE_ELASTICSEARCH_URL |
http://localhost:9200 |
elasticsearch 地址 |
OPENAI_SIMPLE_VECTORSTORE_ELASTICSEARCH_API_KEY |
空 | elasticsearch API key(优先于账号密码) |
OPENAI_SIMPLE_VECTORSTORE_ELASTICSEARCH_USERNAME |
空 | elasticsearch 用户名 |
OPENAI_SIMPLE_VECTORSTORE_ELASTICSEARCH_PASSWORD |
空 | elasticsearch 密码 |
OPENAI_SIMPLE_VECTORSTORE_ELASTICSEARCH_VERIFY_CERTS |
True |
是否校验证书(自签 HTTPS 集群可设 false) |
OPENAI_SIMPLE_VECTORSTORE_ELASTICSEARCH_TIMEOUT |
60 |
elasticsearch 请求超时秒数(别名 ELASTICSEARCH_TIMEOUT / ES_TIMEOUT) |
OPENAI_SIMPLE_VECTORSTORE_SQLITE_VEC_URI |
./sqlite_vec.db |
sqlite-vec 本地数据库文件路径 |
OPENAI_SIMPLE_VECTORSTORE_QDRANT_URL |
http://localhost:6333 |
qdrant 服务地址(别名 QDRANT_URL) |
OPENAI_SIMPLE_VECTORSTORE_QDRANT_API_KEY |
空 | qdrant API key(别名 QDRANT_API_KEY) |
OPENAI_SIMPLE_VECTORSTORE_QDRANT_PREFER_GRPC |
False |
是否优先使用 gRPC(别名 QDRANT_PREFER_GRPC) |
OPENAI_SIMPLE_VECTORSTORE_QDRANT_TIMEOUT |
60 |
qdrant-client 请求超时秒数(别名 QDRANT_TIMEOUT) |
OPENAI_SIMPLE_VECTORSTORE_QDRANT_ON_DISK |
False |
向量/HNSW/payload 是否落盘的总开关(别名 QDRANT_ON_DISK) |
OPENAI_SIMPLE_VECTORSTORE_QDRANT_VECTOR_ON_DISK |
继承总开关 | 向量列是否落盘(别名 QDRANT_VECTOR_ON_DISK) |
OPENAI_SIMPLE_VECTORSTORE_QDRANT_HNSW_ON_DISK |
继承总开关 | HNSW 图是否落盘(别名 QDRANT_HNSW_ON_DISK) |
OPENAI_SIMPLE_VECTORSTORE_QDRANT_PAYLOAD_ON_DISK |
继承总开关 | payload 索引是否落盘(别名 QDRANT_PAYLOAD_ON_DISK) |
OPENAI_BASE_URL |
http://localhost/v1 |
OpenAI 兼容服务基础地址(embeddings / rerank 共用) |
OPENAI_API_KEY |
空 | OpenAI 兼容服务密钥(embeddings / rerank 共用) |
OPENAI_EMBEDDINGS_BASE_URL |
同 OPENAI_BASE_URL |
embeddings 服务地址(单独覆盖) |
OPENAI_EMBEDDINGS_API_KEY |
同 OPENAI_API_KEY |
embeddings 服务密钥(单独覆盖) |
OPENAI_EMBEDDINGS_MODEL |
bge-m3 |
embeddings 模型名 |
OPENAI_EMBEDDINGS_MAX_SIZE |
1024 |
embeddings 单段文本上限,超出截断 |
OPENAI_EMBEDDINGS_DIMS |
1024 |
嵌入向量维度(需与模型输出一致) |
OPENAI_RERANK_BASE_URL |
同 OPENAI_BASE_URL |
rerank 服务地址(单独覆盖) |
OPENAI_RERANK_API_KEY |
同 OPENAI_API_KEY |
rerank 服务密钥(单独覆盖) |
OPENAI_RERANK_MODEL |
bge-reranker-v2-m3 |
rerank 模型名 |
OPENAI_RERANK_MAX_SIZE |
1024 |
rerank 单段文本上限,超出截断 |
大多数变量提供无前缀短别名,常用如:
VECTOR_DB/VECTOR_DB_TYPE、REDIS_URL/REDIS_STACK_URL、MILVUS_URI/MILVUS_TOKEN、PGVECTOR_URL/PGVECTOR_DSN/DATABASE_URL、ES_URL/ES_API_KEY/ES_USER/ES_PASSWORD、SQLITE_VEC_URI、QDRANT_URL/QDRANT_API_KEY/QDRANT_TIMEOUT、BASE_URL/API_KEY、EMBEDDINGS_*/RERANK_*。完整别名列表见openai_simple_vectorstore/settings.py。兼容
openai-redis-vectorstore:OPENAI_REDIS_VECTORSTORE_REDIS_STACK_URL、OPENAI_EMBEDDINGS_*、OPENAI_RERANK_*、OPENAI_BASE_URL、OPENAI_API_KEY等变量可直接使用。环境变量在
import openai_simple_vectorstore时读取;运行中修改后需重新导入,或直接 给create_vector_store(...)传显式参数(优先级更高)。
扩展新的向量数据库后端
继承共享抽象基类 VectorStore 并实现以下方法,再注册到工厂即可:
- 实现
get_cached_vectorstore(引擎的获取与缓存) - 实现
_search_index,返回[(page_id, distance, item), ...] - 实现
get_item/delete/delete_many/flush - (可选)若要支持
iter_uids:实现_engine_for_index(index_name)(索引不存在时 返回None且不创建),并在引擎上提供iter_page_ids(batch_size)生成器,只读 分页产出page_id(不加载向量/内容)。base 的iter_uids会自动拼接<index_name>:<page_id>前缀,count_uids基于iter_uids计数。 redis 这类 key 即完整 uid 的后端可直接覆盖iter_uids。 - (建议)声明
backend = "mydb",索引名归一化会据此选择规则;未声明时默认采用 保守规则(仅把:替换为_)。若该后端允许的字符集与默认规则不同,同时覆盖normalize_index_name()。
from openai_simple_vectorstore.base import VectorStore, IndexField
from openai_simple_vectorstore.registry import register_vector_store, create_vector_store
class MyStore(VectorStore):
# 后端标识:决定索引名归一化规则等后端专属行为
backend = "mydb"
# 可选:当 ``_search_index`` 返回的 item 已是"与 get_item 同源的完整序列化
# metadata"(如 qdrant 的 with_payload payload)时置 True,base 在
# include_embedding=False 时可直接复用、省去逐条 get_item 的 N+1 往返。
# 仅返回部分字段(如 redis 默认只返回 page_id)的后端必须保持 False。
_search_payload_is_document = False
def get_cached_vectorstore(self, **kwargs): ...
def _search_index(self, engine, query_embedding, index_name,
filters, filter_expression, k): ...
def get_item(self, uid): ...
def delete(self, uid): ...
def delete_many(self, uids): ...
def flush(self, index_name=None): ...
# 可选:后端的索引名字符集与默认规则不同时覆盖
def normalize_index_name(self, index_name=None): ...
# 可选:支持 iter_uids / count_uids
def _engine_for_index(self, index_name): ... # 不存在返回 None,不创建
# 引擎侧:def iter_page_ids(self, batch_size=1000): yield page_id ...
# 可选的统一字段模型 / 能力矩阵 / schema 查询契约
def _capabilities(self): ...
def _describe_index(self, index_name): ...
def _ensure_index_created(self, index_name, fields): ...
register_vector_store("mydb", MyStore)
vs = create_vector_store(vector_db="mydb")
base 会统一处理 embeddings 生成、rerank、过滤、去重、排序、relevance_score 与
Document 组装,后端只需专注各自的检索实现。
通过 entry-points 分发第三方后端
除手动 register_vector_store 外,外部包也可在自己的 pyproject.toml 中声明
entry-point,本库导入时会自动发现并注册:
[project.entry-points."openai_simple_vectorstore.backends"]
mydb = "mypackage.backends.mydb:MyVectorStore"
- entry-point 的
name即后端标识(如mydb),value指向VectorStore子类或 返回该类的工厂函数。 - 任一后端加载失败只记录 warning,不影响其它后端;可用
discover_vector_stores()手动触发并拿到本次新注册的名称列表。 - 内置后端同样以 entry-points 声明(见本库
pyproject.toml),安装后即可用create_vector_store(vector_db="mydb")创建。
create_vector_store(vector_db=...) 对常见写法做了别名归一(大小写、空格不敏感),
如 redis-search / rediss → redis,es / opensearch → elasticsearch,
postgres / postgresql / pg → pgvector,sqlite / sqlite_vec → sqlite-vec,
milvus_lite / milvuslite → milvus-lite。
_search_payload_is_document契约:置True表示_search_index返回的item是完整的序列化 metadata(与get_item内部交给get_unserialized_metadata的 dict 同源),base 会在include_embedding=False时直接反序列化复用它。若后端搜索只返回少量字段(如只返回page_id),必须保持False,否则会静默返回缺失内容/核心字段的文档。
字段模型与过滤语义
各引擎通过统一的字段声明 / 能力矩阵 / schema 查询接口,向业务暴露"哪些扩展字段可过滤、索引现状如何":
IndexField:声明字段类型,支持tag/tag_multi/numeric/text/vector; 向量字段必须给出dims,并与引擎配置的embeddings_dims一致。capabilities():返回能力矩阵(Capabilities)——indexed_types表示后端建成 原生可过滤列的类型(如 redis、elasticsearch),meta_filter_types表示在元数据上做 Python/表达式侧兜底过滤的类型(如 milvus / sqlite-vec / pgvector 的部分类型)。describe_index(index_name):返回归一化的IndexSchemaInfo——exists=False表示 索引尚未创建(此时fields返回声明/期望字段,方便业务预创建)。索引存在且后端有可读的 逐字段线上 schema 时返回线上真实字段(redis / elasticsearch / milvus / milvus-lite); qdrant / pgvector / sqlite-vec 无逐字段线上 schema(payload / metadata JSON),fields恒为声明字段。create_index(index_name, fields):按声明的IndexField列表构建索引(显式传入的fields优先于实例构造时的declared_fields);已存在时仅补齐可增量添加的部分 (redisFT.ALTER/ elasticsearchput_mapping/ qdrant payload 索引),不会重建。compare_index_schema(index_name)/rebuild_index(index_name, force=...):只读检测 线上 schema 与期望是否漂移;返回的IndexDiff中exists=False表示索引已不存在 (如被外部删除),drifted=True表示字段 / 维度 / 距离度量不一致。重建一律是破坏性的 (删除并重新索引、丢失该索引下的数据),只能显式调用rebuild_index(force=True无条件重建,force=False仅在检测到漂移时重建),不会在创建 / ensure 索引时自动触发。check_index_fields(index_name)/build_index_fields(index_name):检查声明字段中 哪些缺少后端原生过滤索引(IndexFieldStatus.missing),并按需补建。补建是显式 API, 不会在 ensure / 写入路径上隐式执行——缺字段时 qdrant 仍能正确过滤(仅变慢), milvus / pgvector / sqlite-vec 由元数据侧兜底,redis / elasticsearch 则会过滤报错或 失配;建议由调用方主动调用补建。例外:首次创建索引(redisFT.CREATE/ elasticsearch 建 index / qdrant 建 collection)时会按当时声明一并建好原生索引, 仅当之后新增字段时才需要显式补建。
常用只读查询:
caps = vs.capabilities()
caps.indexed_types # 该后端原生建索引的字段类型,如 ("tag", "tag_multi", "numeric")
caps.meta_filter_types # 只能元数据侧兜底过滤的类型
caps.supports("numeric") # 该类型是否可过滤
info = vs.describe_index("faq:kb")
info.index_name, info.exists, info.fields # fields 为归一化后的 IndexField 列表
diff = vs.compare_index_schema("faq:kb")
diff.exists, diff.drifted, diff.details # drifted=True 表示字段/维度/度量与期望不一致
status = vs.check_index_fields("faq:kb")
status.indexed_names, status.missing_names, status.unsupported_names
过滤统一使用 Filter 对象(tag 精确、tag_multi 多值命中、numeric 区间 via op="range");
base 把归一化后的 filters 传入各后端的 engine/search。后端只需实现
_build_filter_expression(filters, filter_expression) 把归一化过滤器组装为原生过滤表达式。
- 索引初始化一次性:引擎实例首次执行
ensure_*(建表 / 建 collection / 建 index) 时完成创建或存在性探测,之后所有读写路径不再重复探测(内部_index_ready/_table_ready标记)。若索引被外部删除或 schema 漂移,用compare_index_schema检测后显式rebuild_index,而不是期望每次操作都自动ensure。 - redis
tag_multi的逗号限制:RediSearch 对 HASH 字段的 TAG 以逗号分隔且 不解析反斜杠转义(实测a\,b会被拆成两个标签),因此单个标签值内部不能包含 逗号;普通多值标签(元素本身不含分隔符)可被正确索引与过滤。
概念说明
- 逻辑索引名 / 物理索引名:公开接口接收逻辑名(可含
:/-/.), 经get_index_name按实例后端规则归一化为物理名后落到后端;uid 前缀使用物理名。 详见上文「索引名归一化」。 - uid:
<index_name>:<page_id>,用于唯一定位一条记录;其中index_name是 归一化后的物理索引名。 - relevance_score:
1 - distance,取值[0, 1],越大越相关。该分数与embeddings_score_threshold统一按余弦距离语义计算,因此所有后端的向量度量 均固定为 cosine;redis 的vector_store_distance_metric也仅接受cosine, 声明的向量IndexField同样只接受metric="cosine",传入其它度量(IP / L2 等) 会在实例化时报ValueError,避免返回错误的相似度。 - 二阶段召回:
similarity_search_and_rerank先用向量检索取k * scale条候选, 再经 rerank 重排取前k条。 - 检索错误诊断:任一后端的向量检索(含超时、连接失败等)都会在抛出前
记录并返回带后端地址 + 索引名 + 检索参数的
RuntimeError,便于定位, 不会把底层驱动的原始调用堆栈直接抛给调用方;索引不存在(未初始化)仍按 空结果处理。 - 连接/查询超时:各远程后端默认 60 秒(
OPENAI_SIMPLE_VECTORSTORE_*_TIMEOUT, qdrant 见上文),也可在实例化时用redis_timeout/milvus_timeout/pgvector_timeout/es_timeout覆盖(qdrant_timeout同理)。 - elasticsearch 批量写入非事务:
insert_many逐条写入,任一条失败时会尽力 回滚(删除)本次已写入的文档后再抛出,尽量表现为“全成功或全不写入”,但 ES 无多 文档事务,回滚本身失败时仅记录 warning,不掩盖原始异常。 - elasticsearch numeric 字段:
numeric映射为float,ES 默认coerce=true,因此字符串数值(如"7")会被接受并转换为数值,属预期行为。
API 速查
| 方法 | 作用 | 关键参数 / 返回 |
|---|---|---|
create_vector_store(vector_db=None, **kwargs) |
按后端标识创建实例 | vector_db 可省略(读环境变量);其余参数透传给后端类 |
get_vector_store_class(vector_db=None) |
获取后端类(不实例化) | 未知后端抛 RuntimeError |
register_vector_store(name, cls=None) |
手动注册后端,可作装饰器 | 返回类本身 |
discover_vector_stores(group=...) |
自动发现 entry-points 后端 | 返回新注册的后端名列表 |
insert(text, metadata=None, page_id=None, kb_id=None, doc_id=None, category=None, index_name=None, ...) |
写入单条文本 | 返回 <归一化 index>:<page_id>;同 page_id 覆盖 |
insert_many(texts, metadatas=None, kb_id=None, doc_id=None, page_ids=None, category=None, index_name=None, ...) |
批量写入 | 返回与输入同序的 uid 列表;长度不一致抛 RuntimeError(422) |
get_item(uid) |
读取单条 | 返回平铺元数据 dict(含 embedding 字节);不存在返回 {} |
delete(uid) / delete_many(uids) |
删除单条 / 批量 | 返回实际删除条数 |
flush(index_name=None) |
清空索引 | 破坏性;是否删除底层索引/表随后端而异 |
iter_uids(index_name=None) |
只读枚举全部 uid | 生成器;索引不存在返回空迭代 |
count_uids(index_name=None) |
统计 uid 数量 | 基于 iter_uids 全量计数 |
similarity_search_with_relevance_scores(query, ..., k=4, include_embedding=False) |
向量检索 | 返回按相关度降序的 Document 列表 |
similarity_search_and_rerank(query, ..., k=4, scale=2, ...) |
二阶段召回 | 先取 k*scale 候选,再 rerank 取前 k |
rerank(query, docs, k=4, ...) |
对已有文档 rerank | 返回按 rerank 分数降序的 Document 列表 |
create_index(index_name=None, fields=None) |
创建/补字段(幂等) | fields 会并入实例声明 |
describe_index(index_name=None) |
查询归一化 schema | 返回 IndexSchemaInfo(exists / fields) |
compare_index_schema(index_name=None) |
检测漂移 | 返回 IndexDiff(exists / drifted / details) |
rebuild_index(index_name=None, force=True) |
重建索引 | 破坏性;force=False 仅在漂移时重建 |
check_index_fields(index_name=None) |
检查缺失的原生过滤索引 | 返回 IndexFieldStatus;bool(status) 表示有待补建项 |
build_index_fields(index_name=None) |
补建缺失的原生过滤索引 | 幂等,返回补建后的 IndexFieldStatus |
capabilities() |
后端能力矩阵 | 返回 Capabilities(indexed_types / meta_filter_types) |
get_index_name(index_name=None) / normalize_index_name(index_name=None) |
获取物理索引名 | 按实例后端规则归一化 |
错误与诊断
| 场景 | 表现 | 处理建议 |
|---|---|---|
| 检索的索引不存在 / 未创建 | 按空结果处理,记录 warning,不抛异常 | 用 describe_index(...).exists 判断是否需要 create_index |
| 后端连接 / 检索失败 | RuntimeError,消息含后端地址、索引名、k、过滤条件、query 维度与原始错误 |
按消息中的 endpoint / index / params 定位;真实驱动异常保留在消息末尾 |
过滤字段未声明、不可过滤、空值、None、非法 op、low > high |
ValueError |
先通过 IndexField / filterable_metadata_fields 声明字段 |
IndexField 非法(未知类型、向量缺 dims、非 cosine 度量、字段重名) |
实例化 / 声明时 ValueError |
修正声明;向量度量固定为 cosine |
insert_many 的 metadatas / page_ids 与 texts 长度不一致 |
RuntimeError(422) |
调用前校验三个列表等长 |
| 后端未配置 URL / 驱动,或 embeddings / rerank 请求失败 | RuntimeError(500) |
补齐 *_URL、安装对应 extra 驱动,或检查模型服务与 API key |
get_item / delete / iter_uids 的目标不存在 |
分别返回 {} / 0 / 空迭代 |
用 describe_index 或 iter_uids 先确认索引状态 |
| 第三方后端未实现必需钩子 | NotImplementedError |
实现对应引擎方法;iter_uids 需 _engine_for_index + iter_page_ids |
检索失败的
RuntimeError带结构化index_missing标记(仅真正“索引未建”时才被当作 空结果);其它错误不会被静默吞掉。库内日志使用标准logging,Logger 名为openai_simple_vectorstore,可按需配置级别或处理器。
版本记录
0.3.3(2026-10-31)
- 索引名归一化下沉到库内(行为变更):
get_index_name不再拒绝含:的逻辑名, 而是按实例后端规则归一化为物理合法名;新增公开的normalize_index_name()(模块级与 实例方法)及VectorStore.backend后端标识。qdrant / milvus / milvus-lite 把[^A-Za-z0-9_]统一替换为_、数字开头前置_、空名回退_index、截断到 255; redis / elasticsearch / pgvector / sqlite-vec 仅把 uid 分隔符:替换为_,保留 连字符等既有字符。归一化覆盖写入 / 读取 / 删除 /flush/ schema 查询 / 多索引联合 检索与_split_uid,使绕过上层 ORM 直接调用本库时与写入侧命中的是同一物理索引, 规则与django-vectorstore-indexed-model保持一致。 - 升级提示:此前
index_name含:会抛ValueError,现在会被归一化而非报错; qdrant / milvus 系列中旧版本以连字符等非[A-Za-z0-9_]字符创建的物理 collection 将按新名查找,如仍在使用这类索引,需按新名迁移数据或显式rebuild_index重建。 其余后端的既有索引名(除:外)不受影响。 - 新增公开枚举接口
iter_uids/count_uids:VectorStore.iter_uids(index_name=None)只读遍历索引下全部 uid(<index_name>:<page_id>,与insert返回值同格式),count_uids基于其计数。此前业务侧要枚举 uid 只能绕过封装直接访问后端私有连接 (qdrant_client/ redisredis_instance),现统一收敛到公开接口。 - 全后端分页实现,不整表加载:
iter_uids经_engine_for_index(索引不存在返回None且不创建)+ 引擎iter_page_ids(batch_size)生成器完成,仅取page_id、不加载 向量与内容,并按后端分页拉取(qdrant scroll / redisscan_iter/ elasticsearchsearch_after/ pgvector 与 sqlite-vec keyset / milvusquery_iterator或 limit-offset), 避免一次性载入全量 key 或触发大偏移。索引不存在时返回空迭代、不抛异常。 - redis 覆盖
iter_uids:redis key 即完整 uid,用scan_iter(match=...)遍历, 索引名按 glob 转义(与flush口径一致),避免*等元字符误匹配同前缀索引。 - 约定:
iter_uids的索引名经get_index_name按后端规则归一化后精确匹配;第三方 后端未实现_engine_for_index时抛NotImplementedError(与get_item一致)。 - 修复 milvus(独立部署)
delete返回值失真:milvus 服务端的delete(ids=...)返回的delete_count恒等于请求的 id 个数(删除不存在的 page_id 也计入),导致delete对不存在的 uid 误报删除 1 条。现改为以删除前 实际存在的条数为准,delete/delete_many返回值与真实删除数一致 (milvus-lite 本就返回真实计数,行为不变)。
0.3.2(2026-10-05)
- 检索默认不再取回向量字段(性能修复):
similarity_search_with_relevance_scores/similarity_search_and_rerank新增include_embedding参数,默认False。此前每个命中 都会逐条get_item取回向量字段,向量检索本身是 O(1) 往返,而取数是 O(k)(rerank 为 O(k*scale))次网络往返,是高 k / 生产网络 RTT 下的主要延迟来源。默认关闭后检索结果vs_embedding为None,需要原始向量时传include_embedding=True恢复原行为。 - qdrant 复用检索 payload 消除 N+1:qdrant 检索使用
with_payload=True,命中已携带内容 与全部元数据;关闭include_embedding时直接反序列化复用该 payload,完全省去逐条get_item往返(实测 k=100 由 ~140ms 降至 ~17ms,rerank 100 候选由 ~250ms 降至 ~13ms)。 - 多索引检索只计算一次 query embedding:
index_names联合检索时,query embedding 只与 query 及 embeddings 配置有关、与索引无关;改为先解析各索引引擎,再在循环外只计算一次 embedding 供所有索引复用,避免对每个索引重复发起相同的 embeddings 请求(并保持"后端未 配置先于 embedding 报错"的既有次序)。 - 入口校验与防御性加固:声明的向量
IndexField仅接受metric="cosine";redis 的vector_store_distance_metric也仅接受cosine(非 cosine 在实例化时报ValueError, 避免 relevance score /embeddings_score_threshold静默失真);Connection()未给base_url时回退OPENAI_BASE_URL(不再拼出"/v1/..."相对 URL);numeric 过滤拒绝NaN(避免比较恒为 False 导致静默失配);schema / 引擎钩子补NotImplementedError缺省实现 (第三方后端遗漏实现时不再抛AttributeError);create_index(fields=...)显式字段重名报错。 include_embedding置于参数末尾:similarity_search_with_relevance_scores/similarity_search_and_rerank新增的include_embedding放在参数列表末尾,避免调用方按 位置传参时与既有参数错绑。_split_uid收敛到基类:uid 拆解统一由基类VectorStore._split_uid提供,删除 6 个 引擎中的重复实现。
0.3.1(2026-10-02)
- 索引生命周期收敛:
ensure_*(建表 / collection / index)改为每个引擎实例只探测 一次(内部_index_ready/_table_ready),读写热路径不再重复探测;新增IndexDiff.exists区分"未创建"与"漂移";新增check_index_fields/build_index_fields按需补建后端原生过滤索引(显式 API,不在 ensure / 写入路径隐式触发)。 - qdrant 落盘拆分可配:向量 / HNSW 图 / payload 索引三部分是否落盘可分别配置
(
qdrant_vector_on_disk/qdrant_hnsw_on_disk/qdrant_payload_on_disk,qdrant_on_disk为兜底总开关);默认全部走内存(False,相对 0.3.0 的行为变更)。 - 统一检索错误诊断:任一后端的向量检索(含超时、连接失败)抛出带后端地址 + 索引名 +
检索参数的
RuntimeError,并用结构化index_missing标记只吞"真正未建索引",避免按 文案子串匹配误吞真实错误。 - 可配置超时:redis / milvus / pgvector / elasticsearch / qdrant 新增连接与请求超时
(
*_timeout参数或OPENAI_SIMPLE_VECTORSTORE_*_TIMEOUT,默认 60 秒),redisvl 的SearchIndex同样受 socket 超时约束。 - 跨引擎正确性修复:
- redis:
tag_multi改为直接以 TAG 分隔符拼接/拆分(RediSearch HASH 不解析反斜杠转义),FT.INFO兼容 RESP2/RESP3 与dims=/DIM两种向量参数,describe_index返回线上 FT schema,flushglob 转义并清理缓存引擎,PREFIX改为index_name + ":"以隔离 同名前缀索引(旧索引需rebuild_index后生效); - pgvector:共享表
to_regclass为 NULL 视为"未建表"(不再建幻影表),每连接锁改用WeakKeyDictionary安装,批量写入改为整批事务 + 失败回滚,filter_expression字面%转义; - sqlite-vec:整批写入持有连接锁并支持回滚,读/删不再隐式建表;
- milvus / milvus-lite:含单引号的
page_id回退到自建转义表达式,删除不再因可见性滞后 短路,describe_index缺失向量维度时逐级回退; - elasticsearch:
insert_many中途失败时回滚,覆盖写恢复批处理前旧文档; - qdrant:
filter_expression的 must/should/must_not 归一,numeric 等值过滤改用Range(gte=v, lte=v)。
- redis:
- 过滤与字段模型加固:
Filter取值归一(字符串不逐字符拆分、集合排序、接受单个Filter/dict),拒绝未声明字段、空值 /None、非法op与low > high;numeric 取值 统一转float;tag_multi全后端恒返回 list;核心 id 字段恒按字符串解码;create_index(index_name, fields=...)保留与向量同名的显式声明并合并进实例声明。 - 打包:
requires-python提升至>=3.10,核心依赖补numpy并同步requirements.txt,pydantic>=2与各 extras 增加版本下界,entry-points 导入时自动注册。 - 补充修复:redis 向量
datatype配置生效且capabilities不再把text当作可过滤类型; elasticsearchnum_candidates封顶 10000;qdrant 无FloatIndexParams时 numeric 索引 保持 float;sqlite-vecensure_table加锁并双重检查;并统一澄清flush/describe_index/ 引擎缓存等注释与 README 表述。另修复:自定义document_schema不含标准分数字段时(README 允许只传子模型)检索排序 / rerank 不再抛AttributeError/ValueError(排序改用内部计算的分数,写入 rerank 分数时容忍缺失 字段);milvus-lite 不再在模块顶层硬导入pymilvus.milvus_client.index.IndexParams(改为回退分支内惰性导入,并优先尝试旧签名),避免旧版 pymilvus 下整个后端 import 失败。
0.3.0(2026-09-24)
- 新增 qdrant 后端(原生 payload 过滤、
uuid5point id 映射、可配置on_disk, 同时作用于向量、HNSW 图与 payload 索引),并补齐capabilities/describe_index/create_index/ 漂移检测与重建。 create_index(index_name, fields=...)语义统一:显式传入的fields优先于实例构造时的declared_fields,redis / elasticsearch / qdrant 均生效;已存在索引时按可增量方式补齐 字段(redisFT.ALTER ... SCHEMA ADD、elasticsearchput_mapping、qdrant payload 索引)。- 索引重建收敛为显式调用:
rebuild_index不再在创建 / ensure 时自动触发;新增只读的compare_index_schema漂移检测(force=False仅在漂移时重建)。 - 修复 redis 索引 schema 漂移误报:
FT.INFO向量参数兼容dims=/DIM键值对两种格式, 字段类型与向量维度 / 距离度量归一化后比对。 - 移除未被调用的死钩子
_prepare_engine(milvus / milvus-lite / pgvector / elasticsearch / sqlite-vec)。 - 包元数据完善:描述列出全部后端并补充 embeddings / rerank 关键词。
0.2.0(2026-09-08)
- 演进为完整字段模型:新增
IndexField声明(tag/tag_multi/numeric/text/vector)、capabilities()能力矩阵、describe_index()归一化 schema 查询、create_index()schema 声明建索引。 - 过滤 DSL 升级为统一的
Filter(含numeric区间op="range")并新增validate_filters; 各引擎_search_index收敛为(engine, query_embedding, index_name, filters, filter_expression, k), 移除旧的kb_ids/categories参数(内部经Filter适配为vs_kb_id/vs_category字段)。 - redis / elasticsearch / milvus / milvus-lite / pgvector / sqlite-vec 全部适配新签名,
并补齐
capabilities/describe_index/_ensure_index_created。 Document新增vs_metadata字段:检索结果携带业务自定义元数据(如声明IndexField的可过滤标量vs_score),非标准vs_*模型字段在_extract_custom_metadata中聚拢。- milvus(独立部署)insert / delete 不再显式
flush():写入后检索可见依赖服务端自动落盘 (cacheFlushInterval,默认约 3s),存在短暂写后读不一致窗口。
0.1.0(2026-09-08)
- 首个正式版本:在 redis-search 与 milvus 基础上新增 pgvector、elasticsearch、 sqlite-vec 三个后端(factory + registry 注册,支持 extras 选择性安装)。
- 统一 embeddings / rerank 二阶段召回、插入 / 删除 / 更新(upsert)/ 查询 / 刷新等接口。
- 新增 kb / category 元数据过滤语义:各引擎在向量候选上按元数据过滤。
- milvus(独立部署)与 milvus-lite 支持同 page_id 更新(upsert)与删除后立即可见。
- elasticsearch 显式声明 kb / doc / category 等 keyword 映射以支持过滤,并自动迁移旧 schema。
License
Metadata
Release files for openai-simple-vectorstore 0.3.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| openai_simple_vectorstore-0.3.3.tar.gz | 144.9 kB | Without provenance | Details for openai_simple_vectorstore-0.3.3.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| openai_simple_vectorstore-0.3.3-py3-none-any.whl | Python 3 | none | any | Details for openai_simple_vectorstore-0.3.3-py3-none-any.whl |
Total release size: 266.1 kB
Release files / openai_simple_vectorstore-0.3.3.tar.gz
| Download URL | openai_simple_vectorstore-0.3.3.tar.gz |
|---|---|
| Size | 144.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
14b8b8844919ec1f4ad3a54f6a7914f8095516990730d2370e3f0c29adbbe118
|
|
BLAKE2b-256 checksum How to use checksums |
2356fbce8d336b2e4658b888efd1efde363e17901797a0e820dfae27debd7637
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.9
|
Release files / openai_simple_vectorstore-0.3.3-py3-none-any.whl
| Download URL | openai_simple_vectorstore-0.3.3-py3-none-any.whl |
|---|---|
| Size | 121.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2764bdf2e2ae29b2ba3982fc3fb8f28c048f261d94f67a3a7a910e6158c65338
|
|
BLAKE2b-256 checksum How to use checksums |
393a9899e401400bc23da569fbfeba6f83e72b07018450790ee721f8d02f1ea4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.9
|
Security for openai-simple-vectorstore 0.3.3
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.