Sei ricette per ottenere una precisione quasi del 100% nella conversione da testo a SQL in AlloyDB - codelab

1. Introduzione

In questo codelab, imparerai a ottenere i risultati migliori utilizzando l'API QueryData. Scopri come generare risultati accurati e ripetibili quando effettui richieste in linguaggio naturale al tuo database. Per aiutarti a raggiungere questo obiettivo, la guida ti illustra sei ricette in diversi capitoli, esplorando i vari aspetti della conversione dal linguaggio naturale a SQL (NL2SQL) e le sue applicazioni pratiche.

Prerequisiti

  • Una conoscenza di base di Google Cloud e della console Google Cloud
  • Competenze di base con l'interfaccia a riga di comando e Cloud Shell

Obiettivi didattici

  • Come eseguire il deployment di un cluster AlloyDB e dell'istanza principale
  • Come attivare QueryData, le funzioni di AI e le viste sicure con parametri
  • Come creare un set di contesti QueryData
  • Come utilizzare diversi modelli e contesti nell'API QueryData
  • Come rendere più flessibili i modelli QueryData utilizzando le sfaccettature
  • Come risolvere l'ambiguità dei dati con la ricerca di valori e l'indicizzazione dei trigrammi
  • Come utilizzare le funzioni AI per la ricerca semantica con i modelli
  • Come utilizzare le visualizzazioni sicure con parametri con l'API QueryData

Che cosa ti serve

  • Un account Google Cloud e un progetto Google Cloud
  • Un browser web come Chrome che supporta la console Google Cloud e Cloud Shell

2. Configurazione e requisiti

Configurazione del progetto

  1. Accedi alla console Google Cloud. Se non hai ancora un account Gmail o Google Workspace, devi crearne uno.

Utilizza un account personale anziché un account di lavoro o della scuola.

  1. Crea un nuovo progetto o riutilizzane uno esistente. Per creare un nuovo progetto nella console Google Cloud, fai clic sul pulsante Seleziona un progetto nell'intestazione per aprire una finestra popup.

295004821bab6a87.png

Nella finestra Seleziona un progetto, fai clic su Nuovo progetto, che aprirà una finestra di dialogo per il nuovo progetto.

37d264871000675d.png

Nella finestra di dialogo, inserisci un nome per il progetto e scegli la posizione.

96d86d3d5655cdbe.png

  • Il nome del progetto è il nome visualizzato per i partecipanti a questo progetto. Il nome del progetto non viene utilizzato dalle API di Google e può essere modificato in qualsiasi momento.
  • L'ID progetto è univoco in tutti i progetti Google Cloud ed è immutabile (non può essere modificato dopo l'impostazione). La console Google Cloud genera automaticamente un ID univoco, ma puoi personalizzarlo. Se l'ID generato non ti piace, puoi generarne un altro casuale o fornirne uno tuo per verificarne la disponibilità. Nella maggior parte dei codelab, dovrai fare riferimento all'ID progetto, che in genere è identificato con il segnaposto PROJECT_ID.
  • Per tua informazione, esiste un terzo valore, un numero di progetto, utilizzato da alcune API. Scopri di più su tutti e tre questi valori nella documentazione.

Abilita fatturazione

Configurare un account di fatturazione personale

Se hai configurato la fatturazione utilizzando i crediti Google Cloud, puoi saltare questo passaggio.

Per configurare un account di fatturazione personale, vai qui per abilitare la fatturazione nella console Cloud.

Alcune note:

  • Il completamento di questo lab dovrebbe costare meno di 5 $in risorse cloud.
  • Per evitare ulteriori addebiti, puoi seguire i passaggi alla fine di questo lab per eliminare le risorse.
  • I nuovi utenti hanno diritto alla prova senza costi di 300$.

Avvia Cloud Shell

Sebbene Google Cloud possa essere gestito da remoto dal tuo laptop, in questo codelab utilizzerai Google Cloud Shell, un ambiente a riga di comando in esecuzione nel cloud.

Nella console Google Cloud, fai clic sull'icona di Cloud Shell nella barra degli strumenti in alto a destra:

Attiva Cloud Shell

In alternativa, puoi premere G e poi S. Questa sequenza attiverà Cloud Shell se ti trovi nella console Google Cloud o utilizza questo link.

