1. Introducción
En este codelab, aprenderás a obtener los mejores resultados con la API de QueryData. Descubrirás cómo generar resultados precisos y repetibles cuando realices solicitudes en lenguaje natural a tu base de datos. Para ayudarte a lograrlo, la guía te explica seis recetas en varios capítulos, en los que se exploran diferentes aspectos del lenguaje natural a SQL (NL2SQL) y sus aplicaciones prácticas.
Requisitos previos
- Conocimientos básicos de Google Cloud y la consola de Google Cloud
- Habilidades básicas con la interfaz de línea de comandos y Cloud Shell
Qué aprenderás
- Cómo implementar un clúster y una instancia principal de AlloyDB
- Cómo habilitar QueryData, las funciones potenciadas por IA y las vistas seguras parametrizadas
- Cómo crear un conjunto de contexto de QueryData
- Cómo usar diferentes plantillas y contextos en la API de QueryData
- Cómo hacer que las plantillas de QueryData sean más flexibles con facetas
- Cómo resolver la ambigüedad de los datos con la búsqueda de valores y la indexación de trigramas
- Cómo usar las funciones de IA para la búsqueda semántica con plantillas
- Cómo usar vistas seguras con parámetros con la API de QueryData
Requisitos
- Una cuenta y un proyecto de Google Cloud
- Un navegador web, como Chrome, que admita la consola de Google Cloud y Cloud Shell
2. Configuración y requisitos
Configuración del proyecto
- Accede a la consola de Google Cloud. Si aún no tienes una cuenta de Gmail o de Google Workspace, debes crear una.
Usa una cuenta personal en lugar de una cuenta laboral o educativa.
- Crea un proyecto nuevo o reutiliza uno existente. Para crear un proyecto nuevo en la consola de Google Cloud, haz clic en el botón Seleccionar un proyecto del encabezado, que abrirá una ventana emergente.

En la ventana Selecciona un proyecto, haz clic en Nuevo proyecto, que abrirá un cuadro de diálogo para el proyecto nuevo.

En el cuadro de diálogo, ingresa un nombre para el proyecto y elige la ubicación.

- El Nombre del proyecto es el nombre visible de los participantes de este proyecto. El nombre del proyecto no se usa en las APIs de Google y se puede cambiar en cualquier momento.
- El ID del proyecto es único en todos los proyectos de Google Cloud y es inmutable (no se puede cambiar después de configurarlo). La consola de Google Cloud genera automáticamente un ID único, pero puedes personalizarlo. Si no te gusta el ID generado, puedes generar otro aleatorio o proporcionar el tuyo para verificar su disponibilidad. En la mayoría de los codelabs, deberás hacer referencia al ID del proyecto, que suele identificarse con el marcador de posición PROJECT_ID.
- Recuerda que hay un tercer valor, un número de proyecto, que usan algunas APIs. Obtén más información sobre estos tres valores en la documentación.
Habilitar facturación
Configura una cuenta de facturación personal
Si configuraste la facturación con créditos de Google Cloud, puedes omitir este paso.
Para configurar una cuenta de facturación personal, ve aquí para habilitar la facturación en la consola de Cloud.
Algunas notas:
- Completar este lab debería costar menos de USD 5 en recursos de Cloud.
- Puedes seguir los pasos al final de este lab para borrar recursos y evitar cargos adicionales.
- Los usuarios nuevos pueden acceder a la prueba gratuita de USD 300.
Inicie Cloud Shell
Si bien Google Cloud y Spanner se pueden operar de manera remota desde tu laptop, en este codelab usarás Google Cloud Shell, un entorno de línea de comandos que se ejecuta en la nube.
En Google Cloud Console, haz clic en el ícono de Cloud Shell en la barra de herramientas en la parte superior derecha:

También puedes presionar G y, luego, S. Esta secuencia activará Cloud Shell si estás en la consola de Google Cloud o usas este vínculo.
El aprovisionamiento y la conexión al entorno deberían tomar solo unos minutos. Cuando termine el proceso, debería ver algo como lo siguiente:

