Aplikacja do wyszukiwania zabawek z bazami danych w chmurze, środowiskiem uruchomieniowym bez serwera i integracjami z oprogramowaniem open source

1. Przegląd

Wyobraź sobie, że wchodzisz do sklepu z zabawkami wirtualnie lub stacjonarnie i bez trudu znajdujesz idealny prezent. Możesz opisać, czego szukasz, przesłać zdjęcie zabawki lub nawet zaprojektować własną kreację, a sklep od razu zrozumie Twoje potrzeby i zapewni Ci spersonalizowane wrażenia. To nie jest futurystyczna fantazja, ale rzeczywistość oparta na AI, technologii chmurowej i wizji spersonalizowanego e-commerce.

Wyzwanie: znalezienie idealnego produktu, który odpowiada Twoim wyobrażeniom, może być trudne. Ogólne wyszukiwane hasła, słowa kluczowe i wyszukiwania przybliżone często nie przynoszą oczekiwanych rezultatów, przeglądanie nieskończonych stron może być nużące, a rozbieżność między tym, co sobie wyobrażasz, a tym, co jest dostępne, może prowadzić do frustracji.

Rozwiązanie: aplikacja demonstracyjna bezpośrednio rozwiązuje ten problem, wykorzystując moc AI do zapewnienia prawdziwie spersonalizowanego i płynnego środowiska dzięki wyszukiwaniu kontekstowemu i niestandardowemu generowaniu produktu pasującego do kontekstu wyszukiwania.

Co utworzysz

W ramach tego modułu:

  1. Tworzenie instancji AlloyDB i wczytywanie zbioru danych Toys
  2. Włączanie rozszerzeń pgvector i modelu generatywnej AI w AlloyDB
  3. Generuj wektory z opisu produktu i przeprowadzaj w czasie rzeczywistym wyszukiwanie podobieństwa kosinusowego dla tekstu wyszukiwanego przez użytkownika.
  4. Wywoływanie Gemini 2.0 Flash w celu opisania obrazu przesłanego przez użytkownika na potrzeby wyszukiwania kontekstowego zabawek
  5. Wywoływanie Imagen 3 w celu niestandardowego tworzenia zabawki na podstawie zainteresowań użytkownika
  6. Wywołaj narzędzie do prognozowania cen utworzone za pomocą zestawu narzędzi generatywnej AI dla baz danych, aby uzyskać szczegółowe informacje o cenie niestandardowej zabawki.
  7. Wdrażanie rozwiązania w bezserwerowych funkcjach Cloud Run

Wymagania

  • przeglądarka, np. Chrome lub Firefox;
  • Projekt Google Cloud z włączonymi płatnościami.

2. Architektura

Przepływ danych: przyjrzyjmy się bliżej temu, jak dane przepływają przez nasz system:

  1. Wyszukiwanie kontekstowe z wykorzystaniem techniki RAG (Retrieval Augmented Generation) opartej na AI

Wyobraź sobie, że zamiast szukać tylko „czerwonego samochodu”, system rozumie te informacje:

„mały pojazd odpowiedni dla 3-letniego chłopca”.

AlloyDB jako podstawa: do przechowywania danych o zabawkach, w tym opisów, adresów URL obrazów i innych istotnych atrybutów, używamy AlloyDB, czyli usługi w pełni zarządzanej i zgodnej z PostgreSQL bazy danych Google Cloud.

pgvector do wyszukiwania semantycznego: pgvector, rozszerzenie PostgreSQL, umożliwia przechowywanie wektorów dystrybucyjnych opisów zabawek i zapytań użytkowników. Umożliwia to wyszukiwanie semantyczne, co oznacza, że system rozumie znaczenie słów, a nie tylko dokładne słowa kluczowe.

Podobieństwo cosinusowe na potrzeby trafności: używamy podobieństwa cosinusowego do pomiaru podobieństwa semantycznego między wektorem wyszukiwania użytkownika a wektorami opisu zabawki, aby wyświetlać najtrafniejsze wyniki.

Indeks ScaNN zapewniający szybkość i dokładność: aby zapewnić szybkie i dokładne wyniki, zwłaszcza w miarę wzrostu naszego asortymentu zabawek, integrujemy indeks ScaNN (Scalable Nearest Neighbors). Znacznie zwiększa to wydajność i przypominanie wyszukiwania wektorowego.

  1. Wyszukiwanie i interpretowanie obrazów za pomocą Gemini 2.0 Flash

