1. Descripción general
Imagina entrar a una juguetería de forma virtual o en persona, donde encontrar el regalo perfecto es muy fácil. Puedes describir lo que buscas, subir una foto de un juguete o incluso diseñar tu propia creación, y la tienda comprenderá tus necesidades al instante y te brindará una experiencia personalizada. No es una fantasía futurista, sino una realidad potenciada por la IA, la tecnología de la nube y una visión del comercio electrónico personalizado.
El desafío: Encontrar el producto perfecto que coincida con tu imaginación puede ser difícil. Los términos de búsqueda genéricos, las palabras clave y las búsquedas aproximadas suelen ser insuficientes, navegar por páginas interminables puede ser tedioso y la desconexión entre lo que imaginas y lo que está disponible puede generar frustración.
La solución: La aplicación de demostración aborda este desafío de frente, ya que aprovecha el poder de la IA para brindar una experiencia verdaderamente personalizada y fluida con la búsqueda contextual y la generación personalizada del producto que coincide con el contexto de la búsqueda.
Qué compilarás
Como parte de este lab, harás lo siguiente:
- Crea una instancia de AlloyDB y carga el conjunto de datos de Toys
- Habilita las extensiones de pgvector y del modelo de IA generativa en AlloyDB
- Genera incorporaciones a partir de la descripción del producto y realiza una búsqueda de similitud del coseno en tiempo real para el texto de búsqueda del usuario.
- Invoca a Gemini 2.0 Flash para describir la imagen que subió el usuario para la búsqueda contextual de juguetes
- Invocar Imagen 3 para crear de forma personalizada un juguete según el interés del usuario
- Invoca una herramienta de predicción de precios creada con la Caja de herramientas de IA generativa para bases de datos para obtener detalles sobre el precio del juguete personalizado.
- Implementa la solución en Cloud Run Functions sin servidores
Requisitos
2. Arquitectura
Flujo de datos: Analicemos con más detalle cómo se mueven los datos a través de nuestro sistema:
- Búsqueda contextual con RAG (generación mejorada por recuperación) potenciada por IA
Piensa en ello de esta manera: En lugar de solo buscar "automóvil rojo", el sistema comprende lo siguiente:
"Vehículo pequeño adecuado para un niño de 3 años".
AlloyDB como base: Usamos AlloyDB, la base de datos completamente administrada y compatible con PostgreSQL de Google Cloud, para almacenar nuestros datos de juguetes, incluidas las descripciones, las URLs de las imágenes y otros atributos relevantes.
pgvector para la búsqueda semántica: pgvector, una extensión de PostgreSQL, nos permite almacenar embeddings de vectores de las descripciones de los juguetes y las búsquedas de los usuarios. Esto permite la búsqueda semántica, lo que significa que el sistema comprende el significado detrás de las palabras, no solo las palabras clave exactas.
Similitud de coseno para la relevancia: Usamos la similitud de coseno para medir la similitud semántica entre el vector de búsqueda del usuario y los vectores de descripción de los juguetes, y así mostrar los resultados más relevantes.
Índice de ScaNN para velocidad y precisión: Para garantizar resultados rápidos y precisos, especialmente a medida que crece nuestro inventario de juguetes, integramos el índice de ScaNN (vecinos más cercanos escalables). Esto mejora significativamente la eficiencia y la recuperación de nuestra búsqueda de vectores.
- Búsqueda y comprensión basadas en imágenes con Gemini 2.0 Flash
En lugar de escribir el contexto como texto, supongamos que el usuario quiere subir una foto de un juguete familiar con el que quiere realizar una búsqueda. Los usuarios pueden subir una imagen del juguete que les gusta y obtener funciones relevantes con ella. Aprovechamos el modelo Gemini 2.0 Flash de Google, al que se invoca con LangChain4j, para analizar la imagen y extraer el contexto pertinente, como el color, el material, el tipo y el grupo etario previsto del juguete.
- Cómo crear el juguete de tus sueños personalizado con IA generativa: Imagen 3
La verdadera magia ocurre cuando los usuarios deciden crear su propio juguete. Con Imagen 3, les permitimos describir el juguete de sus sueños con instrucciones de texto sencillas. Imagina poder decir: "Quiero un dragón de peluche con alas moradas y una cara amigable" y ver cómo ese dragón cobra vida en tu pantalla. Luego, Imagen 3 genera una imagen del juguete diseñado de forma personalizada, lo que le brinda al usuario una visualización clara de su creación.
- Predicción de precios potenciada por agentes y caja de herramientas de IA generativa para bases de datos
Implementamos una función de predicción de precios que estima el costo de producir el juguete diseñado a medida. Esto se basa en un agente que incluye una sofisticada herramienta de cálculo de precios.
Caja de herramientas de IA generativa para bases de datos: Este agente se integra sin problemas con nuestra base de datos a través de la nueva herramienta de código abierto de Google, la caja de herramientas de IA generativa para bases de datos. Esto permite que el agente acceda a datos en tiempo real sobre los costos de materiales, los procesos de fabricación y otros factores relevantes para proporcionar una estimación de precios precisa. Obtén más información aquí.
- Java Spring Boot, Gemini Code Assist y Cloud Run para un desarrollo optimizado y una implementación sin servidores
Toda la aplicación se compila con Java Spring Boot, un marco de trabajo sólido y escalable. Aprovechamos Gemini Code Assist durante todo el proceso de desarrollo, en particular para el desarrollo de front-end, lo que aceleró significativamente el ciclo de desarrollo y mejoró la calidad del código. Usamos Cloud Run para implementar toda la aplicación y Cloud Run Functions para implementar las funcionalidades de la base de datos y del agente como extremos independientes.
3. Antes de comenzar
Crea un proyecto
- En la página del selector de proyectos de la consola de Google Cloud, selecciona o crea un proyecto de Google Cloud.
- Asegúrate de que la facturación esté habilitada para tu proyecto de Cloud. Obtén información para verificar si la facturación está habilitada en un proyecto .
- Usarás Cloud Shell, un entorno de línea de comandos que se ejecuta en Google Cloud y que viene precargado con bq. Haz clic en Activar Cloud Shell en la parte superior de la consola de Google Cloud.

