Приложение для поиска в магазине игрушек с облачными базами данных, бессерверной средой выполнения и интеграцией с открытым исходным кодом

1. Обзор

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

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

Решение: Демонстрационное приложение решает эту задачу напрямую, используя возможности искусственного интеллекта для обеспечения по-настоящему персонализированного и удобного взаимодействия с контекстным поиском и индивидуальной генерацией продукта, соответствующего контексту поиска.

Что вы построите

В рамках этой лабораторной работы вы:

  1. Создайте экземпляр AlloyDB и загрузите набор данных Toys.
  2. Включите расширения pgvector и генеративного ИИ в AlloyDB.
  3. Сгенерируйте векторные представления на основе описания продукта и выполните поиск сходства по косинусному закону в реальном времени для поискового текста пользователя.
  4. Для контекстного поиска игрушек используйте Gemini 2.0 Flash, чтобы описать загруженное пользователем изображение.
  5. Используйте Imagen 3 для создания пользовательской игрушки на основе интересов пользователя.
  6. Для получения подробной информации о цене созданного вами продукта воспользуйтесь инструментом прогнозирования цен, разработанным с помощью Gen AI Toolbox for Databases.
  7. Разверните решение в бессерверной среде Cloud Run Functions.

Требования

  • Браузер, например Chrome или Firefox.
  • Проект Google Cloud с включенной функцией выставления счетов.

2. Архитектура

Поток данных: Давайте подробнее рассмотрим, как данные перемещаются в нашей системе:

  1. Контекстный поиск с использованием алгоритма RAG (Retrieval Augmented Generation), основанного на искусственном интеллекте.

Представьте себе: вместо того, чтобы просто искать «красную машину», система понимает следующее:

«Небольшой автомобиль, подходящий для 3-летнего мальчика».

AlloyDB в качестве основы: Мы используем AlloyDB, полностью управляемую базу данных Google Cloud, совместимую с PostgreSQL, для хранения наших тестовых данных, включая описания, URL-адреса изображений и другие соответствующие атрибуты.

pgvector для семантического поиска: pgvector, расширение PostgreSQL, позволяет хранить векторные представления как описаний тестовых примеров, так и поисковых запросов пользователей. Это обеспечивает семантический поиск, то есть система понимает смысл слов, а не только точные ключевые слова.

Косинусное сходство для оценки релевантности: Мы используем косинусное сходство для измерения семантического сходства между вектором поиска пользователя и векторами описания игрушек, чтобы отобразить наиболее релевантные результаты.

Индекс ScaNN для скорости и точности: Для обеспечения быстрых и точных результатов, особенно по мере роста нашего ассортимента игрушек, мы интегрируем индекс ScaNN (Scalable Nearest Neighbors). Это значительно повышает эффективность и полноту нашего векторного поиска.

  1. Поиск и распознавание изображений с помощью Gemini 2.0 Flash

Вместо того чтобы вводить контекст в виде текста, предположим, пользователь хочет загрузить изображение знакомой игрушки, по которой он хочет выполнить поиск. Пользователи могут загрузить изображение понравившейся игрушки и получить соответствующие характеристики. Мы используем модель Google Gemini 2.0 Flash, запущенную с помощью LangChain4j, для анализа изображения и извлечения релевантного контекста, такого как цвет игрушки, материал, тип и целевая возрастная группа.

  1. Создание игрушки вашей мечты с помощью генеративного ИИ: Imagen 3

Настоящее волшебство происходит, когда пользователи решают создать свою собственную игрушку. С помощью Imagen 3 мы позволяем им описать игрушку своей мечты, используя простые текстовые подсказки. Представьте, что вы можете сказать: «Я хочу плюшевого дракона с фиолетовыми крыльями и дружелюбным лицом», и увидеть, как этот дракон оживает на вашем экране! Затем Imagen 3 генерирует изображение созданной вами игрушки, предоставляя пользователю наглядную визуализацию своего творения.

  1. Прогнозирование цен с помощью агентов и инструментария Gen AI для баз данных.

Мы внедрили функцию прогнозирования цен, которая оценивает стоимость производства игрушки, изготовленной по индивидуальному заказу. Эта функция работает на основе агента, включающего в себя сложный инструмент расчета цен.

Gen AI Toolbox for Databases: Этот агент интегрирован с нашей базой данных с помощью нового инструмента с открытым исходным кодом от Google, Gen AI Toolbox for Databases. Это позволяет агенту получать доступ к данным в режиме реального времени о стоимости материалов, производственных процессах и других важных факторах для предоставления точной оценки стоимости. Подробнее об этом можно прочитать здесь .

  1. Java Spring Boot, Gemini Code Assist и Cloud Run для упрощения разработки и развертывания бессерверных приложений.

Всё приложение построено на основе Java Spring Boot, надёжного и масштабируемого фреймворка. На протяжении всего процесса разработки, особенно для фронтенда, мы использовали Gemini Code Assist, что значительно ускорило цикл разработки и повысило качество кода. Для развертывания всего приложения мы использовали Cloud Run, а для развертывания базы данных и агентских функций в качестве независимых конечных точек — Cloud Run Functions.

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