Załóżmy, że zamiast wpisywać kontekst w formie tekstu użytkownik chce przesłać zdjęcie znanej zabawki, za pomocą której chce wyszukać informacje. Użytkownicy mogą przesłać zdjęcie zabawki, która im się podoba, i uzyskać odpowiednie funkcje. Do analizy obrazu i wyodrębniania odpowiedniego kontekstu, np. koloru, materiału, typu i grupy wiekowej zabawki, używamy modelu Gemini 2.0 Flash od Google, który jest wywoływany za pomocą LangChain4j.

  1. Budowanie wymarzonej zabawki dostosowanej za pomocą generatywnej AI: Imagen 3

Prawdziwa magia zaczyna się jednak, gdy użytkownicy zdecydują się stworzyć własną zabawkę. Dzięki Imagen 3 mogą opisać wymarzoną zabawkę za pomocą prostych promptów tekstowych. Wyobraź sobie, że możesz powiedzieć: „Chcę pluszowego smoka z fioletowymi skrzydłami i przyjazną twarzą”, a potem zobaczyć go na ekranie. Następnie Imagen 3 generuje obraz zaprojektowanej przez użytkownika zabawki, dzięki czemu może on zobaczyć, jak wygląda jego dzieło.

  1. Prognozowanie cen oparte na agentach i zestawie narzędzi MCP dla baz danych

Wprowadziliśmy funkcję prognozowania ceny, która szacuje koszt wyprodukowania zaprojektowanej przez Ciebie zabawki. Jest to możliwe dzięki agentowi, który zawiera zaawansowane narzędzie do obliczania cen.

Narzędzia MCP dla baz danych: ten agent jest bezproblemowo zintegrowany z naszą bazą danych za pomocą nowego narzędzia open source od Google, czyli narzędzi MCP dla baz danych. Dzięki temu agent może uzyskać dostęp do danych w czasie rzeczywistym dotyczących kosztów materiałów, procesów produkcyjnych i innych istotnych czynników, aby podać dokładną szacunkową cenę. Więcej informacji na ten temat znajdziesz tutaj.

  1. Java Spring Boot, Gemini Code Assist i Cloud Run do sprawnego tworzenia i wdrażania aplikacji bezserwerowych

Cała aplikacja jest zbudowana przy użyciu Java Spring Boot, czyli solidnej i skalowalnej platformy. W procesie programowania korzystaliśmy z Gemini Code Assist, zwłaszcza w przypadku programowania front-endu, co znacznie przyspieszyło cykl programowania i poprawiło jakość kodu. Do wdrożenia całej aplikacji użyliśmy Cloud Run, a do wdrożenia funkcji bazy danych i funkcji agenta jako niezależnych punktów końcowych – Cloud Run Functions.

3. Zanim zaczniesz

Utwórz projekt

  1. W konsoli Google Cloud na stronie wyboru projektu wybierz lub utwórz projekt Google Cloud.
  2. Sprawdź, czy w projekcie Cloud włączone są płatności. Dowiedz się, jak sprawdzić, czy w projekcie są włączone płatności .
  3. Będziesz używać Cloud Shell, czyli środowiska wiersza poleceń działającego w Google Cloud, które jest wstępnie załadowane narzędziem bq. U góry konsoli Google Cloud kliknij Aktywuj Cloud Shell.

Obraz przycisku aktywowania Cloud Shell

  1. Po połączeniu z Cloud Shell sprawdź, czy uwierzytelnianie zostało już przeprowadzone, a projekt jest już ustawiony na Twój identyfikator projektu, używając tego polecenia:
gcloud auth list
  1. Aby potwierdzić, że polecenie gcloud zna Twój projekt, uruchom w Cloud Shell to polecenie:
gcloud config list project
  1. Jeśli projekt nie jest ustawiony, użyj tego polecenia, aby go ustawić:
gcloud config set project <YOUR_PROJECT_ID>
  1. Włącz wymagane interfejsy API, uruchamiając w terminalu Cloud Shell te polecenia:

Istnieje też jedno polecenie, które umożliwia uruchomienie poniższych funkcji, ale jeśli korzystasz z konta próbnego, możesz napotkać problemy z limitami podczas próby włączenia tych funkcji zbiorczo. Dlatego polecenia są wyróżnione w osobnych wierszach.

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

Alternatywą dla polecenia gcloud jest wyszukanie poszczególnych usług w konsoli lub skorzystanie z tego linku.

Jeśli pominiesz jakiś interfejs API, możesz go włączyć w trakcie wdrażania.

Informacje o poleceniach gcloud i ich użyciu znajdziesz w dokumentacji.

4. Konfiguracja bazy danych

W tym module użyjemy AlloyDB jako bazy danych do przechowywania danych sklepu z zabawkami. Używa klastrów do przechowywania wszystkich zasobów, takich jak bazy danych i logi. Każdy klaster ma instancję główną, która zapewnia punkt dostępu do danych. Tabele będą zawierać rzeczywiste dane.

