importpsycopgfrompgvector.psycopgimportregister_vectorclassPgClient:def__init__(self,url:str,dataset:str,sentence_emb_dim:int,token_emb_dim:int):self.dataset=datasetself.sentence_emb_dim=sentence_emb_dimself.token_emb_dim=token_emb_dimself.conn=psycopg.connect(url,autocommit=True)withself.conn.cursor()ascursor:cursor.execute("CREATE EXTENSION IF NOT EXISTS vchord CASCADE;")register_vector(self.conn)defcreate(self):withself.conn.cursor()ascursor:cursor.execute(f"CREATE TABLE IF NOT EXISTS {self.dataset}_corpus ""(id INT BY DEFAULT AS IDENTITY PRIMARY KEY, text TEXT, "f"emb vector({self.sentence_emb_dim}), embs vector({self.token_emb_dim})[]);")
classPgClient:...defquery(self,doc:str,topk:int):sentence_emb=sentence_encoder.encode_query(doc)withself.conn.cursor()ascursor:cursor.execute(f"SELECT id, text FROM {self.dataset}_corpus ORDER BY emb <-> %s LIMIT {topk}")res=cursor.fetchall()returnres
为了支持 MaxSim 重排序,我们需要创建一个 SQL 函数:
classPgClient:def__init__(self,url:str,dataset:str,sentence_emb_dim:int,token_emb_dim:int):...self.conn.execute("""
CREATE OR REPLACE FUNCTION max_sim(document vector[], query vector[]) RETURNS double precision AS $$
WITH queries AS (
SELECT row_number() OVER () AS query_number, * FROM (SELECT unnest(query) AS query)
),
documents AS (
SELECT unnest(document) AS document
),
similarities AS (
SELECT query_number, document <=> query AS similarity FROM queries CROSS JOIN documents
),
max_similarities AS (
SELECT MAX(similarity) AS max_similarity FROM similarities GROUP BY query_number
)
SELECT SUM(max_similarity) FROM max_similarities
$$ LANGUAGE SQL
""")
接下来,我们就可以对向量搜索召回的候选文档进行重排序了:
classPgClient:defrerank(self,query:str,ids:list[int],topk:int):token_embs=[embforembintoken_encoder.encode_query(query)]withself.conn.cursor()ascursor:cursor.execute(f"SELECT id, text FROM {self.dataset}_corpus WHERE id = ANY(%s) ORDER BY "f"max_sim(embs, %s) DESC LIMIT {topk}"(ids,token_embs))res=cursor.fetchall()returnres