Elasticsearch:语义文本 - 更简单、更好、更精炼、更强大 8.18

作者:来自 Elastic Mike Pellegrini

我们最新的 semantic_text 迭代带来了大量改进。除了简化 _source 中的表示之外,其好处还包括减少冗长程度、更高效的磁盘利用率以及更好地与其他 Elasticsearch 功能集成。你现在可以使用突出显示来检索与你的查询最相关的块。也许最重要的是,它现在是一个正式发的功能!

我们在 [semantic_text](https://www.elastic.co/guide/en/elasticsearch/reference/current/semantic-text.html "semantic_text") 字段类型上经历了一段漫长的旅程,而这一最新版本承诺让语义搜索变得前所未有的简单。除了在 _source 中简化 semantic_text 的表示外,它还带来了减少冗余、更高效的磁盘利用率以及更好的 Elasticsearch 其他功能集成等优势。你现在可以使用高亮功能来检索与你查询最相关的文本片段。而最重要的是,它现已成为一个正式发布的功能!

语义搜索的演进

多年来,我们对语义搜索的方法不断发展,目标是让它尽可能简单。在 semantic_text 字段类型推出之前,执行语义搜索需要手动完成以下步骤:

  • 配置映射以兼容你的嵌入向量。
  • 配置一个摄取管道,以使用机器学习模型生成嵌入向量。
  • 使用该管道来摄取你的文档。
  • 在查询时使用相同的机器学习模型为查询文本生成嵌入向量。

当时,我们称这种设置为 "简单",但我们知道,我们可以让它变得更简单。于是,semantic_text 诞生了。

初始阶段

我们在 Elasticsearch 8.15 中引入了 semantic_text,目标是简化语义搜索。如果你对 semantic_text 还不熟悉,建议先阅读我们的原始博客文章,以了解我们的方法背景。

我们最初将 semantic_text 作为 Beta 功能发布,是有充分理由的。在软件开发中,有一句广为流传的真理:让某样东西变得简单,往往是极其困难的,semantic_text 也不例外。在它的背后,有许多复杂的组件协同工作,以实现这种 "魔法般" 的语义搜索体验。我们希望花时间确保这些组件正确无误,然后再将该功能正式推出。事实证明,这段时间的投入是值得的 ------ 我们在最初的方法上进行了多次迭代,增加了新功能,并优化了存储,使 semantic_text 变得更简单、更精炼,并且更具长期可维护性。

我们最初的实现依赖于修改 _source 来存储推理结果。这导致 semantic_text 字段具有相对复杂的子字段结构:

bash 复制代码
1.  GET test-index/_doc/doc1
2.  {
3.    "_index": "test-sparse",
4.    "_id": "doc1",
5.    "_source": {
6.      "infer_field": {
7.        "text": "these are not the droids you're looking for. He's free to go around",
8.        "inference": {
9.          "inference_id": "my-elser-endpoint",
10.          "model_settings": {
11.            "task_type": "sparse_embedding"
12.          },
13.          "chunks": [
14.            {
15.              "text": "these are not the droids you're looking for. He's free to go around",
16.              "embeddings": {
17.                "##oid": 1.9103845,
18.                "##oids": 1.768872,
19.                "free": 1.693662,
20.                "dr": 1.6103356,
21.                "around": 1.4376559,
22.                "these": 1.1396849

24.                ...
25.              }
26.            }
27.          ]
28.        }
29.      }
30.    }
31.  }

这种结构带来了一些问题:

  • 过于冗长。 除了原始值外,它还包含元数据和文本分片信息,使 API 响应既难以阅读,又比实际需要的更大。
  • 增加了磁盘上的索引大小。 由于嵌入向量可能非常大,它们实际上被存储了两次:一次用于 Lucene 索引以支持检索,另一次存储在 _source 中。这极大地影响了 semantic_text 在大规模数据集上的可扩展性。
  • 管理起来不够直观。 原始提供的文本值存储在 text 子字段中,因此需要特殊处理才能在后续操作中获取该值。这导致 semantic_text 字段的行为与文本字段族中的其他字段不一致,从而带来了一系列复杂的连锁反应,使得它更难集成到更高级的工作流中。

语义文本即文本

我们的改进版本优雅地解决了这些问题,通过优化 semantic_text_source 中的表示方式,使其更加简洁。与其在 semantic_text 字段内直接存储元数据和文本分片信息的复杂子字段结构,我们现在改用一个 隐藏的元字段(metafield) 来处理这些内容。这意味着,我们不再需要修改 _source 来存储推理结果。

从实际效果来看,这一改进意味着你提交用于索引的文档 _source,在检索时依然会以相同的 _source 返回,不会再被额外修改或扩展。

bash 复制代码
1.  GET test-index/_doc/doc1
2.  {
3.    "_index": "test-sparse",
4.    "_id": "doc1",
5.    "_source": {
6.      "infer_field": "these are not the droids you're looking for. He's free to go around"
7.    }
8.  }

结构简化带来的变化

你会注意到,_source 结构中不再包含 textinference 之类的子字段。现在,_source 保持与索引时提供的内容完全一致,变得更加简单!

🚨 注意 :如果你的代码依赖于解析 semantic_text 字段在搜索结果或 Get API 返回值中的子字段结构,这将是一个 破坏性变更 。也就是说,如果你以前解析的是 infer_field.text 子字段的值,你需要更新代码,使其改为解析 infer_field 的值。我们尽力避免破坏性变更,但由于 _source 结构调整而移除子字段结构,这次变更不可避免。

这一简化带来的主要优势:

  • 更易使用。 不再需要解析复杂的子字段结构来获取原始文本值,直接读取字段值即可。
  • 减少冗余。 元数据和文本分片信息不会再干扰 API 响应,使其更加简洁。
  • 提高磁盘利用率。 嵌入向量不再存储在 _source 中,减少了存储占用。
  • 更好的集成性。 使 semantic_text 更好地支持 Elasticsearch 其他功能,如多字段(multi-fields)、文档的部分更新(partial updates)以及重新索引(reindexing)。

让我们稍微扩展一下最后一点,因为它涵盖了几个领域。通过这种简化,semantic_text 字段现在可以用作 multi-fields 的源和目标

perl 复制代码
1.  PUT multi-field-source-example-index
2.  {
3.    "mappings": {
4.      "properties": {
5.        "inference": {
6.          "type": "semantic_text",
7.          "fields": {
8.            "text": {
9.              "type": "text"
10.             }
11.          }
12.        }
13.      }
14.    }
15.  }

17.  PUT multi-field-target-example-index
18.  {
19.    "mappings": {
20.      "properties": {
21.        "text": {
22.          "type": "text",
23.          "fields": {
24.            "inference": {
25.              "type": "semantic_text"
26.             }
27.          }
28.        }
29.      }
30.    }
31.  }

semantic_text 字段现在还支持通过 Bulk API 进行部分文档更新:

perl 复制代码
1.  PUT partial-update-example-index
2.  {
3.    "mappings": {
4.      "properties": {
5.        "inference": {
6.          "type": "semantic_text"
7.        },
8.        "source_field": {
9.          "type": "text",
10.          "copy_to": "inference"
11.        }
12.      }
13.    }
14.  }

16.  POST my-index/_doc/1
17.  {
18.    "inference": "a test value",
19.    "source_field": "another test value"
20.  }

22.  POST my-index/_bulk
23.  { "update": {"_id": "1"} }
24.  { "doc": {"source_field": "a different test value"} }

现在,你可以将数据重新索引到使用不同 inference_idsemantic_text 字段中。

markdown 复制代码
1.  PUT source-index
2.  {
3.    "mappings": {
4.      "properties": {
5.        "inference": {
6.          "type": "semantic_text",
7.          "inference_id": "my-elser-endpoint"
8.        }
9.      }
10.    }
11.  }

13.  PUT dest-index
14.  {
15.    "mappings": {
16.      "properties": {
17.        "inference": {
18.          "type": "semantic_text",
19.          "inference_id": "my-e5-endpoint"
20.        }
21.      }
22.    }
23.  }

25.  POST dest-index/_doc/1
26.  {
27.    "inference": "a test value"
28.  }

30.  POST _reindex
31.  {
32.    "source": {
33.      "index": "source-index"
34.    },
35.    "dest": {
36.      "index": "dest-index"
37.    }
38.  }

语义高亮(Semantic Highlighting)

semantic_text 最受欢迎的功能请求之一,就是能够在字段内检索最相关的文本片段。这一功能对于 RAG(检索增强生成)应用至关重要。此前,我们(非官方地)通过 inner_hits 提供了一些临时解决方案,但现在我们决定淘汰 inner_hits,转而采用更精简的方案:高亮(highlighting)

高亮是一种常见的 词法搜索技术 ,通常用于文本字段。由于 semantic_text 属于文本字段家族,因此将高亮技术适配到 semantic_text 是合理的。为此,我们新增了 语义高亮(semantic highlighter),它可以帮助你检索与查询最相关的文本片段:

bash 复制代码
1.  PUT highlighting-index
2.  {
3.    "mappings": {
4.      "properties": {
5.        "inference": {
6.          "type": "semantic_text"
7.        }
8.      }
9.    }
10.  }

12.  POST highlighting-index/_doc/1
13.  {
14.    "inference": ["Yosemite is one of the most popular national parks in the USA", "Park visitors should dispose of their trash properly in bear-proof trash bins to avoid attracting bears", "Bears are quite clever and curious though, so visitors should always be on the lookout for bear activity regardless"]
15.  }

17.  GET highlighting-index/_search
18.  {
19.    "query": {
20.      "semantic": {
21.        "field": "inference",
22.        "query": "Where should I throw away my trash?"
23.      }
24.    },
25.    "highlight": {
26.      "fields": {
27.        "inference": {
28.          "order": "score",
29.          "number_of_fragments": 1
30.        }
31.      }
32.    }
33.  }

35.  {
36.      "took": 6,
37.      "timed_out": false,
38.      "_shards": {
39.          "total": 1,
40.          "successful": 1,
41.          "skipped": 0,
42.          "failed": 0
43.      },
44.      "hits": {
45.          "total": {
46.              "value": 1,
47.              "relation": "eq"
48.          },
49.          "max_score": 15.898441,
50.          "hits": [
51.              {
52.                  "_index": "highlighting-index",
53.                  "_id": "1",
54.                  "_score": 15.898441,
55.                  "_source": {
56.                      "inference": [
57.                          "Yosemite is one of the most popular national parks in the USA",
58.                          "Park visitors should dispose of their trash properly in bear-proof trash bins to avoid attracting bears",
59.                          "Bears are quite clever and curious though, so visitors should always be on the lookout for bear activity regardless"
60.                      ]
61.                  },
62.                  "highlight": {
63.                      "inference": [
64.                          "Park visitors should dispose of their trash properly in bear-proof trash bins to avoid attracting bears"
65.                      ]
66.                  }
67.              }
68.          ]
69.      }
70.  }

73.  GET highlighting-index/_search
74.  {
75.    "query": {
76.      "semantic": {
77.        "field": "inference",
78.        "query": "Are bears smart?"
79.      }
80.    },
81.    "highlight": {
82.      "fields": {
83.        "inference": {
84.          "order": "score",
85.          "number_of_fragments": 1
86.        }
87.      }
88.    }
89.  }

91.  {
92.      "took": 20,
93.      "timed_out": false,
94.      "_shards": {
95.          "total": 1,
96.          "successful": 1,
97.          "skipped": 0,
98.          "failed": 0
99.      },
100.      "hits": {
101.          "total": {
102.              "value": 1,
103.              "relation": "eq"
104.          },
105.          "max_score": 18.584934,
106.          "hits": [
107.              {
108.                  "_index": "highlighting-index",
109.                  "_id": "1",
110.                  "_score": 18.584934,
111.                  "_source": {
112.                      "inference": [
113.                          "Yosemite is one of the most popular national parks in the USA",
114.                          "Park visitors should dispose of their trash properly in bear-proof trash bins to avoid attracting bears",
115.                          "Bears are quite clever and curious though, so visitors should always be on the lookout for bear activity regardless"
116.                      ]
117.                  },
118.                  "highlight": {
119.                      "inference": [
120.                          "Bears are quite clever and curious though, so visitors should always be on the lookout for bear activity regardless"
121.                      ]
122.                  }
123.              }
124.          ]
125.      }
126.  }

请参阅 semantic_text 文档,了解如何使用高亮功能的更多信息。

正式上线

随着 _source 表示方式的调整,我们现在正式宣布 semantic_text 已成为正式发布的功能 🎉!这意味着我们承诺不再对该功能进行破坏性更改,并正式支持在生产环境中使用。作为用户,你可以放心地将 semantic_text 集成到生产工作流中,Elastic 也将持续提供支持,确保长期稳定性。

从 Beta 迁移

为了确保从 Beta 版本的平稳迁移,所有在 Elasticsearch 8.15 至 8.17 版本创建的索引 ,或 在 1 月 30 日之前在 Serverless 中创建的索引 ,仍将按照 Beta _source 结构运行。换句话说,它们仍将使用 Beta 版本的 _source 表示方式。

我们建议尽早迁移到正式发布版本的 _source 结构。你可以通过 重新索引(reindexing)到新索引 来完成迁移:

markdown 复制代码
1.  PUT my-new-index
2.  {
3.    "mappings": {
4.      "properties": {
5.        "inference": {
6.          "type": "semantic_text"
7.        }
8.      }
9.    }
10.  }

12.  POST _reindex
13.  {
14.    "source": {
15.      "index": "my-old-index"
16.    },
17.    "dest": {
18.      "index": "my-new-index"
19.    },
20.    "script": {
21.      "source": "ctx._source.inference = ctx._source.inference.text"
22.    }
23.  }

请注意 script 参数的使用,它用于适配 _source 表示方式的更改。该脚本会从 text 子字段提取值,并直接赋给 semantic_text 字段。

立即尝试

这些更改将在 Elasticsearch 8.18+ (Stack 版)中提供,但如果你想现在就体验,它们已在 Serverless 版本中可用。此外,它们还与我们同期推出的 语义搜索简化 方案完美结合。使用这两者,可以让你的语义搜索能力更上一层楼!

你可以通过 自助式 Search AI 实践课程 亲自体验向量搜索。现在就 开启免费云试用 ,或在本地安装 Elastic 进行尝试!

原文:Semantic Text: Simpler, better, leaner, stronger - Elasticsearch Labs

相关推荐
童先生2 小时前
docker安装的es报错了?failed to obtain node locks怎么破~
elasticsearch·docker·jenkins
neo_Ggx234 小时前
ElasticSearch 入门到放弃(持续更新中)
elasticsearch·搜索引擎·wpf
小兔崽子去哪了4 小时前
初试 Elasticsearch
数据库·elasticsearch
007php0077 小时前
go语言zero框架拉取内部平台开发的sdk报错的修复与实践
大数据·elasticsearch·搜索引擎
SaebaRyo9 小时前
使用Nestjs + ElasticSearch实现电商搜索功能
后端·elasticsearch·nestjs
阿昊真人1 天前
ESP-IDF ubuntu版本 V5.2
linux·ubuntu·elasticsearch
勤不了一点1 天前
【ELK】ElasticSearch 集群常用管理API操作
linux·elk·elasticsearch
kngines1 天前
【实战ES】实战 Elasticsearch:快速上手与深度实践-5.3.1GeoPoint与GeoShape的选型
大数据·数据库·elasticsearch·搜索引擎
苍煜1 天前
Elasticsearch-07-Elasticsearch Java API Client-Elasticsearch 8.0 的高阶api
java·elasticsearch·es8·es client api