1. Wprowadzenie
Z tego ćwiczenia z programowania dowiesz się, jak uzyskać najlepsze wyniki za pomocą interfejsu QueryData API. Dowiesz się, jak generować dokładne i powtarzalne wyniki, gdy wysyłasz do bazy danych zapytania w języku naturalnym. Aby Ci w tym pomóc, w tym przewodniku znajdziesz 6 przepisów w kilku rozdziałach, które omawiają różne aspekty przekształcania języka naturalnego na SQL (NL2SQL) i jego praktyczne zastosowania.
Wymagania wstępne
- Podstawowa wiedza o Google Cloud i konsoli Google Cloud
- Podstawowe umiejętności korzystania z interfejsu wiersza poleceń i Cloud Shell
Czego się nauczysz
- Jak wdrożyć klaster AlloyDB i instancję główną
- Włączanie funkcji QueryData, funkcji AI i sparametryzowanych bezpiecznych widoków
- Jak utworzyć zbiór kontekstów QueryData
- Jak używać różnych szablonów i kontekstów w interfejsie QueryData API
- Jak zwiększyć elastyczność szablonów QueryData za pomocą aspektów
- Jak rozwiązywać problemy z niejednoznacznością danych za pomocą wyszukiwania wartości i indeksowania trigramów
- Jak używać funkcji AI do wyszukiwania semantycznego za pomocą szablonów
- Jak korzystać z bezpiecznych widoków z parametrami w interfejsie QueryData API
Czego potrzebujesz
- konto Google Cloud i projekt w chmurze Google Cloud,
- przeglądarka internetowa, np. Chrome, obsługująca konsolę Google Cloud i Cloud Shell;
2. Konfiguracja i wymagania
Konfiguracja projektu
- Zaloguj się w konsoli Google Cloud. Jeśli nie masz jeszcze konta Gmail lub Google Workspace, musisz je utworzyć.
Używaj konta osobistego zamiast konta służbowego lub szkolnego.
- Utwórz nowy projekt lub użyj istniejącego. Aby utworzyć nowy projekt w konsoli Google Cloud, w nagłówku kliknij przycisk Wybierz projekt. Otworzy się okno wyskakujące.

W oknie Wybierz projekt kliknij Nowy projekt. Otworzy się okno dialogowe nowego projektu.

W oknie wpisz nazwę projektu i wybierz lokalizację.

- Nazwa projektu to wyświetlana nazwa dla uczestników tego projektu. Nazwa projektu nie jest używana przez interfejsy API Google i można ją w każdej chwili zmienić.
- Identyfikator projektu jest unikalny we wszystkich projektach Google Cloud i nie można go zmienić po ustawieniu. Konsola Google Cloud automatycznie generuje unikalny identyfikator, ale możesz go dostosować. Jeśli wygenerowany identyfikator Ci się nie podoba, możesz wygenerować kolejny losowy identyfikator lub podać własny, aby sprawdzić jego dostępność. W większości ćwiczeń z programowania musisz odwoływać się do identyfikatora projektu, który jest zwykle oznaczony zmienną PROJECT_ID.
- Warto wiedzieć, że istnieje jeszcze trzecia wartość, numer projektu, której używają niektóre interfejsy API. Więcej informacji o tych 3 wartościach znajdziesz w dokumentacji.
Włącz płatności
Konfigurowanie osobistego konta rozliczeniowego
Jeśli skonfigurujesz płatności za pomocą środków w Google Cloud, możesz pominąć ten krok.
Aby skonfigurować osobiste konto rozliczeniowe, włącz płatności w Cloud Console.
Uwagi:
- Pod względem opłat za zasoby chmury ukończenie tego modułu powinno kosztować mniej niż 5 USD.
- Jeśli chcesz uniknąć dalszych opłat, wykonaj czynności opisane na końcu tego modułu, aby usunąć zasoby.
- Nowi użytkownicy mogą skorzystać z bezpłatnego okresu próbnego, w którym mają do dyspozycji środki w wysokości 300 USD.
Uruchamianie Cloud Shell
Z Google Cloud można korzystać zdalnie na laptopie, ale w tym ćwiczeniu użyjesz Google Cloud Shell, czyli środowiska wiersza poleceń działającego w chmurze.
W konsoli Google Cloud kliknij ikonę Cloud Shell na pasku narzędzi w prawym górnym rogu:

Możesz też nacisnąć G, a potem S. Ta sekwencja aktywuje Cloud Shell, jeśli korzystasz z konsoli Google Cloud. Możesz też użyć tego linku.
Uzyskanie dostępu do środowiska i połączenie się z nim powinno zająć tylko kilka chwil. Po zakończeniu powinno wyświetlić się coś takiego:

Ta maszyna wirtualna zawiera wszystkie potrzebne narzędzia dla programistów. Zawiera również stały katalog domowy o pojemności 5 GB i działa w Google Cloud, co znacznie zwiększa wydajność sieci i usprawnia proces uwierzytelniania. Wszystkie zadania w tym laboratorium możesz wykonać w przeglądarce. Nie musisz niczego instalować.
3. Zanim zaczniesz
Włącz API
Aby korzystać z usług AlloyDB, Compute Engine, usług sieciowych i platformy agentów, włącz odpowiednie interfejsy API w projekcie Google Cloud.
W terminalu Cloud Shell sprawdź, czy identyfikator projektu jest skonfigurowany:
gcloud config get-value project
Polecenie powinno zwrócić identyfikator Twojego projektu Google Cloud.
Ustaw zmienną środowiskową PROJECT_ID:
PROJECT_ID=$(gcloud config get-value project)
Włącz wymagane interfejsy API:
gcloud services enable alloydb.googleapis.com \
compute.googleapis.com \
cloudresourcemanager.googleapis.com \
servicenetworking.googleapis.com \
geminidataanalytics.googleapis.com \
cloudaicompanion.googleapis.com \
aiplatform.googleapis.com \
dataplex.googleapis.com
Oczekiwane dane wyjściowe:
student@cloudshell:~ (test-project-001-402417)$ gcloud config set project test-project-001-402417
Updated property [core/project].
student@cloudshell:~ (test-project-001-402417)$ PROJECT_ID=$(gcloud config get-value project)
Your active configuration is: [cloudshell-14650]
student@cloudshell:~ (test-project-001-402417)$
student@cloudshell:~ (test-project-001-402417)$ gcloud services enable alloydb.googleapis.com \
compute.googleapis.com \
cloudresourcemanager.googleapis.com \
servicenetworking.googleapis.com \
geminidataanalytics.googleapis.com \
cloudaicompanion.googleapis.com \
aiplatform.googleapis.com \
dataplex.googleapis.com
Operation "operations/acat.p2-4470404856-1f44ebd8-894e-4356-bea7-b84165a57442" finished successfully.
Informacje o każdym włączonym interfejsie API znajdziesz w dokumentacji.
4. Wdrażanie AlloyDB
Utwórz klaster AlloyDB i instancję główną. Możesz wdrożyć go za pomocą gotowego skryptu, który wdroży wszystkie niezbędne zasoby, lub zrobić to samodzielnie krok po kroku, korzystając z dokumentacji AlloyDB.
Wdrażanie AlloyDB za pomocą automatycznego skryptu
W tym podejściu używany jest automatyczny skrypt do wdrażania klastra AlloyDB i podawania niezbędnych szczegółów konfiguracji, aby można było rozpocząć pracę z wdrożonymi zasobami.
W terminalu Cloud Shell uruchom polecenie, aby sklonować skrypt wdrożenia z repozytorium:
REPO_NAME="codelabs"
REPO_URL="https://github.com/GoogleCloudPlatform/$REPO_NAME"
SOURCE_DIR="alloydb-ai-nla"
git clone --no-checkout --filter=blob:none --depth=1 $REPO_URL
cd $REPO_NAME
git sparse-checkout set $SOURCE_DIR
git checkout
cd $SOURCE_DIR
Uruchom skrypt wdrażania:
./deploy_alloydb.sh
Wykonanie skryptu zajmuje od 5 do 7 minut. W danych wyjściowych pojawią się szczegóły wdrożenia klastra. Pamiętaj, że Twoje hasło będzie inne – zapisz je, aby używać go w przyszłości.
... <redacted> ... Creating primary instance: alloydb-aip-01-pr (8 vCPUs for TRIAL cluster) Operation ID: operation-1765988049916-646282264938a-bddce198-9f248715 Creating instance...done. ---------------------------------------- Deployment Process Completed Cluster: alloydb-aip-01 (TRIAL) Instance: alloydb-aip-01-pr Region: us-central1 Initial Password: JBBoDTgixzYwYpkF (if new cluster) ----------------------------------------
Wyświetl nowy klaster i instancję na stronie Klastry AlloyDB w konsoli Google Cloud.

