使用 Spanner AI 和 Graph 实现实时零售推荐

1. 简介

此 Codelab 将引导您使用 Spanner 的 AI 和图表功能来增强现有零售数据库。您将学习在 Spanner 中利用机器学习来更好地为客户提供服务的实用技巧。具体来说,我们将实现 k 最近邻 (kNN) 和近似最近邻 (ANN),以发现符合个人客户需求的新产品。您还将集成 LLM,以针对为何做出特定产品推荐提供清晰的自然语言解释。

除了推荐之外,我们还将深入了解 Spanner 的图功能。您将使用图查询,根据客户交易记录和产品说明对产品之间的关系进行建模。这种方法可以发现深度相关的商品,从而显著提高“购买此商品的客户也购买了”或“相关商品”功能的关联性和有效性。完成此 Codelab 后,您将掌握构建完全由 Google Cloud Spanner 提供支持的智能、可扩缩且响应迅速的零售应用所需的技能。

场景

您在一家电子设备零售商工作。您的电子商务网站有一个标准的 Spanner 数据库,其中包含 ProductsOrdersOrderItems

一位客户访问您的网站,并提出了具体需求:“我想购买一款高性能键盘。我有时会在海滩上编写代码,所以键盘可能会弄湿。”

您的目标是使用 Spanner 的高级功能智能地回答此请求:

  1. 查找: 使用向量搜索,超越简单的关键字搜索,查找其 说明 在语义上与用户请求匹配的产品。
  2. 解释: 使用 LLM 分析最匹配的结果,并解释 为何 推荐的产品非常适合,从而建立客户信任。
  3. 关联: 使用图表查询查找客户经常与推荐产品一起购买的 其他 产品。

2. 准备工作

  1. 创建项目 :在 Google Cloud 控制台的“项目选择器”页面上,选择或创建一个 Google Cloud 项目。
  2. 启用结算功能 :确保您的云项目已启用结算功能。了解如何检查项目是否已启用结算功能
  3. 激活 Cloud Shell :点击控制台中的“激活 Cloud Shell”按钮,激活 Cloud Shell。您可以在 Cloud Shell 终端和编辑器之间切换。

c3c8bfefc88138cc.png

  1. 授权和设置项目 :连接到 Cloud Shell 后,请检查您是否已通过身份验证,以及项目是否已设置为您的项目 ID。
gcloud auth list
gcloud config list project
  1. 如果项目未设置,请使用以下命令进行设置,并将 <PROJECT_ID> 替换为您的实际项目 ID:
export PROJECT_ID=<PROJECT_ID>
gcloud config set project $PROJECT_ID
  1. 启用必需的 API :启用 Spanner、Vertex AI 和 Compute Engine API。这可能需要几分钟时间。
gcloud services enable \
    spanner.googleapis.com \
    aiplatform.googleapis.com \
    compute.googleapis.com
  1. 设置一些您将重复使用的环境变量。
export INSTANCE_ID=my-first-spanner
export INSTANCE_CONFIG=regional-us-central1
  1. 创建免费试用 Spanner 实例 (如果您还没有 Spanner 实例)。您需要一个 Spanner 实例来托管数据库。我们将使用 regional-us-central1 作为配置。您可以根据需要更新此配置。
gcloud spanner instances create $INSTANCE_ID \
   --instance-type=free-instance --config=$INSTANCE_CONFIG \
   --description="Trial Instance" 

3. 架构概览

Spanner 封装了所有必要的功能,但模型除外,这些模型托管在 Vertex AI 上。

4. 第 1 步:设置数据库并提交第一个查询。

首先,我们需要创建数据库、加载示例零售数据,并告知 Spanner 如何与 Vertex AI 通信。

在本部分中,您将使用以下 SQL 脚本。

  1. 前往 Spanner 的产品页面。
  2. 选择正确的实例。

a8792346efb017d5.png

  1. 在屏幕上,选择“探索数据集”。然后在弹出窗口中,选择“零售”选项。

49800ad520771ecb.png

c2a7cd37d5c1c493.png

  1. 前往 Spanner Studio。 Spanner Studio 包含一个与查询编辑器和 SQL 查询结果表集成的探索器窗格。 您可以从这一个界面运行 DDL、DML 和 SQL 语句。您需要展开侧边的菜单,找到放大镜。

b6a188814a821aba.png

  1. 读取 Products 表。创建一个新标签页,或使用已创建的“无标题查询”标签页。

cb65b9aa4e7138b1.png

SELECT *
FROM Products;

5. 第 2 步:创建 AI 模型。

