将生成式 AI 融入 Shopify 的产品推荐应用

将生成式人工智能融入Shopify产品推荐应用

电子商务商家对产品推荐系统的概念并不陌生。然而,传统的实施方法存在一些局限性。在这里,我们将深入探讨我如何将生成式人工智能融入到Shopify的产品推荐应用程序——Search and Discovery中。

这个方法很简单,我只使用了两个GenAI提示来实现整个系统。我将详细介绍这两个提示,并讨论在处理LLM幻觉时面临的挑战。我将展示如何利用DataStax的Vector Search能力和LangStream框架来简化整个设计。

什么是产品推荐系统?

产品推荐系统为消费者在购物过程中的不同阶段提供产品建议,无论是在购物车中还是在产品详细页面上。在Shopify的Search and Discovery应用程序中,产品推荐有两个常见类别。这两个类别分别出现在“您可能还喜欢”和“搭配推荐”部分的购物页面上。这些类别分别代表了相似产品和互补产品。

传统的推荐系统使用启发式和基于规则的逻辑根据给定的上下文来过滤产品,例如当客户将产品放入购物车时。产品的详细信息,如产品类别、供应商、价格或其他产品元数据被用作过滤条件。

研究项目

在阅读Shopify Search and Discovery应用程序的文档时,我发现互补产品必须由客户手动指定。这与“相似产品”类别中的产品推荐形成对比,后者是基于一组过滤规则自动生成的推荐。

我需要找到一种程序化指定互补产品到Shopify应用程序的方法。经过一些实验,我找到了可行的解决方案。产品推荐数据以元数据字段的形式存储在Shopify中,每个产品在产品目录中都有相应的元数据。我使用了Shopify的GraphQL接口来找到正确的元数据。如果您还没有这样做,请安装Shopify的GraphiQL应用程序,该应用程序允许您进行GraphQL查询的实验。

我使用以下查询来获取我已在Shopify Search and Discovery应用程序用户界面上指定互补产品的产品的所有元数据字段列表。

示例:Shopify GraphQL查询以检索产品元数据字段

上述GraphQL查询获得了两个存储产品推荐的元数据字段。一个用于相关(相似)产品,另一个用于互补产品。

示例:Shopify GraphQL查询响应显示产品推荐元数据字段

为了修改互补元数据字段的值,我们可以执行以下GraphQL突变查询。通过设置这个元数据,产品详细页面上的“搭配推荐”窗口被激活,然后显示配置的互补产品集合。

注意,Shopify不允许修改属于私有命名空间的预先存在的元数据字段。技巧在于检查给定产品的字段是否存在,执行删除操作,然后重新创建或添加字段(如果它不存在)。

示例:Shopify GraphQL突变查询更新产品元数据字段值

Shopify应用程序摘要

创建GenAI产品推荐系统的主要工作中,最大的努力是与Shopify生态系统的集成。其中一部分工作是创建一个Shopify管理员界面,允许商家触发推荐预览,并将产品推荐应用到他们的产品目录中。

我使用DataStax的LangStream项目来实现每个产品推荐的后端解决方案。LangStream为我提供了一个批处理系统,用于排队和处理产品推荐。通过一系列的YAML配置,我能够指定数据处理流程,以执行推荐工作流的各个阶段。

下图描述了基于AI的推荐生成的主要工作流程。商家可以通过Shopify GenAI推荐应用程序界面触发此工作流程。该工作流程对我们要生成推荐的一组产品中的每个产品执行一次。

注意:我之前提到我在解决方案中只使用了两个LLM提示。图表中的“互补产品类型映射”查找表是使用其中一个提示生成的,我将在后面讨论。

产品推荐工作流程的六个步骤:

  1. 为给定的源产品请求产品推荐。
  2. 基于源产品类型,从查找表中检索一组互补产品类型。生成一组给定互补产品类型的候选产品。
  3. 将源产品描述传递给LLM嵌入API生成嵌入向量。使用嵌入向量在向量数据库中搜索相似产品。相似产品指的是在意义上与源产品紧密匹配的任何产品。例如,高尔夫球与高尔夫球杆的含义更加相似,而不是狗项圈。
  4. 执行向量数据库搜索,查询输入包括来自步骤3的嵌入向量和互补产品类型列表。搜索结果包括互补产品候选集以及每个产品的各种元数据,例如产品标题、价格、供应商和产品描述。
  5. 制定一个LLM提示,其中包含来自步骤4的候选产品的所有细节,生成推荐的指令,以及结果的格式化指令。将提示发送给LLM聊天API生成产品推荐。
  6. 从步骤5中获得的LLM响应被后处理为一组有效的Shopify产品ID,用于更新源产品的互补产品元数据。