Esta máquina virtual está cargada con todas las herramientas de desarrollo que necesitarás. Ofrece un directorio principal persistente de 5 GB y se ejecuta en Google Cloud, lo que permite mejorar considerablemente el rendimiento de la red y la autenticación. Todo tu trabajo en este codelab se puede hacer en un navegador. No es necesario que instales nada.
3. Antes de comenzar
Habilitar la API
Para usar AlloyDB, Compute Engine, los servicios de redes y la Agent Platform, habilita sus respectivas APIs en tu proyecto de Google Cloud.
En la terminal de Cloud Shell, asegúrate de que tu ID del proyecto esté configurado:
gcloud config get-value project
El comando debería devolver el ID de tu proyecto de Google Cloud.
Configura la variable de entorno PROJECT_ID:
PROJECT_ID=$(gcloud config get-value project)
Habilita las APIs necesarias:
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
Resultado esperado:
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.
Puedes leer sobre cada API habilitada en la documentación.
4. Implementa AlloyDB
Crea un clúster y una instancia principal de AlloyDB. Puedes implementarlo con una secuencia de comandos preparada que implementará todos los recursos necesarios o puedes hacerlo paso a paso por tu cuenta siguiendo la documentación de AlloyDB.
Implementa AlloyDB con una secuencia de comandos automatizada
Este enfoque utiliza una secuencia de comandos automatizada para implementar el clúster de AlloyDB y proporciona los detalles de configuración necesarios para comenzar a trabajar con los recursos implementados.
En la terminal de Cloud Shell, ejecuta el comando para clonar la secuencia de comandos de implementación desde el repositorio:
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
Ejecuta la secuencia de comandos de implementación:
./deploy_alloydb.sh
La secuencia de comandos tarda entre cinco y siete minutos en completarse. En el resultado, se muestran los detalles de la implementación del clúster. Ten en cuenta que tu contraseña será diferente. Regístrala para usarla más adelante.
... <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) ----------------------------------------
Visualiza tu nuevo clúster y tu nueva instancia en la página Clústeres de AlloyDB en la consola de Google Cloud.

5. Prepara la base de datos
Crea una base de datos, habilita la integración de Gemini Enterprise Agent Platform, crea objetos de base de datos y agrega datos de muestra.
Otorga los permisos necesarios a AlloyDB
Otorga los roles de IAM necesarios al agente de servicio de AlloyDB:
Haz clic en Agregar (+) para abrir una pestaña nueva de Cloud Shell.

En la pestaña nueva, ejecuta lo siguiente:
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"
Resultado esperado en la consola:
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
Habilita la API de Data Access
Habilita la API de Data Access en tu clúster de AlloyDB.
En la misma pestaña de la terminal, ejecuta el siguiente 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",
}'
Actualiza las marcas de la base de datos
Antes de habilitar marcas a nivel de la instancia, espera a que finalice el paso de la API de Data Access. Asegúrate de que el estado de tu instancia muestre una marca de verificación verde, como se ilustra en la siguiente imagen.

Para usar las extensiones de IA y la autenticación de IAM para las herramientas basadas en agentes, debes habilitar marcas de bases de datos específicas en la instancia y agregarte como usuario de la base de datos. Habilita los siguientes parámetros:
- alloydb.iam_authentication
- password.enforce_complexity
- google_ml_integration.enable_ai_query_engine
- parameterized_views
En la misma pestaña de la terminal, ejecuta los siguientes comandos:
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 usuario de IAM de AlloyDB para tu cuenta:
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
Cierra la pestaña ejecutando el comando de salida en la terminal:
exit
Conéctate a AlloyDB Studio
En los siguientes capítulos, puedes ejecutar todos los comandos SQL que requieren una conexión de base de datos en AlloyDB Studio.
Para abrir AlloyDB Studio, sigue estos pasos:
- En la consola de Google Cloud, ve a la página Clústeres de AlloyDB para PostgreSQL.
- Selecciona tu instancia principal para abrir su interfaz web.

- Luego, haz clic en AlloyDB Studio a la izquierda:

- Selecciona la base de datos
postgres, elige Autenticación de IAM y haz clic en Autenticar.

Esta acción abrirá la interfaz de AlloyDB Studio.
- Selecciona la pestaña Consulta sin título para abrir el editor de SQL.