Создать проект

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

Изображение кнопки «Активировать Cloud Shell»

  1. После подключения к Cloud Shell необходимо проверить, прошли ли вы аутентификацию и установлен ли идентификатор вашего проекта, используя следующую команду:
gcloud auth list
  1. Выполните следующую команду в Cloud Shell, чтобы убедиться, что команда gcloud знает о вашем проекте.
gcloud config list project
  1. Если ваш проект не задан, используйте следующую команду для его установки:
gcloud config set project <YOUR_PROJECT_ID>
  1. Включите необходимые API, выполнив следующие команды по очереди в терминале Cloud Shell:

Также существует отдельная команда для выполнения приведенных ниже действий, но если вы используете пробную учетную запись, у вас могут возникнуть проблемы с квотами при попытке включить их одновременно. Именно поэтому команды указаны по одной в каждой строке.

gcloud services enable alloydb.googleapis.com
gcloud services enable compute.googleapis.com 
gcloud services enable cloudresourcemanager.googleapis.com 
gcloud services enable servicenetworking.googleapis.com 
gcloud services enable run.googleapis.com 
gcloud services enable cloudbuild.googleapis.com 
gcloud services enable cloudfunctions.googleapis.com 
gcloud services enable aiplatform.googleapis.com

Альтернативой команде gcloud является поиск каждого продукта в консоли или использование этой ссылки .

Если какой-либо API отсутствует, вы всегда можете включить его в процессе реализации.

Для получения информации о командах gcloud и их использовании обратитесь к документации .

4. Настройка базы данных

В этой лабораторной работе мы будем использовать AlloyDB в качестве базы данных для хранения данных о магазине игрушек. Она использует кластеры для хранения всех ресурсов, таких как базы данных и журналы. Каждый кластер имеет основной экземпляр , который обеспечивает точку доступа к данным. Таблицы будут хранить сами данные.

Давайте создадим кластер AlloyDB, экземпляр и таблицу, куда будут загружены данные об электронной коммерции.

Создайте кластер и экземпляр.

  1. Перейдите на страницу AlloyDB в Cloud Console. Большинство страниц в Cloud Console легко найти, используя строку поиска консоли.
  2. На этой странице выберите пункт «СОЗДАТЬ КЛАСТЕР» :

f76ff480c8c889aa.png

  1. Вы увидите экран, похожий на тот, что показан ниже. Создайте кластер и экземпляр со следующими значениями (убедитесь, что значения совпадают, если вы клонируете код приложения из репозитория):
  • идентификатор кластера : " vector-cluster "
  • пароль : " alloydb "
  • Совместимость с PostgreSQL 15
  • Регион : " us-central1 "
  • Сетевые настройки : " default "

538dba58908162fb.png

  1. При выборе сети по умолчанию вы увидите экран, похожий на тот, что показан ниже.

Выберите «НАСТРОЙКА СОЕДИНЕНИЯ» .
7939bbb6802a91bf.png

  1. Затем выберите « Использовать автоматически выделенный диапазон IP-адресов » и продолжите. После проверки информации выберите «СОЗДАТЬ СОЕДИНЕНИЕ». 768ff5210e79676f.png
  2. После настройки сети вы можете продолжить создание кластера. Нажмите кнопку «СОЗДАТЬ КЛАСТЕР» , чтобы завершить настройку кластера, как показано ниже:

e06623e55195e16e.png

Обязательно измените идентификатор экземпляра на

vector-instance

Если изменить это невозможно, не забудьте изменить идентификатор экземпляра во всех последующих ссылках.

Обратите внимание, что создание кластера займет около 10 минут. После успешного завершения процесса вы увидите экран с обзором только что созданного кластера.

5. Ввод данных

Теперь пришло время добавить таблицу с данными о магазине. Перейдите в AlloyDB, выберите основной кластер, а затем AlloyDB Studio:

847e35f1bf8a8bd8.png

Возможно, вам потребуется дождаться завершения создания экземпляра. После этого войдите в AlloyDB, используя учетные данные, которые вы создали при создании кластера. Для аутентификации в PostgreSQL используйте следующие данные:

  • Имя пользователя: " postgres "
  • База данных: " postgres "
  • Пароль: " alloydb "

После успешной аутентификации в AlloyDB Studio команды SQL вводятся в редакторе. Вы можете добавить несколько окон редактора, используя значок плюса справа от последнего окна.

91a86d9469d499c4.png

Команды для AlloyDB будут вводиться в окнах редактора, используя при необходимости параметры «Выполнить», «Форматировать» и «Очистить».

Включить расширения

Для создания этого приложения мы будем использовать расширения pgvector и google_ml_integration . Расширение pgvector позволяет хранить и искать векторные представления. Расширение google_ml_integration предоставляет функции, которые вы используете для доступа к конечным точкам прогнозирования Vertex AI и получения прогнозов в формате SQL. Включите эти расширения, выполнив следующие DDL-скрипты:

CREATE EXTENSION IF NOT EXISTS google_ml_integration CASCADE;
CREATE EXTENSION IF NOT EXISTS vector;

Чтобы проверить, какие расширения включены в вашей базе данных, выполните следующую SQL-команду:

select extname, extversion from pg_extension;

Создайте таблицу

Создайте таблицу, используя приведенный ниже оператор DDL:

CREATE TABLE toys ( id VARCHAR(25), name VARCHAR(25), description VARCHAR(20000), quantity INT, price FLOAT, image_url VARCHAR(200), text_embeddings vector(768)) ;

После успешного выполнения указанной выше команды вы сможете просмотреть таблицу в базе данных.

Ввод данных

Для этой лабораторной работы у нас есть тестовые данные, содержащие около 72 записей в этом SQL-файле . Он включает поля id, name, description, quantity, price, image_url . Остальные поля будут заполнены позже в ходе лабораторной работы.

Скопируйте только первые 5 строк/операторов вставки, затем вставьте эти строки в пустую вкладку редактора и выберите «Выполнить». Если у вас НЕ пробная учетная запись, вы, вероятно, можете скопировать все операторы вставки и запустить.

Чтобы просмотреть содержимое таблицы, разверните раздел «Проводник», пока не увидите таблицу с именем apparels. Выберите триколор (⋮), чтобы увидеть возможность выполнить запрос к таблице. В новой вкладке редактора откроется оператор SELECT.

cfaa52b717f9aaed.png

Предоставить разрешение

Выполните указанную ниже команду, чтобы предоставить пользователю postgres права на выполнение функции embedding :

GRANT EXECUTE ON FUNCTION embedding TO postgres;

Предоставьте учетной записи службы AlloyDB роль пользователя Vertex AI.

Перейдите в терминал Cloud Shell и введите следующую команду:

PROJECT_ID=$(gcloud config get-value project)

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:service-$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")@gcp-sa-alloydb.iam.gserviceaccount.com" \
--role="roles/aiplatform.user"

6. Создайте векторные представления для контекста.

Компьютерам гораздо проще обрабатывать числа, чем текст. Система встраивания преобразует текст в последовательность чисел с плавающей запятой, которые должны представлять текст независимо от его формулировки, используемого языка и т. д.

Рассмотрим описание приморского места. Его можно назвать «на берегу», «на пляже», «в шаговой доступности от номера», «sur la mer», «на берегу океана» и т. д. Все эти термины выглядят по-разному, но их семантическое значение или, в терминологии машинного обучения, их векторные представления должны быть очень близки друг к другу.

Теперь, когда данные и контекст готовы, мы выполним SQL-запрос для добавления векторных представлений описания продукта в таблицу в поле embedding . Существует множество моделей векторного представления, которые можно использовать. Мы используем text-embedding-005 от Vertex AI. Обязательно используйте одну и ту же модель векторного представления во всем проекте!

Примечание: Если вы используете существующий проект Google Cloud, созданный некоторое время назад, вам может потребоваться продолжить использовать более старые версии модели встраивания текста, такие как textembedding-gecko.

Вернитесь на вкладку AlloyDB Studio и введите следующий DML-оператор:

UPDATE toys set text_embeddings = embedding( 'text-embedding-005', description);

Ещё раз взгляните на таблицу toys , чтобы увидеть некоторые встраивания. Обязательно повторно выполните оператор SELECT, чтобы увидеть изменения.

SELECT id, name, description, price, quantity, image_url, text_embeddings FROM toys;

Эта функция должна вернуть вектор эмбеддингов, который выглядит как массив чисел с плавающей запятой, для описания модели, как показано ниже:

7d32f7cd7204e1f3.png

Примечание: В новых проектах Google Cloud, созданных в рамках бесплатного уровня, могут возникнуть проблемы с ограничением количества запросов на встраивание в секунду для моделей встраивания. Мы рекомендуем использовать фильтр по ID, а затем выборочно выбирать от 1 до 5 записей и так далее при генерации встраивания.

7. Выполните векторный поиск.

Теперь, когда таблица, данные и векторные представления готовы, давайте выполним векторный поиск в реальном времени для поискового запроса пользователя.

Предположим, пользователь задает вопрос:

« I want a white plush teddy bear toy with a floral pattern ».

Вы можете найти совпадения, выполнив следующий запрос:

select * from toys
ORDER BY text_embeddings <=> CAST(embedding('text-embedding-005', 'I want a white plush teddy bear toy with a floral pattern') as vector(768))
LIMIT 2;

Давайте рассмотрим этот запрос подробнее:

В этом запросе,

  1. Поисковый запрос пользователя: " I want a white plush teddy bear toy with a floral pattern. "
  2. Мы преобразуем его в векторные представления в методе embedding() , используя модель: text-embedding-005 . Этот шаг должен показаться вам знакомым после предыдущего шага, где мы применили функцию embedding ко всем элементам таблицы.
  3. " <=> " обозначает использование метода расстояния COSINE SIMILARITY . Все доступные меры сходства можно найти в документации pgvector .
  4. Мы преобразуем результат метода встраивания в векторный тип, чтобы обеспечить его совместимость с векторами, хранящимися в базе данных.
  5. Параметр LIMIT 5 означает, что мы хотим извлечь 5 ближайших соседей для искомого текста.

Результат выглядит так:

fa7f0fc3a4c68804.png

Как вы можете видеть в результатах, совпадения довольно близки к поисковому тексту. Попробуйте изменить текст, чтобы посмотреть, как изменятся результаты.

Важное примечание:

Теперь предположим, что мы хотим повысить производительность (время выполнения запроса), эффективность и полноту результатов векторного поиска, используя индекс ScaNN . Пожалуйста, ознакомьтесь с шагами в этом блоге , чтобы сравнить разницу в результатах с использованием индекса и без него.

Дополнительный шаг: повышение эффективности и полноты с помощью индекса ScaNN.

Пропустите этот шаг, если количество записей меньше 100.

Для удобства здесь перечислены шаги по созданию индекса:

  1. Поскольку кластер, экземпляр, контекст и эмбеддинги уже созданы, нам остается только установить расширение ScaNN, используя следующую команду:
CREATE EXTENSION IF NOT EXISTS alloydb_scann;
  1. Далее мы создадим индекс (ScaNN):
CREATE INDEX toysearch_index ON toys
USING scann (text_embeddings cosine)
WITH (num_leaves=9);

В приведенном выше DDL-скрипте apparel_index — это имя индекса.

«Игрушки» — это мой стол.

«Scann» — это метод индексации.

«embedding» — это столбец в таблице, который я хочу проиндексировать.

"Косинус" — это метод измерения расстояния, который я хочу использовать с индексом.

"8" — это количество разделов, применяемых к этому индексу. Установите любое значение от 1 до 1048576. Для получения дополнительной информации о том, как определить это значение, см. раздел "Настройка индекса ScaNN" .

Я использовал квадратный корень из количества точек данных, как рекомендовано в репозитории ScaNN (при разбиении на разделы num_leaves должно быть приблизительно равно квадратному корню из количества точек данных).

  1. Проверьте, создан ли индекс, используя следующий запрос:
SELECT * FROM pg_stat_ann_indexes;
  1. Выполните векторный поиск, используя тот же запрос, что и раньше, но без индекса:
select * from toys
ORDER BY text_embeddings <=> CAST(embedding('text-embedding-005', 'I want a white plush teddy bear toy with a floral pattern') as vector(768))
LIMIT 5;

Приведенный выше запрос — это тот же самый запрос, который мы использовали в лабораторной работе на шаге 8. Однако теперь у нас есть индексированное поле.

  1. Проведите тест с помощью простого поискового запроса с индексом и без него (удалив индекс):

В этом варианте использования всего 72 записи, поэтому индекс фактически не оказывает никакого эффекта. Результаты теста, проведенного в другом варианте использования , следующие:

Тот же самый запрос Vector Search к индексированным данным эмбеддингов обеспечивает качественные результаты поиска и повышает эффективность. Эффективность значительно улучшается (по времени выполнения: 10,37 мс без ScaNN и 0,87 мс с ScaNN) при использовании индекса. Для получения дополнительной информации по этой теме, пожалуйста, обратитесь к этому блогу .

8. Проверка соответствия с LLM

Прежде чем переходить к созданию сервиса для подбора наиболее подходящих вариантов для приложения, давайте воспользуемся моделью генеративного ИИ, чтобы проверить, действительно ли эти потенциальные ответы релевантны и безопасны для предоставления пользователю.

Убедитесь, что экземпляр настроен для работы с Gemini.

Сначала проверьте, включена ли интеграция Google ML для вашего кластера и экземпляра. В AlloyDB Studio выполните следующую команду:

show google_ml_integration.enable_model_support;

Если значение отображается как «включено» , вы можете пропустить следующие 2 шага и сразу перейти к настройке интеграции AlloyDB и Vertex AI Model.

  1. Перейдите к основному экземпляру вашего кластера AlloyDB и нажмите «РЕДАКТИРОВАТЬ ОСНОВНОЙ ЭКЗЕМПЛЯР».

cb76b934ba3735bd.png

  1. Перейдите в раздел «Флаги» в расширенных параметрах конфигурации и убедитесь, что google_ml_integration.enable_model_support flag установлен в значение « on », как показано ниже:

6a59351fcd2a9d35.png

Если параметр не установлен в положение «включено», установите его в положение «включено», а затем нажмите кнопку «ОБНОВИТЬ ЭКЗЕМПЛЯР ». Этот шаг займет несколько минут.

Интеграция AlloyDB и модели искусственного интеллекта Vertex

Теперь вы можете подключиться к AlloyDB Studio и выполнить следующую инструкцию DML для настройки доступа к моделям Gemini из AlloyDB, используя идентификатор вашего проекта там, где это указано. Перед выполнением команды может появиться предупреждение о синтаксической ошибке, но в целом она должна выполниться без проблем.