- Una vez que te conectes a Cloud Shell, verifica que ya te autenticaste y que el proyecto se configuró con tu ID del proyecto usando el siguiente comando:
gcloud auth list
- En Cloud Shell, ejecuta el siguiente comando para confirmar que el comando gcloud conoce tu proyecto.
gcloud config list project
- Si tu proyecto no está configurado, usa el siguiente comando para hacerlo:
gcloud config set project <YOUR_PROJECT_ID>
- Habilita las APIs requeridas ejecutando los siguientes comandos uno por uno en tu terminal de Cloud Shell:
También hay un solo comando para ejecutar lo que se indica a continuación, pero, si eres usuario de una cuenta de prueba, es posible que tengas problemas de cuota cuando intentes habilitar estos elementos de forma masiva. Por eso, los comandos se muestran uno por línea.
gcloud services enable alloydb.googleapis.com
gcloud services enable compute.googleapis.com
gcloud services enable cloudresourcemanager.googleapis.com
gcloud services enable servicenetworking.googleapis.com
gcloud services enable run.googleapis.com
gcloud services enable cloudbuild.googleapis.com
gcloud services enable cloudfunctions.googleapis.com
gcloud services enable aiplatform.googleapis.com
La alternativa al comando de gcloud es buscar cada producto en la consola o usar este vínculo.
Si olvidas alguna API, siempre puedes habilitarla durante el curso de la implementación.
Consulta la documentación para ver los comandos y el uso de gcloud.
4. Configuración de la base de datos
En este lab, usaremos AlloyDB como la base de datos para almacenar los datos de la juguetería. Utiliza clústeres para contener todos los recursos, como bases de datos y registros. Cada clúster tiene una instancia principal que proporciona un punto de acceso a los datos. Las tablas contendrán los datos reales.
Creemos un clúster, una instancia y una tabla de AlloyDB en los que se cargará el conjunto de datos de comercio electrónico.
Crea un clúster y una instancia
- Navega por la página de AlloyDB en Cloud Console. Una forma sencilla de encontrar la mayoría de las páginas en la consola de Cloud es buscarlas con la barra de búsqueda de la consola.
- Selecciona CREATE CLUSTER en esa página:

- Verás una pantalla como la que se muestra a continuación. Crea un clúster y una instancia con los siguientes valores (asegúrate de que los valores coincidan si clonas el código de la aplicación desde el repo):
- ID del clúster: "
vector-cluster" - contraseña: "
alloydb" - Compatible con PostgreSQL 15
- Región: "
us-central1" - Networking: "
default"

- Cuando selecciones la red predeterminada, verás una pantalla como la que se muestra a continuación.
Selecciona CONFIGURAR CONEXIÓN.
- Allí, selecciona "Usar un rango de IP asignado automáticamente" y haz clic en Continuar. Después de revisar la información, selecciona CREAR CONEXIÓN.

- Una vez que configures tu red, podrás continuar con la creación del clúster. Haz clic en CREATE CLUSTER para completar la configuración del clúster, como se muestra a continuación:

Asegúrate de cambiar el ID de la instancia a
vector-instance
Si no puedes cambiarlo, recuerda cambiar el ID de la instancia en todas las referencias futuras.
Ten en cuenta que la creación del clúster tardará alrededor de 10 minutos. Una vez que se complete correctamente, deberías ver una pantalla que muestre el resumen del clúster que acabas de crear.
5. Transferencia de datos
Ahora es momento de agregar una tabla con los datos de la tienda. Navega a AlloyDB, selecciona el clúster principal y, luego, AlloyDB Studio:

Es posible que debas esperar a que termine de crearse la instancia. Una vez que lo hagas, accede a AlloyDB con las credenciales que creaste cuando creaste el clúster. Usa los siguientes datos para autenticarte en PostgreSQL:
- Nombre de usuario : "
postgres" - Base de datos : "
postgres" - Contraseña : "
alloydb"
Una vez que te autentiques correctamente en AlloyDB Studio, ingresa los comandos SQL en el editor. Puedes agregar varias ventanas del editor con el signo más que se encuentra a la derecha de la última ventana.

Ingresarás comandos para AlloyDB en ventanas del editor, y usarás las opciones Ejecutar, Formatear y Borrar según sea necesario.
Habilitar extensiones
Para compilar esta app, usaremos las extensiones pgvector y google_ml_integration. La extensión pgvector te permite almacenar y buscar embeddings de vectores. La extensión google_ml_integration proporciona funciones que usas para acceder a los extremos de predicción de Vertex AI y obtener predicciones en SQL. Habilita estas extensiones ejecutando los siguientes DDL:
CREATE EXTENSION IF NOT EXISTS google_ml_integration CASCADE;
CREATE EXTENSION IF NOT EXISTS vector;
Si quieres verificar las extensiones que se habilitaron en tu base de datos, ejecuta este comando de SQL:
select extname, extversion from pg_extension;
Crea una tabla
Crea una tabla con la siguiente declaración DDL:
CREATE TABLE toys ( id VARCHAR(25), name VARCHAR(25), description VARCHAR(20000), quantity INT, price FLOAT, image_url VARCHAR(200), text_embeddings vector(768)) ;
Si el comando anterior se ejecuta correctamente, podrás ver la tabla en la base de datos.
Transfiere datos
Para este lab, tenemos datos de prueba de alrededor de 72 registros en este archivo SQL. Contiene los campos id, name, description, quantity, price, image_url. Los demás campos se completarán más adelante en el lab.
Copia solo las primeras 5 líneas o instrucciones de inserción y, luego, pégalas en una pestaña del editor en blanco y selecciona EJECUTAR. Si NO tienes una cuenta de facturación de prueba, probablemente puedas copiar todas las instrucciones de inserción y ejecutarlas.
Para ver el contenido de la tabla, expande la sección Explorador hasta que veas la tabla llamada prendas. Selecciona los dos puntos verticales (⋮) para ver la opción de consultar la tabla. Se abrirá una instrucción SELECT en una nueva pestaña del editor.

Otorgar permiso
Ejecuta la siguiente instrucción para otorgar derechos de ejecución en la función embedding al usuario postgres:
GRANT EXECUTE ON FUNCTION embedding TO postgres;
Otorga el rol de usuario de Vertex AI a la cuenta de servicio de AlloyDB
Ve a la terminal de Cloud Shell y ejecuta el siguiente comando:
PROJECT_ID=$(gcloud config get-value project)
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:service-$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")@gcp-sa-alloydb.iam.gserviceaccount.com" \
--role="roles/aiplatform.user"
6. Crea embeddings para el contexto
Es mucho más fácil para las computadoras procesar números que texto. Un sistema de embedding convierte el texto en una serie de números de punto flotante que deberían representar el texto, sin importar cómo se redacte, qué idioma use, etcétera.
Considera describir una ubicación costera. Podría llamarse "en el agua", "frente a la playa", "a pie desde tu habitación hasta el océano", "sur la mer", "на берегу океана", etcétera. Todos estos términos se ven diferentes, pero su significado semántico o, en la terminología del aprendizaje automático, sus incorporaciones deberían estar muy cerca entre sí.
Ahora que los datos y el contexto están listos, ejecutaremos el código SQL para agregar los embeddings de la descripción del producto a la tabla en el campo embedding. Puedes usar una variedad de modelos de embedding. Usamos text-embedding-005 de Vertex AI. Asegúrate de usar el mismo modelo de incorporación en todo el proyecto.
Nota: Si usas un proyecto existente de Google Cloud que se creó hace un tiempo, es posible que debas seguir usando versiones anteriores del modelo de incorporación de texto, como textembedding-gecko.
Vuelve a la pestaña AlloyDB Studio y escribe el siguiente DML:
UPDATE toys set text_embeddings = embedding( 'text-embedding-005', description);
Vuelve a mirar la tabla toys para ver algunas incorporaciones. Asegúrate de volver a ejecutar la instrucción SELECT para ver los cambios.
SELECT id, name, description, price, quantity, image_url, text_embeddings FROM toys;
Esto debería devolver el vector de embeddings, que se ve como un array de números de punto flotante, para la descripción del juguete, como se muestra a continuación:

Nota: Los proyectos de Google Cloud creados recientemente en el nivel gratuito pueden tener problemas de cuota en cuanto a la cantidad de solicitudes de incorporación permitidas por segundo para los modelos de Embedding. Te sugerimos que uses una consulta de filtro para el ID y, luego, elijas de forma selectiva de 1 a 5 registros, y así sucesivamente, mientras generas la incorporación.
7. Realiza una búsqueda de vectores
Ahora que la tabla, los datos y los embeddings están listos, realicemos la búsqueda de vectores en tiempo real para el texto de búsqueda del usuario.
Supongamos que el usuario pregunta lo siguiente:
"I want a white plush teddy bear toy with a floral pattern".
Para encontrar coincidencias, ejecuta la siguiente consulta:
select * from toys
ORDER BY text_embeddings <=> CAST(embedding('text-embedding-005', 'I want a white plush teddy bear toy with a floral pattern') as vector(768))
LIMIT 2;
Analicemos esta consulta en detalle:
En esta consulta,
- El texto de búsqueda del usuario es: "
I want a white plush teddy bear toy with a floral pattern.". - Lo convertimos en embeddings en el método
embedding()con el modelotext-embedding-005. Este paso debería resultarte familiar después del último, en el que aplicamos la función de incorporación a todos los elementos de la tabla. - "
<=>" representa el uso del método de distancia SIMILITUD DEL COSENO. Puedes encontrar todas las medidas de similitud disponibles en la documentación de pgvector. - Convertimos el resultado del método de incorporación al tipo de vector para que sea compatible con los vectores almacenados en la base de datos.
- LIMIT 5 representa que queremos extraer 5 vecinos más cercanos para el texto de búsqueda.
El resultado se ve de la siguiente manera:

Como puedes observar en los resultados, las coincidencias son bastante cercanas al texto de búsqueda. Intenta cambiar el texto para ver cómo cambian los resultados.
Nota importante:
Ahora supongamos que queremos aumentar el rendimiento (tiempo de consulta), la eficiencia y la recuperación de este resultado de la Búsqueda de vectores con el índice ScaNN. Lee los pasos de este blog para comparar la diferencia en el resultado con y sin el índice.
Paso opcional: Mejora la eficiencia y la recuperación con el índice de ScaNN
Si el recuento de registros es inferior a 100, omite este paso.
Para mayor comodidad, aquí se enumeran los pasos para crear el índice:
- Como ya creamos el clúster, la instancia, el contexto y las incorporaciones, solo tenemos que instalar la extensión de ScaNN con la siguiente instrucción:
CREATE EXTENSION IF NOT EXISTS alloydb_scann;
- A continuación, crearemos el índice (ScaNN):
CREATE INDEX toysearch_index ON toys
USING scann (text_embeddings cosine)
WITH (num_leaves=9);
En el DDL anterior, apparel_index es el nombre del índice.
"toys" es mi tabla.
"scann" es el método de indexación.
"embedding" es la columna de la tabla que quiero indexar.
"cosine" es el método de distancia que quiero usar con el índice.
"8" es la cantidad de particiones que se aplicarán a este índice. Se puede establecer en cualquier valor entre 1 y 1048576. Para obtener más información sobre cómo decidir este valor, consulta Cómo ajustar un índice de ScaNN.
Usé la raíz cuadrada de la cantidad de puntos de datos, como se recomienda en el repo de ScaNN (cuando se particiona, num_leaves debe ser aproximadamente la raíz cuadrada de la cantidad de puntos de datos).
- Verifica si se creó el índice con la siguiente consulta:
SELECT * FROM pg_stat_ann_indexes;
- Realiza una búsqueda vectorial con la misma consulta que usamos sin el índice:
select * from toys
ORDER BY text_embeddings <=> CAST(embedding('text-embedding-005', 'I want a white plush teddy bear toy with a floral pattern') as vector(768))
LIMIT 5;
La consulta anterior es la misma que usamos en el lab en el paso 8. Sin embargo, ahora tenemos el campo indexado.
- Prueba con una búsqueda simple con y sin el índice (borrando el índice):
Este caso de uso solo tiene 72 registros, por lo que el índice no tiene un efecto real. Para una prueba realizada en otro caso de uso, los resultados son los siguientes:
La misma búsqueda vectorial en los datos de incorporaciones INDEXADAS genera resultados de búsqueda de calidad y eficiencia. La eficiencia mejora considerablemente (en términos de tiempo de ejecución: 10.37 ms sin ScaNN y 0.87 ms con ScaNN) con el índice. Para obtener más información sobre este tema, consulta este blog.
8. Validación de coincidencias con el LLM
Antes de continuar y crear un servicio para devolver las mejores coincidencias a una aplicación, usemos un modelo de IA generativa para validar si estas posibles respuestas son realmente relevantes y seguras para compartirlas con el usuario.
Cómo asegurarse de que la instancia esté configurada para Gemini
Primero, verifica si la integración de Google ML ya está habilitada para tu clúster y tu instancia. En AlloyDB Studio, ingresa el siguiente comando:
show google_ml_integration.enable_model_support;
Si el valor se muestra como "activado", puedes omitir los próximos 2 pasos y pasar directamente a configurar la integración del modelo de AlloyDB y Vertex AI.
- Ve a la instancia principal de tu clúster de AlloyDB y haz clic en EDITAR INSTANCIA PRINCIPAL.

