空投资讯追踪

为了提供高效的用户体验,提升搜索结果的质量至关重要。优化搜索的一种方法是通过同义词自动扩展查询词。这种方式可以更广泛地解释查询,涵盖语言变化,从而提高结果匹配的准确性。

本文探讨了如何使用大语言模型(LLM)自动识别和生成同义词,并将这些词以编程方式加载到Elasticsearch的同义词API中。

什么时候使用同义词?使用同义词可以成为比向量搜索更快捷和经济高效的解决方案。其实现更为简单,因为不需要深入了解嵌入或复杂的向量摄取过程。此外,资源消耗也较低,因为向量搜索需要更大的存储容量和内存来进行嵌入索引和检索。

另一个重要方面是搜索的区域化。通过同义词,可以根据当地语言和习惯调整术语。这在嵌入可能无法匹配区域表达或特定国家术语的情况下非常有用。例如,一些词或缩写可能在不同地区有不同的含义,但对当地用户来说自然被视为同义词。在巴西,这种情况很常见。"Abacaxi" 和 "ananás" 都是菠萝,但第二个词在东北的一些地区更常使用。同样,东南地区著名的“pão francês”在东北可能被称为“pão careca”。

如何使用LLM生成同义词?为了自动获取同义词,我们可以使用LLM,这些模型通过分析术语的上下文来建议合适的变体。这种方法允许动态扩展同义词,确保更广泛和准确的搜索,而不依赖固定词典。

在这个演示中,我们将使用LLM为电商产品生成同义词。许多搜索由于查询词的变化而返回很少甚至没有结果。通过同义词,我们可以解决这个问题。例如,搜索“智能手机”可以涵盖不同型号的手机,确保用户找到他们想要的产品。

预备条件开始之前,我们需要设置环境并定义所需的依赖项。我们将使用Elastic提供的解决方案在Docker中本地运行Elasticsearch和Kibana。代码将用Python v3.9.6编写,并需以下依赖:

代码语言:bash复制pip install openai==1.59.8 elasticsearch==8.15.1创建产品索引最初,我们将创建一个不支持同义词的产品索引。这将允许我们验证查询,然后与包含同义词的索引进行比较。

在Kibana DevTools中使用以下命令批量加载产品数据集来创建索引:

代码语言:json复制POST _bulk

{"index": {"_index": "products", "_id": 10001}}

{"category": "Electronics", "name": "iPhone 14 Pro"}

{"index": {"_index": "products", "_id": 10007}}

{"category": "Electronics", "name": "MacBook Pro 16-inch"}

{"index": {"_index": "products", "_id": 10013}}

{"category": "Electronics", "name": "Samsung Galaxy Tab S8"}

...使用LLM生成同义词在这一步中,我们将使用LLM动态生成同义词。为此,我们将集成OpenAI API,定义合适的模型和提示。LLM将接收产品的类别和名称,确保同义词在上下文中相关。

代码语言:python复制import json

import logging

from openai import OpenAI

def call_gpt(prompt, model):

try:

logging.info("通过LLM生成同义词...")

response = client.chat.completions.create(

model=model,

messages=[{"role": "user", "content": prompt}],

temperature=0.7,

max_tokens=1000

)

content = response.choices[0].message.content.strip()

return content

except Exception as e:

logging.error(f"使用模型失败: {e}")

return None

def generate_synonyms(category, products):

synonyms = {}

for product in products:

prompt = (

f"你是产品同义词生成专家。根据提供的类别和产品名称生成同义词或相关术语。请遵循以下规则:\n"

f"1. **格式**:第一个词应为主要项目(产品名称的一部分,排除品牌),后跟最多3个用逗号分隔的同义词。\n"

f"2. **排除品牌**:同义词中不要包含品牌名称。\n"

f"3. **同义词数量**:每个产品最多生成3个同义词。\n\n"

f"类别为:**{category}**,产品为:**{product}**。只返回请求格式的同义词,不要附加解释。"

)

response = call_gpt(prompt, "gpt-4o")

synonyms[product] = response

return synonyms从创建的产品索引中,我们将检索“Electronics”类别下的所有商品,并将其名称发送给LLM。预期输出可能类似于:

代码语言:json复制{

"iPhone 14 Pro": ["iPhone", "smartphone", "mobile", "handset"],

"MacBook Pro 16-inch": ["MacBook", "Laptop", "Notebook", "Ultrabook"],

...

}借助生成的同义词,我们可以使用Synonyms API将它们注册到Elasticsearch中。

使用Synonyms API管理同义词Synonyms API提供了一种在系统内直接管理同义词集合的高效方式。每个同义词集合包含同义词规则,其中一组词在搜索中被视为等效。

创建同义词集示例

代码语言:json复制PUT _synonyms/my-synonyms-set

{

"synonyms_set": [

{

"id": "rule-1",

"synonyms": "hello, hi"

},

{

"synonyms": "bye, goodbye"

}

]

}这创建了一个名为“my-synonyms-set”的集合,其中“hello”和“hi”被视为等效词,“bye”和“goodbye”也是如此。

为产品目录实现同义词创建以下是构建同义词集并将其插入到Elasticsearch中的方法。根据LLM建议的同义词映射生成同义词规则。每个规则都有一个ID,对应于产品名称的slug格式,以及LLM计算的同义词列表。

代码语言:python复制import json

import logging

from elasticsearch import Elasticsearch

from slugify import slugify

es = Elasticsearch(

"http://localhost:9200",

api_key="your_api_key"

)

def mount_synonyms(results):

synonyms_set = [{"id": slugify(product), "synonyms": synonyms} for product, synonyms in results.items()]

try:

response = es.synonyms.put_synonym(id="products-synonyms-set", synonyms_set=synonyms_set)

logging.info(json.dumps(response.body, indent=4))

return response.body

except Exception as e:

logging.error(f"创建同义词时出错: {str(e)}")

return None以下是创建同义词集的请求负载:

代码语言:json复制{

"synonyms_set":[

{

"id": "iphone-14-pro",

"synonyms": "iPhone, smartphone, mobile, handset"

},

{

"id": "macbook-pro-16-inch",

"synonyms": "MacBook, Laptop, Notebook, Computer"

},

{

"id": "samsung-galaxy-tab-s8",

"synonyms": "Tablet, Slate, Pad, Device"

},

{

"id": "garmin-forerunner-945",

"synonyms": "Forerunner, smartwatch, fitness watch, GPS watch"

},

{

"id": "bose-quietcomfort-35-headphones",

"synonyms": "Headphones, Earphones, Headset, Cans"

}

]

}在集群中创建同义词集后,我们可以进入下一步,即使用定义的同义词集创建一个支持同义词的新索引。

完整的Python代码,包括LLM生成的同义词和Synonyms API定义的同义词集创建如下:

代码语言:python复制import json

import logging

from elasticsearch import Elasticsearch

from openai import OpenAI

from slugify import slugify

logging.basicConfig(level=logging.INFO)

client = OpenAI(api_key="your-key")

es = Elasticsearch("http://localhost:9200", api_key="your_api_key")

def call_gpt(prompt, model):

try:

logging.info("通过LLM生成同义词...")

response = client.chat.completions.create(

model=model,

messages=[{"role": "user", "content": prompt}],

temperature=0.7,

max_tokens=1000

)

content = response.choices[0].message.content.strip()

return content

except Exception as e:

logging.error(f"使用模型失败: {e}")

return None

def generate_synonyms(category, products):

synonyms = {}

for product in products:

prompt = (

f"你是产品同义词生成专家。根据提供的类别和产品名称生成同义词或相关术语。请遵循以下规则:\n"

f"1. **格式**:第一个词应为主要项目(产品名称的一部分,排除品牌),后跟最多3个用逗号分隔的同义词。\n"

f"2. **排除品牌**:同义词中不要包含品牌名称。\n"

f"3. **同义词数量**:每个产品最多生成3个同义词。\n\n"

f"类别为:**{category}**,产品为:**{product}**。只返回请求格式的同义词,不要附加解释。"

)

response = call_gpt(prompt, "gpt-4o")

synonyms[product] = response

return synonyms

def get_products(category):

