
我们在上一篇文章《Elasticsearch案例:百行代码实现腾讯ES帮助文档的RAG》中给大家介绍了如何通过一个完整的搜索解决方案来快速实现 RAG ,其重点落在效率上 —— 完整而便捷的解决方案套件,使我们整个RAG的构建和上线过程事半功倍。而本文,我们则将重点落在搜索效果上,如何适配各种情况(不同的用户搜索习惯以及可能的缺陷数据),并达到最优效果。
就像之前说的,真正的理解什么是RAG并不容易,实现RAG就更难。现状是大多数时候用户会简单地把实现RAG理解为在企业中加入一个向量数据库。但RAG是一个复杂的概念,它不仅仅是一个向量数据库,实现RAG需要对业务场景有深入的理解,并且需要进行大量的数据处理和算法优化,用户的行为的理解和反馈也是最终效果达成的重要关键。因此,我们需要的更多地是一个混合搜索解决方案,而非仅仅向量搜索。
我们知道,向量检索是一种基于向量空间模型的检索方法,它可以将文本转换为数学上的向量,然后通过计算向量之间的相似度,来实现文本的匹配和检索。向量检索的原理和流程大致如下:
一句话总结,就是向量搜索看起来很好,但是实现起来过于复杂,并且向量搜索要能实现准确的搜索,对于用户也有要求。
以我们在上篇文章提到的 腾讯ES帮助文档的RAG应用 作为例子。我们看看如果只使用向量搜索在某些场景中会获得什么样的反效果:
当我们知道腾讯云有提供特有的高性价比机型,星星海机型时,我们想知道腾讯云ES有没有采用这种机型。但用户又不想输入长长的一串句子时,如果我们只搜索 “星星海”,我们会看到向量搜索无法找到正确的结果:

这是因为向量检索是基于词向量的相似度计算,而词向量是通过大量的文本数据训练出来的,它们往往包含了一些语义和语境的信息。如果查询语句太短,比如只有一个ID、一个哈希码或者一个产品名称,那么它们的词向量可能无法反映出它们的真实含义,也无法和其他相关的文档进行有效的匹配。这样就会导致向量检索的结果不准确,甚至出现一些完全不相关的内容。
类似的,如果我们查询“8XLARGE64”,“99.9%”,这样的一些关键字时,向量搜索会得出一些毫不相干的内容,以至于让背后的大模型毫无用武之地,甚至可能被误导,而在这方面,全文检索则可以轻松胜任:

为了解决这个问题,我们可以采用一些方法,比如:
这些方法都可以在一定程度上改善向量检索在处理简短的查询语句时的问题,但是它们也有一些缺点,比如:
因此,我们需要一种更好的方法,来解决向量检索在处理简短的查询语句时的问题,这就是混合搜索的优势所在。混合搜索可以结合向量检索和关键词检索的优点,实现更快速、更精准、更多样的检索结果。在下一部分,我们将详细介绍混合搜索的原理和优势。
混合搜索是一种结合向量检索和关键词检索的检索方法,它可以利用向量检索的高效性和关键词检索的灵活性,实现更快速、更精准、更多样的检索结果。混合搜索的原理和优势如下:

要做好混合搜索,在项目评估的时候需要注意以下方面:
而使用 Elasticsearch,会给我们的用户提供更多的灵活性。通过在单一接口中,随时按需的使用全文检索,向量检索,混合检索,做到 “按量付费”,而在 Serverless 模式上,动态提供的计算资源配合动态的接口组合,将能更灵活的控制我们的成本。
比如,通过如下函数的定义,我们可以随时根据动态条件确定如何进行搜索,而无需进行代码的改动:
# Search ElasticSearch index and return body and URL of the result
def search(es, embedding_model, query_text, search_mode):
source_fields = ["body_content", "url", "title"]
query = None
knn = []
highlight = None
rank = None
if search_mode in ["全文检索","混合搜索"]:
query = {
"multi_match": {
"query": query_text,
"fields": ["body_content^2","title","headings"],
"boost": 1,
"type": "most_fields",
"analyzer": "ik_max_word"
}
}
if search_mode in ["密集向量搜索", "混合搜索"]:
knn = [{
"field": "ml.inference.headings_embeddings.predicted_value",
"query_vector_builder": {
"text_embedding": {
"model_id": embedding_model,
"model_text": query_text
}
},
"k": 5,
"num_candidates": 10,
"boost": 24
}, {
"field": "ml.inference.body_content_embeddings.predicted_value",
"query_vector_builder": {
"text_embedding": {
"model_id": embedding_model,
"model_text": query_text
}
},
"k": 5,
"num_candidates": 10,
"boost": 24
},
]
if search_mode == "全文检索":
highlight= {
"pre_tags": ["`"],
"post_tags": ["`"],
"fields": {
"body_content": {}
}
}
if search_mode == "混合搜索":
rank = {
"rrf":{
"window_size": 5,
"rank_constant": 2
}
}
resp = es.search(
index="search-tencent-es-doc",
fields=source_fields,
query=query,
knn=knn,
highlight = highlight,
rank = rank,
size=3,
source=False)
return resp
而当向量检索和关键词检索的结果出现不一致和冲突时,Elasticsearch也提供了多种手段方便我们对结果进行调试。目前,elasticsearch提供了比如:线性加权总和和基于结果倒数的融合排序(RRF)两种方式。这两种方式均可以在函数中方便修改,如上面提供的代码样例中:
"boost":1; knn: "boost": 24
rank ={"rrf":{"window_size":5,"rank_constant":2}}
当我们在使用的过程中发现全文检索的检索结果更重要的时候,我们可以适当的调整参数,以下是query:"boost":1
与query:"boost":24的区别:

当然,通过权重打分的调整并不是银弹。很多时候,因为相关性打分方式的不同,不同的搜索方式会产生区别很大的相关性分数,单一的权重很难照顾各种场景,因为提升了全文检索的权重,使得我们无法回答语义检索相关的问题:

因此,我们还提供一种无需根据相关性打分而进行结果有效融合和排序的方式 —— RRF。
在使用了RRF之后,结果不再包含相关性的得分,而是根据多路召回中文档的排名进行融合:

除了排序之外,过滤也是混合搜索或者向量检索中一个非常重要的能力,排除一些不符合条件的文档,既能够让我们的查询更高效,也能够让最终的结果更准确。而 Elasticsearch 相比其他的数据库,更容易实现这一点,具体原因参见《Elasticsearch 中的向量搜索:设计背后的基本原理》一文。
比如,通过定义一个非空字符串的过滤器(这里需要注意的是,Elasticsearch 的企业搜索功能,在创建索引的时候为每个重要的字段创建了各种调优所需的字段类型,使得我们能够在上面进行过滤,比如这里的 body_content.enum,就是应用自动创建的 keyword 类型):
body_content_filter = {
"bool": {
"must": [],
"filter": [],
"should": [],
"must_not": [
{
"bool": {
"minimum_should_match": 1,
"should": [
{
"match_phrase": {
"body_content.enum": ""
}
}
]
}
}
]
}
}我们可以将向量搜索时可能匹配上的异常文档(比如这里的body_content为空字符串的文档)给过滤掉:
if search_mode in ["密集向量搜索", "混合搜索"]:
knn = [{
"field": "ml.inference.headings_embeddings.predicted_value",
"query_vector_builder": {
"text_embedding": {
"model_id": embedding_model,
"model_text": query_text
}
},
"k": 5,
"num_candidates": 10,
"boost": 24,
"filter": body_content_filter
}, {
"field": "ml.inference.body_content_embeddings.predicted_value",
"query_vector_builder": {
"text_embedding": {
"model_id": embedding_model,
"model_text": query_text
}
},
"k": 5,
"num_candidates": 10,
"boost": 24,
"filter": body_content_filter
},
]
在拥有完善解决方案的前提下,构建 RAG 应用相对来说是容易的。但调试检索的相关性则更需要对搜索相关经验和能力的加持。值得庆幸的是,在这方面,Elasticsearch 仍然走在了最前面,通过提供最完善和最丰富的查询和排序的调优能力,以及可以广泛获取的社区支持,甚至是生成式大模型在公共数据集上对 Elasticsearch 能力的学习,我们可以非常方便地从各种渠道获得帮助,以对查询进行调优,这一点,对于我们的技术选型至关重要,也对最终项目的成败至关重要。
有时候,好的选择,大于用力。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。