趋近智
一个处理用户搜索请求的实用设计包含了查询嵌入 (embedding)、ANN 搜索、过滤和排序等概念。设计这种查询流程是构建有效语义搜索系统的根本。它概述了从原始用户查询到相关结果排序列表的步骤。
其核心是,处理语义搜索查询涉及几个不同的阶段。我们将分解一个典型流程,同时记住,具体实现可能因所选工具和应用要求而异。
处理语义搜索查询的典型流程,从用户输入开始,到格式化结果结束。包括元数据过滤和重新排序等可选步骤。
让我们审视每个步骤:
这是系统从用户那里接收搜索词、问题或短语的入口。它通常是原始文本。
在生成嵌入 (embedding)之前,通常有益于应用一些基本预处理,类似于数据索引期间可能已完成的操作:
目标是将查询标准化为适合嵌入模型的格式。
这是应用语义搜索核心能力的地方。预处理后的查询文本被送入索引阶段使用的相同嵌入模型(或兼容模型,例如与文档编码器配对的专用查询编码器)。
# Python 代码片段
from sentence_transformers import SentenceTransformer
# 假设已加载 'embedding_model',例如 SentenceTransformer('all-MiniLM-L6-v2')
query_text = "latest developments in sustainable energy"
preprocessed_query = preprocess_function(query_text) # 应用第 2 点中的步骤
# 生成向量
query_vector = embedding_model.encode(preprocessed_query).tolist()
# query_vector 现在是浮点数列表,例如 [0.12, -0.05, ..., 0.89]
输出是一个表示查询语义的稠密向量。
生成的查询向量被发送到向量数据库。核心操作是近似最近邻 (ANN) 搜索。此请求的重要参数 (parameter)通常包括:
ef_search,IVF 的 nprobe)的参数,用于在搜索过程中控制准确性/速度的权衡。这些在第 3 章中讨论过。{ "year": { "$gte": 2023 } } (查找 2023 年及以后的项目){ "category": "technology" } (查找“技术”类别中的项目)向量数据库处理过滤的方式不同。有些支持预过滤(在 ANN 搜索之前进行过滤),如果过滤器显着减小了搜索空间,这种方式可以更快。而另一些则执行后过滤(过滤 个 ANN 结果)。在这里了解您的数据库能力。
# 使用数据库客户端的 Python 代码片段
k = 20 # 要获取的结果数量
search_params = {"ef_search": 128} # HNSW 参数示例
metadata_filter = {"status": "published", "region": "EMEA"}
# 向向量数据库发送请求
# 'search' 方法的签名在不同数据库之间差异很大
search_results = vector_db_client.search(
collection_name="articles",
query_vector=query_vector,
limit=k,
search_params=search_params,
filter=metadata_filter
)
# search_results 可能包含 ID、距离/分数以及可能的元数据
# 例如,[{'id': 'doc456', 'score': 0.85}, {'id': 'doc123', 'score': 0.82}, ...]
向量数据库返回一个候选项目列表,通常包括:
如果文档的完整内容未存储在向量数据库中或未直接返回,您需要使用获取的 ID 从主数据存储(如关系数据库、文档存储或文件系统)中获取完整内容。
初始 ANN 搜索结果纯粹根据向量相似度进行排序。尽管通常有效,但通过添加重新排序步骤有时可以提高相关性。这涉及根据额外标准重新排序前 个候选结果(其中 可以是初始 或更大的一组获取结果):
重新排序会增加延迟,但可以显著提高结果的感知质量。
# 重新排序步骤
# 假设 'candidates' 是来自 vector_db_client.search 的列表
# 假设 'fetch_full_content' 通过 ID 获取文档文本
def apply_reranking(query_text, candidates):
reranked_results = []
for candidate in candidates:
doc_id = candidate['id']
initial_score = candidate['score']
doc_content = fetch_full_content(doc_id)
# 示例:使用交叉编码器
# rerank_score = cross_encoder_model.predict([(query_text, doc_content)])
# 示例:与新鲜度结合(需要元数据)
# publish_date = candidate['metadata']['publish_date']
# recency_boost = calculate_recency_boost(publish_date)
# final_score = initial_score * 0.7 + rerank_score * 0.3 # 组合分数
# final_score = initial_score * recency_boost # 根据新鲜度提升
# 为简单起见,我们暂时只使用初始分数
final_score = initial_score
reranked_results.append({'id': doc_id, 'final_score': final_score, 'content_snippet': doc_content[:200]}) # 添加片段
# 按新的 final_score 排序(相似度分数降序)
reranked_results.sort(key=lambda x: x['final_score'], reverse=True)
return reranked_results
final_results = apply_reranking(query_text, search_results)
最后,准备好用于展示的排序结果列表。这通常包括:
在设计查询流程时,请考虑以下几点:
ef_search 或 nprobe 时)、获取完整文档以及复杂的重新排序都会造成延迟。监控端到端延迟并优化瓶颈。这个实用设计提供了一个蓝图。在下一章中,我们将了解如何使用特定的向量数据库客户端和库来实现这些步骤,构建一个功能性的语义搜索应用。
这部分内容有帮助吗?
© 2026 ApX Machine LearningAI伦理与透明度•