RyanCodrai turbovec
一个包含1000万文档的语料库以float32格式存储需要31 GB内存。turbovec将其压缩至4 GB,并且搜索速度比FAISS更快。turbovec是一个带有Python绑定的Rust向量索引,基于Google Research的TurboQuant算法构建——这是一种数据无关的量化器,能够匹配香农失真下界,无需码本训练,也无需独立的训练阶段。
在线增量导入。添加向量后即可索引——无需训练步骤、无需参数调优、无需随语料增长而重建。
比FAISS更快。手写的NEON(ARM)和AVX-512BW(x86)内核在ARM上比FAISS IndexPQFastScan快12–20%,在x86上与之持平或更优。
搜索时过滤。向search()传递ID白名单(或槽位位掩码),内核会直接处理。你始终能从允许集合中获得最多k个结果——无需过度获取,选择性过滤下召回率不受影响。
纯本地运行。无需托管服务,数据不会离开你的机器或VPC。可搭配任意开源嵌入模型,构建完全隔离的RAG堆栈。
正在构建注重隐私、内存或延迟的RAG?你来对地方了。
pip install turbovec
from turbovec import TurboQuantIndex
index = TurboQuantIndex(dim=1536, bit_width=4)
index.add(vectors)
index.add(more_vectors)
scores, indices = index.search(query, k=10)
index.write("my_index.tq")
loaded = TurboQuantIndex.load("my_index.tq")
需要删除后仍能保留的稳定ID?使用IdMapIndex:
import numpy as np
from turbovec import IdMapIndex
index = IdMapIndex(dim=1536, bit_width=4)
index.add_with_ids(vectors, np.array([1001, 1002, 1003], dtype=np.uint64))
scores, ids = index.search(query, k=10) # ids 是你的 uint64 外部ID
index.remove(1002) # 按ID O(1)删除
index.write("my_index.tvim")
loaded = IdMapIndex.load("my_index.tvim")
混合检索(过滤搜索)
将结果限制为其他系统(SQL、BM25、ACL、时间窗口等)生成的候选集:
import numpy as np
from turbovec import IdMapIndex
idx = IdMapIndex(dim=1536, bit_width=4)
idx.add_with_ids(vectors, ids)
# 阶段1:外部系统将范围缩小到候选ID
allowed = np.array(db.execute("SELECT id FROM docs WHERE tenant=?", (t,)).fetchall(), dtype=np.uint64)
# 阶段2:在候选集内进行密集重排序
scores, ids = idx.search(query, k=10, allowlist=allowed)
过滤在SIMD内核中以32向量块粒度执行:没有允许槽位的块在LUT查找或评分工作之前就会被短路处理,而已评分块中未被允许的单个槽位会在堆插入时丢弃。因此,选择性白名单(仅允许索引中一小部分)能避免大部分SIMD开销,而非先计算再丢弃结果。输出长度为min(k, len(allowed))——当白名单小于k时,你会精确获得len(allowed)个结果,而非填充的降级结果。
完整参考见docs/api.md。
框架集成
可直接替换各框架内置的参考向量/文档存储。相同的公共接口、相同的持久化语义、相同的检索器和管道连接——只需替换导入语句,保持管道不变。
- LangChain —
pip install turbovec[langchain]· 替换langchain_core.vectorstores.InMemoryVectorStore - LlamaIndex —
pip install turbovec[llama-index]· 替换llama_index.core.vector_stores.SimpleVectorStore - Haystack —
pip install turbovec[haystack]· 替换haystack.document_stores.in_memory.InMemoryDocumentStore - Agno —
pip install turbovec[agno]· 替换agno.vectordb.lancedb.LanceDb
Rust
cargo add turbovec
use turbovec::TurboQuantIndex;
let mut index = TurboQuantIndex::new(1536, 4);
index.add(&vectors);
let results = index.search(&queries, 10);
index.write("index.tv").unwrap();
let loaded = TurboQuantIndex::load("index.tv").unwrap();
需要删除后仍能保留的稳定外部ID:
use turbovec::IdMapIndex;
let mut index = IdMapIndex::new(1536, 4);
index.add_with_ids(&vectors, &[1001, 1002, 1003]);
let (scores, ids) = index.search(&queries, 10);
index.remove(1002);
index.write("index.tvim").unwrap();
let loaded = IdMapIndex::load("index.tvim").unwrap();
召回率
TurboQuant vs FAISS IndexPQ(LUT256, nbits=8)——论文第4.4节基线。100K向量,k=64。FAISS PQ子量化器数量与TurboQuant的比特率匹配(2-bit时m=d/4,4-bit时m=d/2)。在OpenAI d=1536和d=3072上,TurboQuant在2-bit和4-bit下R@1比FAISS高0.4–3.4个百分点,两者在k=4时均收敛到1.0。GloVe d=200是更困难的场景——低维度下渐近Beta假设更宽松。TurboQuant在4-bit下R@1比FAISS高0.3个百分点,2-bit下落后1.2个百分点,两者在k≈16时接近FAISS。
关于基线的说明。我们与FAISS IndexPQ(LUT256, nbits=8, float32 LUT)比较,因为这是大多数用户会使用的生产级PQ标准。这比TurboQuant论文中自定义的u8-LUT PQ基线更强——FAISS在评分时使用更高精度的LUT,并使用k-means++进行码本训练。我们在OpenAI d=1536/d=3072上复现了论文的TurboQuant数值,并在低维嵌入上达到了与其他社区参考实现相似的数值(参见d=384时的turboquant-py)。GloVe上的可见差距反映了FAISS作为强基线的表现,而非TurboQuant实现问题。
完整结果:
- [d=1536 2-bit](链接)
- [d=1536 4-bit](链接)
- [d=3072 2-bit](链接)
- [d=3072 4-bit](链接)
- [GloVe 2-bit](链接)
- [GloVe 4-bit](链接)
压缩与搜索速度
所有基准测试:100K向量,1K查询,k=64,5次运行中位数。
ARM(Apple M3 Max) 在ARM上,TurboQuant在所有配置下比FAISS FastScan快12–20%。
x86(Intel Xeon Platinum 8481C / Sapphire Rapids,8 vCPUs) 在x86上,TurboQuant在所有4-bit配置下领先1–6%,在2-bit单线程下与FAISS差距在~1%以内。2-bit多线程行(d=1536和d=3072)是唯一略落后于FAISS的配置(2–4%)。