5. Przygotowywanie bazy danych
Utwórz bazę danych, włącz integrację z Gemini Enterprise Agent Platform, utwórz obiekty bazy danych i dodaj przykładowe dane.
Przyznawanie niezbędnych uprawnień do AlloyDB
Przypisz wymagane role uprawnień do agenta usługi AlloyDB:
Otwórz nową kartę Cloud Shell, klikając Dodaj (+).

W nowej karcie wpisz:
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"
Oczekiwane dane wyjściowe konsoli:
student@cloudshell:~ (test-project-001-402417)$ PROJECT_ID=$(gcloud config get-value project) Your active configuration is: [cloudshell-11039] student@cloudshell:~ (test-project-001-402417)$ 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" Updated IAM policy for project [test-project-001-402417]. bindings: - members: - serviceAccount:service-4470404856@gcp-sa-alloydb.iam.gserviceaccount.com role: roles/aiplatform.user - members: ... etag: BwYIEbe_Z3U= version: 1
Włączanie interfejsu Data Access API
Włącz interfejs Data Access API w klastrze AlloyDB.
Na tej samej karcie terminala uruchom to polecenie:
PROJECT_ID=$(gcloud config get-value project)
REGION=us-central1
ADBCLUSTER=alloydb-aip-01
curl -X PATCH \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
https://alloydb.googleapis.com/v1alpha/projects/$PROJECT_ID/locations/$REGION/clusters/$ADBCLUSTER/instances/$ADBCLUSTER-pr?updateMask=dataApiAccess \
-d '{
"dataApiAccess": "ENABLED",
}'
Aktualizowanie flag bazy danych
Zanim włączysz flagi na poziomie instancji, poczekaj, aż zakończy się krok dotyczący interfejsu Data Access API. Sprawdź, czy stan instancji jest oznaczony zielonym znacznikiem wyboru, jak pokazano na ilustracji poniżej.

Aby korzystać z rozszerzeń AI i uwierzytelniania z użyciem uprawnień w przypadku narzędzi opartych na agentach, musisz włączyć określone flagi bazy danych w instancji i dodać siebie jako użytkownika bazy danych. Włącz te flagi:
- alloydb.iam_authentication
- password.enforce_complexity
- google_ml_integration.enable_ai_query_engine
- parameterized_views
Na tej samej karcie terminala uruchom te polecenia:
PROJECT_ID=$(gcloud config get-value project)
REGION=us-central1
ADBCLUSTER=alloydb-aip-01
gcloud beta alloydb instances update $ADBCLUSTER-pr \
--database-flags password.enforce_complexity=on,alloydb.iam_authentication=on,google_ml_integration.enable_ai_query_engine=on,parameterized_views.enabled=on \
--region=$REGION \
--cluster=$ADBCLUSTER \
--project=$PROJECT_ID \
--update-mode=FORCE_APPLY
Utwórz użytkownika uprawnień do AlloyDB na swoim koncie:
REGION=us-central1
ADBCLUSTER=alloydb-aip-01
gcloud alloydb users create $(gcloud config get-value account) \
--cluster=$ADBCLUSTER \
--superuser=true \
--region=$REGION \
--type=IAM_BASED
Zamknij kartę, uruchamiając w terminalu polecenie exit:
exit
Łączenie z AlloyDB Studio
W kolejnych rozdziałach możesz uruchamiać wszystkie polecenia SQL, które wymagają połączenia z bazą danych w AlloyDB Studio.
Aby otworzyć AlloyDB Studio, wykonaj te czynności:
- W konsoli Google Cloud otwórz stronę Klastry w AlloyDB for PostgreSQL.
- Wybierz instancję główną, aby otworzyć jej interfejs internetowy.

- Następnie po lewej stronie kliknij AlloyDB Studio:

- Wybierz bazę danych
postgres, kliknij Uwierzytelnianie IAM i kliknij Uwierzytelnij.

Otworzy się interfejs AlloyDB Studio.
- Aby otworzyć edytor SQL, kliknij kartę Bez tytułu.

Otworzy się edytor, w którym możesz uruchamiać polecenia SQL, jak pokazano na tym obrazie:

Utwórz bazę danych
Tworzenie bazy danych na potrzeby szybkiego startu
Aby utworzyć bazę danych, uruchom w edytorze AlloyDB Studio to polecenie:
CREATE DATABASE quickstart_db
Oczekiwane dane wyjściowe:
Statement executed successfully
Połącz z bazą danych quickstart_db
W AlloyDB Studio kliknij Zmień bazę danych lub użytkownika.

Na liście baz danych wybierz quickstart_db, wybierz uwierzytelnianie IAM i uwierzytelnij się.