现在,我们来使用 Spanner 对象创建远程模型。这些 SQL 语句会创建链接到 Vertex AI 端点的 Spanner 对象。

  1. 在 Spanner Studio 中打开一个新标签页,然后创建两个模型。第一个是 EmbeddingsModel,它可让您生成嵌入。第二个是 LLMModel,它可让您与 LLM(在我们的示例中为 gemini-2.5-flash)互动。请确保您已将 <PROJECT_ID> 更新为您的项目 ID。
### Create the Embedding Model object in Spanner
CREATE MODEL EmbeddingsModel INPUT(
content STRING(MAX),
) OUTPUT(
embeddings STRUCT<statistics STRUCT<truncated BOOL, token_count FLOAT32>, values ARRAY<FLOAT32>>,
) REMOTE OPTIONS (
endpoint = '//aiplatform.googleapis.com/projects/<PROJECT_ID>/locations/us-central1/publishers/google/models/text-embedding-005'
);

### Create the LLM Model object in Spanner
CREATE MODEL LLMModel INPUT(
prompt STRING(MAX),
) OUTPUT(
content STRING(MAX),
) REMOTE OPTIONS (
endpoint = '//aiplatform.googleapis.com/projects/<PROJECT_ID>/locations/us-central1/publishers/google/models/gemini-2.5-flash',
default_batch_size = 1
);
  1. 注意: 请记得将 PROJECT_ID 替换为您的实际 $PROJECT_ID

67f60ff3a90e926c.png

测试此步骤: 您可以在 SQL 编辑器中运行以下命令,验证模型是否已创建。

SELECT *
FROM information_schema.models;

9d2c9cab3733a964.png

6. 第 3 步:生成和存储向量嵌入

我们的 Product 表包含文本说明,但 AI 模型理解的是 向量 (数字数组)。我们需要添加一个新列来存储这些向量,然后通过 EmbeddingsModel 运行所有产品说明来填充该列。

  1. 创建一个新表来支持嵌入。首先,创建一个可以支持嵌入的表。我们使用的嵌入模型与产品表示例嵌入不同。您需要确保嵌入是从同一模型生成的,这样向量搜索才能正常运行。
CREATE TABLE products_with_embeddings (
   ProductID INT64,
   embedding_vector ARRAY<FLOAT32>(vector_length=>768),
   embedding_text STRING(MAX)
)
PRIMARY KEY (ProductID);
  1. 使用模型生成的嵌入填充新表。为简单起见,我们在此处使用 insert into 语句。这会将查询结果推送到您刚刚创建的表中。

SQL 语句首先获取并连接我们要生成嵌入的所有相关文本列。然后,我们返回相关信息,包括我们使用的文本。这通常不是必需的,但我们将其包含在内,以便您可视化结果。

INSERT INTO products_with_embeddings (productId, embedding_text, embedding_vector)
SELECT
ProductID,
content as embedding_text,
embeddings.values as embedding_vector
FROM ML.PREDICT(
 MODEL EmbeddingsModel,
 (
   SELECT
   ProductID,
   embedding_text AS content
   FROM (
     SELECT
       ProductID,
       CONCAT(
         Category,
         " ",
         Description,
         " ",
         Name
       ) AS embedding_text
       FROM products)));
  1. 检查新嵌入。现在,您应该会看到生成的嵌入。
SELECT *
FROM products_with_embeddings
LIMIT 1;

d40975087736e604.png

7. 第 4 步:为 ANN 搜索创建向量索引

如需即时搜索数百万个向量,我们需要一个索引。此索引支持 (ANN) 搜索,这种搜索速度极快,并且可以横向扩缩。

  1. 运行以下 DDL 查询来创建索引。我们将 COSINE 指定为距离指标,这非常适合语义文本搜索。请注意,WHERE 子句实际上是必需的,因为 Spanner 会将其作为查询的要求。
CREATE VECTOR INDEX DescriptionEmbeddingIndex
   ON products_with_embeddings(embedding_vector)
   WHERE embedding_vector IS NOT NULL
OPTIONS (
 distance_type = 'COSINE'
);
  1. 在“操作”标签页中查看索引创建的状态。

2ad9c88bc86b77f0.png

8. 第 5 步:使用 K 最近邻 (KNN) 搜索查找推荐产品

现在到了有趣的部分!我们来查找与客户查询匹配的产品:“我想购买一款高性能键盘。我有时会在海滩上编写代码,所以键盘可能会弄湿。”

我们将从 K- (KNN) 搜索开始。这是一种 精确 搜索,它会将查询向量与 每个 产品向量进行比较。它非常精确,但在非常大的数据集上可能会很慢(这就是我们在第 5 步中构建 ANN 索引的原因)。

