这东西解决什么问题

向量数据库这两年选择太多了。Milvus、Qdrant、Weaviate、Chroma,每个都要单独部署一个服务,配置端口、管理进程,对于很多场景来说太重了。

阿里在今年2月开源了Zvec,定位是"向量数据库里的SQLite"——进程内运行,不需要启动任何服务端,pip install完直接用。6月12日刚发布的v0.5.0加了三个东西:原生全文检索、混合检索、DiskANN磁盘索引。GitHub上目前10500+ Star,这两天一直挂在Trending首页。

我花了一下午把v0.5.0的新功能跑了一遍,下面记录完整过程。

安装

Python 3.10到3.14都支持:

pip install zvec

macOS ARM64、Linux x86_64/ARM64、Windows x86_64都能用。我在M2 Mac上装的,没遇到编译问题,几秒钟就装完了。

Node.js的话用npm:

npm install @zvec/zvec

Go和Rust也有官方SDK,不过这篇用Python演示。

基本用法:创建Collection和向量检索

Zvec的概念和其他向量数据库差不多:Collection对应一个表,里面存Document,每个Document包含标量字段和向量字段。

先来一个最简单的例子:

import zvec

# 定义schema:一个标量字段 + 一个向量字段
schema = zvec.CollectionSchema(
    name="articles",
    fields=[
        zvec.FieldSchema(
            name="publish_year",
            data_type=zvec.DataType.INT32,
            index_param=zvec.InvertIndexParam(enable_range_optimization=True),
        ),
    ],
    vectors=[
        zvec.VectorSchema(
            name="embedding",
            data_type=zvec.DataType.VECTOR_FP32,
            dimension=768,
            index_param=zvec.HnswIndexParam(metric_type=zvec.MetricType.COSINE),
        ),
    ],
)

# 创建collection,数据存在本地目录
collection = zvec.create_and_open(
    path="./articles_data",
    schema=schema,
)

注意几个点: - path是一个本地目录,所有数据和索引都存在这里 - 向量维度要在schema里写死,后面不能改 - 索引类型选了HNSW+Cosine,这是最常用的组合

插入数据:

collection.insert(
    zvec.Doc(
        id="article_001",
        vectors={"embedding": [0.1, 0.2, ...共768个float]},
        fields={"publish_year": 2026},
    )
)

# 批量插入也是一样,传列表就行
collection.insert([
    zvec.Doc(id="article_002", vectors={"embedding": vec2}, fields={"publish_year": 2025}),
    zvec.Doc(id="article_003", vectors={"embedding": vec3}, fields={"publish_year": 2026}),
])

插入之后记得调optimize(),这一步会构建向量索引:

collection.optimize()

如果数据量小不调也行,Zvec会用临时索引做检索,但大数据量下性能差距很明显。

查询:

results = collection.query(
    queries=zvec.Query(
        field_name="embedding",
        vector=[0.3, 0.1, ...共768个float],
    ),
    topk=10,
)

for doc in results:
    print(f"ID: {doc['id']}, Score: {doc['score']:.4f}")

带过滤条件的查询:

results = collection.query(
    queries=zvec.Query(
        field_name="embedding",
        vector=query_vec,
    ),
    topk=10,
    filter="publish_year >= 2025",
)

到这一步,已经是一个能用的向量检索了。整个过程没有启动任何服务、没有配置任何端口。数据就存在本地一个目录里,程序退出数据不丢(WAL保证持久化)。

v0.5.0新功能一:全文检索

这是这次更新我最想试的功能。之前做RAG的时候,向量检索找语义相关的内容,但有时候用户搜一个精确的函数名或者错误码,向量检索就不太灵了。通常的做法是再接一个Elasticsearch做关键词搜索,然后把两路结果合并。

Zvec v0.5.0直接内置了全文检索。在schema里给字符串字段加一个FTS索引就行:

schema = zvec.CollectionSchema(
    name="docs",
    fields=[
        zvec.FieldSchema(
            name="content",
            data_type=zvec.DataType.STRING,
            index_param=zvec.FtsIndexParam(),  # 加这行就开启全文检索
        ),
        zvec.FieldSchema(
            name="title",
            data_type=zvec.DataType.STRING,
        ),
    ],
    vectors=[
        zvec.VectorSchema(
            name="embedding",
            data_type=zvec.DataType.VECTOR_FP32,
            dimension=768,
            index_param=zvec.HnswIndexParam(metric_type=zvec.MetricType.COSINE),
        ),
    ],
)

collection = zvec.create_and_open(path="./docs_fts", schema=schema)

插入带文本内容的文档:

collection.insert([
    zvec.Doc(
        id="doc_1",
        fields={
            "content": "Python的asyncio模块提供了async/await语法来编写并发代码",
            "title": "Python异步编程入门",
        },
        vectors={"embedding": embed("Python的asyncio模块...")},
    ),
    zvec.Doc(
        id="doc_2",
        fields={
            "content": "使用pip install报错ModuleNotFoundError的常见解决方法",
            "title": "pip安装问题排查",
        },
        vectors={"embedding": embed("使用pip install报错...")},
    ),
])

collection.optimize()

纯关键词搜索:

results = collection.query(
    queries=zvec.FtsQuery(
        field_name="content",
        text="asyncio await",
    ),
    topk=5,
)

搜"ModuleNotFoundError"这种精确错误名也能命中,不用依赖向量语义。

v0.5.0新功能二:混合检索

全文检索单独用意义不大,关键是和向量检索混合起来。Zvec的MultiQuery能在一次调用里同时做向量检索和全文检索,然后自动融合排序:

results = collection.query(
    queries=zvec.MultiQuery([
        zvec.Query(
            field_name="embedding",
            vector=query_embedding,
        ),
        zvec.FtsQuery(
            field_name="content",
            text="asyncio",
        ),
    ]),
    topk=10,
)

这个比自己手动做两次查询再合并方便太多了。以前用Milvus+ES做类似的事情,光是结果融合和去重就写了好几十行代码。

v0.5.0新功能三:DiskANN索引

第三个新功能是DiskANN索引。HNSW索引全在内存里,100万条768维向量大概要3-4GB内存。DiskANN把索引主体放磁盘,内存占用降到原来的几分之一。

用法就是把索引参数换一下:

vectors=[
    zvec.VectorSchema(
        name="embedding",
        data_type=zvec.DataType.VECTOR_FP32,
        dimension=768,
        index_param=zvec.DiskAnnIndexParam(metric_type=zvec.MetricType.COSINE),
    ),
]

代价是查询延迟会高一些。官方benchmark显示,1000万条数据下,DiskANN的延迟大概是HNSW的2-3倍,但内存占用只有十分之一。对于数据量大但查询频率不高的场景(比如离线分析、本地知识库),DiskANN很合适。

实际场景:本地文档RAG

把上面的功能串起来,做一个本地文档问答系统。不需要部署任何外部服务,整个系统就是一个Python脚本:

import zvec
from openai import OpenAI

client = OpenAI()  # 用你自己的embedding服务

def get_embedding(text):
    resp = client.embeddings.create(
        model="text-embedding-3-small",
        input=text,
    )
    return resp.data[0].embedding

# 创建collection
schema = zvec.CollectionSchema(
    name="knowledge_base",
    fields=[
        zvec.FieldSchema(
            name="content",
            data_type=zvec.DataType.STRING,
            index_param=zvec.FtsIndexParam(),
        ),
        zvec.FieldSchema(
            name="source",
            data_type=zvec.DataType.STRING,
        ),
    ],
    vectors=[
        zvec.VectorSchema(
            name="embedding",
            data_type=zvec.DataType.VECTOR_FP32,
            dimension=1536,
            index_param=zvec.HnswIndexParam(metric_type=zvec.MetricType.COSINE),
        ),
    ],
)

db = zvec.create_and_open(path="./knowledge_db", schema=schema)

# 导入文档(这里简化,实际项目要做分块)
docs = [
    {"id": "1", "content": "Zvec支持Python 3.10到3.14...", "source": "readme.md"},
    {"id": "2", "content": "调用optimize()构建索引后检索速度提升10倍...", "source": "guide.md"},
]

for doc in docs:
    db.insert(zvec.Doc(
        id=doc["id"],
        fields={"content": doc["content"], "source": doc["source"]},
        vectors={"embedding": get_embedding(doc["content"])},
    ))

db.optimize()

# 混合检索
def search(question, topk=5):
    q_vec = get_embedding(question)
    results = db.query(
        queries=zvec.MultiQuery([
            zvec.Query(field_name="embedding", vector=q_vec),
            zvec.FtsQuery(field_name="content", text=question),
        ]),
        topk=topk,
    )
    return results

整个知识库就是本地一个文件夹。备份就是拷贝目录,迁移也是拷贝目录。这个简单程度是Milvus、Qdrant这些C/S架构的向量数据库做不到的。

踩坑记录

跑的过程中遇到了几个问题,记一下:

1. schema定义后不能改

Collection创建之后,字段名、向量维度、索引类型都不能改。如果要改schema,只能新建Collection重新导入数据。这和SQLite的ALTER TABLE限制类似。

2. 字段名必须完全匹配

插入数据时字段名写错了不会报错,但那个字段的数据会丢失。比如schema里定义的是publish_year,插入时写成publishYear,不会有任何错误提示,但查询时这个字段就是空的。这个行为挺坑的,希望后续版本能加个校验。

3. optimize()的时机

小数据量(几千条以内)不调optimize()也能查,但一旦数据量上万,不调optimize()查询会慢很多。我的建议是批量导入完成后统一调一次,不要每插一条就调。

4. 并发限制

多进程读没问题,但写只能单进程。如果你的应用需要多个worker同时写入,要自己加锁或者用队列。

5. 中文全文检索

FTS默认的分词器对中文的支持还在完善中。我测试时"Python异步编程"能搜到,但"异步"单独搜有时候会漏。如果中文全文检索对你很关键,建议再等等或者自己做分词后存入。

和其他向量数据库的对比

ZvecChromaMilvusQdrant
架构嵌入式(进程内)嵌入式/C-S客户端-服务端客户端-服务端
安装pip installpip installDocker部署Docker部署
全文检索v0.5.0原生支持不支持不支持有payload索引
混合检索原生MultiQuery不支持支持支持
磁盘索引DiskANN不支持DiskANN支持
SDK语言Python/Node/Go/RustPython/JS多语言多语言
适用场景本地应用、边缘设备、原型原型、小项目生产级大规模生产级中大规模

选择建议:如果你的数据量在千万级以下,不想维护额外服务,Zvec够用了。需要分布式、高可用、多节点的场景,还是得上Milvus或Qdrant。

总结

Zvec v0.5.0加了全文检索和混合检索之后,嵌入式向量数据库该有的功能基本齐了。个人项目、本地RAG、边缘部署,pip install完就能跑,不用折腾Docker和服务配置,省心不少。

DiskANN索引让它能处理更大规模的数据而不吃太多内存,不再只能当原型验证的工具了。

代码仓库:https://github.com/alibaba/zvec 文档:https://zvec.org

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