LlamaIndex × YugabyteDB:使用 YBVectorStore 构建分布式向量检索与混合搜索
本指南以 LlamaIndex 官方 API 参考文档中公开的 YBVectorStore 类为核心,系统讲解如何在 LlamaIndex 生态中接入 YugabyteDB 作为向量存储后端,覆盖安装配置、参数语义、底层建表与索引机制、密集/稀疏混合检索、元数据过滤以及增删改查全流程。读完本文,你将能够独立完成 YugabyteDB 向量库的初始化、索引构建、混合查询与日常维护,并将其无缝接入 LlamaIndex 的 VectorStoreIndex 检索链路。
一、概述:为什么选择 YugabyteDB 作为向量存储
YBVectorStore 是 LlamaIndex 官方的 YugabyteDB 向量存储集成,位于 llama-index-integrations/vector_stores/llama-index-vector-stores-yugabytedb,包名为 llama-index-vector-stores-yugabytedb。它继承自 LlamaIndex 核心的 BasePydanticVectorStore(参见 test_vector_stores_yugabytedb.py 中的继承关系断言),因此天然兼容 LlamaIndex 统一的 add / query / delete / get_nodes 向量存储接口。
从 base.py 的依赖关系看,该集成底层由四块技术拼装而成:
- pgvector:提供 PostgreSQL/YugabyteDB 原生的向量列类型与余弦距离计算;
- SQLAlchemy:负责动态建模、ORM 映射与连接管理;
- psycopg2-yugabytedb:YugabyteDB 定制的 psycopg2 驱动,支持集群感知的负载均衡连接;
- YugabyteDB 自身的
ybhnsw索引:为海量向量提供近似最近邻(ANN)检索能力。
对应的运行时依赖声明在 pyproject.toml 中:pgvector>=0.3.6,<1.0.0、psycopg2-yugabytedb>=2.9.3.5、sqlalchemy[asyncio]>=1.4.49,<2.1、sqlalchemy-yugabytedb>=1.0.0.1,并要求 Python 版本 >=3.10,<4.0。
二、安装与前置条件
pip install llama-index-vector-stores-yugabytedb
安装后还需满足两项运行前置:
- 一个可访问的 YugabyteDB 实例。官方测试指引(见 tests/README.md)推荐通过
./bin/yugabyted start启动本地节点; vector扩展可用。YBVectorStore在初始化时会自动执行CREATE EXTENSION IF NOT EXISTS vector(见 base.py),若数据库账号权限不足,可通过initialization_fail_on_error=True将初始化错误升级为硬失败以便排查。
三、快速上手:最小可用示例
官方 README(README.md)给出了最小示例,from_params 类方法会依据连接参数自动拼接连接串并完成初始化:
from llama_index.vector_stores.yugabytedb import YBVectorStore
vector_store = YBVectorStore.from_params(
host="localhost",
user="yugabyte",
password="yugabyte",
port=5433,
load_balance="True",
database="yugabyte",
table_name="test_table",
schema_name="test_schema",
embed_dim=1536,
)
类文档(base.py)中的示例同样演示了 from_params 用法,并额外展示了 use_halfvec=True 启用半精度向量:
from llama_index.vector_stores.yugabytedb import YBVectorStore
vector_store = YBVectorStore.from_params(
database="vector_db",
host="localhost",
password="password",
port=5432,
user="yugabytedb",
table_name="paul_graham_essay",
embed_dim=1536, # openai embedding dimension
use_halfvec=True, # Enable half precision
)
随后即可与 LlamaIndex 索引无缝拼接:
from llama_index.core import VectorStoreIndex
from llama_index.core.node_parser import SimpleNodeParser
# 假设已有 documents 列表
nodes = SimpleNodeParser().get_nodes_from_documents(documents)
vector_store.add(nodes)
index = VectorStoreIndex.from_vector_store(vector_store)
query_engine = index.as_query_engine()
response = query_engine.query("你的问题")
四、核心参数详解
YBVectorStore 的参数分为两类:from_params 的连接参数,以及底层构造函数 __init__ 的存储行为参数(两者最终汇合于 __init__,见 base.py)。
4.1 连接参数(from_params)
| 参数 | 默认值 | 说明 |
|---|---|---|
host / port / database / user / password |
None |
经典连接五项;from_params 会拼出形如 yugabytedb+psycopg2://user:password@host:port/database?<query> 的连接串 |
load_balance |
False |
是否启用 YugabyteDB 驱动层面的负载均衡,对多节点集群连接至关重要 |
topology |
None |
映射为连接串中的 topology_keys,用于约束流量在指定拓扑(可用区/region)内路由 |
yb_servers_refresh_interval |
300 |
集群节点列表刷新间隔(秒) |
fallback_to_topology_keys_only |
False |
当拓扑键不可达时是否仅回退到拓扑键内节点 |
failed_host_ttl_seconds |
5 |
失败主机被临时拉黑的最短时间(秒) |
connection_string |
None |
直接传入完整连接串或 sqlalchemy.engine.URL,可跳过上述拼接逻辑 |
这些查询参数由
from_params通过urllib.parse.urlencode拼进连接串(见 base.py),更多驱动级参数可查阅 YugabyteDB psycopg2 驱动文档。
4.2 存储行为参数(init)
| 参数 | 默认值 | 说明 |
|---|---|---|
table_name |
"llamaindex" |
逻辑表名,会自动小写;实际物理表名为 data_{table_name} |
schema_name |
"public" |
表所在 schema,自动小写;只允许字母、数字与下划线 |
embed_dim |
1536 |
向量维度,须与所用 Embedding 模型输出维度一致(如 OpenAI 默认 1536) |
hybrid_search |
False |
是否启用「稠密向量 + 全文检索」混合模式,开启后会额外创建 TSVECTOR 计算列 |
text_search_config |
"english" |
全文检索的文本搜索配置(如 english、simple),hybrid 模式下必填,否则抛 ValueError |
cache_ok |
False |
自定义 TSVector 类型装饰器是否可安全参与 SQLAlchemy 编译缓存键 |
perform_setup |
True |
是否在首次使用时自动执行 schema/extension/建表/HNSW 索引等初始化 DDL |
debug |
False |
透传给 SQLAlchemy create_engine(echo=...),开启后打印 SQL 日志 |
use_jsonb |
False |
元数据列使用 JSONB 而非 JSON;JSONB 支持 @> 包含操作符过滤 |
hnsw_kwargs |
None |
提供则创建 HNSW 索引(详见下文);None 表示关闭 ANN 索引 |
create_engine_kwargs |
{} |
透传给 create_engine 的额外关键字参数 |
use_halfvec |
False |
向量列使用 HALFVEC(半精度)以节省存储并提升检索吞吐 |
indexed_metadata_keys |
None |
需要建立 B-tree 索引的元数据键集合,元素为 (key, PGType) 二元组 |
initialization_fail_on_error |
False |
初始化某一步失败时是否直接抛出异常(默认仅记录 warning 继续) |
engine |
None |
复用外部传入的 SQLAlchemy Engine,跳过内部创建 |
其中 indexed_metadata_keys 的 PGType 必须取自白名单:text、int、integer、numeric、float、double precision、boolean、date、timestamp、uuid(类型映射见 base.py),传入其他类型会直接抛 ValueError。
五、底层原理:动态建表与自动初始化
YBVectorStore 最核心的实现技巧是用 SQLAlchemy 动态建模。get_data_model 函数(base.py)以 type() 动态生成表模型,表结构为:
| 列 | 类型 | 说明 |
|---|---|---|
id |
BIGINT 自增主键 |
行主键 |
text |
VARCHAR NOT NULL |
节点原文 |
metadata_ |
JSON 或 JSONB |
节点元数据(去掉文本后的序列化结果) |
node_id |
VARCHAR |
LlamaIndex 节点 ID |
embedding |
Vector(embed_dim) 或 HALFVEC(embed_dim) |
向量列 |
text_search_tsv |
TSVECTOR(计算列) |
仅 hybrid 模式存在,由 to_tsvector('<config>', text) 持久化计算 |
每次 add/query 等操作触发 _initialize()(base.py)时,会按顺序执行四步幂等初始化:
_create_schema_if_not_exists:校验 schema 名合法性,查询information_schema.schemata,不存在则CREATE SCHEMA IF NOT EXISTS;_create_extension:CREATE EXTENSION IF NOT EXISTS vector;_create_tables_if_not_exists:metadata.create_all建表并创建元数据 B-tree 索引与ref_doc_id索引;- 若提供
hnsw_kwargs,则_create_hnsw_index创建 HNSW 索引。
由于 DDL 全部是 IF NOT EXISTS 幂等语义,多个 YBVectorStore 实例指向同一表时不会互相破坏——测试 test_hnsw_index_creation(test_yugabytedb.py)专门验证了创建两个实例后 pg_indexes 中 HNSW 索引恰好只有一份。若某一步失败,默认只记录 PG Setup: ... 警告日志;只有 initialization_fail_on_error=True 时才会向上抛出。
六、HNSW 近似最近邻索引
当传入 hnsw_kwargs 时,YBVectorStore 会使用 YugabyteDB 原生的 ybhnsw 索引加速向量检索:
vector_store = YBVectorStore.from_params(
host="localhost",
user="yugabyte",
password="yugabyte",
port=5433,
load_balance="True",
database="yugabyte",
table_name="hnsw_table",
schema_name="public",
embed_dim=1536,
hnsw_kwargs={
"hnsw_m": 16, # 每层最大连接数
"hnsw_ef_construction": 64, # 建图时的动态候选列表大小
"hnsw_ef_search": 40, # 检索时的候选列表大小(可选)
"hnsw_dist_method": "vector_cosine_ops", # 可选,默认自动推断
},
)
从 base.py 的实现可以看到三个关键行为:
hnsw_ef_construction与hnsw_m必填,缺失直接抛ValueError;- 距离算子默认自动推断:
use_halfvec=True时用halfvec_l2_ops,否则用vector_cosine_ops,也可显式指定hnsw_dist_method覆盖; - 生成的索引名为
{物理表名}_embedding_idx,例如data_test_table_embedding_idx,对应 SQL:CREATE INDEX IF NOT EXISTS data_xxx_embedding_idx ON schema.data_xxx USING ybhnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 64);
七、半精度向量:use_halfvec
use_halfvec=True 时,embedding 列改用 pgvector 的 HALFVEC 类型(base.py),以 16 位浮点存储向量,从而将向量存储占用减半。官方测试对普通向量与半精度向量并行验证了同一套能力矩阵:写入查询、元数据过滤、IN/CONTAINS/IS_EMPTY 操作符、删除、清空等场景均以参数化方式覆盖(见 test_yugabytedb.py),说明半精度模式功能上与全精度等价,只是精度/性能取舍不同。
八、混合搜索:稠密向量 + 全文检索
开启 hybrid_search=True 后,YBVectorStore 在稠密余弦检索之外,还会基于 text_search_tsv 计算列执行 PostgreSQL 全文检索。查询通过 VectorStoreQueryMode 区分三种模式(base.py):
from llama_index.core.vector_stores.types import VectorStoreQuery, VectorStoreQueryMode
# 模式一:纯稠密(默认)
q_dense = VectorStoreQuery(query_embedding=[0.1, 0.2, ...], similarity_top_k=5)
# 模式二:纯稀疏全文检索
q_sparse = VectorStoreQuery(
query_str="who is the fox?",
sparse_top_k=5,
mode=VectorStoreQueryMode.SPARSE,
)
# 模式三:混合(dense + sparse 结果合并去重)
q_hybrid = VectorStoreQuery(
query_embedding=[0.1, 0.2, ...],
query_str="fox",
similarity_top_k=5,
mode=VectorStoreQueryMode.HYBRID,
)
混合查询的内部流程(_hybrid_query,base.py)值得注意:
sparse_top_k未指定时回退到similarity_top_k;- 稠密与稀疏结果按
node_id去重后简单拼接,不支持alpha加权参数(传入会打印 warning 后忽略); - 稀疏分支使用
plainto_tsquery生成查询,并将&替换为|以执行 OR 语义、提高召回率(见_build_sparse_query); - 稀疏排序基于
ts_rank。
测试覆盖了混合查询的多种情形(test_yugabytedb.py):带 sparse_top_k、缺省 sparse_top_k、句子级查询、叠加元数据过滤等;同时验证了混合模式下必须提供 query_str,否则抛出 "query_str must be specified for a sparse vector query."。
九、元数据过滤
9.1 操作符映射
_to_postgres_operator(base.py)将 LlamaIndex 的 FilterOperator 映射为 SQL 操作符:
| FilterOperator | SQL | 说明 |
|---|---|---|
EQ / GT / LT / NE / GTE / LTE |
= > < != >= <= |
标量比较;数值类型会自动 ::float 强转 |
IN / NIN |
IN / NOT IN |
元数据为单值、过滤值为列表 |
CONTAINS |
@> |
元数据为列表、过滤值为单值(依赖 JSONB @>) |
TEXT_MATCH / TEXT_MATCH_INSENSITIVE |
LIKE / ILIKE |
自动包裹 %...% 模糊匹配 |
IS_EMPTY |
IS NULL |
判断键不存在 |
过滤条件支持 MetadataFilters 的 and/or 组合,并可嵌套(_recursively_apply_filters)。典型用法:
from llama_index.core.vector_stores.types import (
MetadataFilters, MetadataFilter, FilterOperator,
)
filters = MetadataFilters(
filters=[
MetadataFilter(key="category", value="news", operator=FilterOperator.EQ),
MetadataFilter(
key="tags",
value=["ai", "llm"],
operator=FilterOperator.IN,
),
],
condition="and",
)
q = VectorStoreQuery(query_embedding=embedding, similarity_top_k=10, filters=filters)
9.2 元数据键预索引
对于高频过滤字段,可通过 indexed_metadata_keys 提前建立 B-tree 索引,避免每次查询全表扫描:
vector_store = YBVectorStore.from_params(
...,
indexed_metadata_keys={("category", "text"), ("year", "int")},
)
从 get_data_model 可见,每个键会生成形如 {indexname}_{key}_{type} 的 btree 索引,例如 test_table_idx_category_text,其底层利用 metadata_->>key 的 cast 表达式实现。
十、增删改查与生命周期管理
YBVectorStore 实现了 LlamaIndex 向量存储的标准接口:
| 方法 | 签名要点 | 行为 |
|---|---|---|
add(nodes) |
接收 List[BaseNode] |
逐条写入,返回写入的 node_id 列表 |
query(query) |
接收 VectorStoreQuery |
按 mode 分发稠密/稀疏/混合查询,返回 VectorStoreQueryResult(nodes、similarities、ids) |
delete(ref_doc_id) |
按文档 ID 删除 | 依据 metadata_->>'doc_id' 匹配删除整篇文档的节点 |
delete_nodes(node_ids, filters) |
按节点 ID 与/或元数据过滤删除 | node_ids 与 filters 同时为空时直接返回;组合条件按 AND 语义 |
get_nodes(node_ids, filters) |
按 ID 或过滤条件取回节点 | 二者至少提供一个;返回完整 BaseNode(含 embedding) |
clear() |
无参 | 清空整张表 |
close() |
异步方法 | 释放 SQLAlchemy Engine 连接池 |
节点写入时(_node_to_table_row),text 存 MetadataMode.NONE 下的纯文本,metadata_ 存 node_to_metadata_dict(remove_text=True, flat_metadata=...) 序列化结果;查询回读时优先通过 metadata_dict_to_node 还原节点,若元数据格式异常则回退到 Legacy TextNode 逻辑以保证向后兼容(见 _db_rows_to_query_result)。
连接管理上,client 属性在初始化前返回 None、初始化后返回 SQLAlchemy Engine;也支持外部传入 engine 复用既有连接(测试 test_custom_engines 验证了该路径)。
十一、测试与验证
仓库为 YBVectorStore 提供了两层测试:
- 集成测试 test_yugabytedb.py:需要本地 YugabyteDB(连接参数
host=localhost, user=yugabyte, password=yugabyte, port=5433),未连接时自动skipif。覆盖实例创建、写入查询、HNSW/半精度/混合搜索全组合、各类元数据操作符、delete_nodes的多种组合语义、get_nodes参数化用例、clear等; - 结构测试 test_vector_stores_yugabytedb.py:断言类继承自
BasePydanticVectorStore,保证接口契约合规。
运行方式(见 tests/README.md):先 ./bin/yugabyted start 启动节点,再执行 uv run -- pytest。
十二、注意事项与限制
hybrid_search与text_search_config:启用混合搜索必须显式指定text_search_config,否则构造时抛ValueError;- 混合搜索不支持
alpha:稠密与稀疏结果是等权拼接去重,无法调节二者权重; hnsw_kwargs的必填项:hnsw_ef_construction与hnsw_m缺一不可;- schema 名严格受限:仅允许
^[A-Za-z_][A-Za-z0-9_]*$模式,防止 SQL 注入; - 表名会被小写化并加
data_前缀:物理表名与逻辑table_name不一致,排查问题时需注意; - 连接参数可走
connection_string:生产环境建议直接传入完整连接串(含负载均衡等查询参数),避免逐项拼参; perform_setup=False的场景:当表结构已由外部 DBA 预先创建、应用只有只读/写入权限时,可关闭自动 DDL 以避免权限报错。
综上,YBVectorStore 是 LlamaIndex 官方向量存储生态中面向分布式数据库场景的成熟选择,既保留 pgvector 的简单建模方式,又借助 YugabyteDB 的 ybhnsw 与全文检索能力,让 RAG 应用在水平扩展的 SQL 集群上同时获得向量检索与关键词检索能力。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00