如何用vecs在带pgvector的PostgreSQL中实现混合搜索?
基于vecs实现PostgreSQL混合搜索(向量相似度+关键词匹配)
核心思路
vecs基于pgvector构建,可结合PostgreSQL原生全文检索能力(tsvector),通过先向量召回再关键词过滤重排或双结果加权融合的方式实现混合搜索,以下是具体落地步骤:
步骤1:创建带全文检索字段的集合
在vecs中定义集合时,额外添加用于关键词搜索的字段,并创建全文索引优化性能:
import vecs # 连接PostgreSQL实例 vx = vecs.create_client("postgresql://user:password@host:port/dbname") # 创建集合:包含向量、原始文本、全文检索字段 docs = vx.get_or_create_collection( name="documents", dimension=1536, # 匹配你的Embedding模型维度 schema={ "content": vecs.Text(), # 存储原始文本 "content_tsv": vecs.TSVector() # 用于全文检索的tsvector字段 } ) # 为全文检索字段创建GIN索引 vx.execute("CREATE INDEX IF NOT EXISTS idx_docs_tsv ON documents USING GIN (content_tsv);")
步骤2:插入数据时同步生成tsvector
插入文档时,自动将原始文本转换为tsvector存储:
from sqlalchemy import func # 示例数据:(ID, 向量, 原始文本) data = [ ("doc_1", [0.1, 0.2, ..., 0.1536], "PostgreSQL搭配pgvector实现向量搜索"), ("doc_2", [0.3, 0.4, ..., 0.1536], "vecs库简化PostgreSQL向量操作流程"), # 更多数据... ] # 批量插入,同步生成tsvector for doc_id, vec, content in data: docs.upsert( records=[(doc_id, vec, content, func.to_tsvector('english', content))] )
步骤3:两种混合搜索实现方式
方式一:向量召回+关键词过滤重排
先通过vecs召回TopN相似向量,再对结果做关键词匹配,最后按向量相似度+关键词匹配度加权排序:
def hybrid_search(query_vec, query_text, top_k=10): # 1. 向量召回Top20(留冗余供后续过滤) vec_results = docs.query( data=query_vec, limit=20, include_values=True ) # 2. 提取召回ID,执行关键词搜索并计算匹配度 doc_ids = [res.id for res in vec_results] keyword_res = vx.query( """ SELECT id, content, ts_rank(content_tsv, to_tsquery('english', %s)) as keyword_rank FROM documents WHERE id = ANY(%s) AND content_tsv @@ to_tsquery('english', %s) """, params=(query_text, doc_ids, query_text) ) # 3. 加权融合得分(可根据业务调整权重) vec_score_map = {res.id: res.score for res in vec_results} hybrid_list = [] for row in keyword_res: hybrid_score = vec_score_map[row.id] * 0.6 + row.keyword_rank * 0.4 hybrid_list.append({ "id": row.id, "content": row.content, "hybrid_score": hybrid_score }) # 4. 排序取TopK hybrid_list.sort(key=lambda x: x["hybrid_score"], reverse=True) return hybrid_list[:top_k]
方式二:双搜索结果合并重排
分别执行向量搜索和关键词搜索,合并结果去重后按加权得分排序:
def hybrid_search_v2(query_vec, query_text, top_k=10): # 1. 向量搜索结果 vec_res = docs.query( data=query_vec, limit=15, include_values=True ) vec_score_map = {res.id: {"score": res.score, "content": res.values["content"]} for res in vec_res} # 2. 关键词搜索结果 keyword_res = vx.query( """ SELECT id, content, ts_rank(content_tsv, to_tsquery('english', %s)) as keyword_rank FROM documents WHERE content_tsv @@ to_tsquery('english', %s) ORDER BY keyword_rank DESC LIMIT 15 """, params=(query_text, query_text) ) keyword_score_map = {row.id: {"rank": row.keyword_rank, "content": row.content} for row in keyword_res} # 3. 合并结果并计算混合得分 all_ids = set(vec_score_map.keys()).union(set(keyword_score_map.keys())) hybrid_list = [] for doc_id in all_ids: vec_score = vec_score_map.get(doc_id, {}).get("score", 0) keyword_rank = keyword_score_map.get(doc_id, {}).get("rank", 0) # 自定义权重,比如语义优先则提高向量权重 hybrid_score = vec_score * 0.7 + keyword_rank * 0.3 hybrid_list.append({ "id": doc_id, "content": vec_score_map.get(doc_id, {}).get("content") or keyword_score_map[doc_id]["content"], "hybrid_score": hybrid_score }) # 4. 排序取TopK hybrid_list.sort(key=lambda x: x["hybrid_score"], reverse=True) return hybrid_list[:top_k]
注意事项
- 权重比例需根据业务场景调整:偏向语义搜索则提高向量得分权重,偏向精准关键词则提高匹配度权重。
- 可通过PostgreSQL的
ALTER TEXT SEARCH CONFIGURATION调整全文检索的词典、停用词规则,优化关键词匹配效果。 - 数据量较大时,优先使用「向量召回+关键词过滤」的方式,避免全表扫描影响性能。
内容的提问来源于stack exchange,提问作者KZiovas
相关产品推荐
相关产品推荐