Bastano pochi istanti per eseguire il provisioning e connettersi all'ambiente. Al termine, dovresti vedere un risultato simile a questo:

Screenshot del terminale Google Cloud Shell che mostra che l'ambiente è connesso

Questa macchina virtuale è caricata con tutti gli strumenti per sviluppatori di cui avrai bisogno. Offre una home directory permanente da 5 GB e viene eseguita su Google Cloud, migliorando notevolmente le prestazioni e l'autenticazione della rete. Tutto il lavoro in questo codelab può essere svolto all'interno di un browser. Non devi installare nulla.

3. Prima di iniziare

Abilita API

Per utilizzare AlloyDB, Compute Engine, i servizi di rete e la piattaforma dell'agente, abilita le rispettive API nel tuo progetto Google Cloud.

All'interno del terminale Cloud Shell, assicurati che l'ID progetto sia configurato:

gcloud config get-value project

Il comando dovrebbe restituire l'ID progetto Google Cloud.

Imposta la variabile di ambiente PROJECT_ID:

PROJECT_ID=$(gcloud config get-value project)

Abilita le API richieste:

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

Output previsto:

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.

Puoi leggere informazioni su ogni API abilitata nella documentazione.

4. Esegui il deployment di AlloyDB

Crea il cluster AlloyDB e l'istanza principale. Puoi eseguirne il deployment utilizzando uno script preparato che eseguirà il deployment di tutte le risorse necessarie oppure puoi farlo passo dopo passo seguendo la documentazione di AlloyDB.

Esegui il deployment di AlloyDB utilizzando lo script automatizzato

Questo approccio utilizza uno script automatizzato per eseguire il deployment del cluster AlloyDB e fornisce i dettagli di configurazione necessari per iniziare a lavorare con le risorse di cui è stato eseguito il deployment.

Nel terminale Cloud Shell, esegui il comando per clonare lo script di deployment dal repository:

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

Esegui lo script di deployment:

./deploy_alloydb.sh

Il completamento dello script richiede dai 5 ai 7 minuti. L'output mostra i dettagli del deployment del cluster. Tieni presente che la password sarà diversa. Registrala per un uso futuro.

...
<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)
----------------------------------------
 

Visualizza il nuovo cluster e la nuova istanza nella pagina Cluster AlloyDB della console Google Cloud.

4271eb55bcc9ec84.png

5. Prepara il database

Crea un database, attiva l'integrazione di Gemini Enterprise Agent Platform, crea oggetti di database e aggiungi dati di esempio.

Concedere le autorizzazioni necessarie ad AlloyDB

Concedi i ruoli IAM richiesti all'agente di servizio AlloyDB:

Apri una nuova scheda di Cloud Shell facendo clic su Aggiungi (+).

abc505ac4d41f24e.png

Nella nuova scheda, esegui:

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"

Output console previsto:

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
 

Abilita l'API Data Access

Abilita l'API Data Access sul tuo cluster AlloyDB.

Nella stessa scheda del terminale, esegui questo comando:

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",
 }'

Aggiorna i flag di database

Prima di attivare i flag a livello di istanza, attendi il completamento del passaggio dell'API Data Access. Assicurati che lo stato dell'istanza mostri un segno di spunta verde, come illustrato nell'immagine seguente.

6284731b8eff74c.jpeg

Per utilizzare le estensioni AI e l'autenticazione IAM per gli strumenti agentici, devi abilitare flag di database specifici nell'istanza e aggiungerti come utente del database. Attiva i seguenti flag:

  • alloydb.iam_authentication
  • password.enforce_complexity
  • google_ml_integration.enable_ai_query_engine
  • parameterized_views

Nella stessa scheda del terminale, esegui questi comandi:

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

Crea un utente AlloyDB IAM per il tuo account:

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

Chiudi la scheda eseguendo il comando exit nel terminale:

exit

Connettersi ad AlloyDB Studio

Nei capitoli seguenti, puoi eseguire tutti i comandi SQL che richiedono una connessione al database in AlloyDB Studio.

Per aprire AlloyDB Studio:

  1. Nella console Google Cloud, vai alla pagina Cluster per AlloyDB per PostgreSQL.
  2. Seleziona l'istanza principale per aprire la relativa interfaccia web.