Se abrirá un editor en el que podrás ejecutar comandos de SQL, como se muestra en la siguiente imagen:

Crear base de datos
Crea la base de datos de inicio rápido
En el editor de AlloyDB Studio, ejecuta el siguiente comando para crear la base de datos:
CREATE DATABASE quickstart_db
Resultado esperado:
Statement executed successfully
Conéctate a quickstart_db
Haz clic en Cambiar base de datos o usuario en AlloyDB Studio.

Selecciona quickstart_db en la lista de bases de datos, selecciona Autenticación de IAM y autentícate.

Se abrirá una nueva conexión en la que podrás trabajar con objetos de la base de datos quickstart_db.
Habilita las extensiones
Verifica todas las extensiones requeridas para asegurarte de que estén habilitadas en tu base de datos y tengan las versiones mínimas correctas.
Verifica que todas las extensiones requeridas existan en tu base de datos:
SELECT extname, extversion FROM pg_extension WHERE extname IN ('vector', 'google_ml_integration', 'parameterized_views');
Resultado esperado:
extname | extversion -----------------------+---------------- google_ml_integration | 1.5.9 parameterized_views | 1.0 vector | 0.8.2.google-1
Si no devuelve todas las filas anteriores, significa que algunas de las extensiones no están habilitadas.
Antes de habilitar las extensiones, debemos asegurarnos de que todas las marcas de la base de datos ,"google_ml_integration.enable_ai_query_engine" y "parameterized_views.enabled" estén habilitadas en la instancia.
En AlloyDB Studio, mientras estás conectado a quickstart_db, ejecuta lo siguiente:
show google_ml_integration.enable_ai_query_engine;
show parameterized_views.enabled;
Si alguna de ellas muestra el estado "off", intenta repetir el paso "Actualizar marcas de la base de datos" y vuelve a verificar el estado con los comandos "show…".
Habilita las extensiones necesarias.
En AlloyDB Studio, mientras estás conectado a quickstart_db, ejecuta lo siguiente:
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS parameterized_views;
Verifica las versiones de las extensiones para parameterized_views y google_ml.
En AlloyDB Studio, mientras estás conectado a quickstart_db, ejecuta lo siguiente:
SELECT extname, extversion FROM pg_extension WHERE extname IN ('vector', 'google_ml_integration', 'parameterized_views');
Resultado esperado:
extname | extversion -----------------------+---------------- google_ml_integration | 1.5.9 parameterized_views | 1.0 vector | 0.8.2.google-1
Si la versión es inferior a la requerida, intenta actualizar la extensión parameterized_views.
Para parameterized_views, prueba lo siguiente:
ALTER EXTENSION parameterized_views UPDATE;
Vuelve a verificar la versión.
En AlloyDB Studio, mientras estás conectado a quickstart_db, ejecuta lo siguiente:
SELECT extname, extversion FROM pg_extension WHERE extname IN ('vector', 'google_ml_integration', 'parameterized_views');
Resultado esperado:
extname | extversion -----------------------+---------------- google_ml_integration | 1.5.9 parameterized_views | 1.0 vector | 0.8.2.google-1
6. Datos de muestra
Ahora debes crear objetos en la base de datos y cargar datos. Usarás un conjunto de datos ficticio de una empresa de bienes raíces. Contiene datos ficticios sobre ventas de propiedades y agentes. Los datos se proporcionaron en el lenguaje SQL real_estate_data.sql del repositorio clonado.
Cree un bucket de almacenamiento
Usa el SDK de Google (gcloud) para importar datos desde tu repositorio clonado a la base de datos de AlloyDB. Para ello, debes crear un bucket de Cloud Storage y otorgar acceso a la cuenta de servicio de AlloyDB. Como alternativa, siempre puedes intentar hacerlo con la consola web, como se describe en la documentación.
En la terminal de Google Cloud Shell, ejecuta lo siguiente:
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
Cargar datos
El volcado de SQL de la base de datos se encuentra en la carpeta del repositorio clonado. En el siguiente comando, se supone que usaste tu directorio principal como punto de partida cuando clonaste el repositorio en el paso anterior mientras creabas el clúster de AlloyDB.
Copia el archivo de volcado de la base de datos en tu bucket de 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 el conjunto de datos a 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
El comando cargará el conjunto de datos de muestra en la base de datos quickstart_db.
Verifica la importación contando los registros en la tabla real_estate.agents con AlloyDB Studio:
SELECT COUNT(*) FROM real_estate.agents;
7. Prepara la ontología del esquema
Configura tu conjunto de contexto de QueryData, proporciona metadatos de esquema y ejecuta el agente del ADK.
Crear metadatos
Los metadatos consisten en comentarios SQL aplicados a las tablas y columnas de la base de datos. Estos comentarios guían el motor de QueryData cuando selecciona objetos de la base de datos.
Si importaste tu esquema de datos con el volcado de SQL proporcionado, todos los metadatos ya se crearon en tu esquema.
A continuación, se muestra un ejemplo de un fragmento de metadatos aplicado al esquema 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.'; ...
Si trabajas con tu propio conjunto de datos, puedes seguir la guía documentada para agregar los metadatos de tu esquema.
Crea un contexto de ontología de esquema
Un conjunto de contexto contiene plantillas de búsqueda, facetas y búsquedas de valores que ayudan al modelo de IA a generar búsquedas. Sin el contexto establecido, un agente de IA intentará generar una consulta usando solo la información disponible del esquema de public, principalmente en función de los nombres de las tablas, las columnas y los metadatos, para deducir los objetos correctos que permitan responder una solicitud de consulta natural.
El conjunto de contexto QueryData es un archivo JSON con plantillas de búsqueda, facetas y búsquedas de valores. Puedes consultar diferentes ejemplos en la documentación.
Carga el conjunto de contexto de QueryData en la consola web
Para cargar un conjunto de contexto, abre AlloyDB Studio y, en el panel izquierdo, en la parte inferior, verás Context sets y tres puntos.