- Navega a la sección Flags en las Opciones de configuración avanzada. y asegúrate de que
google_ml_integration.enable_model_support flagesté configurado como "on", como se muestra a continuación:

Si no está configurado como "activado", configúralo como "activado" y, luego, haz clic en el botón ACTUALIZAR INSTANCIA. Este paso tardará unos minutos.
Integración de modelos de AlloyDB y Vertex AI
Ahora puedes conectarte a AlloyDB Studio y ejecutar la siguiente instrucción DML para configurar el acceso al modelo de Gemini desde AlloyDB, usando el ID de tu proyecto donde se indique. Es posible que se te advierta sobre un error de sintaxis antes de ejecutar el comando, pero debería ejecutarse sin problemas.
Primero, creamos la conexión del modelo de Gemini 1.5, como se muestra a continuación. Recuerda reemplazar $PROJECT_ID en el siguiente comando por el ID de tu proyecto de Google Cloud.
CALL
google_ml.create_model( model_id => 'gemini-1.5',
model_request_url => 'https://us-central1-aiplatform.googleapis.com/v1/projects/$PROJECT_ID/locations/us-central1/publishers/google/models/gemini-1.5-pro:streamGenerateContent',
model_provider => 'google',
model_auth_type => 'alloydb_service_agent_iam');
Puedes verificar los modelos configurados para el acceso con el siguiente comando en AlloyDB Studio:
select model_id,model_type from google_ml.model_info_view;
Por último, debemos otorgar permiso a los usuarios de la base de datos para que ejecuten la función ml_predict_row y realicen predicciones a través de los modelos de Google Vertex AI. Ejecuta el siguiente comando:
GRANT EXECUTE ON FUNCTION ml_predict_row to postgres;
Nota: Si usas un proyecto de Google Cloud existente y un clúster o una instancia de AlloyDB existentes creados hace un tiempo, es posible que debas quitar las referencias antiguas al modelo gemini-1.5 y volver a crearlas con la instrucción CALL anterior, y volver a ejecutar grant execute on function ml_predict_row en caso de que tengas problemas en las próximas invocaciones de gemini-1.5.
Cómo evaluar las respuestas
Si bien terminaremos usando una consulta grande en la siguiente sección para garantizar que las respuestas de la consulta sean razonables, la consulta puede ser difícil de comprender. Ahora veremos las partes y cómo se unen en unos minutos.
- Primero, enviaremos una solicitud a la base de datos para obtener las 10 coincidencias más cercanas a la búsqueda de un usuario.
- Para determinar la validez de las respuestas, usaremos una consulta externa en la que explicaremos cómo evaluar las respuestas. Utiliza el campo
recommended_text, que es el texto de búsqueda, ycontent(que es el campo de descripción del juguete) de la tabla interna como parte de la consulta. - Con esa información, revisaremos la "calidad" de las respuestas que se muestran.
- El
predict_rowdevuelve su resultado en formato JSON. El código "-> 'candidates' -> 0 -> 'content' -> 'parts' -> 0 -> 'text'"" se usa para extraer el texto real de ese JSON. Para ver el JSON real que se devuelve, puedes quitar este código. - Por último, para obtener la respuesta del LLM, la extraemos con
REGEXP_REPLACE(gemini_validation,'[^a-zA-Z,: ]','','g')
SELECT id,
name,
content,
quantity,
price,
image_url,
recommended_text,
REGEXP_REPLACE(gemini_validation, '[^a-zA-Z,: ]', '', 'g') AS gemini_validation
FROM (SELECT id,
name,
content,
quantity,
price,
image_url,
recommended_text,
CAST(ARRAY_AGG(LLM_RESPONSE) AS TEXT) AS gemini_validation
FROM (SELECT id,
name,
content,
quantity,
price,
image_url,
recommended_text,
json_array_elements(google_ml.predict_row(model_id => 'gemini-1.5',
request_body => CONCAT('{ "contents": [ { "role": "user", "parts": [ { "text": "User wants to buy a toy and this is the description of the toy they wish to buy: ', recommended_text, '. Check if the following product items from the inventory are close enough to really, contextually match the user description. Here are the items: ', content, '. Return a ONE-LINE response with 3 values: 1) MATCH: if the 2 contexts are reasonably matching in terms of any of the color or color family specified in the list, approximate style match with any of the styles mentioned in the user search text: This should be a simple YES or NO. Choose NO only if it is completely irrelevant to users search criteria. 2) PERCENTAGE: percentage of match, make sure that this percentage is accurate 3) DIFFERENCE: A clear one-line easy description of the difference between the 2 products. Remember if the user search text says that some attribute should not be there, and the record has it, it should be a NO match. " } ] } ] }')::JSON)) -> 'candidates' -> 0 -> 'content' -> 'parts' -> 0 -> 'text' :: TEXT AS LLM_RESPONSE
FROM (SELECT id,
name,
description AS content,
quantity,
price,
image_url,
'Pink panther standing' AS recommended_text
FROM toys
ORDER BY text_embeddings <=> embedding('text-embedding-005',
'Pink panther standing')::VECTOR
LIMIT 1) AS xyz) AS X
GROUP BY id,
name,
content,
quantity,
price,
image_url,
recommended_text) AS final_matches
-- WHERE REGEXP_REPLACE(gemini_validation, '[^a-zA-Z,: ]', '', 'g') LIKE '%MATCH%:%YES%';
Si bien esto aún puede parecer abrumador, esperamos que ahora lo entiendas un poco mejor. Los resultados indican si hay una coincidencia, qué porcentaje representa y una explicación de la clasificación.
Ten en cuenta que el modelo de Gemini tiene la transmisión habilitada de forma predeterminada, por lo que la respuesta real se distribuye en varias líneas:

9. Lleva la búsqueda de juguetes a la nube sin servidores
¿Todo listo para llevar esta app a la Web? Sigue los pasos que se indican a continuación para hacer que este motor de conocimiento sea sin servidores con las funciones de Cloud Run:
- Ve a Cloud Run Functions en la consola de Google Cloud para CREAR una nueva función de Cloud Run o usa el siguiente vínculo: https://console.cloud.google.com/functions/add.
- Selecciona el entorno como "Cloud Run function". Proporciona el nombre de la función "get-toys-alloydb" y elige la región "us-central1". Establece la autenticación en "Permitir invocaciones no autenticadas" y haz clic en SIGUIENTE. Elige Java 17 como entorno de ejecución y Editor directo para el código fuente.
- De forma predeterminada, se establecería el punto de entrada en "
gcfv2.HelloHttpFunction". Reemplaza el código de marcador de posición enHelloHttpFunction.javaypom.xmlde tu función de Cloud Run por el código de HelloHttpFunction.java y pom.xml, respectivamente. - Recuerda cambiar el marcador de posición <<YOUR_PROJECT>> y las credenciales de conexión de AlloyDB por tus valores en el archivo Java. Las credenciales de AlloyDB son las que usamos al comienzo de este codelab. Si usaste valores diferentes, modifícalos en el archivo Java.
- Haz clic en Implementar.
Una vez implementada, para permitir que Cloud Function acceda a nuestra instancia de base de datos de AlloyDB, crearemos el conector de VPC.
PASO IMPORTANTE:
Una vez que te dispongas a realizar la implementación, deberías poder ver las funciones en la consola de Cloud Run Functions de Google Cloud. Busca la función recién creada (get-toys-alloydb), haz clic en ella y, luego, en EDITAR y cambia lo siguiente:
- Ir a Configuración del entorno de ejecución, la compilación, las conexiones y la seguridad
- Aumenta el tiempo de espera a 180 segundos.
- Ve a la pestaña CONEXIONES:

- En la configuración de entrada, asegúrate de que esté seleccionada la opción "Permitir todo el tráfico".
- En la sección Configuración de salida, haz clic en el menú desplegable Red, selecciona la opción "Agregar un conector de VPC nuevo" y sigue las instrucciones que aparecen en el cuadro de diálogo emergente:

- Proporciona un nombre para el conector de VPC y asegúrate de que la región sea la misma que la de tu instancia. Deja el valor de la red como predeterminado y establece la subred como rango de IP personalizado con el rango de IP 10.8.0.0 o uno similar que esté disponible.
- Expande SHOW SCALING SETTINGS y asegúrate de que la configuración sea exactamente la siguiente:

- Haz clic en CREAR. Este conector debería aparecer ahora en la configuración de salida.
- Selecciona el conector recién creado.
- Opta por que todo el tráfico se enrute a través de este conector de VPC.
- Haz clic en SIGUIENTE y, luego, en IMPLEMENTAR.
10. Prueba la función de Cloud Run
Una vez que se implemente la Cloud Function actualizada, deberías ver el extremo generado. Copia ese valor y reemplázalo en el siguiente comando:
También puedes probar la función de Cloud Run de la siguiente manera:
PROJECT_ID=$(gcloud config get-value project)
curl -X POST <<YOUR_ENDPOINT>> \
-H 'Content-Type: application/json' \
-d '{"search":"I want a standing pink panther toy"}' \
| jq .
Y el resultado sería el siguiente:

Eso es todo. Así de simple es realizar una búsqueda de vectores de similitud con el modelo de Embeddings en los datos de AlloyDB.
11. Compila el cliente de la aplicación web
En esta parte, compilaremos una aplicación web para que el usuario interactúe con ella y encuentre juguetes que coincidan según el texto, la imagen y hasta cree un juguete nuevo según sus necesidades. Como la aplicación ya está compilada, puedes seguir los pasos que se indican a continuación para copiarla en tu IDE y ejecutarla.
- Como usamos Gemini 2.0 Flash para describir la imagen que el usuario puede subir para encontrar juguetes similares, necesitamos obtener la CLAVE DE API para esta aplicación. Para ello, ve a https://aistudio.google.com/apikey, obtén la clave de API para tu proyecto de Google Cloud activo en el que implementarás esta aplicación y guárdala en algún lugar:

- Navega a la terminal de Cloud Shell
- Clona el repo con el siguiente comando:
git clone https://github.com/AbiramiSukumaran/toysearch
cd toysearch
- Una vez que se clone el repo, deberías poder acceder al proyecto desde tu editor de Cloud Shell.
- Debes borrar las carpetas "get-toys-alloydb" y "toolbox-toys" del proyecto clonado, ya que estas 2 son código de Cloud Run Functions al que se puede hacer referencia desde el repo cuando las necesites.
- Ve a GenerateToy.java en la carpeta web, busca la siguiente línea y quítala, ya que permitir contenido para adultos podría requerir un permiso especial que tal vez no esté disponible para algunas cuentas de facturación de prueba:
paramsMap.put("personGeneration", "allow_adult");
- Asegúrate de que todas las variables de entorno necesarias estén configuradas antes de compilar e implementar la app. Navega a la terminal de Cloud Shell y ejecuta lo siguiente:
PROJECT_ID=$(gcloud config get-value project)
export PROJECT_ID=$PROJECT_ID
export GOOGLE_API_KEY=<YOUR API KEY that you saved>
- Compila y ejecuta la app de forma local:
Asegúrate de estar en el directorio del proyecto y ejecuta los siguientes comandos:
mvn package
mvn spring-boot:run
- Implementa en Cloud Run
gcloud run deploy --source .
12. Comprende los detalles de la IA generativa
No se requiere ninguna acción. Para que lo tengas en cuenta:
Ahora que tienes la aplicación para implementar, tómate un momento para comprender cómo logramos la búsqueda (texto e imagen) y la generación.
- Búsqueda vectorial basada en texto del usuario:
Esto ya se abordó en las Cloud Run Functions que implementamos en la sección "Lleva la aplicación de Vector Search a la Web".
- Búsqueda de vectores basada en la carga de imágenes:
En lugar de escribir el contexto como texto, supongamos que el usuario quiere subir una foto de un juguete familiar con el que quiere realizar una búsqueda. Los usuarios pueden subir una imagen del juguete que les gusta y obtener funciones relevantes con ella.
Aprovechamos el modelo Gemini 2.0 Flash de Google, al que se invoca con LangChain4j, para analizar la imagen y extraer el contexto pertinente, como el color, el material, el tipo y el grupo etario previsto del juguete.
En solo 5 pasos, tomamos la entrada de datos multimodales del usuario para obtener resultados coincidentes con la invocación de un modelo de lenguaje grande a través de un framework de código abierto. Aquí te indicamos cómo hacerlo:
package cloudcode.helloworld.web;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.googleai.GoogleAiGeminiChatModel;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.model.output.Response;
import dev.langchain4j.data.message.ImageContent;
import dev.langchain4j.data.message.TextContent;
import java.util.Base64;
import java.util.Optional;
public class GeminiCall {
public String imageToBase64String(byte[] imageBytes) {
String base64Img = Base64.getEncoder().encodeToString(imageBytes);
return base64Img;
}
public String callGemini(String base64ImgWithPrefix) throws Exception {
String searchText = "";
// 1. Remove the prefix
String base64Img = base64ImgWithPrefix.replace("data:image/jpeg;base64,", "");
// 2. Decode base64 to bytes
byte[] imageBytes = Base64.getDecoder().decode(base64Img);
String image = imageToBase64String(imageBytes);
// 3. Get API key from environment variable
String apiKey = Optional.ofNullable(System.getenv("GOOGLE_API_KEY"))
.orElseThrow(() -> new IllegalArgumentException("GOOGLE_API_KEY environment variable not set"));
// 4. Invoke Gemini 2.0
ChatLanguageModel gemini = GoogleAiGeminiChatModel.builder()
.apiKey(apiKey)
.modelName("gemini-2.0-flash-001")
.build();
Response<AiMessage> response = gemini.generate(
UserMessage.from(
ImageContent.from(image, "image/jpeg"),
TextContent.from(
"The picture has a toy in it. Describe the toy in the image in one line. Do not add any prefix or title to your description. Just describe that toy that you see in the image in one line, do not describe the surroundings and other objects around the toy in the image. If you do not see any toy in the image, send response stating that no toy is found in the input image.")));
// 5. Get the text from the response and send it back to the controller
searchText = response.content().text().trim();
System.out.println("searchText inside Geminicall: " + searchText);
return searchText;
}
}
- Descubre cómo usamos Imagen 3 para crear un juguete personalizado basado en la solicitud del usuario con IA generativa.
Luego, Imagen 3 genera una imagen del juguete diseñado de forma personalizada, lo que le brinda al usuario una visualización clara de su creación. Así lo hicimos en solo 5 pasos:
// Generate an image using a text prompt using an Imagen model
public String generateImage(String projectId, String location, String prompt)
throws ApiException, IOException {
final String endpoint = String.format("%s-aiplatform.googleapis.com:443", location);
PredictionServiceSettings predictionServiceSettings =
PredictionServiceSettings.newBuilder().setEndpoint(endpoint).build();
// 1. Set up the context and prompt
String context = "Generate a photo-realistic image of a toy described in the following input text from the user. Make sure you adhere to all the little details and requirements mentioned in the prompt. Ensure that the user is only describing a toy. If it is anything unrelated to a toy, politely decline the request stating that the request is inappropriate for the current context. ";
prompt = context + prompt;
// 2. Initialize a client that will be used to send requests. This client only needs to be created
// once, and can be reused for multiple requests.
try (PredictionServiceClient predictionServiceClient =
PredictionServiceClient.create(predictionServiceSettings)) {
// 3. Invoke Imagen 3
final EndpointName endpointName =
EndpointName.ofProjectLocationPublisherModelName(
projectId, location, "google", "imagen-3.0-generate-001"); //"imagegeneration@006"; imagen-3.0-generate-001
Map<String, Object> instancesMap = new HashMap<>();
instancesMap.put("prompt", prompt);
Value instances = mapToValue(instancesMap);
Map<String, Object> paramsMap = new HashMap<>();
paramsMap.put("sampleCount", 1);
paramsMap.put("aspectRatio", "1:1");
paramsMap.put("safetyFilterLevel", "block_few");
paramsMap.put("personGeneration", "allow_adult");
paramsMap.put("guidanceScale", 21);
paramsMap.put("imagenControlScale", 0.95); //Setting imagenControlScale
Value parameters = mapToValue(paramsMap);
// 4. Get prediction response image
PredictResponse predictResponse =
predictionServiceClient.predict(
endpointName, Collections.singletonList(instances), parameters);
// 5. Return the Base64 Encoded String to the controller
for (Value prediction : predictResponse.getPredictionsList()) {
Map<String, Value> fieldsMap = prediction.getStructValue().getFieldsMap();
if (fieldsMap.containsKey("bytesBase64Encoded")) {
bytesBase64EncodedOuput = fieldsMap.get("bytesBase64Encoded").getStringValue();
}
}
return bytesBase64EncodedOuput.toString();
}
}
Predicción de precios
En la sección anterior, analizamos cómo Imagen genera la imagen de un juguete que el usuario desea diseñar por su cuenta. Para que puedan comprarlo, la aplicación debe establecer un precio, y empleamos una lógica intuitiva para definir un precio para el juguete personalizado hecho a pedido. La lógica consiste en usar el precio promedio de los 5 juguetes que mejor coinciden (en términos de descripción) con el juguete que diseña el usuario.
La predicción del precio del juguete generado es una parte importante de esta aplicación, y usamos un enfoque basado en agentes para generarla. Presentamos la Caja de herramientas de IA generativa para bases de datos.
13. Caja de herramientas de IA generativa para bases de datos
La Caja de herramientas de IA generativa para bases de datos es un servidor de código abierto de Google que facilita la creación de herramientas de IA generativa para interactuar con bases de datos. Te permite desarrollar herramientas de forma más fácil, rápida y segura, ya que se encarga de las complejidades, como la agrupación de conexiones, la autenticación y mucho más. Te ayuda a crear herramientas de IA generativa que permiten que tus agentes accedan a los datos de tu base de datos.
Estos son los pasos que debes seguir para configurar la herramienta y hacer que nuestra aplicación sea agentiva: Vínculo al codelab de Toolbox
Ahora tu aplicación puede usar este extremo de Cloud Run Function implementado para completar el precio junto con el resultado de Imagen generado para la imagen de juguete personalizada hecha a pedido.
14. Prueba tu aplicación web
Ahora que todos los componentes de tu aplicación están compilados e implementados, está lista para publicarse en la nube. Prueba tu aplicación en todas las situaciones. Aquí tienes un vínculo a un video en el que se muestra lo que puedes esperar:
https://www.youtube.com/shorts/ZMqUAWsghYQ
Así se ve la página de destino:

15. Limpia
Sigue estos pasos para evitar que se apliquen cargos a tu cuenta de Google Cloud por los recursos que usaste en esta publicación:
- En la consola de Google Cloud, ve a la página Administrar recursos.
- En la lista de proyectos, elige el proyecto que deseas borrar y haz clic en Borrar.
- En el diálogo, escribe el ID del proyecto y, luego, haz clic en Cerrar para borrarlo.
16. Felicitaciones
¡Felicitaciones! Realizaste correctamente una búsqueda y generación contextuales de Toystore con AlloyDB, pgvector, Imagen y Gemini 2.0, y aprovechaste las bibliotecas de código abierto para crear integraciones sólidas. Al combinar las capacidades de AlloyDB, Vertex AI y Vector Search, dimos un gran paso para que las búsquedas contextuales y vectoriales sean accesibles, eficientes y realmente basadas en el significado.