1d7298e7096e7313.png

  1. Quindi, fai clic su AlloyDB Studio a sinistra:

a33131c72ad29478.png

  1. Seleziona il database postgres, scegli Autenticazione IAM e fai clic su Autentica.

8ddfb250344ca749.jpeg

Questa azione apre l'interfaccia di AlloyDB Studio.

  1. Seleziona la scheda Query senza titolo per aprire l'editor SQL.

6696bc771fab9983.png

Si apre un editor in cui puoi eseguire comandi SQL, come mostrato nell'immagine seguente:

ae34288e5bf237c7.png

Crea database

Crea il database di avvio rapido

Nell'editor di AlloyDB Studio, esegui questo comando per creare il database:

CREATE DATABASE quickstart_db

Output previsto:

Statement executed successfully

Connettiti a quickstart_db

Fai clic su Cambia database/utente in AlloyDB Studio.

6f5c98f5d0b3d550.png

Seleziona quickstart_db dall'elenco dei database, seleziona Autenticazione IAM e autenticati.

a1940c26c36ff840.jpeg

Si aprirà una nuova connessione in cui potrai lavorare con gli oggetti del database quickstart_db.

Attivare le estensioni

Controlla tutte le estensioni richieste per verificare che siano attive nel database e che abbiano le versioni minime corrette.

Verifica che nel database esistano tutte le estensioni richieste:

SELECT extname, extversion FROM pg_extension WHERE extname IN ('vector', 'google_ml_integration', 'parameterized_views');

Output previsto:

        extname        |   extversion   
-----------------------+----------------
 google_ml_integration | 1.5.9
 parameterized_views   | 1.0
 vector                | 0.8.2.google-1

Se non restituisce tutte le righe precedenti, significa che alcune estensioni non sono abilitate.

Prima di attivare le estensioni, dobbiamo assicurarci che tutti i flag del database "google_ml_integration.enable_ai_query_engine" e "parameterized_views.enabled" siano attivati nell'istanza.

In AlloyDB Studio, mentre è connesso a quickstart_db, esegui:

show google_ml_integration.enable_ai_query_engine;
show parameterized_views.enabled;

Se uno di questi mostra "off", prova a ripetere il passaggio "Aggiorna flag del database" e controlla di nuovo lo stato utilizzando i comandi "show...".

Attiva le estensioni richieste.

In AlloyDB Studio, mentre è connesso a quickstart_db, esegui:

CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS parameterized_views;

Controlla le versioni delle estensioni per parameterized_views e google_ml.

In AlloyDB Studio, mentre è connesso a quickstart_db, esegui:

SELECT extname, extversion FROM pg_extension WHERE extname IN ('vector', 'google_ml_integration', 'parameterized_views');

Output previsto:

        extname        |   extversion   
-----------------------+----------------
 google_ml_integration | 1.5.9
 parameterized_views   | 1.0
 vector                | 0.8.2.google-1

Se la versione è inferiore a quella richiesta, prova ad aggiornare l'estensione parameterized_views.

Per parameterized_views, prova:

ALTER EXTENSION parameterized_views UPDATE;

Verifica di nuovo la versione.

In AlloyDB Studio, mentre è connesso a quickstart_db, esegui:

SELECT extname, extversion FROM pg_extension WHERE extname IN ('vector', 'google_ml_integration', 'parameterized_views');

Output previsto:

        extname        |   extversion   
-----------------------+----------------
 google_ml_integration | 1.5.9
 parameterized_views   | 1.0
 vector                | 0.8.2.google-1

6. Dati di esempio

Ora devi creare oggetti nel database e caricare i dati. Utilizzerai un set di dati fittizio di una società immobiliare. Contiene dati fittizi su vendite di immobili e agenti. I dati sono stati forniti nello script SQL real_estate_data.sql nel repository clonato.

Crea bucket di archiviazione

Utilizza Google SDK (gcloud) per importare i dati dal repository clonato nel database AlloyDB. A questo scopo, devi creare un bucket Cloud Storage e concedere l'accesso al service account AlloyDB. In alternativa, puoi sempre provare a farlo utilizzando la console web, come descritto nella documentazione.

Nel terminale Google Cloud Shell esegui:

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

Carica dati

Il dump SQL del database si trova nella cartella del repository clonato. Il seguente comando presuppone che tu abbia utilizzato la tua home directory come punto di partenza quando hai clonato il repository nel passaggio precedente durante la creazione del cluster AlloyDB.