Otworzy się nowe połączenie, w którym możesz pracować z obiektami z bazy danych quickstart_db.
Włączanie rozszerzeń
Sprawdź wszystkie wymagane rozszerzenia, aby upewnić się, że są włączone w bazie danych i mają odpowiednie minimalne wersje.
Sprawdź, czy w bazie danych znajdują się wszystkie wymagane rozszerzenia:
SELECT extname, extversion FROM pg_extension WHERE extname IN ('vector', 'google_ml_integration', 'parameterized_views');
Oczekiwane dane wyjściowe:
extname | extversion -----------------------+---------------- google_ml_integration | 1.5.9 parameterized_views | 1.0 vector | 0.8.2.google-1
Jeśli nie zwróci wszystkich powyższych wierszy, oznacza to, że niektóre rozszerzenia są wyłączone.
Zanim włączysz rozszerzenia, musisz się upewnić, że w instancji są włączone wszystkie flagi bazy danych „google_ml_integration.enable_ai_query_engine” i „parameterized_views.enabled”.
W AlloyDB Studio po połączeniu z bazą danych quickstart_db wykonaj to polecenie:
show google_ml_integration.enable_ai_query_engine;
show parameterized_views.enabled;
Jeśli któryś z nich jest wyłączony, powtórz krok „Aktualizowanie flag bazy danych” i ponownie sprawdź stan za pomocą poleceń „show…”.
Włącz wymagane rozszerzenia.
W AlloyDB Studio po połączeniu z bazą danych quickstart_db wykonaj to polecenie:
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS parameterized_views;
Sprawdź wersje rozszerzeń parameterized_views i google_ml.
W AlloyDB Studio po połączeniu z bazą danych quickstart_db wykonaj to polecenie:
SELECT extname, extversion FROM pg_extension WHERE extname IN ('vector', 'google_ml_integration', 'parameterized_views');
Oczekiwane dane wyjściowe:
extname | extversion -----------------------+---------------- google_ml_integration | 1.5.9 parameterized_views | 1.0 vector | 0.8.2.google-1
Jeśli wersja jest niższa niż wymagana, spróbuj zaktualizować rozszerzenie parameterized_views.
W przypadku sparametryzowanych widoków spróbuj:
ALTER EXTENSION parameterized_views UPDATE;
Sprawdź jeszcze raz wersję.
W AlloyDB Studio po połączeniu z bazą danych quickstart_db wykonaj to polecenie:
SELECT extname, extversion FROM pg_extension WHERE extname IN ('vector', 'google_ml_integration', 'parameterized_views');
Oczekiwane dane wyjściowe:
extname | extversion -----------------------+---------------- google_ml_integration | 1.5.9 parameterized_views | 1.0 vector | 0.8.2.google-1
6. Przykładowe dane
Teraz musisz utworzyć obiekty w bazie danych i załadować dane. Użyjesz fikcyjnego zbioru danych firmy z branży nieruchomości. Zawiera fikcyjne dane o sprzedaży nieruchomości i agentach. Dane zostały podane w skrypcie SQL real_estate_data.sql w sklonowanym repozytorium.
Tworzenie zasobnika Cloud Storage
Użyj pakietu Google SDK (gcloud), aby zaimportować dane ze sklonowanego repozytorium do bazy danych AlloyDB. W tym celu musisz utworzyć zasobnik Cloud Storage i przyznać dostęp do konta usługi AlloyDB. Możesz też spróbować zrobić to za pomocą konsoli internetowej, zgodnie z opisem w dokumentacji.
W terminalu Google Cloud Shell wykonaj to polecenie:
PROJECT_ID=$(gcloud config get-value project)
REGION=us-central1
gcloud storage buckets create gs://$PROJECT_ID-import --project=$PROJECT_ID --location=$REGION
gcloud storage buckets add-iam-policy-binding gs://$PROJECT_ID-import --member="serviceAccount:service-$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")@gcp-sa-alloydb.iam.gserviceaccount.com" --role=roles/storage.objectViewer
Wczytaj dane
Zrzut SQL bazy danych znajduje się w folderze sklonowanego repozytorium. Poniższe polecenie zakłada, że podczas klonowania repozytorium w poprzednim kroku w trakcie tworzenia klastra AlloyDB jako punktu początkowego użyto katalogu domowego.
Skopiuj plik zrzutu bazy danych do zasobnika Cloud Storage:
REPO_NAME="codelabs"
SOURCE_DIR="alloydb-ai-nla"
cd ~/$REPO_NAME/$SOURCE_DIR
gcloud storage cp ~/$REPO_NAME/$SOURCE_DIR/real_estate_data.sql gs://$PROJECT_ID-import
Zaimportuj zbiór danych do bazy danych quickstart_db:
PROJECT_ID=$(gcloud config get-value project)
CLUSTER_NAME=alloydb-aip-01
REGION=us-central1
IAM_USER=$(gcloud config get-value account)
gcloud alloydb clusters import $CLUSTER_NAME --region=us-central1 --database=quickstart_db --gcs-uri=gs://$PROJECT_ID-import/real_estate_data.sql --project=$PROJECT_ID --sql --user=$IAM_USER
Polecenie wczyta przykładowy zbiór danych do bazy danych quickstart_db.
Sprawdź import, zliczając rekordy w tabeli real_estate.agents za pomocą AlloyDB Studio:
SELECT COUNT(*) FROM real_estate.agents;
7. Przygotowywanie ontologii schematu
Skonfiguruj zbiór kontekstów QueryData, podaj metadane schematu i uruchom agenta ADK.
Tworzenie metadanych
Metadane składają się z komentarzy SQL zastosowanych do tabel i kolumn bazy danych. Te komentarze pomagają silnikowi QueryData w wybieraniu obiektów bazy danych.
Jeśli schemat danych został zaimportowany za pomocą dostarczonego zrzutu SQL, wszystkie metadane są już utworzone w schemacie.
Oto przykładowy fragment kodu metadanych zastosowany w schemacie real_estate:
COMMENT ON TABLE real_estate.agents IS 'Stores profile, contact, brokerage affiliation, and active status for individual real estate agents.'; COMMENT ON COLUMN real_estate.agents.agent_id IS 'Primary key uniquely identifying each real estate agent in the database.'; ...
Jeśli pracujesz z własnym zbiorem danych, możesz skorzystać z dokumentacji, aby dowiedzieć się, jak dodać metadane do schematu.
Tworzenie kontekstu ontologii schematu
Zbiór kontekstów zawiera szablony zapytań, aspekty i wyszukiwania wartości, które pomagają modelowi AI generować zapytania. Bez ustawionego kontekstu agent AI spróbuje wygenerować zapytanie, korzystając tylko z informacji dostępnych w public schemacie, głównie na podstawie nazw tabel, kolumn i metadanych, próbując wywnioskować odpowiednie obiekty, aby odpowiedzieć na zapytanie w języku naturalnym.
Zbiór kontekstowy QueryData to plik JSON z szablonami zapytań, aspektami i wyszukiwaniami wartości. Różne przykłady znajdziesz w dokumentacji.
Wczytywanie zestawu kontekstu QueryData w konsoli internetowej
Aby wczytać zestaw kontekstowy, otwórz AlloyDB Studio. W lewym panelu u dołu zobaczysz ikonę Context sets i 3 kropki.

Kliknij te 3 kropki i wybierz Create context set.
W otwartym oknie wpisz te wartości:
- Nazwa:
sixrecipes - Opis:
Real Estate Query Data - Przesyłanie pliku kontekstu: pozostaw go pustego
Jeśli naciśniesz przycisk Create po raz pierwszy, inicjowanie pamięci kontekstu może potrwać trochę czasu.
Sprawdź identyfikator zestawu kontekstowego QueryData. Aby to zrobić, ponownie kliknij 3 kropki po prawej stronie nazwy zestawu kontekstowego i wybierz Edit context set.

Identyfikator zestawu kontekstowego pojawi się u góry otwartej karty po prawej stronie.