Haz clic en esos tres puntos y elige Create context set.
En el diálogo que se abrió, ingresa los siguientes valores:
- Nombre:
sixrecipes - Descripción:
Real Estate Query Data - Sube el archivo de contexto: Déjalo vacío
Cuando presiones el botón Create, es posible que la inicialización del almacenamiento del contexto tarde un tiempo si lo haces por primera vez.
Vuelve a hacer clic en los tres puntos que se encuentran a la derecha del nombre del conjunto de contexto y elige Edit context set para verificar el ID del conjunto de contexto de QueryData.

El ID del conjunto de contexto aparece en la parte superior de la pestaña abierta a la derecha.

Guarda este ID para los pasos posteriores.
Iniciar el agente de IA
Para interactuar con el backend de QueryData y generar y ejecutar consultas, puedes usar la interfaz de AlloyDB Studio, como se describe en la documentación. Sin embargo, en este lab, usarás un agente de IA como la principal herramienta de comunicación con la base de datos. Los agentes de IA son un escenario más realista para las cargas de trabajo futuras, y el framework de ADK de Google proporciona una excelente interfaz web en la que puedes ver todos los pasos y las interacciones entre los modelos, las APIs y las aplicaciones.
Revisa la configuración de la fuente del agente en Cloud Shell:
REPO_NAME="codelabs"
SOURCE_DIR="alloydb-ai-nla"
cd ~/$REPO_NAME/$SOURCE_DIR/data_agent
edit agent.py
En el archivo, puedes ver dónde definimos la lista de tablas en nuestro esquema. Volveremos a tratar este tema en uno de nuestros próximos pasos.
# 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",
]
Además, hay un procedimiento call_querydata_api para trabajar con la API de QueryData junto con un conjunto de herramientas para trabajar con las habilidades de la API de 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]
)
Aquí se muestra el agente raíz con una instrucción y las herramientas disponibles.
# 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],
)
Revisa data_agent/SKILL.md para comprender las instrucciones del sistema del agente:
REPO_NAME="codelabs"
SOURCE_DIR="alloydb-ai-nla"
cd ~/$REPO_NAME/$SOURCE_DIR/data_agent
edit SKILL.md
Antes de iniciar el agente, debes preparar un archivo .env con parámetros para las solicitudes de la API de Agent y QueryData.
Exporta el ID del conjunto de contexto a una variable de entorno:
export QUERYDATA_CONTEXTSET=<context set id from the Create Context Set chapter>
Crea tu archivo de configuración del entorno de ejecución (.env) en el directorio 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
Instala las dependencias e inicia el servicio web del agente:
cd ~/$REPO_NAME/$SOURCE_DIR/
uv sync
Inicia el agente:
uv run adk web --allow_origins 'regex:https://.*\.cloudshell\.dev'
Deberías ver un resultado como el siguiente con el extremo 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)