Copia il file di dump del database nel bucket 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

Importa il set di dati in 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

Il comando caricherà il set di dati di esempio nel database quickstart_db.

Verifica l'importazione contando i record nella tabella real_estate.agents utilizzando AlloyDB Studio:

SELECT COUNT(*) FROM real_estate.agents;

7. Prepara l'ontologia dello schema

Configura il set di contesti QueryData, fornisci i metadati dello schema ed esegui l'agente ADK.

Crea metadati

I metadati sono costituiti da commenti SQL applicati a tabelle e colonne del database. Questi commenti guidano il motore QueryData durante la selezione degli oggetti di database.

Se hai importato lo schema dei dati utilizzando il dump SQL fornito, tutti i metadati sono già stati creati nello schema.

Ecco un esempio di snippet di metadati applicati allo schema 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.';

...

Se lavori con il tuo set di dati, puoi seguire la guida documentata su come aggiungere i metadati per lo schema.

Crea contesto dell'ontologia dello schema

Un insieme di contesto contiene modelli di query, sfaccettature e ricerche di valori che aiutano il modello di AI a generare query. Senza il contesto impostato, un agente AI tenterà di generare una query utilizzando solo le informazioni disponibili dallo schema public, basandosi principalmente sui nomi delle tabelle, sulle colonne e sui metadati, cercando di dedurre gli oggetti giusti per rispondere a una richiesta di query in linguaggio naturale.

Il set di contesto QueryData è un file JSON con modelli di query, sfaccettature e ricerche di valori. Puoi consultare diversi esempi nella documentazione.

Carica il set di contesto QueryData nella console web

Per caricare un insieme di contesto, apri AlloyDB Studio e nel riquadro a sinistra in basso vedrai Context sets e tre puntini.

d3578ceee65cd0dd.jpeg

Fai clic sui tre puntini e scegli Create context set.

Nella finestra di dialogo aperta, inserisci i seguenti valori:

  • Nome: sixrecipes
  • Descrizione: Real Estate Query Data
  • Carica file di contesto: lascialo vuoto

Quando premi il pulsante Create, l'inizializzazione dell'archiviazione del contesto potrebbe richiedere un po' di tempo se lo fai per la prima volta.

Controlla l'ID del set di contesto QueryData facendo di nuovo clic sui tre puntini a destra del nome del set di contesto e scegli Edit context set.

6739fb9f54d7f79d.jpeg

L'ID del set di contesto viene visualizzato in alto nella scheda aperta a destra.

746b38c52a70bb3c.jpeg

Salva questo ID per i passaggi successivi.

Avviare l'agente AI

Per interagire con il backend QueryData per generare ed eseguire query, puoi utilizzare l'interfaccia di AlloyDB Studio come descritto nella documentazione. In questo lab, tuttavia, utilizzerai un agente AI come strumento principale di comunicazione con il database. Gli agenti AI sono uno scenario più realistico per i futuri workload e il framework ADK di Google fornisce un'ottima interfaccia web in cui puoi vedere tutti i passaggi e le interazioni tra modelli, API e applicazioni.

Esamina la configurazione dell'origine dell'agente in Cloud Shell:

REPO_NAME="codelabs"
SOURCE_DIR="alloydb-ai-nla"
cd ~/$REPO_NAME/$SOURCE_DIR/data_agent
edit agent.py

Nel file puoi vedere dove definiamo l'elenco delle tabelle nel nostro schema. Torneremo su questo argomento in uno dei passaggi successivi.

# 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",
]

Esiste una procedura call_querydata_api per utilizzare l'API QueryData, nonché un insieme di strumenti per lavorare con le competenze dell'API QueryData.

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]
)

Ecco l'agente principale con un prompt e gli strumenti disponibili.

# 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],
)

Esamina data_agent/SKILL.md per comprendere le istruzioni di sistema dell'agente:

REPO_NAME="codelabs"
SOURCE_DIR="alloydb-ai-nla"
cd ~/$REPO_NAME/$SOURCE_DIR/data_agent
edit SKILL.md

Prima di avviare l'agente, devi preparare un file .env con i parametri per le richieste API Agent e QueryData.