Utwórzmy klaster, instancję i tabelę AlloyDB, do których zostanie wczytany zbiór danych e-commerce.

Tworzenie klastra i instancji

  1. Otwórz stronę AlloyDB w konsoli Cloud. Najprostszym sposobem na znalezienie większości stron w Cloud Console jest wyszukanie ich za pomocą paska wyszukiwania w konsoli.
  2. Na tej stronie kliknij UTWÓRZ KLASTER:

f76ff480c8c889aa.png

  1. Wyświetli się ekran podobny do tego poniżej. Utwórz klaster i instancję z tymi wartościami (upewnij się, że wartości są zgodne, jeśli klonujesz kod aplikacji z repozytorium):
  • id klastra: „vector-cluster
  • password: "alloydb"
  • Zgodność z PostgreSQL 15
  • Region:us-central1
  • Sieć: „default

538dba58908162fb.png

  1. Po wybraniu sieci domyślnej zobaczysz ekran podobny do tego poniżej.

Kliknij SKONFIGURUJ POŁĄCZENIE.
7939bbb6802a91bf.png

  1. Następnie wybierz „Użyj automatycznie przydzielonego zakresu adresów IP” i kliknij Dalej. Po sprawdzeniu informacji kliknij UTWÓRZ POŁĄCZENIE. 768ff5210e79676f.png
  2. Po skonfigurowaniu sieci możesz kontynuować tworzenie klastra. Kliknij UTWÓRZ KLASTER, aby dokończyć konfigurowanie klastra, jak pokazano poniżej:

e06623e55195e16e.png

Pamiętaj, aby zmienić identyfikator instancji na

vector-instance

Jeśli nie możesz go zmienić, pamiętaj, aby zmienić identyfikator instancji we wszystkich kolejnych odwołaniach.

Pamiętaj, że utworzenie klastra zajmie około 10 minut. Po zakończeniu procesu powinien wyświetlić się ekran z omówieniem utworzonego klastra.

5. Pozyskiwanie danych

Teraz dodaj tabelę z danymi o sklepie. Otwórz AlloyDB, wybierz klaster główny, a następnie AlloyDB Studio:

847e35f1bf8a8bd8.png

Może być konieczne poczekanie na zakończenie tworzenia instancji. Gdy to zrobisz, zaloguj się w AlloyDB przy użyciu danych logowania utworzonych podczas tworzenia klastra. Do uwierzytelniania w PostgreSQL użyj tych danych:

  • Nazwa użytkownika: „postgres
  • Baza danych: „postgres
  • Hasło: „alloydb

Po pomyślnym uwierzytelnieniu w AlloyDB Studio polecenia SQL są wprowadzane w Edytorze. Możesz dodać wiele okien Edytora, klikając znak plusa po prawej stronie ostatniego okna.

91a86d9469d499c4.png

Polecenia dla AlloyDB będziesz wpisywać w oknach edytora, używając w razie potrzeby opcji Uruchom, Formatuj i Wyczyść.

Włącz rozszerzenia

Do utworzenia tej aplikacji użyjemy rozszerzeń pgvectorgoogle_ml_integration. Rozszerzenie pgvector umożliwia przechowywanie wektorów dystrybucyjnych i wyszukiwanie ich. Rozszerzenie google_ml_integration udostępnia funkcje, których możesz używać do uzyskiwania dostępu do punktów końcowych prognozowania Vertex AI w celu uzyskiwania prognoz w SQL. Włącz te rozszerzenia, uruchamiając te DDL:

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

Jeśli chcesz sprawdzić, które rozszerzenia są włączone w bazie danych, uruchom to polecenie SQL:

select extname, extversion from pg_extension;

Tworzenie tabeli

Utwórz tabelę za pomocą instrukcji DDL poniżej:

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

Po pomyślnym wykonaniu powyższego polecenia powinna być widoczna tabela w bazie danych.

Pozyskiwanie danych

W tym module użyjemy danych testowych zawierających około 72 rekordy w pliku SQL. Zawiera pola id, name, description, quantity, price, image_url. Pozostałe pola wypełnisz w dalszej części tego modułu.

Skopiuj tylko pierwsze 5 wierszy lub instrukcji wstawiania, a następnie wklej je w pustej karcie edytora i kliknij URUCHOM. Jeśli NIE korzystasz z próbnego konta rozliczeniowego, prawdopodobnie możesz skopiować wszystkie instrukcje wstawiania i je uruchomić.

Aby wyświetlić zawartość tabeli, rozwiń sekcję Eksplorator, aż zobaczysz tabelę o nazwie apparels. Kliknij ikonę trzech kropek (⋮), aby wyświetlić opcję Zapytanie do tabeli. Instrukcja SELECT otworzy się w nowej karcie Edytora.