此查询执行两项操作:

  1. 子查询使用 ML.PREDICT 获取客户查询的嵌入向量。
  2. 外部查询使用 COSINE_DISTANCE 计算查询向量与每个产品的 embedding_vector 之间的“距离”。距离越小,匹配度越高。
SELECT
    productid,
    embedding_text,
    COSINE_DISTANCE(
      embedding_vector,
      (
        SELECT embeddings.values
        FROM ML.PREDICT(
          MODEL EmbeddingsModel,
          (SELECT "I'd like to buy a high performance keyboard. I sometimes code while I'm at the beach so it may get wet." AS content)
        )
      )
    ) AS distance
FROM products_with_embeddings
WHERE embedding_vector IS NOT NULL
ORDER BY distance
LIMIT 5;

您应该会看到一个产品列表,其中防水键盘位于最顶部。

9. 第 6 步:使用近似 (ANN) 搜索查找推荐产品

KNN 非常棒,但对于一个包含数百万种产品且每秒查询次数达数千次的生产系统,我们需要 ANN 索引的速度。

使用索引需要您指定 APPROX_COSINE_DISTANCE 函数。

  1. 获取文本的向量嵌入,就像您在上面所做的那样。我们将该结果与 products_with_embeddings 表中的记录进行交叉联接,以便您可以在 APPROX_COSINE_DISTANCE 函数中使用它。
WITH vector_query as
(
 SELECT embeddings.values as vector
 FROM ML.PREDICT(
 MODEL EmbeddingsModel,
  (SELECT "I'd like to buy a high performance keyboard. I sometimes code while I'm at the beach so it may get wet." as content)
 )
)
SELECT
ProductID,
embedding_text,
APPROX_COSINE_DISTANCE(embedding_vector, vector, options => JSON '{\"num_leaves_to_search\": 10}') distance
FROM products_with_embeddings @{force_index=DescriptionEmbeddingIndex},
vector_query
WHERE embedding_vector IS NOT NULL
ORDER BY distance
LIMIT 5;

预期输出: 结果应与 KNN 查询相同或非常相似,但它通过使用索引执行效率更高。您可能不会在示例中注意到这一点。

10. 第 7 步:使用 LLM 解释推荐产品

仅显示产品列表很好,但解释为何或为何不适合则更好。我们可以使用 LLMModel (Gemini) 来执行此操作。

此查询将我们在第 4 步中的 KNN 查询嵌套在 ML.PREDICT 调用中。我们使用 CONCAT 为 LLM 构建提示,并为其提供以下信息:

  1. 明确的说明(“回答‘是’或‘否’,并解释原因…”)。
  2. 客户的原始查询。
  3. 每个最匹配产品的名称和说明。

然后,LLM 会根据查询评估每个产品,并提供自然语言响应。

SELECT
   ProductID,
   embedding_text,
   content AS LLMResponse
FROM ML.PREDICT(
   MODEL LLMModel,
   (
     SELECT
       ProductID,
       embedding_text,
       CONCAT(
         "Answer with ‘Yes' or ‘No' and explain why: Is this a good fit for me?",
         "I'd like to buy a high performance keyboard. I sometimes code while I'm at the beach so it may get wet. \n",
         "Product Description:", embedding_text
       ) AS prompt,
     FROM products_with_embeddings
     WHERE embedding_vector IS NOT NULL
     ORDER BY COSINE_DISTANCE(
       embedding_vector,
       (
         SELECT embeddings.values
         FROM ML.PREDICT(
           MODEL EmbeddingsModel,
           (SELECT "I'd like to buy a high performance keyboard. I sometimes code while I'm at the beach so it may get wet." AS content)
         )
       )
     )
     LIMIT 5
   ),
   STRUCT(1056 AS maxOutputTokens)
);

预期输出: 您将获得一个包含新 LLMResponse 列的表。响应应如下所示:“否。 原因如下:* “防水”不是“防水”。“防水”键盘可以应对泼溅、小雨或溢出”

11. 第 8 步:创建属性图表

现在,我们来介绍另一种类型的推荐:“购买此商品的客户也购买了…”

这是一个基于关系的查询。属性图表 是实现此目的的完美工具。Spanner 可让您在现有表之上创建图表,而无需复制数据。

此 DDL 语句定义了我们的图表:

  • 节点: ProductUser 表。节点是您要从中派生关系的实体,您想知道购买了您的产品的客户也购买了“XYZ”产品。
  • Orders 表,它使用标签“Purchased”将 User(来源)连接到 Product(目标)。边提供了用户与他们购买的商品之间的关系。
CREATE PROPERTY GRAPH RetailGraph
 NODE TABLES (
   products_with_embeddings,
   Orders
 )
 EDGE TABLES (
   OrderItems
     SOURCE KEY (OrderID) REFERENCES Orders
     DESTINATION KEY (ProductID) REFERENCES products_with_embeddings
     LABEL Purchased
 );

12. 第 9 步:结合使用向量搜索和图表查询

这是最强大的一步。我们将在单个语句中结合使用 AI 向量搜索和图表查询 ,以查找相关产品。

此查询分为三个部分读取,以 NEXT statement 分隔,我们将其分解为多个部分。

  1. 首先,我们使用向量搜索找到最佳匹配项。
  2. ML.PREDICT 使用 EmbeddingsModel 从用户的文本查询生成向量嵌入。
  3. 该查询计算此新嵌入与所有商品的存储 p.embedding_vector 之间的 COSINE_DISTANCE。
  4. 它会选择并返回距离最小(语义相似度最高)的单个 bestMatch 产品。
  5. 接下来,我们遍历图表以搜索关系。

NEXT MATCH (bestMatch)<-[:Purchased]-(user:Orders)-[:Purchased]->(purchasedWith:products_with_embeddings)

  1. 该查询从 bestMatch 回溯到常见的 Orders 节点 (user),然后向前到其他 purchasedWith 产品。
  2. 它会过滤掉原始产品,并使用 GROUP BY 和 COUNT(1) 聚合商品的共同购买频率。
  3. 它会返回共同购买频率最高的前 3 个共同购买产品 (purchasedWith),并按共同购买频率排序。

此外,我们还找到了用户订单关系。

NEXT MATCH (bestMatch)<-[:Purchased]-(user:Orders)-[purchased:Purchased]->(purchasedWith)

  1. 此中间步骤执行遍历模式以绑定关键实体:bestMatch、连接用户:Orders 节点和 purchasedWith 商品。
  2. 它专门将关系本身绑定为 purchased,以便在下一步中提取数据。
  3. 此模式可确保建立上下文,以提取订单特定和产品特定的详细信息。
  4. 最后,我们输出结果,以便在作为 SQL 结果返回之前将图表节点格式化。
GRAPH RetailGraph
MATCH (p:products_with_embeddings)
WHERE p.embedding_vector IS NOT NULL
RETURN p AS bestMatch
ORDER BY COSINE_DISTANCE(
 p.embedding_vector,
 (
   SELECT embeddings.values
   FROM ML.PREDICT(
     MODEL EmbeddingsModel,
     (SELECT "I'd like to buy a high performance keyboard. I sometimes code while I'm at the beach so it may get wet." AS content)
   )
 )
)
LIMIT 1

NEXT
MATCH (bestMatch)<-[:Purchased]-(user:Orders)-[:Purchased]->(purchasedWith:products_with_embeddings)
FILTER bestMatch.productId <> purchasedWith.productId
RETURN bestMatch, purchasedWith
GROUP BY bestMatch, purchasedWith
ORDER BY COUNT(1) DESC
LIMIT 3

NEXT
MATCH (bestMatch)<-[:Purchased]-(user:Orders)-[purchased:Purchased]->(purchasedWith)
RETURN
 TO_JSON(Purchased) AS purchased,
 TO_JSON(user.OrderID) AS user,
 TO_JSON(purchasedWith.productId) AS purchasedWith;

预期输出: 您将看到表示共同购买频率最高的前 3 个商品的 JSON 对象,提供交叉销售推荐。

13. 清理

为避免产生费用,您可以删除自己创建的资源。

  1. 删除 Spanner 实例: 删除实例也会删除数据库。
gcloud spanner instances delete my-first-spanner --quiet
  1. 删除 Google Cloud 项目: 如果您创建此项目只是为了完成此 Codelab,那么删除该项目是最简单的清理方法。
  • 前往 Google Cloud 控制台中的管理资源 页面。
  • 选择您的项目,然后点击删除

🎉 恭喜!

您已成功使用 Spanner AI 和 Graph 构建了一个复杂的实时推荐系统!

您已了解如何将 Spanner 与 Vertex AI 集成以进行嵌入和 LLM 生成,如何执行高速向量搜索(KNN 和 ANN)以查找语义相关的产品,以及如何使用图表查询来发现产品关系。您构建的系统不仅可以 查找 产品,还可以 解释 推荐产品和 推荐 相关商品,所有这些都来自一个可扩缩的数据库。