作者:来自 Elastic Andre Luiz
了解如何使用 LLM 来自动识别和生成同义词, 使术语可以通过程序方式加载到 Elasticsearch 同义词 API 中。
提高搜索结果的质量对于提供高效的用户体验至关重要。优化搜索的一种方法是通过同义词自动扩展查询词。这样可以更广泛地解释查询内容,覆盖语言变体,从而改进结果匹配。
本博客探讨了如何利用 大语言模型(LLMs) 自动识别和生成同义词,使这些术语可以通过程序方式加载到 Elasticsearch 的 synonym API 中。
何时使用同义词?
使用 同义词 相较于 向量搜索 是一种更快速且更具成本效益的解决方案。它的实现更简单,因为它不需要深入了解 embeddings 或复杂的向量摄取过程。
此外,同义词的 资源消耗更低 ,因为向量搜索需要更大的存储空间和内存来进行 embedding 索引和检索。
另一个重要方面是 搜索的区域化 。通过 同义词 ,可以根据本地语言和习惯调整术语,这在 embeddings 可能无法匹配地区表达或特定国家术语的情况下非常有用。例如,一些单词或缩写在不同地区可能有不同的含义,但本地用户通常会将它们视为同义词:
-
在 巴西 ,"abacaxi" 和 "ananás" 都指 菠萝(pineapple),但在东北部,第二个词更常见。
-
在巴西东南部,常见的 "pão francês"(法式面包)在东北部可能被称为 "pão careca"。
这种 区域化 的差异使得 同义词 在搜索优化中发挥关键作用。
如何使用 LLMs 生成同义词?
为了自动获取同义词, 你可以使用 LLMs, 这些模型可以分析术语的上下文并建议合适的变体。 这种方法可以动态扩展同义词, 确保更广泛和更精准的搜索, 而无需依赖固定的词典。
在本示例中, 我们将使用 LLM 来为 电商产品 生成同义词。 许多搜索由于查询词的不同变体而返回较少甚至没有结果。 通过 同义词 , 我们可以解决这个问题。 例如, 搜索 "smartphone" 时, 结果可以涵盖不同型号的手机, 确保用户能够找到他们需要的产品。
先决条件
在开始之前, 需要设置环境 并定义所需的依赖项 。 我们将使用 Elastic 提供的解决方案 在 Docker 中本地运行 Elasticsearch 和 Kibana。 代码将使用 Python v3.9.6 编写,并需要以下依赖项:
ini
`pip install openai==1.59.8 elasticsearch==8.15.1`AI写代码
创建产品索引
首先, 我们将创建一个不支持同义词的产品索引。 这将使我们能够验证查询, 然后将其与包含同义词的索引进行比较。
要创建索引, 我们使用以下命令在 Kibana DevTools 中批量加载产品数据集:
json
`
1. POST _bulk
2. {"index": {"_index": "products", "_id": 10001}}
3. {"category": "Electronics", "name": "iPhone 14 Pro"}
4. {"index": {"_index": "products", "_id": 10007}}
5. {"category": "Electronics", "name": "MacBook Pro 16-inch"}
6. {"index": {"_index": "products", "_id": 10013}}
7. {"category": "Electronics", "name": "Samsung Galaxy Tab S8"}
8. {"index": {"_index": "products", "_id": 10037}}
9. {"category": "Electronics", "name": "Apple Watch Series 8"}
10. {"index": {"_index": "products", "_id": 10049}}
11. {"category": "Electronics", "name": "Kindle Paperwhite"}
12. {"index": {"_index": "products", "_id": 10067}}
13. {"category": "Electronics", "name": "Samsung QLED 4K TV"}
14. {"index": {"_index": "products", "_id": 10073}}
15. {"category": "Electronics", "name": "HP Spectre x360 Laptop"}
16. {"index": {"_index": "products", "_id": 10079}}
17. {"category": "Electronics", "name": "Apple AirPods Pro"}
18. {"index": {"_index": "products", "_id": 10115}}
19. {"category": "Electronics", "name": "Amazon Echo Show 10"}
20. {"index": {"_index": "products", "_id": 10121}}
21. {"category": "Electronics", "name": "Apple iPad Air"}
22. {"index": {"_index": "products", "_id": 10127}}
23. {"category": "Electronics", "name": "Apple AirPods Max"}
24. {"index": {"_index": "products", "_id": 10151}}
25. {"category": "Electronics", "name": "Sony WH-1000XM4 Headphones"}
26. {"index": {"_index": "products", "_id": 10157}}
27. {"category": "Electronics", "name": "Google Pixel 6 Pro"}
28. {"index": {"_index": "products", "_id": 10163}}
29. {"category": "Electronics", "name": "Apple MacBook Air"}
30. {"index": {"_index": "products", "_id": 10181}}
31. {"category": "Electronics", "name": "Google Pixelbook Go"}
32. {"index": {"_index": "products", "_id": 10187}}
33. {"category": "Electronics", "name": "Sonos Beam Soundbar"}
34. {"index": {"_index": "products", "_id": 10199}}
35. {"category": "Electronics", "name": "Apple TV 4K"}
36. {"index": {"_index": "products", "_id": 10205}}
37. {"category": "Electronics", "name": "Samsung Galaxy Watch 4"}
38. {"index": {"_index": "products", "_id": 10211}}
39. {"category": "Electronics", "name": "Apple MacBook Pro 16-inch"}
40. {"index": {"_index": "products", "_id": 10223}}
41. {"category": "Electronics", "name": "Amazon Echo Dot (4th Gen)"}
`AI写代码
使用 LLM 生成同义词
在此步骤中, 我们将使用 LLM 动态生成同义词。 为此, 我们将集成 OpenAI API , 定义合适的模型和提示词。 LLM 将接收产品类别和名称, 确保生成的同义词在上下文中具有相关性。
python
`
1. import json
2. import logging
4. from openai import OpenAI
6. def call_gpt(prompt, model):
7. try:
8. logging.info("generate synonyms by llm...")
9. response = client.chat.completions.create(
10. model=model,
11. messages=[{"role": "user", "content": prompt}],
12. temperature=0.7,
13. max_tokens=1000
14. )
15. content = response.choices[0].message.content.strip()
16. return content
17. except Exception as e:
18. logging.error(f"Failed to use model: {e}")
19. return None
21. def generate_synonyms(category, products):
22. synonyms = {}
24. for product in products:
25. prompt = f"You are an expert in generating synonyms for products. Based on the category and product name provided, generate synonyms or related terms. Follow these rules:\n"
26. prompt += "1. **Format**: The first word should be the main item (part of the product name, excluding the brand), followed by up to 3 synonyms separated by commas.\n"
27. prompt += "2. **Exclude the brand**: Do not include the brand name in the synonyms.\n"
28. prompt += "3. **Maximum synonyms**: Generate a maximum of 3 synonyms per product.\n\n"
29. prompt += f"The category is: **{category}**, and the product is: **{product}**. Return only the synonyms in the requested format, without additional explanations."
31. response = call_gpt(prompt, "gpt-4o")
32. synonyms[product] = response
34. return synonyms
`AI写代码
从创建的产品索引中, 我们将检索 "Electronics"(电子产品) 类别中的所有商品, 并将它们的名称发送到 LLM。 预期的输出将类似于:
json
`
1. {
2. "iPhone 14 Pro": ["iPhone", "smartphone", "mobile", "handset"],
3. "MacBook Pro 16-inch": ["MacBook", "Laptop", "Notebook", "Ultrabook"],
4. "Samsung Galaxy Tab S8": ["Tab", "Tablet", "Slate", "Pad"],
5. "Bose QuietComfort 35 Headphones": ["Headphones", "earphones", "earbuds", "headset"]
6. }
`AI写代码
使用生成的同义词, 我们可以通过 Synonyms API 将它们注册到 Elasticsearch 中。
使用 Synonyms API 管理同义词
Synonyms API 提供了一种有效的方式, 让我们直接在系统中管理同义词集合。 每个同义词集合由同义词规则组成, 在这些规则中,一组词在搜索中被视为等同。
创建同义词集合的示例:
markdown
`
1. PUT _synonyms/my-synonyms-set
2. {
3. "synonyms_set": [
4. {
5. "id": "rule-1",
6. "synonyms": "hello, hi"
7. },
8. {
9. "synonyms": "bye, goodbye"
10. }
11. ]
12. }
`AI写代码
这将创建一个名为 "my-synonyms-set" 的同义词集,其中将 "hello" 和" hi" 视为同义词,"bye" 和 "goodbye" 也视为同义词。
实现产品目录的同义词创建
以下是负责构建同义词集并将其插入 Elasticsearch 的方法。 同义词规则是基于 LLM 提供的同义词映射生成的。 每条规则都有一个 ID,对应于产品名称的 slug 格式,以及由 LLM 计算得出的同义词列表。
python
`
1. import json
2. import logging
4. from elasticsearch import Elasticsearch
5. from slugify import slugify
7. es = Elasticsearch(
8. "http://localhost:9200",
9. api_key="your_api_key"
10. )
12. def mount_synonyms(results):
13. synonyms_set = [{"id": slugify(product), "synonyms": synonyms} for product, synonyms in
14. results.items()]
16. try:
17. response = es.synonyms.put_synonym(id="products-synonyms-set",
18. synonyms_set=synonyms_set)
20. logging.info(json.dumps(response.body, indent=4))
21. return response.body
22. except Exception as e:
23. logging.error(f"Error create synonyms: {str(e)}")
24. return None
`AI写代码
以下是创建同义词集的请求负载:
json
`
1. {
2. "synonyms_set":[
3. {
4. "id": "iphone-14-pro",
5. "synonyms": "iPhone, smartphone, mobile, handset"
6. },
7. {
8. "id": "macbook-pro-16-inch",
9. "synonyms": "MacBook, Laptop, Notebook, Computer"
10. },
11. {
12. "id": "samsung-galaxy-tab-s8",
13. "synonyms": "Tablet, Slate, Pad, Device"
14. },
15. {
16. "id": "garmin-forerunner-945",
17. "synonyms": "Forerunner, smartwatch, fitness watch, GPS watch"
18. },
19. {
20. "id": "bose-quietcomfort-35-headphones",
21. "synonyms": "Headphones, Earphones, Headset, Cans"
22. }
23. ]
24. }
`AI写代码
在集群中创建了同义词集后,我们可以进入下一步,即使用定义的同义词集创建一个带有同义词支持的新索引。
下面是使用 LLM 生成的同义词和通过 Synonyms API 定义的同义词集创建的完整 Python 代码:
ini
`
1. import json
2. import logging
4. from elasticsearch import Elasticsearch
5. from openai import OpenAI
6. from slugify import slugify
8. logging.basicConfig(level=logging.INFO)
10. client = OpenAI(
11. api_key="your-key",
12. )
14. es = Elasticsearch(
15. "http://localhost:9200",
16. api_key="your_api_key"
17. )
20. def call_gpt(prompt, model):
21. try:
22. logging.info("generate synonyms by llm...")
23. response = client.chat.completions.create(
24. model=model,
25. messages=[{"role": "user", "content": prompt}],
26. temperature=0.7,
27. max_tokens=1000
28. )
29. content = response.choices[0].message.content.strip()
30. return content
31. except Exception as e:
32. logging.error(f"Failed to use model: {e}")
33. return None
36. def generate_synonyms(category, products):
37. synonyms = {}
39. for product in products:
40. prompt = f"You are an expert in generating synonyms for products. Based on the category and product name provided, generate synonyms or related terms. Follow these rules:\n"
41. prompt += "1. **Format**: The first word should be the main item (part of the product name, excluding the brand), followed by up to 3 synonyms separated by commas.\n"
42. prompt += "2. **Exclude the brand**: Do not include the brand name in the synonyms.\n"
43. prompt += "3. **Maximum synonyms**: Generate a maximum of 3 synonyms per product.\n\n"
44. prompt += f"The category is: **{category}**, and the product is: **{product}**. Return only the synonyms in the requested format, without additional explanations."
46. response = call_gpt(prompt, "gpt-4o")
47. synonyms[product] = response
49. return synonyms
52. def get_products(category):
53. query = {
54. "size": 50,
55. "_source": ["name"],
56. "query": {
57. "bool": {
58. "filter": [
59. {
60. "term": {
61. "category.keyword": category
62. }
63. }
64. ]
65. }
66. }
67. }
68. response = es.search(index="products", body=query)
70. if response["hits"]["total"]["value"] > 0:
71. product_names = [hit["_source"]["name"] for hit in response["hits"]["hits"]]
72. return product_names
73. else:
74. return []
77. def mount_synonyms(results):
78. synonyms_set = [{"id": slugify(product), "synonyms": synonyms} for product, synonyms in
79. results.items()]
81. try:
82. es_client = get_client_es()
83. response = es_client.synonyms.put_synonym(id="products-synonyms-set",
84. synonyms_set=synonyms_set)
86. logging.info(json.dumps(response.body, indent=4))
87. return response.body
88. except Exception as e:
89. logging.error(f"Erro update synonyms: {str(e)}")
90. return None
93. if __name__ == '__main__':
94. category = "Electronics"
95. products = get_products("Electronics")
96. llm_synonyms = generate_synonyms(category, products)
97. mount_synonyms(llm_synonyms)
`AI写代码
创建支持同义词的索引
将创建一个新索引,其中所有来自 products 索引的数据将被重新索引。该索引将使用 synonyms_filter ,该过滤器应用之前创建的 products-synonyms-set。
下面是配置为使用同义词的索引映射:
json
`
1. PUT products_02
2. {
3. "settings": {
4. "analysis": {
5. "filter": {
6. "synonyms_filter": {
7. "type": "synonym",
8. "synonyms_set": "products-synonyms-set",
9. "updateable": true
10. }
11. },
12. "analyzer": {
13. "synonyms_analyzer": {
14. "type": "custom",
15. "tokenizer": "standard",
16. "filter": [
17. "lowercase",
18. "synonyms_filter"
19. ]
20. }
21. }
22. }
23. },
24. "mappings": {
25. "properties": {
26. "ID": {
27. "type": "long"
28. },
29. "category": {
30. "type": "keyword"
31. },
32. "name": {
33. "type": "text",
34. "analyzer": "standard",
35. "search_analyzer": "synonyms_analyzer"
36. }
37. }
38. }
39. }
`AI写代码
重新索引 products 索引
现在,我们将使用 Reindex API 将数据从 products 索引迁移到新的 products_02 索引,该索引包括同义词支持。以下代码在 Kibana DevTools 中执行:
json
`
1. POST _reindex
2. {
3. "source": {
4. "index": "products"
5. },
6. "dest": {
7. "index": "products_02"
8. }
9. }
`AI写代码
迁移后,products_02 索引将被填充并准备好验证使用配置的同义词集的搜索。
使用同义词验证搜索
让我们比较两个索引之间的搜索结果。我们将在两个索引上执行相同的查询,并验证是否使用同义词来检索结果。
在 products 索引中搜索(没有同义词)
我们将使用 Kibana 执行搜索并分析结果。在 Analytics > Discovery 菜单中,我们将创建一个数据视图来可视化我们创建的索引中的数据。
在 Discovery 中,点击 Data View 并定义名称和索引模式。对于 "products" 索引,我们将使用 "products" 模式。然后,我们将重复这个过程,为 "products_02" 索引创建一个新的数据视图,使用 "products_02" 模式。
配置好数据视图后,我们可以返回 Analytics > Discovery 并开始验证。
在这里,选择 DataView products 并搜索术语 "tablet" 后,我们没有得到任何结果,尽管我们知道有像 "Kindle Paperwhite" 和 "Apple iPad Air" 这样的产品。
在 products_02 索引中搜索(支持同义词)
在支持同义词的 "products_synonyms" 数据视图上执行相同的查询时,产品成功地被检索到了。这证明了配置的同义词集正常工作,确保搜索词的不同变体返回预期的结果。
我们可以通过直接在 Kibana DevTools 中运行相同的查询来实现相同的结果。只需使用 Elasticsearch Search API 搜索 products_02 索引:
结论
在 Elasticsearch 中实现同义词提高了产品目录搜索的准确性和覆盖面。关键的区别在于使用了 LLM,它自动且上下文相关地生成同义词,消除了预定义列表的需求。该模型分析了产品名称和类别,确保了与电子商务相关的同义词。
此外,Synonyms API 简化了词典管理,允许动态修改同义词集。通过这种方法,搜索变得更加灵活,能够适应不同的用户查询模式。
这一过程可以通过新数据和模型调整不断改进,确保越来越高效的研究体验。
参考资料
在本地运行 Elasticsearch
Run Elasticsearch locally | Elasticsearch Guide [8.17] | Elastic
Synonyms API
Synonyms APIs | Elasticsearch Guide [8.17] | Elastic
想获得 Elastic 认证?了解下次 Elasticsearch 工程师培训的时间!
Elasticsearch 拥有丰富的新功能,帮助你为使用案例构建最佳搜索解决方案。深入了解我们的示例笔记本,了解更多内容,开始免费云试用,或立即在本地机器上尝试 Elastic。
原文:How to automate synonyms and upload using our Synonyms API - Elasticsearch Labs