Haz clic en la URL del servidor web generada (p.ej., http://127.0.0.1:8000) en Cloud Shell para iniciar la interfaz del agente. Si no funciona, en Cloud Shell, haz clic en Vista previa en la Web -> Vista previa en el puerto 8000 para abrir la interfaz del agente.
Verifica que data_agent esté seleccionado en el menú desplegable de la parte superior izquierda.

Generar y ejecutar consultas
Ejecutemos la consulta Q1
Ejecuta esta instrucción en lenguaje natural en la interfaz de chat del agente para evaluar el rendimiento de QueryData base sin plantillas ni facetas personalizadas:
How many properties are currently for sale in San Diego?
Y presiona el botón de flecha.

Iniciará el flujo de trabajo del agente cuando seleccione la habilidad adecuada y use esa habilidad y la función para enviar la solicitud al endpoint de API y recuperar la búsqueda generada y los resultados.

Si colocas el cursor del mouse sobre los pasos, puedes ver exactamente qué hacían. También puedes ver eso y mucho más en el panel izquierdo cuando eliges un paso u otro. Puede ayudarte a comprender cómo funcionan los agentes con las habilidades y las APIs.

Envía una consulta de seguimiento (P2):
List agents who sold a property for over $1000000.
Notarás que el agente va directamente a la habilidad de la API de QueryData, ya que ya escribió en la memoria las habilidades disponibles y no necesita volver a pasar por la fase de descubrimiento.
El código SQL generado se parece a esta instrucción:
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;
Prueba con otra búsqueda (P3):
What is the average price of a single-family home with a garage in San Diego?
QueryData genera consultas estándar con un conjunto de contexto vacío, pero las tareas comerciales a menudo requieren una lógica empresarial específica y un comportamiento determinístico.
8. Logra una alta precisión y relevancia comercial incorporando la interpretación
La inyección de interpretación en las instrucciones garantiza que las búsquedas generadas coincidan con una lógica empresarial específica.
Probarás un ejemplo de este tipo con un cliente que solicita casas de un solo nivel en Del Mar. La pregunta tiene dos métricas importantes y representa un patrón frecuente para una selección.
Prueba la siguiente instrucción en lenguaje natural (P4) en el agente.
Single level Del Mar houses
El motor devuelve una búsqueda general, pero puede incluir algunas condiciones adicionales, como 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;
Obtenemos una fila devuelta como resultado:
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.
Prueba otra instrucción (P5):
Del Mar houses close to good schools
Es lo suficientemente inteligente como para usar las columnas proximity_miles y school_ranking para ordenar las propiedades, pero no evalúa el peso ni la importancia de cada métrica.
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;
Generaliza la consulta de ejemplo en una plantilla
La lógica empresarial requiere lo siguiente:
- Proximidad en un radio de 3.2 kilómetros
- Clasificación de la escuela de 5 o menos
- Ordenamiento ponderado: 80% de ranking de la institución educativa y 20% de proximidad (puntuación = 0.8 * ranking_de_la_institución_educativa + 0.2 * proximidad_en_millas)
Esta es la fórmula de clasificación ponderada:
Score = 0.8 * school_ranking + 0.2 * proximity_miles
Descarga el archivo de contexto sixrecipe_context1_1.json desde Cloud Shell con la interfaz de línea de comandos en Cloud Shell:
cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context1_1.json
O bien, usa el elemento de menú Download del menú de Cloud Shell:

Sube el sixrecipe_context1_1.json a tu conjunto de contexto sixrecipe con AlloyDB Studio para definir esta estructura estándar. Haz clic en los tres puntos a la derecha del contexto sixrecipes y usa "Editar conjunto de contexto" para subir el archivo nuevo. Reemplazará de manera efectiva el contexto vacío anterior por el nuevo.
La plantilla define reglas explícitas de plantillas de SQL para reglas de buena proximidad a la escuela:
{
"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\""
}
}
]
}
Vuelve a ejecutar la instrucción:
Del Mar houses close to good schools
La consulta resultante ahora coincide con la lógica empresarial definida en la plantilla:
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"
Junto con la sentencia de SQL generada, se produce la siguiente intención:
intentExplanation: "Houses in Del Mar close to good schools (within 2 miles of schools with a rank of at least 5)."
Puedes ver la intención en el panel izquierdo de la interfaz web del ADK, como se muestra aquí:

Los resultados muestran dos propiedades ordenadas según nuestra puntuación:
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
En el siguiente capítulo, ampliarás el alcance de la plantilla.
Cómo insertar la interpretación capturando e interpretando varias preguntas
Puedes ampliar el alcance de nuestra plantilla para que se use no solo para "cerca de buenas escuelas", sino también para agregar otras condiciones al manifiesto, por ejemplo, para "casas para familias con niños".
Descarga el archivo de contexto actualizado:
cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context1_2.json
Sube el archivo sixrecipe_context1_2.json actualizado a tu conjunto de contexto.
En el nuevo archivo de configuración del contexto, reemplazamos nuestro manifiesto de "manifest": "A given city houses close to good schools." al manifiesto "manifest": "A given city houses close to good schools, or are great for families with kids.". Expande las aplicaciones de esta plantilla para que se pueda usar en más de un caso.
Envía esta consulta (P6):
Houses in Waterloo great for families with kids.
El agente usa el manifiesto de plantilla actualizado para aplicar tu lógica de búsqueda estandarizada y devolver la misma búsqueda que antes, con la siguiente intención:
intentExplanation : "Houses in Waterloo close to good schools (within 2 miles of schools with a rank of at least 5)."
9. Usa plantillas facetadas para lograr una alta precisión y mantener la flexibilidad
Las facetas son condiciones SQL modulares que se combinan de forma dinámica con plantillas para controlar los calificadores (p.ej., "familiar para principiantes", "vista al océano") sin crear plantillas separadas para cada permutación.
En lugar de crear una plantilla para cada condición o consulta, podemos preparar fragmentos que se pueden aplicar a las plantillas existentes y agregar más dimensiones a la consulta.
Define una faceta para las propiedades con los siguientes atributos:
- Una casa unifamiliar
- Un estado de ficha activa
- De tres a cuatro habitaciones y al menos dos baños
- Ubicación en un municipio con una tasa de delincuencia inferior a 500 por cada 100,000 residentes
Descarga el archivo de contexto facetado:
cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context2_1.json
Sube sixrecipe_context2_1.json a tu conjunto de contexto de sixrecipes en AlloyDB Studio para configurar las facetas.
"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)"
}
}
]
Consulta con la búsqueda por facetas
Ejecuta esta búsqueda con lenguaje natural (P7):
Del Mar houses close to good schools for a starter family.
La app del motor agrega tus condiciones de faceta directamente a la consulta 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"
Estos son los resultados:
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
Cómo agregar más facetas
¿Qué sucede si queremos agregar una dimensión más basada en el nuevo patrón emergente, como una vista de ventana para la propiedad?
Agrega la coincidencia semántica de la vista al mar dentro de una definición de faceta.
{
"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)"
}
El valor 0.4 en la definición de faceta anterior es un umbral calibrado de forma experimental. Descarga el archivo de contexto multifacético:
cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context2_2.json
Sube sixrecipe_context2_2.json a tu conjunto de contexto y ejecuta esta instrucción (P8):
Ocean view Del Mar homes near good schools.
El resultado contiene condiciones estándar combinadas con la coincidencia de similitud de vectores a través de 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";
Y la consulta devuelve una propiedad:
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).
Para verificar los resultados, consulta la descripción de la propiedad en AlloyDB Studio y ejecuta la siguiente consulta:
select description from real_estate.properties where address_postal_code='92014' and description is not null;
Se mostrará la descripción con la frase "magnífico espectáculo 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. Desambiguación de tus datos privados con las búsquedas de valores
Las solicitudes ambiguas pueden coincidir con varias entidades, como confundir el nombre de una ubicación con el nombre de una empresa. Las búsquedas de valores asignan términos a conceptos de la base de datos para ayudar a resolver estas referencias.
La búsqueda de valores proporciona asociaciones entre el nombre de la columna y los valores de la columna, y un tipo de concepto que puede ser, por ejemplo, una ciudad, un país o un nombre. Además, como indexó no solo los metadatos, sino también muestras representativas de los datos en una columna, puede asociar un valor utilizado en la solicitud en lenguaje natural con un tipo de concepto potencial y con la tabla y la columna que se pueden usar en el SQL resultante.
Ambigüedad en una búsqueda
En la vida real, puede ser aún más difícil debido a los errores ortográficos de diferentes nombres y ubicaciones. Ejecuta esta instrucción (P9) que contiene un error tipográfico y un término ambiguo:
Westwod's sold properties in the last 1 month.
Aquí hay dos problemas: el nombre está mal escrito y no queda claro si hablamos de propiedades vendidas en la ubicación con nombres similares o de una agencia de bienes raíces. Cuando ejecutamos la consulta, se muestra una consulta como la siguiente.
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
Corrigió el nombre y supuso que la pregunta era sobre la ubicación de las propiedades, tratando de encontrar propiedades en la ciudad de Westwood.
Habilita la extensión pg_trgm en AlloyDB Studio para admitir comparaciones de cadenas aproximadas:
CREATE EXTENSION IF NOT EXISTS pg_trgm;
Después de eso, podemos usar la extensión para agregar búsquedas de valores a nuestro conjunto de contexto de QueryData.
Esta es la lista de búsquedas de valores que queremos usar:
"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"
}
]
Descarga el archivo de contexto de búsqueda de valores:
cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context3_1.json
Sube sixrecipe_context3_1.json al conjunto de información contextual de QueryData sixrecipes y guárdalo.
Vuelve a ejecutar la instrucción (P9):
Westwod's sold properties in the last 1 month.
Devolvió una respuesta de aclaración porque, en la búsqueda de valores, encontró 3 entidades diferentes con valores similares:
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?
El registro nos da una pista de por qué eligió esa ruta en particular.