Esporta l'ID set di contesti in una variabile di ambiente:

export QUERYDATA_CONTEXTSET=<context set id from the Create Context Set chapter>

Crea il file di configurazione dell'ambiente di runtime (.env) nella directory 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

Installa le dipendenze e avvia il servizio web dell'agente:

cd ~/$REPO_NAME/$SOURCE_DIR/
uv sync

Avvia l'agente:

uv run adk web --allow_origins 'regex:https://.*\.cloudshell\.dev'

Dovresti visualizzare un output simile al seguente con l'endpoint 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)

4aa60270e31efe37.jpeg

Fai clic sull'URL del server web generato (ad es. http://127.0.0.1:8000) in Cloud Shell per avviare l'interfaccia dell'agente. Se non funziona, in Cloud Shell fai clic su Anteprima web -> Anteprima sulla porta 8000 per aprire l'interfaccia dell'agente.

Verifica che data_agent sia selezionato nel menu a discesa in alto a sinistra.

ad695db289666da3.jpeg

Generare ed eseguire query

Eseguiamo la query Q1

Esegui questo prompt in linguaggio naturale nell'interfaccia di chat dell'agente per valutare le prestazioni di base di QueryData senza modelli o sfaccettature personalizzati:

How many properties are currently for sale in San Diego?

e premi il pulsante Freccia.

77773a81de6360a2.jpeg

Avvierà il flusso di lavoro dell'agente quando selezionerà la skill appropriata e la utilizzerà, insieme alla funzione, per inviare la richiesta all'endpoint API e ricevere la query generata e i risultati.

de9b3e4854b9a506.jpeg

Se passi il cursore del mouse sopra i passaggi, puoi vedere esattamente cosa fanno. Puoi visualizzare queste e molte altre informazioni nel riquadro a sinistra quando scegli un passaggio. Può aiutarti a capire come funzionano gli agenti con le competenze e le API.

605dcf936ba38cb8.jpeg

Invia una query di follow-up (Q2):

List agents who sold a property for over $1000000.

Noterai che l'agente va direttamente alla skill dell'API QueryData, poiché ha già scritto nella memoria le skill disponibili e non ha bisogno di ripetere la fase di rilevamento.

La query SQL generata è simile alla seguente istruzione:

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;

Prova con un'altra query (Q3):

What is the average price of a single-family home with a garage in San Diego?

QueryData genera query standard con un insieme di contesti vuoto, ma le attività aziendali spesso richiedono una logica di business specifica e un comportamento deterministico.

8. Ottenere un'elevata precisione e pertinenza aziendale inserendo l'interpretazione

L'inserimento dell'interpretazione nei prompt garantisce che le query generate corrispondano a una logica di business specifica.

Proverai un esempio con un cliente che chiede case a un solo piano a Del Mar. La domanda ha due metriche importanti e rappresenta un pattern frequente per una selezione.

Prova il seguente prompt in linguaggio naturale (Q4) nell'agente.

Single level Del Mar houses

Il motore restituisce una query generica, ma potrebbe includere alcune condizioni aggiuntive come 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;

Otteniamo una riga restituita come risultato:

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.

Prova un altro prompt (Q5):

Del Mar houses close to good schools

È abbastanza intelligente da utilizzare le colonne proximity_miles e school_ranking per ordinare le proprietà, ma non valuta la ponderazione e l'importanza di ogni metrica.

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;

Generalizzare la query di esempio in un modello

La logica di business richiede:

  • Vicinanza entro un raggio di 2 miglia
  • Ranking della scuola pari o inferiore a 5
  • Ordinamento ponderato: 80% ranking della scuola + 20% vicinanza (punteggio = 0,8 * ranking_scuola + 0,2 * vicinanza_miglia)

Ecco la formula del ranking ponderato:

‬‭‬‭‬‭‬‭‬Score = 0.8 * school_ranking + 0.2 * proximity_miles

Scarica il file di contesto sixrecipe_context1_1.json da Cloud Shell utilizzando l'interfaccia a riga di comando in Cloud Shell:

cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context1_1.json

In alternativa, utilizzando la voce di menu Download dal menu di Cloud Shell:

c0d781806d966dcb.jpeg

Carica sixrecipe_context1_1.json nel set di contesto sixrecipe utilizzando AlloyDB Studio per definire questa struttura standard. Fai clic sui tre puntini a destra del contesto sixrecipes e utilizza "Modifica set di contesti" per caricare il nuovo file. Sostituirà effettivamente il vecchio contesto vuoto con quello nuovo.

Il modello definisce regole esplicite per un buon posizionamento delle scuole:

{
  "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\""
      }
    }
  ]
}

Esegui di nuovo il prompt:

Del Mar houses close to good schools

La query risultante ora corrisponde alla logica di business definita nel modello:

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"

Insieme all'istruzione SQL prodotta, viene generato il seguente intent:

intentExplanation: "Houses in Del Mar close to good schools (within 2 miles of schools with a rank of at least 5)."

Puoi vedere l'intent nel riquadro a sinistra dell'interfaccia web dell'ADK, come mostrato qui:

bfb9d6bc718f57cf.jpeg

I risultati mostrano due proprietà ordinate in base al nostro punteggio:

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

Nel capitolo successivo espanderai l'ambito del modello.

Inserire l'interpretazione acquisendo e interpretando più domande

Puoi ampliare l'ambito del nostro modello in modo che venga utilizzato non solo per "vicino a scuole di qualità", ma anche aggiungendo altre condizioni al manifest per essere utilizzato, ad esempio, per "case per famiglie con bambini".

Scarica il file di contesto aggiornato:

cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context1_2.json

Carica il file sixrecipe_context1_2.json aggiornato nel set di contesti.

Nel nuovo file di set di contesto stiamo sostituendo il manifest da: "manifest": "A given city houses close to good schools." al manifest: "manifest": "A given city houses close to good schools, or are great for families with kids.". Amplia le applicazioni di questo modello in modo che possa essere utilizzato per più di un caso.

Invia questa query (Q6):

Houses in Waterloo great for families with kids.

L'agente utilizza il manifest del modello aggiornato per applicare la logica di query standardizzata, restituendo la stessa query di prima, con l'intento di:

intentExplanation
:
"Houses in Waterloo close to good schools (within 2 miles of schools with a rank of at least 5)."

9. Utilizzare i modelli sfaccettati per ottenere un'elevata precisione mantenendo la flessibilità

I facet sono condizioni SQL modulari combinate dinamicamente con i modelli per gestire i qualificatori (ad es. "famiglia con bambini", "vista sull'oceano") senza creare modelli separati per ogni permutazione.

Invece di creare un modello per ogni query o condizione, possiamo preparare frammenti che possono essere applicati ai modelli esistenti aggiungendo più dimensioni alla query.

Definisci una sfaccettatura per le proprietà con i seguenti attributi:

  • Una casa unifamiliare
  • Stato della scheda attivo
  • Da tre a quattro camere da letto e almeno due bagni
  • Località in un comune con un tasso di criminalità inferiore a 500 reati ogni 100.000 abitanti

Scarica il file di contesto sfaccettato:

cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context2_1.json

Carica sixrecipe_context2_1.json nel set di contesto sixrecipes in AlloyDB Studio per configurare le sfaccettature.

  "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)"
      }
    }
  ]

Esegui questa query in linguaggio naturale (Q7):

Del Mar houses close to good schools for a starter family.

Il motore aggiunge le condizioni dei facet direttamente alla query di base:

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"

Ecco i risultati:

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

Aggiungere altri facet

E se volessimo aggiungere un'altra dimensione in base al nuovo pattern emergente, ad esempio una vista dalla finestra della proprietà?

Aggiungi la corrispondenza semantica della vista sull'oceano all'interno di una definizione di sfaccettatura.

    {
      "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)"
      }

Il valore 0,4 nella definizione del facet riportata sopra è una soglia calibrata sperimentalmente. Scarica il file di contesto sfaccettato:

cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context2_2.json

Carica sixrecipe_context2_2.json nel set di contesto ed esegui questo prompt (Q8):

Ocean view Del Mar homes near good schools.

L'output contiene condizioni standard combinate con la corrispondenza della somiglianza vettoriale tramite 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";

La query restituisce una proprietà:

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).

Verifica i risultati controllando la descrizione della proprietà in AlloyDB Studio ed eseguendo la seguente query:

select description from real_estate.properties where address_postal_code='92014' and description is not null;

Verrà visualizzata la descrizione con "magnifico spettacolo marino".

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. Disambiguazione dei dati privati utilizzando le ricerche di valori

Le richieste ambigue possono corrispondere a più entità, ad esempio quando si confonde il nome di una località con il nome di un'attività. Le ricerche di valori mappano i termini ai concetti del database per risolvere questi riferimenti.

La ricerca di valori fornisce associazioni tra il nome della colonna più i valori nella colonna e un tipo di concetto che può essere, ad esempio, una città, un paese o un nome. Poiché ha indicizzato non solo i metadati, ma anche campioni rappresentativi dei dati in una colonna, può associare un valore utilizzato nella richiesta in linguaggio naturale a un potenziale tipo di concetto e alla tabella e alla colonna che possono essere utilizzate nel codice SQL risultante.

Ambiguità in una query

Nella vita reale può essere ancora più difficile a causa di errori ortografici di nomi e località diversi. Esegui questo prompt (Q9) contenente un errore di battitura e un termine ambiguo:

Westwod's sold properties in the last 1 month.

Qui ci sono due problemi: il nome è scritto in modo errato e non è chiaro se si tratta di immobili venduti nella località con nomi simili o di un'agenzia immobiliare. Quando eseguiamo la query, viene restituita una query simile alla seguente.

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

Ha corretto il nome e ha presupposto che la domanda riguardasse la posizione delle proprietà, cercando di trovare proprietà nella città di Westwood.

Abilita l'estensione pg_trgm in AlloyDB Studio per supportare i confronti di stringhe fuzzy:

CREATE EXTENSION IF NOT EXISTS pg_trgm;

Dopodiché, possiamo utilizzare l'estensione per aggiungere ricerche di valori al nostro set di contesti QueryData.

Ecco l'elenco delle ricerche di valori che vogliamo utilizzare:

"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"
    }
  ]

Scarica il file di contesto di ricerca dei valori:

cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context3_1.json

Carica sixrecipe_context3_1.json nel set di contesti QueryData sixrecipes e salvalo.

Esegui di nuovo il prompt (D9):

Westwod's sold properties in the last 1 month.

Ha restituito una risposta di chiarimento perché nella ricerca di valori ha trovato tre entità diverse con valori simili:

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?

La traccia ci fornisce un indizio sul motivo per cui è stato scelto quel particolare percorso

88c52932dcbef8f9.jpeg

Ecco il testo completo:

"[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?"

Rimozione dell'ambiguità dalla query

Perfeziona il prompt di input (Q10):

Westwood city location properties sold in September 2025.

Ora abbiamo la query e i risultati:

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. Ricerca semantica e funzioni di AI

Le funzioni di AlloyDB AI consentono il filtraggio semantico su dati non strutturati. Utilizza funzioni come ai.if e google_ml.embedding direttamente nelle query SQL insieme agli operatori di database standard.

Modello con operatori ILIKE

Come primo passaggio, carica il nuovo file di contesto con gli operatori ILIKE.

Scarica il file di contesto del modello ILIKE:

cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context4_1.json

Carica sixrecipe_context4_1.json nel tuo insieme di contesti per valutare la corrispondenza standard dei pattern ILIKE per le case "pronte per essere abitate".

    {
      "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%')"
      }
    }

Invia questa query (Q11):

List move-in ready houses.

Query risultante:

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%')

Restituisce un record corrispondente.

Query Results
address_street        address_postal_code
40 Main St, Unit 8        N2H 2G8

Modello con funzione AI.IF

Scarica il file di contesto del modello AI.IF:

cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context4_2.json

Carica sixrecipe_context4_2.json nel set di contesto per valutare la funzione di filtro semantico 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\")"
      }
    }

Ripeti il prompt (Q12):

List move-in ready houses.

SQL generato:

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")

In questo modo otteniamo la stessa proprietà nel risultato:

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

Scarica il file di contesto della ricerca incorporata:

cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context4_3.json

Carica sixrecipe_context4_3.json nel tuo set di contesto per valutare la ricerca vettoriale semantica utilizzando google_ml.embedding e cerca case con nuovi elettrodomestici per la cucina.

Aggiorna il set di contesto utilizzando il file sixrecipe_context4_3.json con un facet aggiuntivo utilizzando la ricerca vettoriale di 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)"
      }
    }

Il valore 0,4 nella definizione del facet riportata sopra è una soglia calibrata sperimentalmente.

Esegui questa query (Q13):

List move-in ready houses with upgraded kitchen appliances.

SQL generato:

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))

Ecco i risultati della query e il suggerimento in linguaggio naturale:

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. Sicurezza e privacy

Ogni volta che l'API QueryData genera query, esiste il rischio per la privacy che le query generate possano esporre dati appartenenti ad altri utenti. Le viste sicure con parametri (PSV) applicano i limiti di sicurezza a livello di riga durante la generazione di NL2SQL.

Crea vista sicura parametrizzata

Abbiamo una tabella real_estate.offers con informazioni sulle offerte per tutte le nostre transazioni immobiliari. Vogliamo che i nostri agenti vedano solo le proprie offerte, non quelle di altri agenti. Creiamo un PSV con la possibilità di filtrare le righe in base all'agent_id.

Crea una vista sicura con parametri per la tabella delle offerte in 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;

Verifica la definizione della vista di sicurezza parametrizzata:

SELECT definition
FROM parameterized_views.all_parameterized_views
WHERE schemaname = 'real_estate' AND viewname = 'secure_offers';

E la tabella real_estate.offers?

Limitare l'accesso ai dati QueryData utilizzando le viste sicure con parametri di AlloyDB

In agent.py, la visualizzazione della sicurezza con parametri real_estate.secure_offers è registrata in TABLE_IDS anziché in real_estate.offers:

Il codice sorgente di 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",
]

e passa il contesto di runtime della sessione per un agente con agent_id=1:

            "parameterized_secure_view_parameters": {
                "parameters": {
                    "key": "agent_id",
                    "value": "1"
                }
            }
        },

Di conseguenza, ogni richiesta API QueryData trasmetterà questo parametro e, quando la visualizzazione sicura viene inclusa nella richiesta, verrà applicato il filtro che restituirà solo i risultati per quell'agente specifico.

Esegui questa query (Q14):

List the offer id and listing price in 2025 where the offer was within 5% of the final selling price.

SQL generato:

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

L'esecuzione di query sulla visualizzazione filtra i risultati in base al parametro della sessione dell'agente attivo, proteggendo i dati dei record non autorizzati e restituendo solo due record.

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

Se esegui la seguente query sulla tabella base real_estate.offers in AlloyDB Studio:

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

Restituisce più di due record, mostrando i dati di tutti gli agenti.

Il lab è terminato. Hai imparato a utilizzare l'API QueryData, le funzioni AI e le viste sicure con parametri di AlloyDB.

13. Liberare spazio

Elimina le istanze e il cluster AlloyDB al termine del lab.

Elimina il cluster AlloyDB e tutte le istanze

Il cluster viene eliminato con l'opzione force, che elimina anche tutte le istanze appartenenti al cluster.

Se il terminale si è disconnesso, connettiti di nuovo e definisci le variabili di progetto e di ambiente in 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)

Elimina il cluster:

gcloud alloydb clusters delete $ADBCLUSTER --region=$REGION --force

Output console previsto:

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.   

Elimina backup di AlloyDB

Elimina tutti i backup AlloyDB per il cluster:

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

Output console previsto:

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.                                                                                                                                                                                                                                                            

Elimina il bucket Cloud Storage

Elimina il bucket di importazione Cloud Storage per evitare addebiti di archiviazione continui:

gcloud storage rm -r gs://${PROJECT_ID}-import 

14. Complimenti

Congratulazioni per aver completato il codelab.

Argomenti trattati

  • Come eseguire il deployment di un cluster AlloyDB e dell'istanza principale
  • Come abilitare QueryData, le funzioni AI e le viste sicure con parametri per AlloyDB
  • Come creare un set di contesti QueryData
  • Come utilizzare diversi modelli e contesti nell'API QueryData
  • Come rendere più flessibili i modelli QueryData utilizzando le sfaccettature
  • Come risolvere l'ambiguità dei dati con la ricerca di valori e l'indicizzazione dei trigrammi
  • Come utilizzare le funzioni AI per la ricerca semantica con i modelli
  • Come utilizzare le viste sicure parametrizzate con l'API QueryData

Sondaggio

Output:

Come utilizzerai questo tutorial?

Solo lettura Lettura e completamento degli esercizi