设置向量数据库

向量数据库包含Shopify商户店铺中产品的元数据。向量数据库中存储的数据的目的是为了使候选产品能够使用向量搜索。使用Shopify API无法执行此类搜索。此外,将产品元数据存储在向量数据库中避免了对此数据执行后续查询Shopify API的需求。将向量数据库与Shopify产品数据库保持同步是另一篇博客文章的主题。

在下面的步骤中,我们将创建一个包含每个产品五个元数据的表。元数据来自Shopify,而向量值是通过调用LLM嵌入API并使用产品描述作为输入进行计算的。

  • 产品ID(来自Shopify)
  • 产品描述(来自Shopify)
  • 产品标题(来自Shopify)
  • 产品价格(来自Shopify)
  • 产品类型(一个Shopify产品类型值)
  • 产品向量(来自LLM嵌入API)
  • 示例:设置具有向量搜索功能的产品表及相关索引的Astra DB CQL命令

向向量数据库填充产品元数据将需要进行一次导入操作,并进行后续更新,以使数据库与商户的产品目录保持同步。商户可以将其在Shopify上的产品目录导出为CSV文件,然后将其导入到他们选择的导入程序中。

这里有几个适当的选择,包括实施自定义解决方案或使用LangChain和CassIO的组合。无论选择哪种解决方案,都需要计算每个导入产品描述的嵌入向量值。

利用GenAI提示来计算互补产品类型

我们可以利用LLM帮助我们创建一个互补产品类型查找表。找到互补产品的关键机制是找到哪些产品类别与彼此配对良好。这个配对过程通常由人类完成,他们根据现有选择应用他们的现实世界经验创建一个合理互补列表。

这个问题域非常适合应用LLM对世界的理解来生成合理的推荐。下面的表格是互补产品映射的一个很好的示例。

示例:互补产品类型查找表

Shopify商店中的每个产品都包括一个产品’Type’属性。产品类型的值是任意的,并由商店操作员设置。我们可以通过执行GraphQL查询到Shopify Storefront API来检索商户产品目录中的所有产品类型。

示例:用于产品类型的Shopify GraphQL查询

示例:产品类型结果

利用上述产品类型信息,我们可以制定一个LLM提示,自动生成所有互补产品类型组合。

注意:我们希望LLM响应的输出是一个格式良好的JSON文档,可以进行解析以提取结果。提示包括以下参数:

  • {{ MAX_COMPLEMENTARY_CATEGORIES }} — 限制每个产品类型生成的互补类型数量的值
  • {{ COMMA_SEP_LIST_OF_PRODUCT_CATEGORIES }} — 商户Shopify店铺中所有可用产品类型类别的逗号分隔列表
  • {{ CUSTOM_NATURAL_LANGUAGE_RULES }} — 这是一个或多个由商户提供的自然语言规则,用于修改推荐行为

LLM示例提示:生成互补产品类别

GenAI产品推荐的提示

生成推荐的LLM提示需要包含所有候选产品的产品元数据。我们希望LLM将这些产品作为上下文用于最终的推荐。这种方法称为检索增强生成(RAG)。 

在看LLM提示本身之前,我们将查看检索候选产品元数据的向量搜索查询。以下向量查询可用于筛选候选产品列表。查询的输入假定您已计算出以下值:

  • 源产品描述的嵌入向量
  • 源产品的互补产品类型列表

示例向量搜索CQL查询:生成候选产品推荐列表