Este es el texto 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?"
Cómo eliminar la ambigüedad de la búsqueda
Define mejor la instrucción de entrada (P10):
Westwood city location properties sold in September 2025.
Ahora tenemos la búsqueda y los resultados:
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. Búsqueda semántica y funciones potenciadas por IA
Las funciones IA de AlloyDB permiten el filtrado semántico en datos no estructurados. Usa funciones como ai.if y google_ml.embedding directamente en las consultas de SQL junto con los operadores de bases de datos estándar.
Plantilla con operadores ILIKE
Como primer paso, sube el nuevo archivo de contexto con operadores ILIKE.
Descarga el archivo de contexto de la plantilla de ILIKE:
cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context4_1.json
Sube sixrecipe_context4_1.json a tu conjunto de contexto para evaluar la coincidencia de patrones ILIKE estándar para las casas "listas para mudarse".
{
"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%')"
}
}
Envía esta consulta (P11):
List move-in ready houses.
Consulta resultante:
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%')
Devuelve un registro coincidente.
Query Results address_street address_postal_code 40 Main St, Unit 8 N2H 2G8
Plantilla con la función AI.IF
Descarga el archivo de contexto de la plantilla AI.IF:
cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context4_2.json
Sube sixrecipe_context4_2.json a tu conjunto de contexto para evaluar la función de filtrado semántico de 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\")"
}
}
Repite la instrucción (P12):
List move-in ready houses.
SQL generado:
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")
Esto nos da la misma propiedad en el resultado:
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
Facetas con la búsqueda de vectores
Descarga el archivo de contexto de búsqueda de la incorporación:
cloudshell download ~/codelabs/alloydb-ai-nla/sixrecipe_context4_3.json
Sube sixrecipe_context4_3.json a tu conjunto de contexto para evaluar la búsqueda de vectores semánticos con google_ml.embedding y buscar casas con electrodomésticos de cocina nuevos.
Actualiza el conjunto de contexto con el archivo sixrecipe_context4_3.json con una faceta adicional usando la búsqueda de vectores de 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)"
}
}
El valor 0.4 en la definición de faceta anterior es un umbral calibrado de forma experimental.
Ejecuta esta consulta (P13):
List move-in ready houses with upgraded kitchen appliances.
SQL generado:
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))
Estos son los resultados de la búsqueda y la recomendación en lenguaje natural:
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. Seguridad y privacidad
Cada vez que la API de QueryData genera consultas, existe el riesgo de que estas expongan datos que pertenecen a otros usuarios. Las vistas seguras parametrizadas (PSV) aplican límites de seguridad a nivel de la fila durante la generación de NL2SQL.
Crea una vista segura parametrizada
Tenemos una tabla real_estate.offers con información sobre las ofertas de todos nuestros acuerdos inmobiliarios. Queremos que nuestros agentes solo vean sus propios acuerdos, pero no los de otros agentes. Creamos un PSV con la capacidad de filtrar filas según el agent_id.
Crea una vista segura parametrizada para la tabla de ofertas en 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 definición de la vista de seguridad parametrizada:
SELECT definition
FROM parameterized_views.all_parameterized_views
WHERE schemaname = 'real_estate' AND viewname = 'secure_offers';
¿Qué sucede con la tabla real_estate.offers?
Limita el acceso a los datos de QueryData con vistas seguras parametrizadas de AlloyDB
En agent.py, la vista de seguridad parametrizada real_estate.secure_offers se registra en TABLE_IDS en lugar de real_estate.offers:
El código fuente de 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",
]
Y pasa el contexto de tiempo de ejecución de la sesión para un agente con agent_id=1:
"parameterized_secure_view_parameters": {
"parameters": {
"key": "agent_id",
"value": "1"
}
}
},
Como resultado, cada solicitud a la API de QueryData pasará ese parámetro y, cuando se incluya la vista segura en la solicitud, se aplicará el filtrado y solo se devolverán los resultados para ese agente en particular.
Ejecuta esta consulta (P14):
List the offer id and listing price in 2025 where the offer was within 5% of the final selling price.
SQL generado:
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
Cuando se consulta la vista, los resultados se filtran según el parámetro de sesión del agente activo, lo que protege los datos de registros no autorizados y muestra solo dos registros.
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
Si ejecutas la siguiente consulta en la tabla base real_estate.offers en 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
Devuelve más de dos registros, mostrando datos de todos los agentes.
Con esto, finaliza el lab. Aprendiste a usar la API de QueryData, las funciones basadas en IA y las vistas seguras parametrizadas de AlloyDB.
13. Limpia el entorno
Cuando termines el lab, destruye las instancias y el clúster de AlloyDB.
Borra el clúster de AlloyDB y todas las instancias
El clúster se destruye con la opción force, que también borra todas las instancias que pertenecen al clúster.
Si se desconectó tu terminal, vuelve a conectarte y define las variables de entorno y del proyecto en 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)
Borra el clúster:
gcloud alloydb clusters delete $ADBCLUSTER --region=$REGION --force
Resultado esperado en la consola:
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.
Borra las copias de seguridad de AlloyDB
Borra todas las copias de seguridad de AlloyDB del clúster:
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
Resultado esperado en la consola:
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.
Borra el bucket de Cloud Storage
Borra el bucket de importación de Cloud Storage para evitar cargos de almacenamiento continuos:
gcloud storage rm -r gs://${PROJECT_ID}-import
14. Felicitaciones
¡Felicitaciones por completar el codelab!
Temas abordados
- Cómo implementar un clúster y una instancia principal de AlloyDB
- Cómo habilitar QueryData, las funciones basadas en IA y las vistas seguras parametrizadas para AlloyDB
- Cómo crear un conjunto de contexto de QueryData
- Cómo usar diferentes plantillas y contextos en la API de QueryData
- Cómo hacer que las plantillas de QueryData sean más flexibles con facetas
- Cómo resolver la ambigüedad de los datos con la búsqueda de valores y la indexación de trigramas
- Cómo usar las funciones de IA para la búsqueda semántica con plantillas
- Cómo usar vistas seguras con parámetros con la API de QueryData
Encuesta
Resultado: