2026/4/1 23:56:56
网站建设
项目流程
个人自建网站,网站可做哪些服务,去水印小程序搭建,网络系统部作者#xff1a;来自 Elastic Ioana Tagirta 亲身体验 Elasticsearch#xff1a;深入了解我们的示例 notebooks#xff0c;开始免费的 cloud 试用#xff0c;或立即在本地机器上试用 Elastic。 在 Elasticsearch 9.2 中#xff0c;我们引入了在 Elasticsearch Query Langu…作者来自 Elastic Ioana Tagirta亲身体验 Elasticsearch深入了解我们的示例 notebooks开始免费的 cloud 试用或立即在本地机器上试用 Elastic。在 Elasticsearch 9.2 中我们引入了在 Elasticsearch Query Language ES|QL 中进行密集 vector search 和 hybrid search 的能力。这延续了我们将 ES|QL 打造成解决现代 search 用例的最佳 search 语言的投入。多阶段检索现代搜索的挑战现代搜索已经不再只是简单的关键词匹配。今天的搜索应用需要理解意图处理自然语言并结合多个排序信号来提供最佳结果。最相关结果的检索是在多个阶段中完成的每个阶段都会逐步细化结果集。过去并非如此当时大多数用例只需要一到两个阶段的检索一次初始查询来获取结果以及一个可能的重新评分阶段。我们从初始检索开始在这一阶段我们广泛搜索以收集与我们查询相关的结果。由于需要筛选所有数据我们应该使用能够快速返回结果的技术即使在索引数十亿文档时也能高效。因此我们采用可靠的技术例如 Elasticsearch 从一开始就支持并优化的词汇搜索或者 Elasticsearch 在速度和准确性上表现出色的 vector search。使用 BM25 的 lexical search 非常快最适合精确的术语匹配或短语匹配而 vector 或 semantic search 更适合处理自然语言查询。Hybrid search 将 lexical 和 vector search 结果结合起来以发挥两者的优势。Hybrid search 解决的挑战在于 vector 和 lexical search 拥有完全不同且不兼容的评分函数它们生成的值在不同区间并遵循不同分布。vector search 得分接近 1 可能意味着非常匹配但 lexical search 并非如此。Hybrid search 方法如 reciprocal rank fusion (RRF) 和 scores 的线性组合会分配新的分数将 lexical 和 vector search 的原始分数融合。在 hybrid search 之后我们可以使用 semantic reranking 和 Learning To Rank (LTR) 等技术这些技术使用专门的 machine learning 模型对结果进行重新排序。利用最相关的结果我们可以使用 large language models (LLMs) 进一步丰富我们的响应或在 Elastic Agent Builder 等工具的 agentic 工作流中将最相关结果作为上下文传递给 LLMs。ES|QL 能够处理检索的所有这些阶段。ES|QL 本身是一个管道语言每条命令会转换输入并将输出发送到下一条命令。每个检索阶段由一个或多个连续的 ES|QL 命令表示。本文展示了 ES|QL 如何支持每个阶段。Vector search - 向量搜索在 Elasticsearch 9.2 中我们在 ES|QL 中引入了密集 vector search 的技术预览支持。这和调用 knn 函数一样简单只需要一个 dense_vector 字段和一个 query vectorFROM books METADATA _score | WHERE KNN(description_vector, ?query_vector) | SORT _score DESC | LIMIT 100此查询执行近似最近邻搜索检索与 query_vector 最相似的 100 个文档。混合搜索Reciprocal rank fusion/RRF在 Elasticsearch 9.2 中我们在 ES|QL 中引入了使用 RRF 和结果线性组合的 hybrid search 支持。这允许将 vector search 和 lexical search 结果合并为单一的结果集。要在 ES|QL 中实现这一点我们需要使用 FORK 和 FUSE 命令。FORK可以运行多个执行分支FUSE则合并结果并使用 RRF 或线性组合分配新的相关性分数。在下面的示例中我们使用 FORK 运行两个独立的分支其中一个使用 match 函数进行 lexical search另一个使用 knn 函数进行 vector search。然后我们使用 FUSE 将结果合并FROM books METADATA _score, _id, _index | FORK (WHERE KNN(description_vector, ?query_vector) | SORT _score DESC | LIMIT 100) (WHERE MATCH(description, ?query) | SORT _score DESC | LIMIT 100) | FUSE // uses RRF by default | SORT _score DESC让我们分解查询以更好地理解执行模型首先来看 FORK 命令的输出FROM books METADATA _score, _id, _index | FORK (WHERE KNN(description_vector, ?query_vector) | SORT _score DESC | LIMIT 100) (WHERE MATCH(description, ?query) | SORT _score DESC | LIMIT 100)FORK 命令输出来自两个分支的结果并添加了一个 _fork 鉴别器列_idtitle_score_fork4001The Hobbit0.88fork13999The Fellowship of the Ring0.88fork14005The Two Towers0.86fork14006The Return of the King0.84fork14123The Silmarillion0.78fork14144The Children of Húrin0.79fork14001The Hobbit4.55fork23999The Fellowship of the Ring4.25fork24123The Silmarillion4.11fork24005The Two Towers3.8fork24006The Return of the King4.1fork2正如你会注意到的某些文档会出现两次这就是我们随后使用 FUSE 来合并表示相同文档的行并分配新的相关性分数的原因。FUSE 分两阶段执行对每一行FUSE 根据所使用的 hybrid search 算法分配新的相关性分数。表示相同文档的行会被合并并计算新的分数。在我们的示例中我们使用 RRF。第一步FUSE 使用 RRF 公式为每一行分配新的分数score(doc) 1 / (rank_constant rank(doc))其中 rank_constant 的默认值为 60rank(doc) 表示文档在结果集中的位置。在第一阶段我们的结果变为_idtitle_score_fork4001The Hobbit1 / (60 1) 0.01639fork13999The Fellowship of the Ring1 / (60 2) 0.01613fork14005The Two Towers1 / (60 3) 0.01587fork14006The Return of the King1 / (60 4) 0.01563fork14123The Silmarillion1 / (60 5) 0.01538fork14144The Children of Húrin1 / (60 6) 0.01515fork14001The Hobbit1 / (60 1) 0.01639fork23999The Fellowship of the Ring1 / (60 2) 0.01613fork24123The Silmarillion1 / (60 3) 0.01587fork24005The Two Towers1 / (60 4) 0.01563fork24006The Return of the King1 / (60 5) 0.01538fork2然后这些行会被合并并分配新的分数。由于 FUSE 命令后跟 SORT _score DESC最终结果为_idtitle_score4001The Hobbit0.01639 0.01639 0.032793999The Fellowship of the Ring0.01613 0.01613 0.032264005The Two Towers0.01587 0.01563 0.03154123The Silmarillion0.01538 0.01587 0.031254006The Return of the King0.01563 0.01538 0.031014144The Children of Húrin0.01515混合搜索scores 的线性组合Reciprocal rank fusion 是执行 hybrid search 最简单的方法但它不是我们在 ES|QL 中支持的唯一 hybrid search 方法。在下面的示例中我们使用 FUSE 通过 scores 的线性组合来合并 lexical 和 semantic search 结果FROM books METADATA _score, _id, _index | FORK (WHERE MATCH(semantic_description, ?query) | SORT _score DESC | LIMIT 100) (WHERE MATCH(description, ?query) | SORT _score DESC | LIMIT 100) | FUSE LINEAR WITH { weights: { fork1: 0.7, fork2: 0.3 } } | SORT _score DESC让我们先分解查询并查看当只运行 FORK 命令时 FUSE 命令的输入。注意我们使用 match 函数它不仅可以查询 lexical 字段如 text 或 keyword还可以查询 semantic_text 字段。第一个 FORK 分支通过查询 semantic_text 字段执行 semantic query而第二个分支执行 lexical queryFROM books METADATA _score, _id, _index | FORK (WHERE MATCH(semantic_description, ?query) | SORT _score DESC | LIMIT 100) (WHERE MATCH(description, ?query) | SORT _score DESC | LIMIT 100)FORK 命令的输出可能包含具有相同 _id 和 _index 值的行这些行表示同一个 Elasticsearch 文档_idtitle_score_fork4001The Hobbit0.88fork13999The Fellowship of the Ring0.88fork14005The Two Towers0.86fork14006The Return of the King0.84fork14123The Silmarillion0.78fork14144The Children of Húrin0.79fork14001The Hobbit4.55fork23999The Fellowship of the Ring4.25fork24123The Silmarillion4.11fork24005The Two Towers3.8fork24006The Return of the King4.1fork2在下一步我们使用 FUSE 合并具有相同 _id 和 _index 值的行并分配新的相关性分数。新的分数是该行在每个 FORK 分支中的分数的线性组合_score 0.7 *_score1 0.3 * _score2这里_score1 和 _score2 分别表示文档在第一个 FORK 分支和第二个 FORK 分支中的分数。注意我们还应用了自定义权重对 semantic score 给予比 lexical score 更高的权重得到这一组文档_idtitle_score4001The Hobbit0.7 * 0.88 0.3 * 4.55 1.9813999The Fellowship of the Ring0.7 * 0.88 0.3 * 4.25 1.8914006The Return of the King0.7 * 0.84 0.3 * 4.1 1.8184123The Silmarillion0.7 * 0.78 0.3 * 4.11 1.7794005The Two Towers0.7 * 0.86 0.3 * 3.8 1.7424144The Children of Húrin0.7 * 0.79 0.3 * 0 0.553一个挑战是 semantic score 和 lexical score 可能不兼容直接进行线性组合因为它们可能遵循完全不同的分布。为缓解这一问题我们首先需要对分数进行归一化使用 score normalization 方法如 minmax。这样可以确保每个 FORK 分支的分数在应用线性组合公式前先被归一化到 0 到 1 之间的值。要使用 FUSE 实现这一点我们需要指定 normalizer 选项FROM books METADATA _score, _id, _index | FORK (WHERE MATCH(semantic_description, ?query) | SORT _score DESC | LIMIT 100) (WHERE MATCH(description, ?query) | SORT _score DESC | LIMIT 100) | FUSE LINEAR WITH { weights: { fork1: 0.7, fork2: 0.3 }, normalizer: minmax } | SORT _score DESCSemantic reranking在这一阶段经过 hybrid search 后我们应该只剩下最相关的文档。我们现在可以使用 semantic reranking 通过 RERANK 命令重新排序结果。默认情况下RERANK 使用最新的 Elastic semantic reranking 机器学习模型因此无需额外配置FROM books METADATA _score, _id, _index | FORK (WHERE KNN(description_vector, ?query_vector) | SORT _score DESC | LIMIT 100) (WHERE MATCH(description, ?query) | SORT _score DESC | LIMIT 100) | FUSE | SORT _score DESC | LIMIT 100 | RERANK ?query ON description | SORT _score DESC我们现在得到了按相关性排序的最佳结果。RERANK 命令区别于其他提供 semantic reranking 集成的产品的一个关键特性是它不要求输入必须是索引中的映射字段。RERANK 只需要一个能求值为字符串的表达式因此可以使用多个字段进行 semantic rerankingFROM books METADATA _score, _id, _index | FORK (WHERE KNN(description_vector, ?query_vector) | SORT _score DESC | LIMIT 100) (WHERE MATCH(description, ?query) | SORT _score DESC | LIMIT 100) | FUSE | SORT _score DESC | LIMIT 100 | RERANK ?query ON CONCAT(title, \n, description) | SORT _score DESCLLM completions现在我们有了一组高度相关、重新排序的结果。在这一阶段你可以选择直接将结果返回给你的应用也可以使用 LLM completions 进一步增强结果。如果你在 retrieval-augmented generation (RAG) 工作流中使用 ES|QL你可以选择直接从 ES|QL 调用你喜欢的 LLM。为此我们新增了 COMPLETION 命令它接受一个 prompt、一个 completion inference ID指定调用哪个 LLM以及一个列标识符指定 LLM 响应输出到哪一列。在下面的示例中我们使用 COMPLETION 添加了一个新的 _completion 列其中包含 content 列的摘要FROM books METADATA _score, _id, _index | FORK (WHERE KNN(description_vector, ?query_vector) | SORT _score DESC | LIMIT 100) (WHERE MATCH(description, ?query) | SORT _score DESC | LIMIT 100) | FUSE | SORT _score DESC | LIMIT 100 | RERANK ?query ON description | SORT _score DESC | LIMIT 10 | COMPLETION CONCAT(Summarize the following:\n, description) WITH { inference_id : my_inference_endpoint }每一行现在都包含一个摘要_idtitle_scoresummary4001The Hobbit0.03279Bilbo helps dwarves reclaim Erebor from the dragon Smaug.3999The Fellowship of the Ring0.03226Frodo begins the quest to destroy the One Ring.4005The Two Towers0.0315The Fellowship splits; war comes to Rohan; Frodo nears Mordor.4123The Silmarillion0.03125Ancient myths and history of Middle-earths First Age.4006The Return of the King0.3101Sauron is defeated and Aragorn is crowned King.4144The Children of Húrin0.01515The tragic tale of Túrin Turambars cursed life.在另一种用例中你可能只是想使用自己在 Elasticsearch 中索引的专有数据来回答问题。在这种情况下我们在前一阶段计算出的最佳搜索结果可以作为 prompt 的上下文FROM books METADATA _score, _id, _index | FORK (WHERE KNN(description_vector, ?query_vector) | SORT _score DESC | LIMIT 100) (WHERE MATCH(description, ?query) | SORT _score DESC | LIMIT 100) | FUSE | SORT _score DESC | LIMIT 100 | RERANK ?query ON description | SORT _score DESC | LIMIT 10 | STATS context VALUES(CONCAT(title, \n, description) | COMPLETION CONCAT(Answer the following question , ?query, based on:\n, context) WITH { inference_id : my_inference_endpoint }由于 COMPLETION 命令解锁了向 LLM 发送任意 prompt 的能力可能性是无穷的。虽然我们只展示了一些示例但 COMPLETION 命令可以用于各种场景从安全分析师根据日志事件是否可能表示恶意行为来分配分数到数据科学家用它分析数据甚至到仅仅根据你的数据生成 Chuck Norris 事实的情况。这只是开始未来我们将扩展 ES|QL以改进长文档的 semantic reranking更好地使用多个 FORK 命令进行 ES|QL 查询的条件执行支持 sparse vector 查询去除近似重复结果以提高结果多样性允许对运行时生成的列进行全文搜索以及更多其他场景。更多教程和指南ES|QL for searchES|QL for search tutorialSemantic_text field typeFORK and FUSE 文档ES|QL search functions原文https://www.elastic.co/search-labs/blog/hybrid-search-multi-stage-retrieval-esql