Для начала создадим подключение к модели Gemini 1.5, как показано ниже. Не забудьте заменить $PROJECT_ID в приведенной ниже команде на идентификатор вашего проекта Google Cloud.

CALL
 google_ml.create_model( model_id => 'gemini-1.5',
   model_request_url => 'https://us-central1-aiplatform.googleapis.com/v1/projects/$PROJECT_ID/locations/us-central1/publishers/google/models/gemini-1.5-pro:streamGenerateContent',
   model_provider => 'google',
   model_auth_type => 'alloydb_service_agent_iam');

Проверить модели, настроенные для доступа, можно с помощью следующей команды в AlloyDB Studio:

select model_id,model_type from google_ml.model_info_view;        

Наконец, нам необходимо предоставить пользователям базы данных разрешение на выполнение функции ml_predict_row для запуска прогнозов с помощью моделей искусственного интеллекта Google Vertex. Выполните следующую команду:

GRANT EXECUTE ON FUNCTION ml_predict_row to postgres;

Примечание: Если вы используете существующий проект Google Cloud и кластер/экземпляр AlloyDB, созданный некоторое время назад, вам может потребоваться удалить старые ссылки на модель gemini-1.5 и создать ее заново с помощью приведенного выше оператора CALL, а затем снова запустить команду grant execute on function ml_predict_row, если у вас возникнут проблемы при последующих вызовах gemini-1.5.

Оценка ответов

Хотя в следующем разделе мы будем использовать один большой запрос, чтобы убедиться в его адекватности, сам запрос может быть сложным для понимания. Сейчас мы рассмотрим его составляющие и через несколько минут увидим, как они объединяются.

  1. Сначала мы отправим запрос в базу данных, чтобы получить 10 наиболее близких совпадений с запросом пользователя.
  2. Чтобы определить, насколько корректны ответы, мы воспользуемся внешним запросом, в котором объясним, как оценивать ответы. В качестве части запроса используется поле recommended_text , которое содержит текст поиска, и content (поле описания игрушки) внутренней таблицы.
  3. Используя эти данные, мы затем оценим «качество» полученных ответов.
  4. Функция predict_row возвращает результат в формате JSON. Код " -> 'candidates' -> 0 -> 'content' -> 'parts' -> 0 -> 'text'" используется для извлечения фактического текста из этого JSON. Чтобы увидеть фактический возвращаемый JSON, вы можете удалить этот код.
  5. Наконец, чтобы получить ответ LLM, мы извлекаем его с помощью REGEXP_REPLACE(gemini_validation, '[^a-zA-Z,: ]', '', 'g')
SELECT id,
       name,
       content,
       quantity,
       price,
       image_url,
       recommended_text,
       REGEXP_REPLACE(gemini_validation, '[^a-zA-Z,: ]', '', 'g') AS gemini_validation
  FROM (SELECT id,
               name,
               content,
               quantity,
               price,
               image_url,
               recommended_text,
               CAST(ARRAY_AGG(LLM_RESPONSE) AS TEXT) AS gemini_validation
          FROM (SELECT id,
                       name,
                       content,
                       quantity,
                       price,
                       image_url,
                       recommended_text,
                       json_array_elements(google_ml.predict_row(model_id => 'gemini-1.5',
                                                                   request_body => CONCAT('{ "contents": [ { "role": "user", "parts": [ { "text": "User wants to buy a toy and this is the description of the toy they wish to buy: ',                                                                                              recommended_text,                                                                                              '. Check if the following product items from the inventory are close enough to really, contextually match the user description. Here are the items: ',                                                                                         content,                                                                                         '. Return a ONE-LINE response with 3 values: 1) MATCH: if the 2 contexts are reasonably matching in terms of any of the color or color family specified in the list, approximate style match with any of the styles mentioned in the user search text: This should be a simple YES or NO. Choose NO only if it is completely irrelevant to users search criteria. 2) PERCENTAGE: percentage of match, make sure that this percentage is accurate 3) DIFFERENCE: A clear one-line easy description of the difference between the 2 products. Remember if the user search text says that some attribute should not be there, and the record has it, it should be a NO match. " } ] } ] }')::JSON)) -> 'candidates' -> 0 -> 'content' -> 'parts' -> 0 -> 'text' :: TEXT AS LLM_RESPONSE
                  FROM (SELECT id,
                               name,
                               description AS content,
                               quantity,
                               price,
                               image_url,
                               'Pink panther standing' AS recommended_text
                          FROM toys
                         ORDER BY text_embeddings <=> embedding('text-embedding-005',
                                                                'Pink panther standing')::VECTOR
                         LIMIT 1) AS xyz) AS X
         GROUP BY id,
                  name,
                  content,
                  quantity,
                  price,
                  image_url,
                  recommended_text) AS final_matches