Zapisz ten identyfikator, aby użyć go w późniejszych krokach.
Uruchom agenta AI
Aby korzystać z backendu QueryData do generowania i wykonywania zapytań, możesz użyć interfejsu AlloyDB Studio zgodnie z opisem w dokumentacji. W tym laboratorium będziesz jednak używać agenta AI jako głównego narzędzia do komunikacji z bazą danych. Agenci AI to bardziej realistyczny scenariusz dla przyszłych zbiorów zadań, a platforma ADK od Google zapewnia świetny interfejs internetowy, w którym możesz zobaczyć wszystkie kroki i interakcje między modelami, interfejsami API i aplikacjami.
Sprawdź konfigurację źródła agenta w Cloud Shell:
REPO_NAME="codelabs"
SOURCE_DIR="alloydb-ai-nla"
cd ~/$REPO_NAME/$SOURCE_DIR/data_agent
edit agent.py
W pliku możesz zobaczyć, gdzie definiujemy listę tabel w naszym schemacie. Wrócimy do tego w jednym z kolejnych kroków.
# List of database tables included in QueryData API requests
TABLE_IDS = [
"real_estate.municipalities",
"real_estate.cities",
"real_estate.agents",
"real_estate.properties",
"real_estate.schools",
"real_estate.school_to_property",
"real_estate.property_transactions",
"real_estate.secure_offers",
]
Istnieje też procedura call_querydata_api pracy z interfejsem QueryData API oraz zestaw narzędzi do pracy z umiejętnościami interfejsu QueryData API.
def call_querydata_api(
prompt: str,
target_project_id: str = project_id,
target_location: str = location,
target_cluster_id: str = cluster_name,
target_instance_id: str = instance_name,
target_database_id: str = database_name,
target_context_set_id: str = context_set_id,
target_table_ids: list[str] = TABLE_IDS,
) -> dict:
"""Calls the Gemini Data Analytics QueryData API to translate natural ...
<Redacted>
...
# ADK File-Based Skill
database_query_skill = load_skill_from_dir(
pathlib.Path(__file__).parent / "skills" / "database-query-skill"
)
skill_toolset = SkillToolset(
skills=[database_query_skill]
)
Oto sam agent główny z promptem i dostępnymi narzędziami.
# Agent configuration
root_agent = Agent(
model=MODEL_ID,
name='root_agent',
description='A helpful assistant for analyst requests.',
instruction=f"""
Answer user questions to the best of your knowledge using provided tools.
Do not try to generate non-existent data but use the grounded data from the database.
When you answer questions about database contents, real estate properties, or sales activity:
1. Refer to database-query-skill for instructions on querying data using QueryData API.
2. Use `call_querydata_api` to submit the natural language prompt to the QueryData API.
CRITICAL: Call `call_querydata_api` EXACTLY ONCE per user request. Do NOT make retries, follow-up calls, or rephrased calls to `call_querydata_api`, even if the result contains zero rows or empty data.
3. Immediately parse the JSON response from that first `call_querydata_api` call.
4. Format your final response to the user containing:
- The generated SQL query (formatted in a Markdown SQL code block).
- The query results formatted as a Markdown table (constructed from `queryResult.columns` and `queryResult.rows`).
- The natural language answer (`naturalLanguageAnswer`).
""",
tools=[call_querydata_api, skill_toolset],
)
Aby poznać instrukcje systemowe agenta, przejrzyj plik data_agent/SKILL.md:
REPO_NAME="codelabs"
SOURCE_DIR="alloydb-ai-nla"
cd ~/$REPO_NAME/$SOURCE_DIR/data_agent
edit SKILL.md
Zanim uruchomisz agenta, musisz przygotować .env z parametrami żądań do interfejsów Agent i QueryData API.
Wyeksportuj identyfikator zbioru kontekstów do zmiennej środowiskowej:
export QUERYDATA_CONTEXTSET=<context set id from the Create Context Set chapter>
Utwórz plik konfiguracji środowiska wykonawczego (.env) w katalogu data_agent:
PROJECT_ID=$(gcloud config get-value project)
REGION=us-central1
REPO_NAME="codelabs"
SOURCE_DIR="alloydb-ai-nla"
cd ~/$REPO_NAME/$SOURCE_DIR/data_agent
echo "GOOGLE_GENAI_USE_ENTERPRISE=true" > .env
echo "GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project -q)" >> .env
echo "GOOGLE_CLOUD_LOCATION=global" >> .env
echo "QUERYDATA_CONTEXTSET=$QUERYDATA_CONTEXTSET" >> .env
echo "ALLOYDB_CLUSTER=alloydb-aip-01" >> .env
echo "ALLOYDB_INSTANCE=alloydb-aip-01-pr" >> .env
echo "ALLOYDB_DATABASE=quickstart_db" >> .env
echo "ALLOYDB_LOCATION=$REGION" >> .env
Zainstaluj zależności i uruchom usługę internetową agenta:
cd ~/$REPO_NAME/$SOURCE_DIR/
uv sync
Uruchom agenta:
uv run adk web --allow_origins 'regex:https://.*\.cloudshell\.dev'
Powinny się wyświetlić dane wyjściowe podobne do tych z punktem końcowym http://127.0.0.1:8000 .
INFO: Started server process [83608] INFO: Waiting for application startup. +-----------------------------------------------------------------------------+ | ADK Web Server started | | | | For local testing, access at http://127.0.0.1:8000. | +-----------------------------------------------------------------------------+ INFO: Application startup complete. INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

Kliknij wygenerowany adres URL serwera WWW (np. http://127.0.0.1:8000) w Cloud Shell, aby uruchomić interfejs agenta. Jeśli to nie zadziała, w Cloud Shell kliknij Podgląd w przeglądarce –> Podejrzyj na porcie 8000, aby otworzyć interfejs agenta.
Sprawdź, czy w menu u góry po lewej stronie wybrana jest opcja data_agent.

Generowanie i wykonywanie zapytań
Uruchommy zapytanie Q1
Uruchom ten prompt w języku naturalnym w interfejsie czatu z agentem, aby ocenić podstawową skuteczność funkcji QueryData bez niestandardowych szablonów ani aspektów:
How many properties are currently for sale in San Diego?
Naciśnij przycisk strzałki.

Uruchomi przepływ pracy agenta, gdy wybierze odpowiednią umiejętność i użyje jej oraz funkcji do wysłania żądania do punktu końcowego interfejsu API i uzyskania wygenerowanego zapytania oraz wyników.

Jeśli najedziesz kursorem myszy na poszczególne kroki, zobaczysz, co dokładnie robią. W panelu po lewej stronie możesz też zobaczyć te i inne informacje, gdy wybierzesz jeden z kroków. Może pomóc w zrozumieniu, jak agenty współpracują z umiejętnościami i interfejsami API.

Prześlij kolejne zapytanie (Q2):
List agents who sold a property for over $1000000.
Zauważysz, że agent przechodzi bezpośrednio do umiejętności interfejsu API QueryData, ponieważ zapisał już w pamięci dostępne umiejętności i nie musi ponownie przechodzić fazy wykrywania.
Wygenerowane zapytanie SQL będzie podobne do tego:
SELECT DISTINCT "a"."agent_id", "a"."firstname", "a"."lastname", "a"."email", "a"."brokerage" FROM "real_estate"."agents" AS "a" INNER JOIN "real_estate"."property_transactions" AS "t" ON "a"."agent_id" = "t"."seller_agent_id" WHERE "t"."sale_price" > 1000000 AND "a"."agent_id" IS NOT NULL ORDER BY "a"."lastname" ASC NULLS LAST;
Wypróbuj inne zapytanie (Q3):
What is the average price of a single-family home with a garage in San Diego?
Funkcja QueryData generuje standardowe zapytania z pustym zestawem kontekstu, ale zadania biznesowe często wymagają określonej logiki biznesowej i deterministycznego działania.
8. Osiąganie wysokiej dokładności i trafności biznesowej dzięki wstawianiu interpretacji
Wstrzykiwanie interpretacji do promptów zapewnia, że wygenerowane zapytania są zgodne z określoną logiką biznesową.
Wypróbuj taki przykład z klientem, który prosi o domy jednopoziomowe w Del Mar. Pytanie zawiera 2 ważne dane i reprezentuje częsty wzorzec wyboru.
Wypróbuj w agencie ten prompt w języku naturalnym (Q4):
Single level Del Mar houses
Silnik zwraca ogólne zapytanie, ale może zawierać dodatkowe warunki, np. is_single_family = TRUE:
SELECT p."property_id", p."address_street", p."price", p."bedrooms", p."bathrooms", p."listing_status", p."is_single_family", p."num_stories", c."city_name" FROM "real_estate"."properties" AS p JOIN "real_estate"."cities" AS c ON p."city_id" = c."city_id" WHERE c."city_name" ILIKE 'Del Mar' AND p."num_stories" = 1 AND p."is_single_family" = TRUE ORDER BY p."property_id" ASC NULLS LAST;
W wyniku otrzymujemy 1 wiersz:
Natural Language Answer The database contains one single-level, single-family house in Del Mar: Address: 999 Main Ave (Postal Code: 90024) Price: $3,800,000.00 Bedrooms: 4 Bathrooms: 3.5 Listing Status: Active Features: Single-family home (1 story), has a garage, and no condo fees.
Przetestuj kolejny prompt (P5):
Del Mar houses close to good schools
Jest wystarczająco inteligentny, aby używać kolumn proximity_miles i school_ranking do sortowania właściwości, ale nie ocenia wagi ani znaczenia poszczególnych danych.
SELECT "p"."property_id", "p"."address_street", "p"."price", "p"."bedrooms", "p"."bathrooms", "s"."school_name", "s"."school_ranking", "sp"."proximity_miles" FROM "real_estate"."properties" AS "p" INNER JOIN "real_estate"."cities" AS "c" ON "p"."city_id" = "c"."city_id" INNER JOIN "real_estate"."school_to_property" AS "sp" ON "p"."property_id" = "sp"."property_id" INNER JOIN "real_estate"."schools" AS "s" ON "sp"."school_id" = "s"."school_id" WHERE "c"."city_name" = 'Del Mar' AND "sp"."proximity_miles" IS NOT NULL AND "s"."school_ranking" IS NOT NULL ORDER BY "sp"."proximity_miles" ASC NULLS LAST, "s"."school_ranking" ASC NULLS LAST;
Uogólnianie przykładowego zapytania w szablonie
Logika biznesowa wymaga:
- W promieniu 2 mil
- Ranking szkoły na poziomie 5 lub niższym
- Zamawianie ważone: 80% – ranking szkoły + 20% – odległość (wynik = 0,8 * ranking_szkoły + 0,2 * odległość_w_milach)
Oto wzór na ranking ważony:
Score = 0.8 * school_ranking + 0.2 * proximity_miles
Pobierz plik kontekstu sixrecipe_context1_1.json z Cloud Shell za pomocą interfejsu wiersza poleceń w Cloud Shell:
cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context1_1.json
Możesz też użyć elementu menu Download w menu Cloud Shell:

Aby zdefiniować tę standardową strukturę, prześlij sixrecipe_context1_1.json do zestawu kontekstowego sixrecipe za pomocą AlloyDB Studio. Kliknij 3 kropki po prawej stronie kontekstu sixrecipes i użyj opcji „Edytuj zestaw kontekstów”, aby przesłać nowy plik. Spowoduje to zastąpienie starego pustego kontekstu nowym.
Szablon definiuje wyraźne reguły szablonu SQL dla reguł dotyczących bliskości dobrych szkół:
{
"facets": [],
"templates": [
{
"nl_query": "Riverside houses close to good schools.",
"sql": "SELECT \"p\".\"address_street\", \"p\".\"address_postal_code\", \"s\".\"school_name\", \"stp\".\"proximity_miles\" FROM \"real_estate\".\"properties\" AS \"p\" JOIN \"real_estate\".\"cities\" AS \"c\" ON \"p\".\"city_id\" = \"c\".\"city_id\" JOIN \"real_estate\".\"school_to_property\" AS \"stp\" ON \"p\".\"property_id\" = \"stp\".\"property_id\" JOIN \"real_estate\".\"schools\" AS \"s\" ON \"stp\".\"school_id\" = \"s\".\"school_id\" WHERE \"c\".\"city_name\" = 'Riverside' AND \"s\".\"school_ranking\" <= 5 AND \"stp\".\"proximity_miles\" <= 2 ORDER BY 0.8 * \"s\".\"school_ranking\" + 0.2 * \"stp\".\"proximity_miles\"",
"intent": "Houses in Riverside close to good schools (within 2 miles of schools with a rank of at least 5).",
"manifest": "A given city houses close to good schools.",
"parameterized": {
"parameterized_intent": "Houses in $1 close to good schools (within 2 miles of schools with a rank of at least 5).",
"parameterized_sql": "SELECT \"p\".\"address_street\", \"p\".\"address_postal_code\", \"s\".\"school_name\", \"stp\".\"proximity_miles\" FROM \"real_estate\".\"properties\" AS \"p\" JOIN \"real_estate\".\"cities\" AS \"c\" ON \"p\".\"city_id\" = \"c\".\"city_id\" JOIN \"real_estate\".\"school_to_property\" AS \"stp\" ON \"p\".\"property_id\" = \"stp\".\"property_id\" JOIN \"real_estate\".\"schools\" AS \"s\" ON \"stp\".\"school_id\" = \"s\".\"school_id\" WHERE \"c\".\"city_name\" = $1 AND \"s\".\"school_ranking\" <= 5 AND \"stp\".\"proximity_miles\" <= 2 ORDER BY 0.8 * \"s\".\"school_ranking\" + 0.2 * \"stp\".\"proximity_miles\""
}
}
]
}
Ponownie uruchom prompta:
Del Mar houses close to good schools
Wynikowe zapytanie jest teraz zgodne z logiką biznesową zdefiniowaną w szablonie:
SELECT "p"."address_street", "p"."address_postal_code", "s"."school_name", "stp"."proximity_miles" FROM "real_estate"."properties" AS "p" JOIN "real_estate"."cities" AS "c" ON "p"."city_id" = "c"."city_id" JOIN "real_estate"."school_to_property" AS "stp" ON "p"."property_id" = "stp"."property_id" JOIN "real_estate"."schools" AS "s" ON "stp"."school_id" = "s"."school_id" WHERE "c"."city_name" = 'Del Mar' AND "s"."school_ranking" <= 5 AND "stp"."proximity_miles" <= 2 ORDER BY 0.8 * "s"."school_ranking" + 0.2 * "stp"."proximity_miles"
Oprócz wygenerowanej instrukcji SQL powstaje też ten zamiar:
intentExplanation: "Houses in Del Mar close to good schools (within 2 miles of schools with a rank of at least 5)."
Zamiar możesz zobaczyć w lewym panelu interfejsu internetowego ADK, jak pokazano tutaj:

Wyniki pokazują 2 usługi posortowane według naszego wyniku:
Query Results address_street address_postal_code school_name proximity_miles 200 Coast Blvd 92014 Del Mar Heights Elementary 0.50 100 Del Mar Heights Rd 92014 Del Mar Heights Elementary 1.00 Answer Based on the data, here are the properties in Del Mar located within 2 miles of a top-rated school (ranking of 5 or better): 200 Coast Blvd (92014) School: Del Mar Heights Elementary Proximity: 0.50 miles 100 Del Mar Heights Rd (92014) School: Del Mar Heights Elementary Proximity: 1.00 miles
W następnym rozdziale rozszerzysz zakres szablonu.
Wprowadzanie interpretacji przez rejestrowanie i interpretowanie wielu pytań
Możesz rozszerzyć zakres naszego szablonu, aby był używany nie tylko w przypadku „domów w pobliżu dobrych szkół”, ale także w innych sytuacjach, np. w przypadku „domów dla rodzin z dziećmi”. W tym celu dodaj do pliku manifestu inne warunki.
Pobierz zaktualizowany plik kontekstu:
cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context1_2.json
Prześlij zaktualizowany plik sixrecipe_context1_2.json do zbioru kontekstów.
W nowym pliku zestawu kontekstów zastępujemy manifest "manifest": "A given city houses close to good schools." manifestem "manifest": "A given city houses close to good schools, or are great for families with kids.". Rozszerza to zastosowania tego szablonu, dzięki czemu można go używać w więcej niż jednym przypadku.
Prześlij to zapytanie (Q6):
Houses in Waterloo great for families with kids.
Agent używa zaktualizowanego pliku manifestu szablonu, aby zastosować ustandaryzowaną logikę zapytania, która zwraca to samo zapytanie co powyżej, w celu:
intentExplanation : "Houses in Waterloo close to good schools (within 2 miles of schools with a rank of at least 5)."
9. Korzystanie z szablonów fasetowych w celu osiągnięcia wysokiej dokładności przy zachowaniu elastyczności
Fasety to modułowe warunki SQL, które są dynamicznie łączone z szablonami w celu obsługi kwalifikatorów (np. „dla początkujących rodzin”, „z widokiem na ocean”) bez konieczności tworzenia osobnych szablonów dla każdej permutacji.
Zamiast tworzyć szablon dla każdego zapytania lub warunku, możemy przygotować fragmenty, które można zastosować do istniejących szablonów, dodając do zapytania więcej wymiarów.
Zdefiniuj aspekt dla właściwości z tymi atrybutami:
- dom jednorodzinny,
- Stan aktywnej informacji
- 3–4 sypialnie i co najmniej 2 łazienki
- Lokalizacja w gminie,w której wskaźnik przestępczości jest niższy niż 500 przestępstw na 100 tys. mieszkańców.
Pobierz plik kontekstu z podziałem na aspekty:
cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context2_1.json
Aby skonfigurować aspekty, prześlij plik sixrecipe_context2_1.json do zestawu kontekstowego sixrecipes w AlloyDB Studio.
"facets": [
{
"sql_snippet": "real_estate.properties.is_single_family = TRUE AND real_estate.properties.listing_status = 'Active' AND real_estate.properties.bedrooms BETWEEN 3 AND 4 AND real_estate.properties.bathrooms >= 2.0 AND 500 > (SELECT m.crime_rate_per_100k FROM real_estate.cities c JOIN real_estate.municipalities m ON c.municipality_id = m.municipality_id AND c.city_id = real_estate.properties.city_id)",
"intent": "for a starter family (active single-family home listings with 3 to 4 bedrooms, 2+ bathrooms, and a crime rate under 500 per 100,000 residents)",
"manifest": "for a starter family",
"parameterized": {
"parameterized_intent": "for a starter family (active single-family home listings with 3 to 4 bedrooms, 2+ bathrooms, and a crime rate under 500 per 100,000 residents)",
"parameterized_sql_snippet": "real_estate.properties.is_single_family = TRUE AND real_estate.properties.listing_status = 'Active' AND real_estate.properties.bedrooms BETWEEN 3 AND 4 AND real_estate.properties.bathrooms >= 2.0 AND 500 > (SELECT m.crime_rate_per_100k FROM real_estate.cities c JOIN real_estate.municipalities m ON c.municipality_id = m.municipality_id AND c.city_id = real_estate.properties.city_id)"
}
}
]
Zapytanie korzystające z wyszukiwania wieloaspektowego
Uruchom to zapytanie w języku naturalnym (Q7):
Del Mar houses close to good schools for a starter family.
Silnik dołącza warunki aspektu bezpośrednio do zapytania podstawowego:
SELECT "p"."address_street", "p"."address_postal_code", "s"."school_name", "stp"."proximity_miles"
FROM "real_estate"."properties" AS "p"
JOIN "real_estate"."cities" AS "c" ON "p"."city_id" = "c"."city_id"
JOIN "real_estate"."school_to_property" AS "stp" ON "p"."property_id" = "stp"."property_id"
JOIN "real_estate"."schools" AS "s" ON "stp"."school_id" = "s"."school_id"
WHERE "c"."city_name" = 'Del Mar'
AND "s"."school_ranking" <= 5
AND "stp"."proximity_miles" <= 2
AND "p"."is_single_family" = TRUE
AND "p"."listing_status" = 'Active'
AND "p"."bedrooms" BETWEEN 3 AND 4
AND "p"."bathrooms" >= 2.0
AND 500 > (
SELECT m.crime_rate_per_100k
FROM real_estate.cities c
JOIN real_estate.municipalities m ON c.municipality_id = m.municipality_id
AND c.city_id = p.city_id
)
ORDER BY 0.8 * "s"."school_ranking" + 0.2 * "stp"."proximity_miles"
Oto wyniki:
Query Results address_street address_postal_code school_name proximity_miles 200 Coast Blvd 92014 Del Mar Heights Elementary 0.50 Natural Language Answer Based on your criteria, there is an active listing for a single-family house in Del Mar that fits your needs: Address: 200 Coast Blvd, Del Mar, 92014 Nearby School: Del Mar Heights Elementary (ranked within the top 5) Proximity to School: 0.50 miles
Dodawanie kolejnych aspektów
A co, jeśli chcemy dodać jeszcze jeden wymiar na podstawie nowego, pojawiającego się wzorca, np. widok z okna w przypadku nieruchomości?
Dodaj w definicji aspektu semantyczne dopasowywanie widoku na ocean.
{
"sql_snippet": "((real_estate.properties.description_embedding <=> google_ml.embedding('gemini-embedding-001', 'unobstructed, panoramic, ocean vista, vast Pacific, breathtaking, commanding view, deep blue horizon, massive, sweeping view')::vector) < 0.4)",
"intent": "with ocean view",
"manifest": "with ocean view",
"parameterized": {
"parameterized_intent": "with ocean view",
"parameterized_sql_snippet": "((real_estate.properties.description_embedding <=> google_ml.embedding('gemini-embedding-001', 'unobstructed, panoramic, ocean vista, vast Pacific, breathtaking, commanding view, deep blue horizon, massive, sweeping view')::vector) < 0.4)"
}
Wartość 0,4 w definicji aspektu powyżej to próg skalibrowany eksperymentalnie. Pobierz plik kontekstu wieloaspektowego:
cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context2_2.json
Prześlij sixrecipe_context2_2.json do zestawu kontekstowego i uruchom ten prompt (Q8):
Ocean view Del Mar homes near good schools.
Dane wyjściowe zawierają standardowe warunki połączone z dopasowywaniem podobieństwa wektorowego za pomocą symbolu google_ml.embedding:
SELECT "p"."address_street", "p"."address_postal_code", "s"."school_name", "stp"."proximity_miles"
FROM "real_estate"."properties" AS "p"
JOIN "real_estate"."cities" AS "c" ON "p"."city_id" = "c"."city_id"
JOIN "real_estate"."school_to_property" AS "stp" ON "p"."property_id" = "stp"."property_id"
JOIN "real_estate"."schools" AS "s" ON "stp"."school_id" = "s"."school_id"
WHERE "c"."city_name" = 'Del Mar' AND "s"."school_ranking" <= 5 AND "stp"."proximity_miles" <= 2
AND (((p.description_embedding <=> google_ml.embedding('gemini-embedding-001', 'unobstructed, panoramic, ocean vista, vast Pacific, breathtaking, commanding view, deep blue horizon, massive, sweeping view')::vector) <= 0.4))
ORDER BY 0.8 * "s"."school_ranking" + 0.2 * "stp"."proximity_miles";
Zapytanie zwraca jedną właściwość:
Query Results address_street address_postal_code school_name proximity_miles 100 Del Mar Heights Rd 92014 Del Mar Heights Elementary 1.00 Answer The ocean-view home in Del Mar near a top-ranked school is located at 100 Del Mar Heights Rd (92014). It is situated just 1.00 mile away from Del Mar Heights Elementary, which is one of the area's highly-ranked schools (ranking of 5 or better).
Sprawdź wyniki, przeglądając opis usługi w AlloyDB Studio i wykonując to zapytanie:
select description from real_estate.properties where address_postal_code='92014' and description is not null;
Wyświetli się opis z informacją o „wspaniałym spektaklu morskim”.
Well-located 2-bedroom condo with excellent proximity to coastal shopping and dining. The balcony provides a magnificent marine spectacle against an azure backdrop. Under contract, high monthly HOA fee.
10. Uściślanie danych prywatnych za pomocą wyszukiwania wartości
Niejednoznaczne prośby mogą pasować do wielu podmiotów, np. nazwa lokalizacji może być mylona z nazwą firmy. Wyszukiwania wartości mapują terminy na pojęcia z bazy danych, aby pomóc w rozwiązywaniu tych odwołań.
Wyszukiwanie wartości zapewnia powiązania między nazwą kolumny i wartościami w kolumnie a typem pojęcia, którym może być np. miasto, kraj lub imię. Ponieważ indeksuje nie tylko metadane, ale też reprezentatywne próbki danych w kolumnie, może powiązać wartość używaną w żądaniu w języku naturalnym z potencjalnym typem pojęcia oraz z tabelą i kolumną, które można wykorzystać w wynikowym kodzie SQL.
Niejednoznaczność zapytania
W rzeczywistości może to być jeszcze trudniejsze ze względu na błędy w pisowni różnych nazw i lokalizacji. Uruchom ten prompt (Q9) zawierający błąd pisowni i niejednoznaczne słowo:
Westwod's sold properties in the last 1 month.
Występują tu 2 problemy: nazwa jest błędnie napisana i nie jest jasne, czy chodzi o nieruchomości sprzedawane w lokalizacji o podobnej nazwie, czy o agencję nieruchomości. Po uruchomieniu zapytania otrzymasz wynik podobny do tego poniżej.
SELECT "p"."property_id", "p"."address_street", "p"."address_postal_code", "p"."price" AS "listing_price", "p"."bedrooms", "p"."bathrooms", "p"."listing_status", "pt"."sale_date", "pt"."sale_price", "c"."city_name" FROM "real_estate"."properties" AS "p" INNER JOIN "real_estate"."property_transactions" AS "pt" ON "p"."property_id" = "pt"."property_id" INNER JOIN "real_estate"."cities" AS "c" ON "p"."city_id" = "c"."city_id" WHERE ("c"."city_name" ILIKE '%Westwood%' OR "c"."city_name" ILIKE '%Westwod%') AND "pt"."sale_date" >= '2025-09-01'::date AND "pt"."sale_date" <= '2025-09-30'::date ORDER BY "pt"."sale_date" DESC NULLS LAST, "pt"."sale_price" DESC NULLS LAST
Poprawił nazwę i założył, że pytanie dotyczy lokalizacji nieruchomości, próbując dopasować nieruchomości w mieście Westwood.
Włącz rozszerzenie pg_trgm w AlloyDB Studio, aby obsługiwać porównania przybliżone ciągów tekstowych:
CREATE EXTENSION IF NOT EXISTS pg_trgm;
Następnie możemy użyć rozszerzenia, aby dodać wyszukiwania wartości do naszego zbioru kontekstów QueryData.
Oto lista wyszukiwań wartości, których chcemy użyć:
"value_searches": [
{
"conceptType": "Municipality name",
"query": "/* Requires extension: pg_trgm */ WITH TrigramMetrics AS (SELECT T.municipality_name AS original_value, (T.municipality_name % $value::text) AS normalized_dist FROM real_estate.municipalities T WHERE T.municipality_name % $value::text) SELECT original_value AS value, 'real_estate.municipalities.municipality_name' AS columns, 'Name of the municipality' AS concept_type, normalized_dist AS distance, ''::text AS context FROM TrigramMetrics",
"description": "Trigram match for municipality name"
},
{
"conceptType": "City name",
"query": "/* Requires extension: pg_trgm */ WITH TrigramMetrics AS (SELECT T.city_name AS original_value, (T.city_name % $value::text) AS normalized_dist FROM real_estate.cities T WHERE T.city_name % $value::text) SELECT original_value AS value, 'real_estate.cities.city_name' AS columns, 'Name of the city' AS concept_type, normalized_dist AS distance, ''::text AS context FROM TrigramMetrics",
"description": "Trigram match for city name"
},
{
"conceptType": "Brokerage name",
"query": "/* Requires extension: pg_trgm */ WITH TrigramMetrics AS (SELECT T.brokerage AS original_value, (T.brokerage % $value::text) AS normalized_dist FROM real_estate.agents T WHERE T.brokerage % $value::text) SELECT original_value AS value, 'real_estate.agents.brokerage' AS columns, 'Name of the brokerage' AS concept_type, normalized_dist AS distance, ''::text AS context FROM TrigramMetrics",
"description": "Trigram match for brokerage name"
}
]
Pobierz plik kontekstu wyszukiwania wartości:
cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context3_1.json
Prześlij plik sixrecipe_context3_1.json do zbioru kontekstów QueryData sixrecipes i zapisz go.
Ponownie uruchom prompta (Q9):
Westwod's sold properties in the last 1 month.
Odpowiedź zawiera wyjaśnienie, ponieważ podczas wyszukiwania wartości znaleziono 3 różne podmioty o podobnych wartościach:
The database requires some clarification to provide the exact information you need: The search term 'Westwod' (Westwood) is ambiguous because it can refer to: A municipality name (e.g., the municipality of Westwood) A city name (e.g., the city of Westwood) A brokerage name (e.g., properties sold by agents affiliated with the Westwood brokerage) Could you please clarify which of these you are referring to?
Ślad podpowiada, dlaczego wybrana została ta konkretna ścieżka.

Oto pełny tekst:
"[NOT_ENOUGH_INFO] The search term 'Westwod' is ambiguous because it can refer to a municipality name, a city name, or a brokerage name. Could you please clarify if you are referring to properties sold within the municipality of Westwood, the city of Westwood, or properties sold by agents affiliated with the Westwood brokerage?"
Usuwanie niejednoznaczności z zapytania
Doprecyzuj prompt wejściowy (Q10):
Westwood city location properties sold in September 2025.
Teraz mamy zapytanie i wyniki:
Generated SQL SELECT "p"."property_id", "p"."address_street", "p"."address_postal_code", "p"."price", "pt"."sale_date", "pt"."sale_price" FROM "real_estate"."properties" AS "p" INNER JOIN "real_estate"."cities" AS "c" ON "p"."city_id" = "c"."city_id" INNER JOIN "real_estate"."property_transactions" AS "pt" ON "p"."property_id" = "pt"."property_id" WHERE "c"."city_name" = 'Westwood' AND "pt"."sale_date" >= '2025-09-01'::date AND "pt"."sale_date" <= '2025-09-30'::date ORDER BY "pt"."sale_date" ASC NULLS LAST Query Results property_id address_street address_postal_code price sale_date sale_price 32 999 Main St 99999 3800000.00 2025-09-12 1000000.00
11. Wyszukiwanie semantyczne i funkcje AI
Funkcje AI w AlloyDB umożliwiają filtrowanie semantyczne nieustrukturyzowanych danych. Używaj funkcji takich jak ai.if i google_ml.embedding bezpośrednio w zapytaniach SQL wraz ze standardowymi operatorami bazy danych.
Szablon z operatorami ILIKE
Najpierw prześlij nowy plik kontekstu z operatorami ILIKE.
Pobierz plik kontekstu szablonu ILIKE:
cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context4_1.json
Prześlij plik sixrecipe_context4_1.json do zestawu kontekstowego, aby ocenić standardowe dopasowywanie wzorców ILIKE w przypadku domów „gotowych do zamieszkania”.
{
"nl_query": "Move-in ready houses",
"sql": "SELECT \"p\".\"address_street\", \"p\".\"address_postal_code\" FROM \"real_estate\".\"properties\" AS \"p\" WHERE \"p\".listing_status = 'Active' AND \"p\".\"description\" IS NOT NULL AND (\"p\".description ILIKE '%turnkey%' OR \"p\".description ILIKE '%move-in%')",
"intent": "Move-in ready houses",
"manifest": "Move-in ready houses",
"parameterized": {
"parameterized_intent": "Move-in ready houses",
"parameterized_sql": "SELECT \"p\".\"address_street\", \"p\".\"address_postal_code\" FROM \"real_estate\".\"properties\" AS \"p\" WHERE \"p\".listing_status = 'Active' AND \"p\".\"description\" IS NOT NULL AND (\"p\".description ILIKE '%turnkey%' OR \"p\".description ILIKE '%move-in%')"
}
}
Prześlij to zapytanie (Q11):
List move-in ready houses.
Wynikowe zapytanie:
SELECT "p"."address_street", "p"."address_postal_code"
FROM "real_estate"."properties" AS "p"
WHERE "p".listing_status = 'Active'
AND "p"."description" IS NOT NULL
AND ("p".description ILIKE '%turnkey%' OR "p".description ILIKE '%move-in%')
Zwraca jeden pasujący rekord.
Query Results address_street address_postal_code 40 Main St, Unit 8 N2H 2G8
Szablon z funkcją AI.IF
Pobierz plik kontekstu szablonu AI.IF:
cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context4_2.json
Prześlij plik sixrecipe_context4_2.json do zestawu kontekstowego, aby ocenić funkcję filtrowania semantycznego ai.if.
{
"nl_query": "List Move-in ready houses",
"sql": "SELECT \"p\".\"address_street\", \"p\".\"address_postal_code\" FROM \"real_estate\".\"properties\" AS \"p\" WHERE \"p\".listing_status = 'Active' AND \"p\".\"description\" IS NOT NULL AND ai.if('Does this description represent a move-in ready house? Description: ' || \"p\".\"description\")",
"intent": "List Move-in ready houses",
"manifest": "List Move-in ready houses",
"parameterized": {
"parameterized_intent": "List Move-in ready houses",
"parameterized_sql": "SELECT \"p\".\"address_street\", \"p\".\"address_postal_code\" FROM \"real_estate\".\"properties\" AS \"p\" WHERE \"p\".listing_status = 'Active' AND \"p\".\"description\" IS NOT NULL AND ai.if('Does this description represent a move-in ready house? Description: ' || \"p\".\"description\")"
}
}
Powtórz prompta (P12):
List move-in ready houses.
Wygenerowany kod SQL:
SELECT "p"."address_street", "p"."address_postal_code"
FROM "real_estate"."properties" AS "p"
WHERE "p".listing_status = 'Active'
AND "p"."description" IS NOT NULL
AND ai.if('Does this description represent a move-in ready house? Description: ' || "p"."description")
W wyniku otrzymujemy tę samą właściwość:
Query Results Address Street Address Postal Code 40 Main St, Unit 8 N2H 2G8 Natural Language Answer The move-in ready home currently available is located at: Address: 40 Main St, Unit 8 Postal Code: N2H 2G8
Aspekty z wyszukiwaniem wektorowym
Pobierz plik kontekstu wyszukiwania osadzania:
cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context4_3.json
Prześlij plik sixrecipe_context4_3.json do zestawu kontekstowego, aby ocenić semantyczne wyszukiwanie wektorowe za pomocą funkcji google_ml.embedding i wyszukiwać domy z nowym sprzętem kuchennym.
Zaktualizuj zestaw kontekstowy za pomocą pliku sixrecipe_context4_3.json z dodatkowym aspektem, korzystając z wyszukiwania wektorowego AlloyDB.
{
"sql_snippet": "((real_estate.properties.description_embedding <=> google_ml.embedding('gemini-embedding-001', 'Upgraded kitchen appliances')::vector) < 0.4)",
"intent": "with upgraded kitchen appliances",
"manifest": "with upgraded kitchen appliances",
"parameterized": {
"parameterized_intent": "with upgraded kitchen appliances",
"parameterized_sql_snippet": "((real_estate.properties.description_embedding <=> google_ml.embedding('gemini-embedding-001', 'Upgraded kitchen appliances')::vector) < 0.4)"
}
}
Wartość 0,4 w definicji aspektu powyżej to próg skalibrowany eksperymentalnie.
Uruchom to zapytanie (Q13):
List move-in ready houses with upgraded kitchen appliances.
Wygenerowany kod SQL:
SELECT "p"."address_street", "p"."address_postal_code" FROM "real_estate"."properties" AS "p" WHERE "p"."listing_status" = 'Active' AND "p"."description" IS NOT NULL AND ai.if('Does this description represent a Move-in ready house? Description: ' || "p"."description") AND ((("p"."description_embedding" <=> google_ml.embedding('gemini-embedding-001', 'Upgraded kitchen appliances')::vector) < 0.4))
Oto wyniki zapytania i rekomendacja w języku naturalnym:
Query Results Address Postal Code 40 Main St, Unit 8 N2H 2G8 Natural Language Answer The move-in ready house with upgraded kitchen appliances is located at: Address: 40 Main St, Unit 8 Postal Code: N2H 2G8
12. Prywatność i bezpieczeństwo
Za każdym razem, gdy interfejs QueryData API generuje zapytania, istnieje ryzyko naruszenia prywatności, ponieważ wygenerowane zapytania mogą ujawniać dane należące do innych użytkowników. Sparametryzowane bezpieczne widoki (PSV) wymuszają granice zabezpieczeń na poziomie wiersza podczas generowania zapytań NL2SQL.
Tworzenie sparametryzowanego bezpiecznego widoku
Mamy tabelę real_estate.offers z informacjami o ofertach wszystkich naszych nieruchomości. Chcemy, aby nasi agenci widzieli tylko swoje transakcje, a nie transakcje innych agentów. Tworzymy plik PSV z możliwością filtrowania wierszy na podstawie identyfikatora agenta.
Utwórz sparametryzowany bezpieczny widok dla tabeli ofert w AlloyDB Studio:
CREATE VIEW real_estate.secure_offers
WITH (security_barrier) AS
SELECT offer_id, property_id, buyer_agent_id,
seller_agent_id, offer_amount, offer_date,
offer_status, offer_expiration_date,
contingencies
FROM real_estate.offers
WHERE buyer_agent_id = $@agent_id;
Sprawdź definicję sparametryzowanego widoku zabezpieczeń:
SELECT definition
FROM parameterized_views.all_parameterized_views
WHERE schemaname = 'real_estate' AND viewname = 'secure_offers';
A co z tabelą real_estate.offers?
Ograniczanie dostępu do danych QueryData za pomocą sparametryzowanych bezpiecznych widoków AlloyDB
W pliku agent.py sparametryzowany widok zabezpieczeń real_estate.secure_offers jest zarejestrowany w TABLE_IDS zamiast real_estate.offers:
Kod źródłowy pliku agent.py:
List of database tables included in QueryData API requests
TABLE_IDS = [
"real_estate.municipalities",
"real_estate.cities",
"real_estate.agents",
"real_estate.properties",
"real_estate.schools",
"real_estate.school_to_property",
"real_estate.property_transactions",
"real_estate.secure_offers",
]
i przekazuje kontekst czasu działania sesji agentowi o identyfikatorze agent_id=1:
"parameterized_secure_view_parameters": {
"parameters": {
"key": "agent_id",
"value": "1"
}
}
},
W rezultacie każde żądanie interfejsu QueryData API będzie przekazywać ten parametr, a gdy w żądaniu zostanie uwzględniony bezpieczny widok, zastosuje on filtrowanie, zwracając tylko wyniki dotyczące danego agenta.
Uruchom to zapytanie (Q14):
List the offer id and listing price in 2025 where the offer was within 5% of the final selling price.
Wygenerowany kod SQL:
SELECT "o"."offer_id", "p"."price" AS "listing_price"
FROM "real_estate"."secure_offers" AS "o"
INNER JOIN "real_estate"."properties" AS "p" ON "o"."property_id" = "p"."property_id"
INNER JOIN "real_estate"."property_transactions" AS "t" ON "o"."property_id" = "t"."property_id"
WHERE "o"."offer_date" >= '2025-01-01'
AND "o"."offer_date" <= '2025-12-31'
AND "o"."offer_amount" IS NOT NULL
AND "t"."sale_price" IS NOT NULL
AND "p"."price" IS NOT NULL
AND ABS("o"."offer_amount" - "t"."sale_price") <= (0.05 * "t"."sale_price")
ORDER BY "o"."offer_id" ASC NULLS LAST
Wysyłanie zapytań do widoku filtruje wyniki według parametru aktywnej sesji agenta, chroniąc nieautoryzowane dane rekordów i zwracając tylko 2 rekordy.
Query Results Offer ID Listing Price 7 $750,000.00 8 $510,000.00 Natural Language Answer Here are the offers from 2025 where the offer amount was within 5% of the final selling price: Offer ID 7: Listing Price of $750,000.00 Offer ID 8: Listing Price of $510,000.00
Jeśli w przypadku podstawowej tabeli real_estate.offers w AlloyDB Studio wykonasz to zapytanie:
SELECT "o"."offer_id", "p"."price" AS "listing_price"
FROM "real_estate"."offers" AS "o"
INNER JOIN "real_estate"."properties" AS "p" ON "o"."property_id" = "p"."property_id"
INNER JOIN "real_estate"."property_transactions" AS "t" ON "o"."property_id" = "t"."property_id"
WHERE "o"."offer_date" >= '2025-01-01'
AND "o"."offer_date" <= '2025-12-31'
AND "o"."offer_amount" IS NOT NULL
AND "t"."sale_price" IS NOT NULL
AND "p"."price" IS NOT NULL
AND ABS("o"."offer_amount" - "t"."sale_price") <= (0.05 * "t"."sale_price")
ORDER BY "o"."offer_id" ASC NULLS LAST
Zwraca więcej niż 2 rekordy, wyświetlając dane wszystkich agentów.
To koniec modułu. Dowiedzieliśmy się, jak korzystać z interfejsu QueryData API, funkcji AI i sparametryzowanych bezpiecznych widoków AlloyDB.
13. Zwalnianie miejsca w środowisku
Po zakończeniu modułu zniszcz instancje i klaster AlloyDB.
Usuwanie klastra AlloyDB i wszystkich instancji
Klaster zostanie zniszczony za pomocą opcji force, która spowoduje też usunięcie wszystkich instancji należących do klastra.
Jeśli terminal został odłączony, połącz się ponownie i zdefiniuj zmienne projektu i środowiska w Cloud Shell:
gcloud config set project <YOUR_PROJECT_ID>
export REGION=us-central1
export ADBCLUSTER=alloydb-aip-01
export PROJECT_ID=$(gcloud config get-value project)
Usuń klaster:
gcloud alloydb clusters delete $ADBCLUSTER --region=$REGION --force
Oczekiwane dane wyjściowe konsoli:
student@cloudshell:~ (test-project-001-402417)$ gcloud alloydb clusters delete $ADBCLUSTER --region=$REGION --force All of the cluster data will be lost when the cluster is deleted. Do you want to continue (Y/n)? Y Operation ID: operation-1697820178429-6082890a0b570-4a72f7e4-4c5df36f Deleting cluster...done.
Usuwanie kopii zapasowych AlloyDB
Usuń wszystkie kopie zapasowe AlloyDB dla klastra:
for i in $(gcloud alloydb backups list \
--filter="CLUSTER_NAME: projects/$PROJECT_ID/locations/$REGION/clusters/$ADBCLUSTER" \
--format="value(name)" \
--sort-by=~createTime) ; do \
gcloud alloydb backups delete $(basename $i) --region $REGION --quiet; done
Oczekiwane dane wyjściowe konsoli:
student@cloudshell:~ (test-project-001-402417)$ for i in $(gcloud alloydb backups list --filter="CLUSTER_NAME: projects/$PROJECT_ID/locations/$REGION/clusters/$ADBCLUSTER" --format="value(name)" --sort-by=~createTime) ; do gcloud alloydb backups delete $(basename $i) --region $REGION --quiet; done Operation ID: operation-1697826266108-60829fb7b5258-7f99dc0b-99f3c35f Deleting backup...done.
Usuwanie zasobnika Cloud Storage
Usuń zasobnik importu Cloud Storage, aby uniknąć naliczania opłat za miejsce na dane:
gcloud storage rm -r gs://${PROJECT_ID}-import
14. Gratulacje
Gratulujemy ukończenia warsztatów!
Omówione zagadnienia
- Jak wdrożyć klaster AlloyDB i instancję główną
- Włączanie funkcji QueryData, funkcji AI i sparametryzowanych bezpiecznych widoków w AlloyDB
- Jak utworzyć zbiór kontekstów QueryData
- Jak używać różnych szablonów i kontekstów w interfejsie QueryData API
- Jak zwiększyć elastyczność szablonów QueryData za pomocą aspektów
- Jak rozwiązywać problemy z niejednoznacznością danych za pomocą wyszukiwania wartości i indeksowania trigramów
- Jak używać funkcji AI do wyszukiwania semantycznego za pomocą szablonów
- Jak używać sparametryzowanych bezpiecznych widoków z interfejsem QueryData API
Ankieta
Dane wyjściowe: