Рекомендации по розничной торговле в реальном времени с помощью Spanner AI и Graph

1. Введение

В этой лабораторной работе вы научитесь использовать возможности искусственного интеллекта и графов Spanner для улучшения существующей базы данных розничной торговли. Вы изучите практические методы использования машинного обучения в Spanner для повышения качества обслуживания клиентов. В частности, мы реализуем алгоритмы k-ближайших соседей (kNN) и приближенных ближайших соседей (ANN) для поиска новых продуктов, соответствующих индивидуальным потребностям клиентов. Вы также получите степень магистра права (LLM), чтобы давать понятные и понятные объяснения причин рекомендации конкретного продукта.

Помимо рекомендаций, мы подробно рассмотрим функциональность графов Spanner. Вы будете использовать запросы к графам для моделирования взаимосвязей между товарами на основе истории покупок клиентов и описаний товаров. Этот подход позволяет находить тесно связанные товары, значительно повышая релевантность и эффективность функций «Клиенты также купили» или «Похожие товары». К концу этой практической работы вы приобретете навыки для создания интеллектуального, масштабируемого и адаптивного приложения для розничной торговли, полностью работающего на базе Google Cloud Spanner.

Сценарий

Вы работаете в компании, торгующей электроникой. На вашем сайте электронной коммерции используется стандартная база данных Spanner с Products , Orders и OrderItems .

Клиент заходит на ваш сайт с конкретной потребностью: «Я хотел бы купить высокопроизводительную клавиатуру. Иногда я пишу код на пляже, так что она может намокнуть».

Ваша цель — использовать расширенные возможности Spanner, чтобы разумно ответить на этот запрос:

  1. Найти: Выйдите за рамки простого поиска по ключевым словам, чтобы найти продукты, описания которых семантически соответствуют запросу пользователя, используя векторный поиск.
  2. Объясните: используйте степень магистра права, чтобы проанализировать наиболее подходящие рекомендации и объяснить , почему они вам подходят, тем самым укрепляя доверие клиентов.
  3. Связать: Используйте графовые запросы, чтобы найти другие продукты, которые клиенты часто покупали вместе с этой рекомендацией.

2. Прежде чем начать

  1. Создание проекта В консоли Google Cloud на странице выбора проекта выберите или создайте проект Google Cloud.
  2. Включите выставление счетов. Убедитесь, что для вашего облачного проекта включено выставление счетов. Узнайте, как проверить, включено ли выставление счетов для проекта .
  3. Активировать Cloud Shell. Активируйте Cloud Shell, нажав кнопку «Активировать Cloud Shell» в консоли. Вы можете переключаться между терминалом Cloud Shell и редактором.

c3c8bfefc88138cc.png

  1. Авторизация и настройка проекта После подключения к Cloud Shell убедитесь, что вы прошли аутентификацию и что для проекта задан ваш идентификатор проекта.
gcloud auth list
gcloud config list project
  1. Если ваш проект не настроен, используйте следующую команду для его настройки, заменив <PROJECT_ID> на фактический идентификатор вашего проекта:
export PROJECT_ID=<PROJECT_ID>
gcloud config set project $PROJECT_ID
  1. Включите необходимые API. Включите API Spanner, Vertex AI и Compute Engine. Это может занять несколько минут.
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. Ознакомьтесь с таблицей «Товары». Создайте новую вкладку или используйте уже созданную вкладку «Запрос без названия».

cb65b9aa4e7138b1.png

SELECT *
FROM Products;

5. Шаг 2: Создание моделей ИИ.

Теперь создадим удалённые модели с объектами Spanner. Эти SQL-операторы создают объекты Spanner, которые связываются с конечными точками Vertex AI.

  1. Откройте новую вкладку в студии Spanner и создайте две модели. Первая — EmbeddingsModel, которая позволит генерировать вложения. Вторая — LLMModel, которая позволит взаимодействовать с LLM (в нашем примере это gemini-2.5-flash). Убедитесь, что вы обновили <PROJECT_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: Создание и сохранение векторных вложений

