背景需求
随着AI浪潮的兴起,RAG技术也逐渐受到广泛关注。许多人起初采用PG+PGVector来存储Dense向量,并通过向量检索实现文档召回。然而,随着用户对RAG召回效果要求的不断提高,仅依赖向量检索的召回方式已难以满足需求。因此,引入多路召回成为提升文档质量的关键手段。多路召回主要包括全文检索、Dense向量检索和Sparse向量检索。本文将重点探讨如何在现有PG+PGVector架构的基础上,集成并实现全文检索功能。
方案选择与确定
在实际调研中,我们对PG自带的全文检索功能和BM25算法进行了比较。由于BM25算法在性能上更为出色,因此全文检索方案最终选择了BM25。然而,目前PG并未支持BM25相关插件,因此需要考虑其他实现方式。我们计划使用本地BM25索引,将索引存储在本地,通过索引查找对应的文档ID,再进一步定位到目标文档。
BM25索引的创建与检索可以通过Whoosh库实现。Whoosh默认支持BM25F算法,而中文分词可借助jieba库对文档和用户问题进行分词,以提高检索的准确性。
具体步骤
1、离线建立文档索引
1.1 whoosh库逐个读取已切分的文档,并生成索引
from whoosh.index import create_in
from whoosh.fields import *
import os
import jieba
from jieba.analyse import ChineseAnalyzer
schema = Schema(title=TEXT(stored=True, analyzer=ChineseAnalyzer()), primaryId=ID(stored=True), content=TEXT(analyzer=ChineseAnalyzer()))
ix = create_in(index_dir, schema)
writer = ix.writer()
writer.add_document(title=u"First document", primaryId=u"111",
content=content=u"This is the first document we've added!")
writer.add_document(title=u"Second document", primaryId=u"112",
content=u"The second one is even more interesting!")
writer.commit()
2、在线文档检索
2.1 文档分词
from jieba import cut
query_string = "user question"
query_terms = list(cut(query_string)) # 分词并转换为列表
print(query_terms)
2.2 基于分词的结果进行检索文档取并集
from whoosh.qparser import QueryParser
from whoosh.index import open_dir # 导入打开索引的模块
ix = open_dir(index_dir) # 替换为您的索引路径
with ix.searcher() as searcher:
for keyword in query_terms:
print("result of ",keyword)
q = QueryParser("content", ix.schema).parse(keyword)
results = searcher.search(q)
# 这里假设您只想打印前3个结果
for result in results[:3]:
print(result, result.score) # 打印结果及其分数