cfaa52b717f9aaed.png

Przyznaj uprawnienia

Uruchom to polecenie, aby przyznać użytkownikowi postgres uprawnienia do wykonywania funkcji embedding:

GRANT EXECUTE ON FUNCTION embedding TO postgres;

Przyznawanie roli Użytkownik Vertex AI kontu usługi AlloyDB

Otwórz terminal Cloud Shell i wpisz to polecenie:

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. Tworzenie wektorów dystrybucyjnych dla kontekstu

Komputery znacznie łatwiej przetwarzają liczby niż tekst. System osadzania przekształca tekst w ciąg liczb zmiennoprzecinkowych, które powinny reprezentować tekst niezależnie od tego, jak jest sformułowany, w jakim języku jest napisany itp.

Rozważ opisanie lokalizacji nadmorskiej. Może to być np. „on the water”, „beachfront”, „walk from your room to the ocean”, „sur la mer”, „на берегу океана” itp. Te terminy wyglądają inaczej, ale ich znaczenie semantyczne lub, w terminologii uczenia maszynowego, ich osadzenia powinny być bardzo zbliżone.

Gdy dane i kontekst będą gotowe, uruchomimy SQL, aby dodać do tabeli w polu embedding wektory osadzeń opisu produktu. Możesz używać różnych modeli osadzania. Korzystamy z text-embedding-005 z Vertex AI. Pamiętaj, aby w całym projekcie używać tego samego modelu wektorów.

Uwaga: jeśli używasz istniejącego projektu Google Cloud utworzonego jakiś czas temu, być może musisz nadal używać starszych wersji modelu do osadzania tekstu, np. textembedding-gecko.

Wróć na kartę AlloyDB Studio i wpisz ten język DML:

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

Jeszcze raz spójrz na toys tabelę, aby zobaczyć niektóre osadzenia. Aby zobaczyć zmiany, ponownie uruchom instrukcję SELECT.

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

Powinien on zwrócić wektor osadzania, który wygląda jak tablica liczb zmiennoprzecinkowych, dla opisu zabawki, jak pokazano poniżej:

7d32f7cd7204e1f3.png

Uwaga: nowo utworzone projekty Google Cloud w ramach bezpłatnego poziomu mogą mieć problemy z limitami liczby żądań osadzania na sekundę do modeli osadzania. Podczas generowania osadzania zalecamy użycie zapytania filtra dla identyfikatora, a następnie selektywne wybieranie 1–5 rekordów itd.

7. Wykonywanie wyszukiwania wektorowego

Tabela, dane i wektory są już gotowe. Przeprowadźmy teraz wyszukiwanie wektorowe w czasie rzeczywistym dla tekstu wyszukiwanego przez użytkownika.

Załóżmy, że użytkownik zada pytanie:

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

Aby znaleźć pasujące elementy, uruchom to zapytanie:

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;

Przyjrzyjmy się temu zapytaniu szczegółowo:

W tym zapytaniu

  1. Tekst wyszukiwania użytkownika to: „I want a white plush teddy bear toy with a floral pattern.
  2. Przekształcamy go w wektory za pomocą metody embedding() i modelu text-embedding-005. Ten krok powinien być Ci znany z poprzedniego, w którym zastosowaliśmy funkcję osadzania do wszystkich elementów w tabeli.
  3. <=>” oznacza użycie metody odległości COSINE SIMILARITY. Wszystkie dostępne miary podobieństwa znajdziesz w dokumentacji pgvector.
  4. Wynik metody wektorów dystrybucyjnych przekształcamy w typ wektorowy, aby był zgodny z wektorami przechowywanymi w bazie danych.
  5. LIMIT 5 oznacza, że chcemy wyodrębnić 5 najbliższych sąsiadów dla tekstu wyszukiwania.

Wynik wygląda tak:

fa7f0fc3a4c68804.png

Jak widać w wynikach, dopasowania są dość zbliżone do tekstu wyszukiwania. Spróbuj zmienić tekst, aby zobaczyć, jak zmieniają się wyniki.

Ważna uwaga:

Załóżmy, że chcemy zwiększyć wydajność (czas zapytania), skuteczność i przypominanie tego wyniku wyszukiwania wektorowego za pomocą indeksu ScaNN. Aby porównać różnicę w wynikach z indeksem i bez niego, przeczytaj instrukcje w tym artykule na blogu.

Krok opcjonalny: zwiększanie wydajności i przypominania za pomocą indeksu ScaNN

Jeśli liczba rekordów jest mniejsza niż 100, zignoruj ten krok.