query = {

"size": 50,

"_source": ["name"],

"query": {

"bool": {

"filter": [

{

"term": {

"category.keyword": category

}

}

]

}

}

}

response = es.search(index="products", body=query)

if response["hits"]["total"]["value"] > 0:

product_names = [hit["_source"]["name"] for hit in response["hits"]["hits"]]

return product_names

else:

return []

def mount_synonyms(results):

synonyms_set = [{"id": slugify(product), "synonyms": synonyms} for product, synonyms in results.items()]

try:

es_client = get_client_es()

response = es_client.synonyms.put_synonym(id="products-synonyms-set", synonyms_set=synonyms_set)

logging.info(json.dumps(response.body, indent=4))

return response.body

except Exception as e:

logging.error(f"更新同义词时出错: {str(e)}")

return None

if __name__ == '__main__':

category = "Electronics"

products = get_products("Electronics")

llm_synonyms = generate_synonyms(category, products)

mount_synonyms(llm_synonyms)创建支持同义词的索引将创建一个新索引,所有products索引中的数据将被重新索引。这个索引将使用synonyms_filter,应用之前创建的products-synonyms-set。

以下是配置为使用同义词的索引映射:

代码语言:json复制PUT products_02

{

"settings": {

"analysis": {

"filter": {

"synonyms_filter": {

"type": "synonym",

"synonyms_set": "products-synonyms-set",

"updateable": true

}

},

"analyzer": {

"synonyms_analyzer": {

"type": "custom",

"tokenizer": "standard",

"filter": [

"lowercase",

"synonyms_filter"

]

}

}

}

},

"mappings": {

"properties": {

"ID": {

"type": "long"

},

"category": {

"type": "keyword"

},

"name": {

"type": "text",

"analyzer": "standard",

"search_analyzer": "synonyms_analyzer"

}

}

}

}重新索引products索引现在,我们将使用Reindex API将数据从products索引迁移到包含同义词支持的新products_02索引。以下代码在Kibana DevTools中执行:

代码语言:json复制POST _reindex

{

"source": {

"index": "products"

},

"dest": {

"index": "products_02"

}

}迁移完成后,products_02索引将被填充并准备好使用配置的同义词集进行搜索验证。

验证同义词搜索让我们比较两个索引之间的搜索结果。我们将在两个索引上执行相同的查询,并验证是否使用同义词来检索结果。

在products索引中搜索(不支持同义词)我们将使用Kibana执行搜索并分析结果。在Analytics > Discovery菜单中,我们将创建一个数据视图以查看我们创建的索引数据。

在Discovery中,点击数据视图并定义名称和索引模式。对于“products”索引,我们将使用“products”模式。然后,我们将重复这个过程为“products_02”索引创建一个新的数据视图,使用“products_02”模式。

配置数据视图后,我们可以返回到Analytics > Discovery并开始验证。

在这里,选择DataView产品并搜索“tablet”一词后,我们没有得到结果,尽管我们知道有诸如“Kindle Paperwhite”和“Apple iPad Air”这样的产品。

在products_02索引中搜索(支持同义词)在支持同义词的“products_synonyms”数据视图中执行相同查询时,产品成功检索。这表明配置的同义词集正常工作,确保搜索词的不同变体返回预期结果。

我们可以通过直接在Kibana DevTools中运行相同的查询来达到相同的效果。只需使用Elasticsearch Search API搜索products_02索引:

结论在Elasticsearch中实现同义词提高了产品目录搜索的准确性和覆盖范围。关键的差异在于使用了LLM,它自动且有上下文地生成同义词,消除了预定义列表的需求。模型分析了产品名称和类别,确保电子商务相关的同义词。

此外,Synonyms API简化了词典管理,允许动态修改同义词集。通过这种方法,搜索变得更加灵活,能够适应不同的用户查询模式。

这个过程可以通过新的数据和模型调整不断改进,确保更高效的搜索体验。

参考文献本地运行Elasticsearch

https://www.elastic.co/guide/en/elasticsearch/reference/current/run-elasticsearch-locally.html

Synonyms API

https://www.elastic.co/guide/en/elasticsearch/reference/current/synonyms-apis.html