现在,我们可以利用候选产品搜索结果生成产品推荐的LLM提示。提示模板包括所有必要数据的参数。此外,提示包含生成所需推荐输出格式的说明。

  • {{ MAX_RECOMMENDATIONS }} — 从提供的产品列表中选择的候选产品的最大数量。此参数在下面的提示模板中使用了两次。第二次使用它是为了通过明确指定要生成的推荐数量来减少LLM的幻觉

  • {{ PRODUCT_DESCRIPTION }} — 我们要为其提供推荐的源产品的描述

  • {{ PRODUCT_TITLE }} — 我们要为其提供推荐的源产品的标题

  • {{ PRODUCT_PRICE }} — 我们要为其提供推荐的源产品的价格

  • 字段 {{ PRODUCT_METADATA_N_.. }} — 将这些字段替换为每个候选产品的数据。使用来自Vector搜索结果的数据。注意:自定义推荐说明可以参考此元数据,比如产品类型、价格,或者可能在产品描述中的其他信息。

  • {{ CUSTOM_INSTRUCTIONS_FOR_PRODUCT_FILTERING }}— 此部分可以包括一系列引用产品元数据的自定义推荐规则。示例规则:

    • “在1月和2月之间,推荐品牌X的产品。”
    •  “尽量推荐同一品牌的产品”
  • {{ CURRENT_DATE }} — 当前日期可以插入作为额外的上下文信息。上述自定义规则可以包含在给定时间范围内激活推荐的语言。

示例LLM提示:生成产品推荐

处理LLM推荐响应后

根据所使用的LLM聊天模型的不同,此提示可能会生成不同的结果。我们使用了OpenAI的ChatGPT 3.5 Turbo和ChatGPT 4.0,两者大部分时间都生成稳定的JSON文档结构。您可以采用各种提示工程方法来改进结果。然后,您的代码可以解析JSON结果,使用前面概述的Shopify GraphQL策略更新产品推荐元数据。

克服LLM幻觉的挑战

LLM聊天系统的响应有时会产生意外的输出和虚构的数据。在使用产品推荐提示时,我遇到了几种幻觉模式。以下部分描述了失败场景以及如何处理每种情况。

基于虚构数据的无效推荐

根据LLM创作的建议结果中参考的产品ID可能是由LLM虚构的,而不是来自RAG上下文。

  • 我经常观察到如果候选产品列表少于所需的推荐数量,LLM会虚构结果。
  • 当候选产品数量比最大推荐数量多2倍时,推荐结果最稳定。

不纯净的JSON输出

LLM在JSON文档响应周围包含了自由格式的文本。

  • 在大多数情况下,含有的JSON文档包含有效的推荐结果,因此我们可以从包含的文本中解析出JSON文档结构并忽略其余部分。

意外的JSON文档结构

JSON文档结构非常稳定,但有时LLM会出现字段名幻觉或包含数组结构。

  • 字段名可能具有不同的大写形式,或者在名称中使用不同的分隔符字符,如破折号与下划线。
  • 为了解决这些问题,我添加了对常见情况下两个或更多变体的字段名的处理。
  • 包含产品推荐的数组的结构有时可能具有不同的父层次结构。测试解析后的JSON文档的结构,然后处理相应的响应,可以提高整体的成功率。

将LLM代理响应与外部参考数据连锁

一个额外的挑战是将LLM查询的结果链式传递给另一个LLM提示的输入时。当RAG输入到提示1生成无效的参考数据传递给提示2时问题会加剧。

在产品推荐的情况下,在Vector数据库中执行用于候选产品的CQL搜索(提示2)时,我们使用LLM生成的互补产品类别集合(提示1)作为CQL查询约束的值形式传递给’product_type IN (?)’子句。

LLM存在可能会稍微改动或生成不完全符合原始有效值集合(RAG输入)的互补product_type值的幻觉。因此,真正的产品类型与LLM生成的产品类型值之间可能存在差异。

  • 使推荐系统对这种幻觉更加健壮的一种策略是通过实施索引分析器来放宽搜索标准的严格性。

词干等技术可以用来提高将LLM幻觉导致的轻微差异与真正的产品元数据值匹配的几率:

词干的一个例子是允许将单词’Runner’与’Running’进行匹配。

示例SAI索引分析器配置:启用词干。

产品推荐系统的未来充满了个性化和增强的客户参与机会。通过整合人工智能和自然语言处理,我们不仅可以提高这些系统的准确性,还可以提供一种前所未有的级别的定制化,极大地增强客户的购物体验。

敬请期待我下一篇博客文章,我将更详细地讨论我如何利用LangStream实施推荐工作流以及该框架如何提供一种安全和可扩展的解决方案。