Dla wygody podajemy tutaj tylko kroki tworzenia indeksu:

  1. Mamy już utworzone klaster, instancję, kontekst i osadzenia, więc wystarczy zainstalować rozszerzenie ScaNN za pomocą tego polecenia:
CREATE EXTENSION IF NOT EXISTS alloydb_scann;
  1. Następnie utworzymy indeks (ScaNN):
CREATE INDEX toysearch_index ON toys
USING scann (text_embeddings cosine)
WITH (num_leaves=9);

W powyższym DDL apparel_index to nazwa indeksu.

„toys” to moja tabela

„scann” to metoda indeksowania.

„embedding” to kolumna w tabeli, którą chcę indeksować.

„cosine” to metoda pomiaru odległości, której chcę używać w przypadku indeksu.

„8” to liczba partycji, które mają być zastosowane do tego indeksu. Ustaw dowolną wartość z zakresu od 1 do 1048576. Więcej informacji o tym, jak określić tę wartość, znajdziesz w artykule Dostrajanie indeksu ScaNN.

Użyłem PIERWIASTKA KWADRATOWEGO z liczby punktów danych zgodnie z zaleceniami w repozytorium ScaNN (podczas dzielenia na partycje liczba węzłów powinna być w przybliżeniu pierwiastkiem kwadratowym z liczby punktów danych).

  1. Sprawdź, czy indeks został utworzony, za pomocą tego zapytania:
SELECT * FROM pg_stat_ann_indexes;
  1. Przeprowadź wyszukiwanie wektorowe za pomocą tego samego zapytania, którego użyliśmy bez indeksu:
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;

Powyższe zapytanie jest takie samo jak to, którego użyliśmy w laboratorium w kroku 8. Teraz jednak pole jest już zaindeksowane.

  1. Przeprowadź test za pomocą prostego zapytania wyszukiwania z indeksem i bez niego (usuwając indeks):

W tym przypadku użycia jest tylko 72 rekordy, więc indeks nie ma większego znaczenia. W przypadku testu przeprowadzonego w innym przypadku użycia wyniki są następujące:

To samo zapytanie wyszukiwania wektorowego dotyczące ZINDEKSOWANYCH danych wektorów dystrybucyjnych daje wysokiej jakości wyniki wyszukiwania i wydajność. Dzięki indeksowi wydajność jest znacznie większa (czas wykonania: 10,37 ms bez ScaNN i 0,87 ms z ScaNN). Więcej informacji na ten temat znajdziesz na tym blogu.

8. Weryfikacja dopasowania za pomocą LLM

Zanim przejdziemy dalej i utworzymy usługę zwracającą najlepsze dopasowania do aplikacji, użyjmy modelu generatywnej AI, aby sprawdzić, czy te potencjalne odpowiedzi są rzeczywiście trafne i bezpieczne dla użytkownika.

Sprawdzanie, czy instancja jest skonfigurowana pod kątem Gemini

Najpierw sprawdź, czy integracja Google ML jest już włączona w przypadku Twojego klastra i instancji. W AlloyDB Studio wpisz to polecenie:

show google_ml_integration.enable_model_support;

Jeśli wartość to „włączone”, możesz pominąć 2 kolejne kroki i przejść bezpośrednio do konfigurowania integracji AlloyDB i modelu Vertex AI.

  1. Otwórz instancję główną klastra AlloyDB i kliknij EDYTUJ INSTANCJĘ GŁÓWNĄ.

cb76b934ba3735bd.png

  1. W sekcji Opcje konfiguracji zaawansowanej otwórz sekcję Flagi. i upewnij się, że google_ml_integration.enable_model_support flag jest ustawiony na „on”, jak pokazano poniżej:

6a59351fcd2a9d35.png

Jeśli nie jest ustawiona na „włączone”, ustaw ją na „włączone”, a następnie kliknij przycisk ZAKTUALIZUJ INSTANCJĘ. Ten krok zajmie kilka minut.

Integracja AlloyDB i modelu Vertex AI

Teraz możesz połączyć się z AlloyDB Studio i uruchomić następującą instrukcję DML, aby skonfigurować dostęp do modelu Gemini z AlloyDB, używając w odpowiednim miejscu identyfikatora projektu. Przed uruchomieniem polecenia może pojawić się ostrzeżenie o błędzie składni, ale powinno ono działać prawidłowo.

Najpierw tworzymy połączenie z modelem Gemini 1.5, jak pokazano poniżej. Pamiętaj, aby w poniższym poleceniu zastąpić $PROJECT_ID identyfikatorem projektu 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');

Modele skonfigurowane pod kątem dostępu możesz sprawdzić za pomocą tego polecenia w AlloyDB Studio:

select model_id,model_type from google_ml.model_info_view;        

Na koniec musimy przyznać użytkownikom bazy danych uprawnienia do wykonywania funkcji ml_predict_row, aby mogli uruchamiać prognozy za pomocą modeli Google Vertex AI. Uruchom to polecenie:

GRANT EXECUTE ON FUNCTION ml_predict_row to postgres;

Uwaga: jeśli używasz istniejącego projektu Google Cloud i istniejącego klastra lub instancji AlloyDB utworzonych jakiś czas temu, może być konieczne usunięcie starych odwołań do modelu gemini-1.5 i ponowne utworzenie ich za pomocą powyższego polecenia CALL oraz ponowne uruchomienie polecenia grant execute on function ml_predict_row w przypadku wystąpienia problemów z przyszłymi wywołaniami modelu gemini-1.5.

Ocena odpowiedzi

W następnej sekcji użyjemy jednego dużego zapytania, aby upewnić się, że odpowiedzi są rozsądne, ale samo zapytanie może być trudne do zrozumienia. Za chwilę przyjrzymy się poszczególnym elementom i sprawdzimy, jak działają razem.

  1. Najpierw wyślemy do bazy danych żądanie pobrania 10 najbardziej zbliżonych wyników do zapytania użytkownika.
  2. Aby określić, jak trafne są odpowiedzi, użyjemy zapytania zewnętrznego, w którym wyjaśnimy, jak je oceniać. Używa pola recommended_text, które jest tekstem wyszukiwania, oraz pola content (czyli pola opisu zabawki) wewnętrznej tabeli jako części zapytania.
  3. Na tej podstawie ocenimy jakość zwróconych odpowiedzi.
  4. Funkcja predict_row zwraca wynik w formacie JSON. Kod „-> 'candidates' -> 0 -> 'content' -> 'parts' -> 0 -> 'text'"” służy do wyodrębniania rzeczywistego tekstu z tego pliku JSON. Aby zobaczyć rzeczywisty zwrócony kod JSON, możesz usunąć ten kod.
  5. Na koniec, aby uzyskać odpowiedź LLM, wyodrębniamy ją za pomocą funkcji 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%';

Chociaż może to nadal wyglądać zniechęcająco, mamy nadzieję, że teraz jest to dla Ciebie bardziej zrozumiałe. Wyniki wskazują, czy występuje dopasowanie, jaki jest jego odsetek i zawierają wyjaśnienie oceny.

Zwróć uwagę, że model Gemini ma domyślnie włączone przesyłanie strumieniowe, więc rzeczywista odpowiedź jest rozłożona na kilka wierszy:

c2b006aeb3f3a2fc.png

9. Przenoszenie wyszukiwarki zabawek do chmury w sposób bezserwerowy

Chcesz udostępnić tę aplikację w internecie? Aby przekształcić ten bezserwerowy silnik wiedzy w funkcję Cloud Run, wykonaj te czynności:

  1. Otwórz Cloud Run Functions w konsoli Google Cloud, aby UTWORZYĆ nową funkcję Cloud Run, lub użyj linku: https://console.cloud.google.com/functions/add.
  2. Wybierz środowisko „Funkcja Cloud Run”. Podaj nazwę funkcji „get-toys-alloydb” i wybierz region „us-central1”. Ustaw uwierzytelnianie na „Zezwalaj na nieuwierzytelnione wywołania” i kliknij DALEJ. Wybierz Java 17 jako środowisko wykonawcze i Edytor wbudowany jako kod źródłowy.
  3. Domyślnie punkt wejścia zostanie ustawiony na „gcfv2.HelloHttpFunction”. Zastąp kod zastępczy w HelloHttpFunction.javapom.xml funkcji Cloud Run kodem z plików HelloHttpFunction.javapom.xml.
  4. Pamiętaj, aby w pliku Java zastąpić obiekt zastępczy <<YOUR_PROJECT>> i dane logowania do połączenia z AlloyDB swoimi wartościami. Dane logowania do AlloyDB to te, których użyliśmy na początku tego samouczka. Jeśli używasz innych wartości, zmień je w pliku Java.
  5. Kliknij Wdróż.

Po wdrożeniu, aby umożliwić funkcji w Cloud Functions dostęp do instancji bazy danych AlloyDB, utworzymy oprogramowanie sprzęgające VPC.

WAŻNY KROK:

Po rozpoczęciu wdrażania funkcje powinny być widoczne w konsoli funkcji Cloud Run w Google. Wyszukaj nowo utworzoną funkcję (get-toys-alloydb), kliknij ją, a potem kliknij EDYTUJ i zmień te ustawienia:

  1. Otwórz Ustawienia środowiska wykonawczego, kompilacji, połączeń i zabezpieczeń
  2. Zwiększ limit czasu do 180 sekund.
  3. Otwórz kartę POŁĄCZENIA:

4e83ec8a339cda08.png

  1. W ustawieniach ruchu przychodzącego upewnij się, że wybrana jest opcja „Zezwalaj na cały ruch”.
  2. W sekcji Ustawienia ruchu wychodzącego kliknij menu Sieć i wybierz opcję „Dodaj nowy łącznik VPC”. Postępuj zgodnie z instrukcjami w wyświetlonym oknie dialogowym, które się pojawi:

8126ec78c343f199.png

  1. Podaj nazwę oprogramowania sprzęgającego VPC i upewnij się, że region jest taki sam jak w przypadku instancji. Pozostaw domyślną wartość Sieć i ustaw Podsieć jako Niestandardowy zakres adresów IP z zakresem adresów IP 10.8.0.0 lub podobnym, który jest dostępny.
  2. Rozwiń POKAŻ USTAWIENIA SKALOWANIA i upewnij się, że konfiguracja jest dokładnie taka:

7baf980463a86a5c.png

  1. Kliknij UTWÓRZ. Ten łącznik powinien być teraz widoczny w ustawieniach ruchu wychodzącego.
  2. Wybierz nowo utworzony łącznik.
  3. Wybierz opcję kierowania całego ruchu przez to oprogramowanie sprzęgające VPC.
  4. Kliknij DALEJ, a potem WDRAŻAJ.

10. Testowanie funkcji Cloud Run

Po wdrożeniu zaktualizowanej funkcji w Cloud Functions powinien pojawić się wygenerowany punkt końcowy. Skopiuj go i zastąp nim fragment w tym poleceniu:

Możesz też przetestować funkcję Cloud Run w ten sposób:

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 .

Wynik:

23861e9091565a64.png

To wszystko. Wyszukiwanie wektorowe podobieństwa za pomocą modelu wektorów dystrybucyjnych w danych AlloyDB jest tak proste.

11. Tworzenie klienta aplikacji internetowej

W tej części stworzymy aplikację internetową, która umożliwi użytkownikom interakcję i znajdowanie pasujących zabawek na podstawie tekstu lub obrazu, a nawet tworzenie nowych zabawek zgodnie z ich potrzebami. Aplikacja jest już utworzona, więc możesz wykonać podane niżej czynności, aby skopiować ją do środowiska IDE i uruchomić.

  1. Do opisywania obrazu, który użytkownik może przesłać w celu znalezienia pasujących zabawek, używamy modelu Gemini 2.0 Flash, dlatego musimy uzyskać klucz API dla tej aplikacji. Aby to zrobić, otwórz stronę https://aistudio.google.com/apikey i uzyskaj klucz interfejsu API dla aktywnego projektu Google Cloud, w którym wdrażasz tę aplikację. Zapisz klucz w bezpiecznym miejscu:

ae2db169e6a94e4a.png

  1. Otwieranie terminala Cloud Shell
  2. Sklonuj repozytorium za pomocą tego polecenia:
git clone https://github.com/AbiramiSukumaran/toysearch

cd toysearch
  1. Po sklonowaniu repozytorium będziesz mieć dostęp do projektu z edytora Cloud Shell.
  2. Musisz usunąć z sklonowanego projektu foldery „get-toys-alloydb” i „toolbox-toys”, ponieważ zawierają one kod funkcji Cloud Run, do którego możesz się odwoływać z repozytorium, gdy będzie to potrzebne.
  3. W folderze web otwórz plik GenerateToy.java i znajdź ten wiersz, a następnie go usuń, ponieważ zezwolenie na treści dla dorosłych może wymagać specjalnych uprawnień, które mogą być niedostępne w przypadku niektórych próbnych kont rozliczeniowych:

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

  1. Zanim utworzysz i wdrożysz aplikację, sprawdź, czy ustawione są wszystkie niezbędne zmienne środowiskowe. Otwórz terminal Cloud Shell i wykonaj te czynności:
PROJECT_ID=$(gcloud config get-value project)

export PROJECT_ID=$PROJECT_ID

export GOOGLE_API_KEY=<YOUR API KEY that you saved>
  1. Skompiluj i uruchom aplikację lokalnie:

Upewnij się, że jesteś w katalogu projektu, i uruchom te polecenia:

mvn package

mvn spring-boot:run 
  1. Wdrażanie w Cloud Run
gcloud run deploy --source .

12. Informacje o generatywnej AI

Nie musisz niczego robić. Dla Twojej informacji:

Aplikacja jest już gotowa do wdrożenia, więc poświęć chwilę na zrozumienie, jak przeprowadziliśmy wyszukiwanie (tekstu i obrazu) oraz generowanie.

  1. Wyszukiwanie wektorowe na podstawie tekstu użytkownika:

Zostało to już uwzględnione w funkcjach Cloud Run, które wdrożyliśmy w sekcji „Korzystanie z aplikacji internetowej wyszukiwania wektorowego”.

  1. Wyszukiwanie wektorowe na podstawie przesłanego obrazu:

Załóżmy, że zamiast wpisywać kontekst w formie tekstu użytkownik chce przesłać zdjęcie znanej zabawki, za pomocą której chce wyszukać informacje. Użytkownicy mogą przesłać zdjęcie zabawki, która im się podoba, i uzyskać odpowiednie funkcje.

Do analizy obrazu i wyodrębniania odpowiedniego kontekstu, np. koloru, materiału, typu i grupy wiekowej zabawki, używamy modelu Gemini 2.0 Flash od Google, który jest wywoływany za pomocą LangChain4j.

W 5 krokach przekształciliśmy dane multimodalne użytkownika w pasujące wyniki, wywołując duży model językowy za pomocą platformy open source. Dowiedz się, jak to zrobić:

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. Dowiedz się, jak za pomocą Imagen 3 i generatywnej AI stworzyliśmy spersonalizowaną zabawkę na podstawie prośby użytkownika.

Następnie Imagen 3 generuje obraz zaprojektowanej przez użytkownika zabawki, dzięki czemu może on zobaczyć, jak wygląda jego dzieło. Oto jak to zrobiliśmy w 5 krokach:

// 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();
    }
  }

Prognozowanie cen

W poprzedniej sekcji omówiliśmy, jak Imagen generuje obraz zabawki, którą użytkownik chce zaprojektować samodzielnie. Aby użytkownicy mogli kupić zabawkę, aplikacja musi określić jej cenę. Zastosowaliśmy intuicyjną logikę, aby zdefiniować cenę niestandardowej zabawki na zamówienie. Logika polega na użyciu średniej ceny 5 najbardziej podobnych zabawek (pod względem opisu) do zabawki zaprojektowanej przez użytkownika.

Prognoza ceny wygenerowanej zabawki jest ważną częścią tej aplikacji. Do jej utworzenia użyliśmy podejścia opartego na agentach. Przedstawiamy zestaw narzędzi generatywnej AI dla baz danych.

13. Zestaw narzędzi generatywnej AI do baz danych

Zestaw narzędzi generatywnej AI dla baz danych to serwer open source od Google, który ułatwia tworzenie narzędzi generatywnej AI do interakcji z bazami danych. Umożliwia łatwiejsze, szybsze i bezpieczniejsze tworzenie narzędzi dzięki obsłudze złożonych procesów, takich jak pula połączeń, uwierzytelnianie i inne. Umożliwia tworzenie narzędzi opartych na generatywnej AI, które pozwalają agentom uzyskiwać dostęp do danych w bazie danych.

Aby przygotować narzędzie i sprawić, że nasza aplikacja będzie działać jak agent, wykonaj te czynności: Link do laboratorium kodu Toolbox

Aplikacja może teraz używać tego wdrożonego punktu końcowego funkcji Cloud Run do wypełniania ceny wraz z wygenerowanym wynikiem Imagen dla niestandardowego obrazu zabawki na zamówienie.

14. Testowanie aplikacji internetowej

Wszystkie komponenty aplikacji zostały utworzone i wdrożone, więc można je wyświetlać w chmurze. Przetestuj aplikację we wszystkich scenariuszach. Oto link do filmu, który pokazuje, czego możesz się spodziewać:

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

Tak wygląda strona docelowa:

241db19e7176e93e.png

15. Czyszczenie danych

Aby uniknąć obciążenia konta Google Cloud opłatami za zasoby użyte w tym poście, wykonaj te czynności:

  1. W konsoli Google Cloud otwórz stronę Zarządzanie zasobami.
  2. Z listy projektów wybierz projekt do usunięcia, a potem kliknij Usuń.
  3. W oknie wpisz identyfikator projektu i kliknij Wyłącz, aby usunąć projekt.

16. Gratulacje

Gratulacje! Udało Ci się przeprowadzić kontekstowe wyszukiwanie i generowanie w sklepie z zabawkami przy użyciu AlloyDB, pgvector, Imagen i Gemini 2.0, a także bibliotek open source do tworzenia niezawodnych integracji. Łącząc możliwości AlloyDB, Vertex AIwyszukiwarki wektorowej, zrobiliśmy ogromny krok naprzód w udostępnianiu wyszukiwania kontekstowego i wektorowego, które jest wydajne i naprawdę oparte na znaczeniu.