-- WHERE REGEXP_REPLACE(gemini_validation, '[^a-zA-Z,: ]', '', 'g') LIKE '%MATCH%:%YES%';

Хотя это всё ещё может показаться сложным, надеюсь, вы сможете лучше это понять. Результаты показывают, есть ли совпадение, каков процент совпадения и некоторое объяснение рейтинга.

Обратите внимание, что в модели Gemini потоковая передача включена по умолчанию, поэтому фактический ответ распределяется по нескольким строкам:

c2b006aeb3f3a2fc.png

9. Перенесите поиск игрушек в облако без использования серверов.

Готовы перенести это приложение в веб-среду? Следуйте приведенным ниже шагам, чтобы сделать этот механизм знаний бессерверным с помощью Cloud Run Functions:

  1. Чтобы создать новую функцию Cloud Run Function, перейдите в раздел Cloud Run Functions в консоли Google Cloud или воспользуйтесь ссылкой: https://console.cloud.google.com/functions/add .
  2. Выберите среду " Cloud Run function ". Укажите имя функции " get-toys-alloydb " и регион "us-central1". Установите аутентификацию в значение "Разрешить неаутентифицированные вызовы" и нажмите "Далее ". Выберите Java 17 в качестве среды выполнения и встроенный редактор для исходного кода.
  3. По умолчанию точка входа будет установлена ​​на " gcfv2.HelloHttpFunction ". Замените код-заполнитель в HelloHttpFunction.java и pom.xml вашей функции Cloud Run кодом из HelloHttpFunction.java и pom.xml соответственно.
  4. Не забудьте заменить заполнитель <<YOUR_PROJECT>> и учетные данные для подключения к AlloyDB на ваши значения в Java-файле. Учетные данные AlloyDB — это те, которые мы использовали в начале этого практического занятия. Если вы использовали другие значения, пожалуйста, измените их в Java-файле.
  5. Нажмите «Развернуть» .

После развертывания, чтобы обеспечить доступ облачной функции к нашему экземпляру базы данных AlloyDB, мы создадим коннектор VPC .

ВАЖНЫЙ ШАГ:

После завершения развертывания вы сможете увидеть функции в консоли Google Cloud Run Functions . Найдите только что созданную функцию ( get-toys-alloydb ), щелкните по ней, затем нажмите «Редактировать» и измените следующие параметры:

  1. Перейдите в раздел «Среда выполнения», «Сборка», «Подключения» и «Параметры безопасности».
  2. Увеличьте время ожидания до 180 секунд.
  3. Перейдите на вкладку «СОЕДИНЕНИЯ»:

4e83ec8a339cda08.png

  1. В настройках входящего трафика убедитесь, что выбран параметр «Разрешить весь трафик».
  2. В настройках исходящего трафика щелкните раскрывающееся меню «Сеть» и выберите пункт «Добавить новый коннектор VPC», затем следуйте инструкциям в появившемся диалоговом окне:

8126ec78c343f199.png

  1. Укажите имя для VPC-коннектора и убедитесь, что регион совпадает с регионом вашего экземпляра. Оставьте значение «Сеть» по умолчанию и установите подсеть как «Пользовательский диапазон IP-адресов» с диапазоном IP-адресов 10.8.0.0 или аналогичным доступным значением.
  2. Разверните раздел «ПОКАЗАТЬ НАСТРОЙКИ МАСШТАБИРОВАНИЯ» и убедитесь, что параметры конфигурации установлены точно следующим образом:

7baf980463a86a5c.png

  1. Нажмите кнопку СОЗДАТЬ, и этот соединитель должен появиться в списке настроек исходящего трафика.
  2. Выберите только что созданный соединитель.
  3. Выберите вариант, при котором весь трафик будет направляться через этот VPC-коннектор.
  4. Нажмите «Далее» , а затем «Развернуть» .

10. Протестируйте функцию запуска облачных сервисов.

После развертывания обновленной облачной функции вы должны увидеть сгенерированную конечную точку. Скопируйте ее и замените в следующей команде:

В качестве альтернативы, вы можете протестировать функцию Cloud Run следующим образом:

PROJECT_ID=$(gcloud config get-value project)

curl -X POST <<YOUR_ENDPOINT>> \
  -H 'Content-Type: application/json' \
  -d '{"search":"I want a standing pink panther toy"}' \
  | jq .

И вот результат:

23861e9091565a64.png

Вот и всё! Выполнить поиск по вектору сходства с использованием модели Embeddings на данных AlloyDB очень просто.

11. Создание клиентского приложения для веб-сайта!

В этой части мы создадим веб-приложение, с помощью которого пользователь сможет взаимодействовать с игрушками, находить подходящие по тексту и изображению, а также создавать новые игрушки в соответствии со своими потребностями. Поскольку приложение уже создано, вы можете выполнить описанные ниже шаги, чтобы скопировать его в свою IDE и запустить приложение.

  1. Поскольку для описания изображения, которое пользователь может загрузить для поиска подходящих игрушек, мы используем Flash Gemini 2.0, нам необходимо получить API-ключ для этого приложения. Для этого перейдите по ссылке https://aistudio.google.com/apikey и получите API-ключ для вашего активного проекта Google Cloud, в котором вы внедряете это приложение, и сохраните ключ где-нибудь:

ae2db169e6a94e4a.png

  1. Перейдите в терминал Cloud Shell.
  2. Клонируйте репозиторий с помощью следующей команды:
git clone https://github.com/AbiramiSukumaran/toysearch

cd toysearch
  1. После клонирования репозитория вы сможете получить доступ к проекту из редактора Cloud Shell .
  2. Необходимо удалить папки "get-toys-alloydb" и "toolbox-toys" из клонированного проекта, поскольку они содержат код Cloud Run Functions, на который можно ссылаться из репозитория при необходимости.
  3. Перейдите в файл GenerateToy.java в папке web, найдите следующую строку и удалите её, поскольку разрешение на использование файлов cookie для взрослых может потребовать специальных прав, которые могут быть недоступны для некоторых пробных учетных записей:

paramsMap.put("personGeneration", "allow_adult");

  1. Перед сборкой и развертыванием приложения убедитесь, что установлены все необходимые переменные среды. Перейдите в терминал Cloud Shell и выполните следующие действия:
PROJECT_ID=$(gcloud config get-value project)

export PROJECT_ID=$PROJECT_ID

export GOOGLE_API_KEY=<YOUR API KEY that you saved>
  1. Соберите и запустите приложение локально:

Убедитесь, что вы находитесь в каталоге проекта, и выполните следующие команды:

mvn package

mvn spring-boot:run 
  1. Развертывание в Cloud Run
gcloud run deploy --source .

12. Понимание деталей генеративного ИИ

Никаких действий не требуется. Просто для вашего понимания:

Теперь, когда приложение готово к развертыванию, уделите немного времени, чтобы понять, как мы реализовали поиск (текстовый и графический) и генерацию результатов.

  1. Поиск векторных изображений на основе текста пользователя:

Эта проблема уже решена в облачных функциях запуска, которые мы развернули в разделе «Переход к веб-версии приложения Vector Search».

  1. Векторный поиск на основе загрузки изображений:

Вместо того чтобы вводить контекст в виде текста, предположим, пользователь хочет загрузить изображение знакомой игрушки, по которой он хочет выполнить поиск. Пользователи могут загрузить изображение понравившейся игрушки и получить соответствующие характеристики.

Для анализа изображения и извлечения релевантного контекста, такого как цвет игрушки, материал, тип и целевая возрастная группа, мы используем модель Google Gemini 2.0 Flash, запущенную с помощью LangChain4j.

Всего за 5 шагов мы преобразовали многомодальный ввод данных пользователем в результаты сопоставления с использованием больших языковых моделей, применяя фреймворк с открытым исходным кодом. Узнайте, как это работает:

package cloudcode.helloworld.web;

import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.googleai.GoogleAiGeminiChatModel;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.model.output.Response;
import dev.langchain4j.data.message.ImageContent;
import dev.langchain4j.data.message.TextContent;
import java.util.Base64;
import java.util.Optional;

public class GeminiCall {
  public String imageToBase64String(byte[] imageBytes) {
    String base64Img = Base64.getEncoder().encodeToString(imageBytes);
    return base64Img;
  }

  public String callGemini(String base64ImgWithPrefix) throws Exception {
    String searchText = "";

    // 1. Remove the prefix
    String base64Img = base64ImgWithPrefix.replace("data:image/jpeg;base64,", "");

    // 2. Decode base64 to bytes
    byte[] imageBytes = Base64.getDecoder().decode(base64Img);
    String image = imageToBase64String(imageBytes);

    // 3. Get API key from environment variable
        String apiKey = Optional.ofNullable(System.getenv("GOOGLE_API_KEY"))
                .orElseThrow(() -> new IllegalArgumentException("GOOGLE_API_KEY environment variable not set"));

    // 4. Invoke Gemini 2.0
    ChatLanguageModel gemini = GoogleAiGeminiChatModel.builder()
        .apiKey(apiKey)
        .modelName("gemini-2.0-flash-001")
        .build();

    Response<AiMessage> response = gemini.generate(
        UserMessage.from(
            ImageContent.from(image, "image/jpeg"),
            TextContent.from(
                "The picture has a toy in it. Describe the toy in the image in one line. Do not add any prefix or title to your description. Just describe that toy that you see in the image in one line, do not describe the surroundings and other objects around the toy in the image. If you do not see any toy in the image, send  response stating that no toy is found in the input image.")));
   
    // 5. Get the text from the response and send it back to the controller
    searchText = response.content().text().trim();
    System.out.println("searchText inside Geminicall: " + searchText);
    return searchText;
  }
}
  1. Узнайте, как мы использовали Imagen 3 для создания персонализированной игрушки по запросу пользователя с помощью генеративного искусственного интеллекта.

Затем Imagen 3 генерирует изображение специально разработанной игрушки, предоставляя пользователю наглядную визуализацию своего творения. Вот как мы это сделали всего за 5 шагов:

// Generate an image using a text prompt using an Imagen model
    public String generateImage(String projectId, String location, String prompt)
        throws ApiException, IOException {
      final String endpoint = String.format("%s-aiplatform.googleapis.com:443", location);
      PredictionServiceSettings predictionServiceSettings =
      PredictionServiceSettings.newBuilder().setEndpoint(endpoint).build();
     
      // 1. Set up the context and prompt
      String context = "Generate a photo-realistic image of a toy described in the following input text from the user. Make sure you adhere to all the little details and requirements mentioned in the prompt. Ensure that the user is only describing a toy. If it is anything unrelated to a toy, politely decline the request stating that the request is inappropriate for the current context. ";
      prompt = context + prompt;

      // 2. Initialize a client that will be used to send requests. This client only needs to be created
      // once, and can be reused for multiple requests.
      try (PredictionServiceClient predictionServiceClient =
          PredictionServiceClient.create(predictionServiceSettings)) {
 
      // 3. Invoke Imagen 3
        final EndpointName endpointName =
            EndpointName.ofProjectLocationPublisherModelName(
                projectId, location, "google", "imagen-3.0-generate-001"); //"imagegeneration@006"; imagen-3.0-generate-001
        Map<String, Object> instancesMap = new HashMap<>();
        instancesMap.put("prompt", prompt);
        Value instances = mapToValue(instancesMap);
        Map<String, Object> paramsMap = new HashMap<>();
        paramsMap.put("sampleCount", 1);
        paramsMap.put("aspectRatio", "1:1");
        paramsMap.put("safetyFilterLevel", "block_few");
        paramsMap.put("personGeneration", "allow_adult");
        paramsMap.put("guidanceScale", 21);
        paramsMap.put("imagenControlScale", 0.95); //Setting imagenControlScale
        Value parameters = mapToValue(paramsMap);
       
      // 4. Get prediction response image
        PredictResponse predictResponse =
            predictionServiceClient.predict(
                endpointName, Collections.singletonList(instances), parameters);

      // 5. Return the Base64 Encoded String to the controller
        for (Value prediction : predictResponse.getPredictionsList()) {
          Map<String, Value> fieldsMap = prediction.getStructValue().getFieldsMap();
          if (fieldsMap.containsKey("bytesBase64Encoded")) {
            bytesBase64EncodedOuput = fieldsMap.get("bytesBase64Encoded").getStringValue();
        }
      }
      return bytesBase64EncodedOuput.toString();
    }
  }

Прогноз цен

В предыдущем разделе мы обсуждали, как Imagen генерирует изображение игрушки, которую пользователь хочет создать самостоятельно. Для того чтобы он мог её купить, приложение должно установить цену, и мы использовали интуитивно понятную логику для определения цены на игрушку, изготовленную на заказ. Логика заключается в использовании средней цены 5 наиболее подходящих игрушек (по описанию) по дизайну, созданному пользователем.

Прогнозирование цены сгенерированной игрушки является важной частью этого приложения, и для этого мы использовали агентный подход. Представляем Gen AI Toolbox для баз данных.

13. Gen AI Toolbox для баз данных

Gen AI Toolbox for Databases — это сервер с открытым исходным кодом от Google, который упрощает создание инструментов Gen AI для взаимодействия с базами данных. Он позволяет разрабатывать инструменты проще, быстрее и безопаснее, обрабатывая такие сложные процессы, как объединение соединений, аутентификация и многое другое. Он помогает создавать инструменты Gen AI, которые позволяют вашим агентам получать доступ к данным в вашей базе данных.

Вот шаги, которые необходимо выполнить, чтобы подготовить ваш инструмент и сделать наше приложение агентским: Ссылка на Toolbox Codelab

Теперь ваше приложение может использовать эту развернутую конечную точку Cloud Run Function для заполнения поля цены вместе с результатом, полученным с помощью Imagen, для изображения игрушки, изготовленной на заказ.

14. Протестируйте ваше веб-приложение.

Теперь, когда все компоненты вашего приложения созданы и развернуты, оно готово к работе в облаке. Протестируйте приложение во всех сценариях. Вот ссылка на видео, демонстрирующее возможные сценарии тестирования:

https://www.youtube.com/shorts/ZMqUAWsghYQ

Вот как выглядит целевая страница:

241db19e7176e93e.png

15. Уборка

Чтобы избежать списания средств с вашего аккаунта Google Cloud за ресурсы, использованные в этой статье, выполните следующие действия:

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

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

Поздравляем! Вы успешно выполнили контекстный поиск и генерацию изображений для магазина игрушек, используя AlloyDB, pgvector, Imagen и Gemini 2.0, а также библиотеки с открытым исходным кодом для создания надежных интеграций. Объединив возможности AlloyDB , Vertex AI и Vector Search , мы сделали огромный шаг вперед, сделав контекстный и векторный поиск доступным, эффективным и действительно содержательным.