В нашей таблице «Продукты» есть текстовые описания, но модель ИИ понимает векторы (массивы чисел). Нам нужно добавить новый столбец для хранения этих векторов и заполнить его, пропустив все описания товаров через 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 - N ближайших соседей (KNN). Это точный поиск, который сравнивает вектор запроса с каждым вектором произведения. Он точный, но может быть медленным на очень больших наборах данных (именно поэтому мы построили индекс ANN для шага 5).

Этот запрос выполняет две функции:

  1. Подзапрос использует ML.PREDICT для получения вектора внедрения для запроса нашего клиента.
  2. Внешний запрос использует функцию COSINE_DISTANCE для вычисления «расстояния» между вектором запроса и вектором вложения каждого продукта. Чем меньше расстояние, тем лучше соответствие.
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).

Этот запрос вкладывает наш запрос KNN из шага 4 в вызов 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 определяет наш граф:

  • Узлы: таблицы Product и User . Узлы — это сущности, от которых вы хотите получить связь. Вам нужно узнать, покупали ли клиенты, купившие ваш продукт, также и товары «XYZ».
  • Ребра: таблица Orders , ​​которая связывает 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: Объедините поиск векторов и графические запросы

Это самый важный шаг. Мы объединим векторный поиск ИИ и графические запросы в одном операторе для поиска связанных продуктов.

Этот запрос считывается из трех частей, разделенных NEXT statement , давайте разобьем его на разделы.

  1. Сначала мы находим наилучшее совпадение с помощью векторного поиска.
  2. ML.PREDICT генерирует векторное вложение из текстового запроса пользователя с помощью EmbeddingsModel.
  3. Запрос вычисляет COSINE_DISTANCE между этим новым вложением и сохраненным p.embedding_vector для всех продуктов.
  4. Он выбирает и возвращает единственный продукт bestMatch с минимальным расстоянием (наивысшим семантическим сходством).
  5. Далее мы проходим по графу в поисках взаимосвязей.

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

  1. Запрос отслеживает от bestMatch до общих узлов Orders (пользователь), а затем перенаправляет к другим продуктам purchasedWith.
  2. Он отфильтровывает исходный продукт и использует GROUP BY и COUNT(1) для агрегирования данных о том, как часто товары покупаются совместно.
  3. Возвращает 3 лучших совместно купленных товара (purchasedWith), упорядоченных по частоте совместного появления.

Кроме того, мы находим взаимосвязь «Порядок пользователя».

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

  1. На этом промежуточном шаге выполняется шаблон обхода для связывания ключевых сущностей: bestMatch, соединяющего узла user:Orders и элемента purchasedWith.
  2. Он специально привязывает само отношение как приобретенное для извлечения данных на следующем этапе.
  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;

Ожидаемый результат: вы увидите JSON-объекты, представляющие три самых популярных совместно приобретаемых товара, с рекомендациями по перекрестным продажам.

13. Уборка

Чтобы избежать расходов, вы можете удалить созданные вами ресурсы.

  1. Удалить экземпляр Spanner: Удаление экземпляра приведет к удалению и базы данных.
gcloud spanner instances delete my-first-spanner --quiet
  1. Удалите проект Google Cloud. Если вы создали этот проект только для кодовой лаборатории, то его удаление — самый простой способ навести порядок.
  • Перейдите на страницу «Управление ресурсами» в консоли Google Cloud.
  • Выберите свой проект и нажмите «Удалить» .

🎉 Поздравляем!

Вы успешно создали сложную систему рекомендаций в реальном времени с использованием Spanner AI и Graph!

Вы узнали, как интегрировать Spanner с Vertex AI для встраивания и генерации LLM, как выполнять высокоскоростной векторный поиск (KNN и ANN) для нахождения семантически релевантных продуктов и как использовать запросы к графам для выявления взаимосвязей между продуктами. Вы создали систему, которая может не только находить продукты, но и объяснять рекомендации и предлагать связанные товары, используя единую масштабируемую базу данных.