大家好,下面将介绍一个开源项目,即一款创新的生成式搜索引擎,能够实现用户与本地文件的智能互动。此项目在微软Copilot等现有工具的基础上,推出了一种开放源代码的替代方案,旨在推动技术共享与创新。
1.系统设计
为构建本地生成式搜索引擎或助手,需要几个组件:
-
内容索引系统:负责存储本地文件内容,并配备信息检索引擎,以便高效地搜索与用户查询或问题最相关的文档。
-
语言模型:用于分析选定的本地文档内容,并据此生成精炼的总结性答案。
-
用户界面:为用户提供直观的操作界面,以便轻松地进行查询和获取信息。
组件之间的交互方式如下所示:

系统设计和架构。使用Qdrant作为向量存储,Streamlit用于用户界面。Llama 3可以通过Nvidia NIM API(700B版本)使用,也可以通过HuggingFace下载(80B版本)。文档分块使用Langchain完成。
构建本地生成式搜索引擎的第一步是创建索引,用以存储和检索本地文件内容。当用户提出问题,系统会通过这个索引快速定位到最相关的文档。随后,选定的文档内容被送入高级语言模型,该模型不仅生成答案,还提供对引用文档的明确标注。最终,用户界面将这些信息以清晰、易于理解的方式展示给用户。
2.语义索引
语义索引旨在通过分析文件内容与查询之间的相似度,提供最相关的文档匹配。索引的构建采用了Qdrant作为其向量存储解决方案。Qdrant客户端库的便利之处在于,它不需要完整的服务器端安装,便能在工作内存中直接进行文档相似性比较,极大地简化了部署流程,仅需通过pip命令安装Qdrant客户端即可。
Qdrant初始化时,需要预先设定所使用的向量化方法和度量标准(注意,hf参数稍后定义)。向量化和度量的具体配置应在客户端初始化阶段完成。以下是Qdrant初始化的一个示例:
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams
client = QdrantClient(path="qdrant/")
collection_name = "MyCollection"
if client.collection_exists(collection_name):
client.delete_collection(collection_name)
client.create_collection(collection_name,vectors_config=VectorParams(size=768, distance=Distance.DOT))
qdrant = Qdrant(client, collection_name, hf)
为构建向量索引,必须对硬盘中的文档进行嵌入处理。需要选择合适的嵌入方法和向量比较度量标准,不同的段落、句子或词嵌入技术将产生不同的结果。在文档向量搜索中,主要挑战之一是非对称搜索问题,这在信息检索领域极为常见,尤其是在处理短查询与长文档匹配时。传统的单词或句子嵌入技术通常针对相似长度的文档进行优化,如果文档长度与查询长度差异过大,就可能导致信息检索效果不佳。
然而,有一种嵌入方法能够有效应对非对称搜索问题。以MSMARCO数据集为例,该数据集基于Bing的搜索查询和文档,由Microsoft发布,并针对此类问题进行了优化。MSMARCO数据集的模型经过微调,能够提供出色的搜索效果,非常适合解决当前面临的问题。
在本次实现中,选用了针对MSMARCO数据集进行过微调的模型,名为:
sentence-transformers/msmarco-bert-base-dot-v5
这个模型基于BERT架构,并针对点积相似性度量进行了特别优化。在初始化Qdrant客户端时,已明确采用点积作为衡量相似性的方法(注意此模型的维度为768):
client.create_collection(collection_name,vectors_config=VectorParams(size=768, distance=Distance.DOT))
在选择相似性度量标准时,虽然余弦相似性是一个可行选择,但鉴于模型已针对点积优化,采用点积能够实现更优的性能表现。点积的优势在于它不仅关注向量间的角度差异,还包括了向量的大小因素,这在评估向量整体相似度时尤为重要。通过归一化处理,可以在特定条件下使两种度量标准达到相同效果。然而,当向量的大小成为一个关键考量时,点积显然是更为合适的度量手段。
模型初始化建议利用GPU以提升计算效率,具体代码实现如下:
model_name = "sentence-transformers/msmarco-bert-base-dot-v5"
model_kwargs = {'device': 'cpu'}
encode_kwargs = {'normalize_embeddings': True}
hf = HuggingFaceEmbeddings(
model_name=model_name,
model_kwargs=model_kwargs,
encode_kwargs=encode_kwargs
)
BERT类模型受限于其内存消耗的二次方增长特性,只能处理有限长度的上下文,通常不超过512个token。面对这一局限,有两种应对策略:一是仅利用文档前512个token生成答案,舍弃之后的内容;二是将文档切分为多个小块,每块作为一个独立单元存储于索引之中。为了保留完整的信息,我们选择了后者。文档的分块工作,计划利用LangChain的内置分块工具来完成:
from langchain_text_splitters import TokenTextSplitter
text_splitter = TokenTextSplitter(chunk_size=500, chunk_overlap=50)
texts = text_splitter.split_text(file_content)
metadata = []
for i in range(0,len(texts)):
metadata.append({"path":file})
qdrant.add_texts(texts,metadatas=metadata)
在编写的代码中,把文本切割成500个token的段落,并设置了50个token的重叠区域,这样做是为了在段落的首尾保持上下文的连贯性。接着,为每个段落创建了包含文档存储路径的元数据,并将其与文本段落一并索引。
在将文件内容索引之前,必须先读取这些文件。而在读取之前,需要先确定哪些文件需要被索引。本项目简化了这一流程,允许用户指定他们希望索引的文件夹。索引器将递归地搜索该文件夹及其子文件夹中的所有文件,并索引那些支持的文件类型,如PDF、Word、PPT和TXT格式。
以下是检索给定文件夹及其子文件夹内所有文件的递归方法:
def get_files(dir):
file_list = []
for f in listdir(dir):
if isfile(join(dir,f)):
file_list.append(join(dir,f))
elif isdir(join(dir,f)):
file_list= file_list + get_files(join(dir,f))
return file_list
完成文件检索后,接下来便是读取这些文件中的文本内容。目前,工具支持的文件格式包括MS Word文档(.docx)、PD
基于Llama3构建本地文件生成式AI搜索引擎

最低0.47元/天 解锁文章
437

被折叠的 条评论
为什么被折叠?



