1. Qué aprenderás
¡Te damos la bienvenida! Hoy comenzaremos un viaje bastante interesante. Comencemos por pensar en InstaVibe, una plataforma popular de eventos sociales. Si bien es exitosa, sabemos que, para algunos usuarios, la planificación real de las actividades grupales puede parecer una tarea tediosa. Imagina que intentas averiguar qué les interesa a todos tus amigos, luego revisas infinitas opciones de eventos o lugares y, por último, coordinas todo. ¡Es mucho! Es precisamente aquí donde podemos introducir la IA y, más específicamente, los agentes inteligentes, para marcar una diferencia real.
La idea es crear un sistema en el que estos agentes puedan encargarse de las tareas más pesadas, como "escuchar" de forma inteligente para comprender las preferencias de los usuarios y sus amigos, y luego sugerir de forma proactiva actividades fantásticas y personalizadas. Nuestro objetivo es transformar la planificación social en InstaVibe en algo fluido y agradable. Para comenzar a crear estos asistentes inteligentes, debemos sentar una base sólida con las herramientas adecuadas.
Este es el concepto que verás:
Conceptos básicos con el ADK de Google: Domina los aspectos fundamentales para crear tu primer agente inteligente con el Kit de desarrollo de agentes (ADK) de Google. Comprende los componentes esenciales, el ciclo de vida del agente y cómo aprovechar las herramientas integradas del framework de manera eficaz.
Extensión de las capacidades del agente con el Protocolo de Contexto del Modelo (MCP): Aprende a equipar a tus agentes con herramientas y contexto personalizados, lo que les permite realizar tareas especializadas y acceder a información específica. Presentar el concepto de Model Context Protocol (MCP) Aprenderás a configurar un servidor de MCP para proporcionar este contexto.
Diseño de interacciones y organización de agentes: Ve más allá de los agentes individuales para comprender la organización de agentes. Diseña patrones de interacción que van desde flujos de trabajo secuenciales simples hasta situaciones complejas que involucran bucles, lógica condicional y procesamiento paralelo. Presentar el concepto de subagentes dentro del marco de trabajo del ADK para administrar tareas modulares
Cómo compilar sistemas multiagente colaborativos: Descubre cómo diseñar sistemas en los que varios agentes colaboran para lograr objetivos complejos. Aprende a implementar el protocolo de comunicación de agente a agente (A2A), que establece una forma estandarizada para que los agentes distribuidos (que pueden ejecutarse en diferentes máquinas o servicios) interactúen de manera confiable.
Cómo llevar agentes a producción en Google Cloud: Haz la transición de tus aplicaciones de agentes de los entornos de desarrollo a la nube. Aprende las prácticas recomendadas para diseñar y, luego, implementar sistemas multiagente sólidos y escalables en Google Cloud Platform (GCP). Obtén estadísticas para aprovechar los servicios de GCP, como Cloud Run, y explora las capacidades del agente de Google Agent Engine más reciente para alojar y administrar tus agentes.
2. Arquitectura
Planificación de redes sociales potenciada por IA con InstaVibe
¿Qué es la supervisión de redes sociales?
La observación de redes sociales es el proceso de supervisar las conversaciones digitales en plataformas como las redes sociales, los foros y los sitios de noticias para comprender lo que las personas dicen sobre un tema, una marca o una industria. Proporciona estadísticas valiosas sobre las opiniones del público, las tendencias y las necesidades de los usuarios. En este taller, aprovecharemos este concepto dentro de un sistema basado en agentes.
Formas parte del equipo de InstaVibe
Imagina que trabajas en "InstaVibe", una startup exitosa con una plataforma popular de eventos sociales dirigida a adultos jóvenes. Todo va bien, pero, como en muchas empresas de tecnología, tu equipo siente la presión de los inversionistas para innovar con la IA. Internamente, también notaste que hay un segmento de usuarios que no participa tanto como otros. Tal vez no les guste iniciar actividades grupales o les resulte difícil el proceso de planificación. Para tu empresa, esto significa una menor permanencia en la plataforma entre este importante grupo de usuarios.
La investigación de tu equipo sugiere que la asistencia impulsada por IA podría mejorar significativamente la experiencia de estos usuarios. La idea es optimizar el proceso de planificación de salidas sociales sugiriendo de forma proactiva actividades relevantes según los intereses del usuario y sus amigos. La pregunta que se hacen tú y tus colegas es la siguiente: ¿Cómo pueden los agentes de IA automatizar las tareas de descubrimiento de intereses, investigación de actividades y, posiblemente, coordinación inicial, que suelen llevar mucho tiempo?
Una solución basada en agentes (concepto de prototipo)
Propones desarrollar una función de prototipo potenciada por un sistema multiagente. A continuación, se incluye un desglose conceptual:
- Agente de creación de perfiles sociales: Este agente emplea técnicas de escucha social para analizar las conexiones, las interacciones y las tendencias públicas potencialmente más amplias de los usuarios relacionadas con sus preferencias. Su propósito es identificar intereses compartidos y características de actividades adecuadas (p.ej., preferencias por reuniones más tranquilas, pasatiempos específicos).
- Agente de planificación de eventos: Con la información del agente de creación de perfiles sociales, este agente busca en los recursos en línea eventos, lugares o ideas específicos que se alineen con los criterios identificados (como la ubicación y los intereses).
- Agente de interacción con la plataforma (con MCP): Este agente toma el plan finalizado del agente de planificación de actividades. Su función clave es interactuar directamente con la plataforma de InstaVibe utilizando una herramienta de MCP (protocolo de contexto del modelo) predefinida. Esta herramienta le brinda al agente la capacidad específica de redactar una sugerencia de evento y crear una publicación en la que se describa el plan.
- Agente de Orchestrator: Este agente actúa como coordinador central. Recibe la solicitud inicial del usuario desde la plataforma de InstaVibe, comprende el objetivo general (p.ej., "Planifica un evento para mí y mis amigos" y, luego, delega tareas específicas a los agentes especializados correspondientes en una secuencia lógica. Administra el flujo de información entre los agentes y garantiza que el resultado final se entregue al usuario.
Elementos y tecnologías arquitectónicos clave
Google Cloud Platform (GCP):
- Vertex AI:
- Modelos de Gemini: Proporcionan acceso a los modelos de lenguaje grande (LLM) de vanguardia de Google, como Gemini, que potencian las capacidades de razonamiento y toma de decisiones de nuestros agentes.
- Vertex AI Agent Engine: Es un servicio administrado que se usa para implementar, alojar y escalar nuestro agente de orquestación, lo que simplifica la producción y abstrae las complejidades de la infraestructura.
- Cloud Run: Es una plataforma sin servidores para implementar aplicaciones alojadas en contenedores. La usamos para lo siguiente:
- Aloja la aplicación web principal de InstaVibe.
- Implementa agentes individuales habilitados para A2A (Planificador, Creación de perfiles sociales, Interacción con la plataforma) como microservicios independientes.
- Ejecuta el servidor de herramientas de MCP, lo que hace que las APIs internas de InstaVibe estén disponibles para los agentes.
- Spanner: Es una base de datos relacional completamente administrada, distribuida a nivel mundial y con coherencia sólida. En este taller, aprovecharemos sus capacidades como base de datos de grafos con sus funciones de lenguaje DDL y de consulta de GRAPH para hacer lo siguiente:
- Modelar y almacenar relaciones sociales complejas (usuarios, amistades, asistencia a eventos, publicaciones)
- Permite realizar consultas eficientes sobre estas relaciones para los agentes de Social Profiling.
- Artifact Registry: Es un servicio completamente administrado para almacenar, administrar y proteger imágenes de contenedores.
- Cloud Build: Es un servicio que ejecuta tus compilaciones en Google Cloud. Lo usamos para compilar automáticamente imágenes de contenedores de Docker a partir del código fuente de nuestro agente y nuestra aplicación.
- Cloud Storage: Lo usan servicios como Cloud Build para almacenar artefactos de compilación y Agent Engine para sus necesidades operativas.
- Frameworks y protocolos principales de agentes:
- Kit de desarrollo de agentes (ADK) de Google: Es el framework principal para lo siguiente:
- Definir la lógica, el comportamiento y los conjuntos de instrucciones principales para los agentes inteligentes individuales
- Administrar los ciclos de vida, el estado y la memoria de los agentes (estado de sesión a corto plazo y conocimiento a largo plazo potencial)
- Herramientas de integración (como la Búsqueda de Google o herramientas personalizadas) que los agentes pueden usar para interactuar con el mundo.
- Organización de flujos de trabajo de varios agentes, incluida la ejecución secuencial, en bucle y paralela de subagentes
- Protocolo de comunicación de agente a agente (A2A): Es un estándar abierto que permite lo siguiente:
- Comunicación y colaboración directas y estandarizadas entre diferentes agentes de IA, incluso si se ejecutan como servicios separados o en diferentes máquinas.
- Los agentes pueden descubrir las capacidades de los demás (a través de las tarjetas de agente) y delegar tareas. Esto es fundamental para que nuestro agente de Orchestrator interactúe con los agentes especializados de Planner, Social y Platform.
- Biblioteca de Python de A2A (a2a-python): Es la biblioteca concreta que se usa para que nuestros agentes del ADK hablen el protocolo A2A. Proporciona los componentes del servidor necesarios para realizar las siguientes acciones:
- Exponer nuestros agentes como servidores que cumplen con A2A
- Manejar automáticamente la publicación de la "Tarjeta de agente" para el descubrimiento
- Recibir y administrar solicitudes de tareas entrantes de otros agentes (como el orquestador)
- Model Context Protocol (MCP): Es un estándar abierto que permite a los agentes realizar las siguientes acciones:
- Conectarse con herramientas, fuentes de datos y sistemas externos y utilizarlos de forma estandarizada
- Nuestro agente de interacción con la plataforma usa un cliente de MCP para comunicarse con un servidor de MCP, que, a su vez, expone herramientas para interactuar con las APIs existentes de la plataforma de InstaVibe.
- Kit de desarrollo de agentes (ADK) de Google: Es el framework principal para lo siguiente:
- Herramientas de depuración:
- A2A Inspector: El Inspector de A2A es una herramienta de depuración basada en la Web que se usa en todo este taller para conectarse con nuestros agentes habilitados para A2A, inspeccionarlos e interactuar con ellos. Si bien no forma parte de la arquitectura de producción final, es una parte esencial de nuestro flujo de trabajo de desarrollo. Ofrece lo siguiente:
- Visor de tarjetas de agentes: Para recuperar y validar las capacidades públicas de un agente
- Interfaz de chat en vivo: Para enviar mensajes directamente a un agente implementado y realizar pruebas de inmediato
- Consola de depuración: Para ver los mensajes JSON-RPC sin procesar que se intercambian entre el inspector y el agente.
- A2A Inspector: El Inspector de A2A es una herramienta de depuración basada en la Web que se usa en todo este taller para conectarse con nuestros agentes habilitados para A2A, inspeccionarlos e interactuar con ellos. Si bien no forma parte de la arquitectura de producción final, es una parte esencial de nuestro flujo de trabajo de desarrollo. Ofrece lo siguiente:
- Modelos de lenguaje (LLM): El "cerebro" del sistema:
- Modelos de Gemini de Google: Específicamente, utilizamos versiones como gemini-2.0-flash. Estos modelos se eligen por los siguientes motivos:
- Razonamiento avanzado y seguimiento de instrucciones: Su capacidad para comprender instrucciones complejas, seguir instrucciones detalladas y razonar sobre las tareas los hace adecuados para potenciar la toma de decisiones de los agentes.
- Uso de herramientas (llamada a funciones): Los modelos de Gemini se destacan por determinar cuándo y cómo usar las herramientas proporcionadas a través del ADK, lo que permite que los agentes recopilen información o realicen acciones.
- Eficiencia (modelos Flash): Las variantes "flash" ofrecen un buen equilibrio entre rendimiento y rentabilidad, y son adecuadas para muchas tareas de agentes interactivos que requieren respuestas rápidas.
- Modelos de Gemini de Google: Específicamente, utilizamos versiones como gemini-2.0-flash. Estos modelos se eligen por los siguientes motivos:
¿Necesitas créditos de Google Cloud?
3. Antes de comenzar
👉 Haz clic en Activar Cloud Shell en la parte superior de la consola de Google Cloud (es el ícono con forma de terminal en la parte superior del panel de Cloud Shell).
👉 Haz clic en el botón "Abrir editor" (tiene forma de carpeta abierta con un lápiz). Se abrirá el editor de código de Cloud Shell en la ventana. Verás un explorador de archivos en el lado izquierdo.
👉 Haz clic en el botón Cloud Code Sign-in en la barra de estado de la parte inferior, como se muestra. Autoriza el complemento según las instrucciones. Si ves Cloud Code - no project en la barra de estado, selecciónalo y, luego, en el menú desplegable "Select a Google Cloud Project", elige el proyecto específico de Google Cloud de la lista de proyectos que creaste.
👉 Busca tu ID del proyecto de Google Cloud:
- Abre la consola de Google Cloud: https://console.cloud.google.com
- Selecciona el proyecto que deseas usar para este taller en el menú desplegable de proyectos que se encuentra en la parte superior de la página.
- Tu ID del proyecto se muestra en la tarjeta de información del proyecto en el panel.
👉Abre la terminal en el IDE de Cloud,
👉💻 En la terminal, verifica que ya te autenticaste y que el proyecto esté configurado con tu ID del proyecto usando el siguiente comando:
gcloud auth list
👉💻 Clona el proyecto instavibe-bootstrap
desde GitHub:
git clone -b adk-1.2.1-a2a-0.2.7 https://github.com/weimeilin79/instavibe-bootstrap.git
chmod +x ~/instavibe-bootstrap/init.sh
chmod +x ~/instavibe-bootstrap/set_env.sh
Información sobre la estructura del proyecto
Antes de comenzar a compilar, dediquemos un momento a comprender el diseño del proyecto instavibe-bootstrap
que acabas de clonar. Esto te ayudará a saber dónde encontrar y editar archivos durante el taller.
instavibe-bootstrap/
├── agents/
│ ├── orchestrate/
│ ├── planner/
│ ├── platform_mcp_client/
│ └── social/
├── instavibe/
│ ├── static/
│ └── templates/
├── tools/
│ └── instavibe/
├── utils/
├── init.sh
└── set_env.sh
A continuación, se incluye un desglose de los directorios clave:
agents/
: Este es el corazón de nuestro sistema de IA. Cada subdirectorio (planner/, social/, etc.) contiene el código fuente de un agente inteligente específico.agent.py
: Dentro de la carpeta de cada agente, este es el archivo principal donde se encuentra la lógica del agente.a2a_server.py
: Este archivo une el agente del ADK con un servidor Agent-to-Agent (A2A).Dockerfile
: Define cómo compilar la imagen del contenedor para implementar el agente en Cloud Run o Agent Engine.
instavibe/
: Este directorio contiene todo el código fuente de la aplicación web de InstaVibe.tools/
: Este directorio se usa para compilar herramientas externas que nuestros agentes pueden usar.instavibe/
contiene el servidor del Protocolo de contexto del modelo (MCP).
Esta estructura modular separa la aplicación web de los distintos componentes de IA, lo que facilita la administración, las pruebas y la implementación de todo el sistema.
👉💻 Ejecuta la secuencia de comandos de inicialización:
Este secuencia de comandos te pedirá que ingreses tu ID del proyecto de Google Cloud.
Ingresa el ID del proyecto de Google Cloud que encontraste en el último paso cuando el script init.sh
te lo solicite:
cd ~/instavibe-bootstrap
./init.sh
👉💻 Configura el ID del proyecto necesario:
gcloud config set project $(cat ~/project_id.txt) --quiet
👉💻 Ejecuta el siguiente comando para habilitar las APIs de Google Cloud necesarias:
gcloud services enable run.googleapis.com \
cloudfunctions.googleapis.com \
cloudbuild.googleapis.com \
artifactregistry.googleapis.com \
spanner.googleapis.com \
apikeys.googleapis.com \
iam.googleapis.com \
compute.googleapis.com \
aiplatform.googleapis.com \
cloudresourcemanager.googleapis.com \
maps-backend.googleapis.com
👉💻 Establece todas las variables de entorno necesarias:
export PROJECT_ID=$(gcloud config get project)
export PROJECT_NUMBER=$(gcloud projects describe ${PROJECT_ID} --format="value(projectNumber)")
export SERVICE_ACCOUNT_NAME=$(gcloud compute project-info describe --format="value(defaultServiceAccount)")
export SPANNER_INSTANCE_ID="instavibe-graph-instance"
export SPANNER_DATABASE_ID="graphdb"
export GOOGLE_CLOUD_PROJECT=$(gcloud config get project)
export GOOGLE_GENAI_USE_VERTEXAI=TRUE
export GOOGLE_CLOUD_LOCATION="us-central1"
Cómo configurar permisos
👉💻 Otorga permisos. En la terminal, ejecuta el siguiente comando :
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/spanner.admin"
# Spanner Database User
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/spanner.databaseUser"
# Artifact Registry Admin
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/artifactregistry.admin"
# Cloud Build Editor
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/cloudbuild.builds.editor"
# Cloud Run Admin
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/run.admin"
# IAM Service Account User
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/iam.serviceAccountUser"
# Vertex AI User
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/aiplatform.user"
# Logging Writer (to allow writing logs)
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/logging.logWriter"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/logging.viewer"
👉 Valida el resultado en tu consola de IAM.
👉💻 Ejecuta los siguientes comandos en la terminal para crear un repositorio de Artifact Registry. Todas las imágenes de Docker para nuestros agentes, el servidor de MCP y la aplicación de InstaVibe se almacenan aquí antes de la implementación en Cloud Run o Agent Engine.
export REPO_NAME="introveally-repo"
gcloud artifacts repositories create $REPO_NAME \
--repository-format=docker \
--location=us-central1 \
--description="Docker repository for InstaVibe workshop"
Configura la plataforma de Maps para las claves de API
Para usar los servicios de Google Maps en tu aplicación de InstaVibe, debes crear una clave de API y restringirla de forma adecuada.
👉 En una pestaña nueva, ve a APIs y servicios > Credenciales. En la página "Credenciales", haz clic en el botón + CREAR CREDENCIALES que se encuentra en la parte superior. Selecciona Clave de API en el menú desplegable.
👉 Aparecerá un cuadro de diálogo que mostrará la clave de API que acabas de crear. La necesitarás más adelante para configurar tu aplicación.
👉 Haz clic en CERRAR en el diálogo “Se creó la clave de API”.
👉 Verás tu nueva clave de API en la lista (p.ej., "clave de API 1"). Haz clic en los tres puntos que se encuentran a la derecha y selecciona Editar clave de API para abrir la página “Restringir y cambiar el nombre de la clave de API”.
👉 En el campo Nombre que se encuentra en la parte superior, cambia el nombre predeterminado a Clave de API de Maps Platform (🚨🚨IMPORTANTE🚨🚨 Usa este nombre).
Maps Platform API Key
👉 En la sección “Restricciones de aplicaciones”, asegúrate de que esté seleccionada la opción Ninguna.
👉 En la sección "Restricciones de API", selecciona el botón de selección Restringir clave.
👉 Haz clic en el menú desplegable Seleccionar APIs. En el cuadro de búsqueda que aparece, escribe Maps JavaScript API
y selecciónalo de la lista.
👉 Haz clic en Aceptar.
👉 Haz clic en el botón SAVE en la parte inferior de la página.
Ahora creaste correctamente una clave de API llamada "Clave de API de Maps Platform", la restringiste para que solo permita el uso de la "API de Maps JavaScript" y te aseguraste de que la API esté habilitada para tu proyecto.
4. Configura la base de datos de grafos
Antes de poder crear nuestros agentes inteligentes, necesitamos una forma de almacenar y comprender las conexiones enriquecidas dentro de nuestra red social InstaVibe. Aquí es donde entra en juego una base de datos de grafos. A diferencia de las bases de datos relacionales tradicionales que almacenan datos en tablas de filas y columnas, una base de datos de grafos está diseñada específicamente para representar y consultar datos en términos de nodos (como personas, eventos o publicaciones) y las relaciones (bordes) que los conectan (como amistades, asistencia a eventos o menciones). Esta estructura es muy útil para las aplicaciones de redes sociales, ya que refleja la forma en que se estructuran las redes sociales del mundo real, lo que hace que sea intuitivo explorar cómo se interconectan las diferentes entidades.
Implementamos esta base de datos de grafos con Google Cloud Spanner. Si bien Spanner se conoce principalmente como una base de datos relacional distribuida a nivel global y con coherencia sólida, también nos permite definir y consultar estructuras de grafos directamente sobre nuestras tablas relacionales.
Esto nos brinda los beneficios combinados de la escalabilidad, la coherencia transaccional y la interfaz SQL familiar de Spanner, junto con el poder expresivo de las consultas de gráficos para analizar la compleja dinámica social crucial para nuestras funciones potenciadas por IA.
👉💻 En la terminal del IDE de Cloud Shell Aprovisionar la infraestructura necesaria en Google Cloud Comenzaremos por crear una instancia de Spanner, que actuará como un contenedor dedicado para nuestras bases de datos. Una vez que la instancia esté lista, crearemos la base de datos de Spanner real dentro de ella, que albergará todas nuestras tablas y los datos del gráfico de InstaVibe:
. ~/instavibe-bootstrap/set_env.sh
gcloud spanner instances create $SPANNER_INSTANCE_ID \
--config=regional-us-central1 \
--description="GraphDB Instance InstaVibe" \
--processing-units=100 \
--edition=ENTERPRISE
gcloud spanner databases create $SPANNER_DATABASE_ID \
--instance=$SPANNER_INSTANCE_ID \
--database-dialect=GOOGLE_STANDARD_SQL
👉💻 Otorga acceso de lectura y escritura a Spanner a la cuenta de servicio predeterminada
echo "Granting Spanner read/write access to ${SERVICE_ACCOUNT_NAME} for database ${SPANNER_DATABASE_ID}..."
gcloud spanner databases add-iam-policy-binding ${SPANNER_DATABASE_ID} \
--instance=${SPANNER_INSTANCE_ID} \
--member="serviceAccount:${SERVICE_ACCOUNT_NAME}" \
--role="roles/spanner.databaseUser" \
--project=${PROJECT_ID}
👉💻 Ahora. Configuraremos un entorno virtual de Python, instalaremos los paquetes de Python necesarios y, luego, configuraremos el esquema de la base de datos de grafos en Spanner, lo cargaremos con datos iniciales y ejecutaremos la secuencia de comandos setup.py
.
. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap
python -m venv env
source env/bin/activate
pip install -r requirements.txt
cd instavibe
python setup.py
👉 En una pestaña nueva del navegador, ve a la consola de Google Cloud, navega a Spanner y verás una lista de tus instancias de Spanner. Haz clic en instavibe-graph-instance
. 👉 En la página de descripción general de la instancia, verás una lista de las bases de datos dentro de esa instancia. Haz clic en
graphdb
.
👉 En el panel de navegación de la izquierda de tu base de datos, haz clic en Spanner Studio .
👉 En el editor de consultas (pestaña Consulta sin título), pega la siguiente consulta de Graph en SQL. Esta búsqueda encontrará todos los nodos de Person y sus relaciones directas de Friendship con otros nodos de Person. Haz clic en EJECUTAR para ver el resultado.
Graph SocialGraph
MATCH result_paths = ((p:Person)-[f:Friendship]-(friend:Person))
RETURN SAFE_TO_JSON(result_paths) AS result_paths
👉 En el mismo editor de consultas, reemplaza el DDL anterior para encontrar a las personas que asistieron al mismo evento, lo que implica una conexión indirecta a través de una actividad compartida.
Graph SocialGraph
MATCH result_paths = (p1:Person)-[:Attended]->(e:Event)<-[:Attended]-(p2:Person)
WHERE p1.person_id < p2.person_id
RETURN SAFE_TO_JSON(result_paths) AS result_paths
👉 Esta consulta explora un tipo diferente de conexión, en la que las personas mencionadas en las publicaciones escritas por amigos de una persona específica ejecutan la siguiente consulta en el editor de consultas.
Graph SocialGraph
MATCH result_paths = (user:Person {name: "Alice"})-[:Friendship]-(friend:Person)-[:Wrote]->(post:Post)-[:Mentioned]->(mentioned_person:Person)
WHERE user <> mentioned_person AND friend <> mentioned_person -- Avoid self-mentions or friend mentioning themselves in their own post if not intended
RETURN SAFE_TO_JSON(result_paths) AS result_paths
Estas consultas solo ofrecen un vistazo al poder de usar Spanner como una base de datos de gráficos para nuestra aplicación de InstaVibe. Al modelar nuestros datos sociales como un gráfico interconectado, permitimos un análisis sofisticado de las relaciones y las actividades, lo que será fundamental para que nuestros agentes de IA comprendan el contexto del usuario, descubran sus intereses y, en última instancia, proporcionen asistencia inteligente para la planificación social.
Ahora que nuestra estructura de datos fundamental está lista y probada, centrémonos en la aplicación de InstaVibe existente con la que interactuarán nuestros agentes.
5. Estado actual de InstaVibe
Para comprender dónde encajarán nuestros agentes de IA, primero debemos implementar y ejecutar la aplicación web existente de InstaVibe. Esta aplicación proporciona la interfaz de usuario y las funcionalidades básicas que se conectan a la base de datos de gráficos de Spanner que ya configuramos.
La aplicación de InstaVibe usa Google Maps para mostrar visualmente las ubicaciones de los eventos en sus páginas de detalles. Para habilitar esta funcionalidad, la aplicación necesita la clave de API que creamos anteriormente. La siguiente secuencia de comandos recuperará la cadena de clave real con el nombre visible que asignamos ("Clave de API de Maps Platform").
👉💻 Regresa al IDE de Cloud Shell. Ejecuta la siguiente secuencia de comandos. Luego, verifica cuidadosamente el resultado para asegurarte de que la clave de GOOGLE_MAPS_API_KEY que se muestra coincida con la clave que creaste y copiaste de la consola de Google Cloud anteriormente.
. ~/instavibe-bootstrap/set_env.sh
export KEY_DISPLAY_NAME="Maps Platform API Key"
GOOGLE_MAPS_KEY_ID=$(gcloud services api-keys list \
--project="${PROJECT_ID}" \
--filter="displayName='${KEY_DISPLAY_NAME}'" \
--format="value(uid)" \
--limit=1)
GOOGLE_MAPS_API_KEY=$(gcloud services api-keys get-key-string "${GOOGLE_MAPS_KEY_ID}" \
--project="${PROJECT_ID}" \
--format="value(keyString)")
echo "${GOOGLE_MAPS_API_KEY}" > ~/mapkey.txt
echo "Retrieved GOOGLE_MAPS_API_KEY: ${GOOGLE_MAPS_API_KEY}"
👉💻 Ahora, compilaremos la imagen del contenedor para la aplicación web de InstaVibe y la enviaremos a nuestro repositorio de Artifact Registry.
. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap/instavibe/
export IMAGE_TAG="latest"
export APP_FOLDER_NAME="instavibe"
export IMAGE_NAME="instavibe-webapp"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="instavibe"
gcloud builds submit . \
--tag=${IMAGE_PATH} \
--project=${PROJECT_ID}
👉💻 Implementa la nueva imagen de compilación de la app web de InstaVibe en Cloud Run
. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap/instavibe/
export IMAGE_TAG="latest"
export APP_FOLDER_NAME="instavibe"
export IMAGE_NAME="instavibe-webapp"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="instavibe"
gcloud run deploy ${SERVICE_NAME} \
--image=${IMAGE_PATH} \
--platform=managed \
--region=${REGION} \
--allow-unauthenticated \
--set-env-vars="SPANNER_INSTANCE_ID=${SPANNER_INSTANCE_ID}" \
--set-env-vars="SPANNER_DATABASE_ID=${SPANNER_DATABASE_ID}" \
--set-env-vars="APP_HOST=0.0.0.0" \
--set-env-vars="APP_PORT=8080" \
--set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
--set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
--set-env-vars="GOOGLE_MAPS_API_KEY=${GOOGLE_MAPS_API_KEY}" \
--project=${PROJECT_ID} \
--min-instances=1
Una vez que la implementación se complete correctamente, los registros de Cloud Run deberían mostrar la URL pública de tu aplicación de InstaVibe en ejecución.
También puedes encontrar esta URL navegando a la sección Cloud Run en la consola de Google Cloud y seleccionando el servicio instavibe.
Abre esa URL en tu navegador web ahora para explorar la plataforma básica de InstaVibe. Ver las publicaciones, los eventos y las conexiones de usuarios que se basan en la base de datos de gráficos que configuramos
Ahora que tenemos nuestra aplicación de destino en ejecución, comencemos a crear el primer agente inteligente para mejorar sus capacidades.
6. Agente básico,planificador de eventos con ADK
Marco de trabajo del ADK
Introducción al framework de ADK de Google Ahora que ya tenemos la base (la app y la base de datos de InstaVibe), podemos comenzar a crear nuestro primer agente inteligente con el Kit de desarrollo de agentes (ADK) de Google.
El Kit de desarrollo de agentes (ADK) es un framework flexible y modular diseñado específicamente para desarrollar y, luego, implementar agentes de IA. Su principio de diseño es hacer que el desarrollo de agentes se parezca más al desarrollo de software tradicional, con el objetivo de que sea mucho más fácil para los desarrolladores crear, implementar y coordinar arquitecturas basadas en agentes que puedan manejar todo, desde tareas simples de un solo propósito hasta flujos de trabajo complejos de varios agentes.
En esencia, el ADK se centra en el concepto de un Agent
, que encapsula instrucciones, configuración (como el modelo de lenguaje elegido, p.ej., Gemini) y un conjunto de Tools
que puede usar para realizar acciones o recopilar información.
Nuestro agente inicial será un "planificador de eventos". Su objetivo principal es tomar las solicitudes de los usuarios para salidas sociales (especificando la ubicación, las fechas y los intereses) y generar sugerencias creativas y personalizadas. Para garantizar que las sugerencias sean pertinentes y se basen en información actual (como eventos específicos que sucedan ese fin de semana), aprovecharemos una de las herramientas integradas de ADK: la Búsqueda de Google. Esto permite que el agente base sus respuestas en los resultados de la Web en tiempo real y recupere los detalles más recientes sobre lugares, eventos y actividades que coincidan con los criterios del usuario.
👉📝 De vuelta en el IDE de Cloud Shell, en ~/instavibe-bootstrap/agents/planner/agent.py
, agrega la siguiente instrucción y mensaje para crear el agente.
from google.adk.agents import Agent
from google.adk.tools import google_search
root_agent = Agent(
name="planner_agent",
model="gemini-2.0-flash",
description="Agent tasked with generating creative and fun dating plan suggestions",
instruction="""
You are a specialized AI assistant tasked with generating creative and fun plan suggestions.
Request:
For the upcoming weekend, specifically from **[START_DATE_YYYY-MM-DD]** to **[END_DATE_YYYY-MM-DD]**, in the location specified as **[TARGET_LOCATION_NAME_OR_CITY_STATE]** (if latitude/longitude are provided, use these: Lat: **[TARGET_LATITUDE]**, Lon: **[TARGET_LONGITUDE]**), please generate a distinct dating plan suggestions.
Constraints and Guidelines for Suggestions:
1. Creativity & Fun: Plans should be engaging, memorable, and offer a good experience for a date.
2. Budget: All generated plans should aim for a moderate budget (conceptually "$$"), meaning they should be affordable yet offer good value, without being overly cheap or extravagant. This budget level should be *reflected in the choice of activities and venues*, but **do not** explicitly state "Budget: $$" in the `plan_description`.
3. Interest Alignment:
Consider the following user interests: **[COMMA_SEPARATED_LIST_OF_INTERESTS, e.g., outdoors, arts & culture, foodie, nightlife, unique local events, live music, active/sports]**. Tailor suggestions specifically to these where possible. The plan should *embody* these interests.
Fallback: If specific events or venues perfectly matching all listed user interests cannot be found for the specified weekend, you should create a creative and fun generic dating plan that is still appealing, suitable for the location, and adheres to the moderate budget. This plan should still sound exciting and fun, even if it's more general.
4. Current & Specific: Prioritize finding specific, current events, festivals, pop-ups, or unique local venues operating or happening during the specified weekend dates. If exact current events cannot be found, suggest appealing evergreen options or implement the fallback generic plan.
5. Location Details: For each place or event mentioned within a plan, you MUST provide its name, precise latitude, precise longitude, and a brief, helpful description.
6. Maximum Activities: The plan must contain a maximum of 3 distinct activities.
RETURN PLAN in MARKDOWN FORMAT
""",
tools=[google_search]
)
Y así definimos nuestro primer agente. Una de las mejores características del ADK es su naturaleza intuitiva y las herramientas útiles que proporciona. Una especialmente útil es la IU para desarrolladores del ADK, que te permite probar tu agente de forma interactiva y ver sus respuestas en tiempo real.
👉💻 Comencemos. Los siguientes comandos iniciarán la IU de ADK DEV:
. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd ~/instavibe-bootstrap/agents
sed -i "s|^\(O\?GOOGLE_CLOUD_PROJECT\)=.*|GOOGLE_CLOUD_PROJECT=${PROJECT_ID}|" ~/instavibe-bootstrap/agents/planner/.env
adk web
Después de ejecutar los comandos, deberías ver un resultado en tu terminal que indique que se inició el servidor web del ADK, similar a este:
+-----------------------------------------------------------------------------+
| ADK Web Server started |
| |
| For local testing, access at http://localhost:8000. |
+-----------------------------------------------------------------------------+
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
👉 A continuación, para acceder a la IU de desarrollo del ADK desde tu navegador, haz lo siguiente:
En el ícono de Vista previa en la Web (a menudo, se ve como un ojo o un cuadrado con una flecha) en la barra de herramientas de Cloud Shell (por lo general, en la parte superior derecha), selecciona Cambiar puerto. En la ventana emergente, establece el puerto en 8000 y haz clic en "Cambiar y obtener vista previa". Luego, Cloud Shell abrirá una nueva pestaña o ventana del navegador en la que se mostrará la IU de ADK Dev.
Una vez que se abra la IU para desarrolladores del ADK en tu navegador, selecciona planner en el menú desplegable de la esquina superior derecha de la IU como el agente con el que deseas interactuar. Ahora, en el diálogo de chat de la derecha, intenta asignarle una tarea a tu agente. Por ejemplo, puedes tener una conversación con el agente:
Search and plan something in Seattle for me this weekend
This weekend and I enjoy food and anime
Sugerir una fecha (tu preferencia)
July 12 2025
Deberías ver que el agente procesa tu solicitud y proporciona un plan basado en los resultados de la Búsqueda de Google.
Ahora bien, interactuar con un agente es una cosa, pero ¿cómo sabemos si se comporta de forma coherente según lo esperado, en especial a medida que realizamos cambios?
Los métodos tradicionales de prueba de software suelen ser insuficientes para los agentes de IA debido a su naturaleza generativa y no determinística. Para cerrar la brecha entre una demostración genial y un agente de producción confiable, es fundamental contar con una estrategia de evaluación sólida. A diferencia de la simple verificación del resultado final de un modelo generativo, la evaluación de un agente a menudo implica analizar su proceso de toma de decisiones y su capacidad para usar herramientas o seguir instrucciones correctamente en diversas situaciones. El ADK proporciona funciones para ayudarte con esto.
👉 En la IU para desarrolladores del ADK, haz clic en la pestaña "Eval" en el panel de navegación de la izquierda. Deberías ver un archivo de prueba precargado llamado plan_eval
. Este archivo contiene entradas y criterios predefinidos para probar nuestro agente de planificación.
👉 Selecciona una situación, como "boston", y haz clic en el botón Run Evaluation. En la ventana emergente que aparece, reduce la puntuación de coincidencia a 0.3 y haz clic en Iniciar.
Esto ejecutará el agente con la entrada de prueba y verificará si su salida cumple con las expectativas definidas. Esto te permite probar de forma sistemática el rendimiento de tu agente.
👉 Ahora, veamos qué sucede con un umbral más estricto. Selecciona la situación "nyc" y vuelve a hacer clic en Run Evaluation. Esta vez, deja la puntuación de coincidencia en su valor predeterminado (Puntuación de coincidencia de la respuesta: 0.7) y haz clic en Iniciar. Notarás que el resultado es Error. Esto es normal, ya que el resultado creativo del agente no coincide perfectamente con la respuesta "ideal" predefinida.
👉 Para comprender por qué falló, haz clic en el ícono de falla en la fila "nyc". Ahora, la IU muestra una comparación en paralelo de la respuesta real del agente y la respuesta esperada del caso de prueba. Esta vista es esencial para la depuración, ya que te permite ver exactamente dónde divergió el resultado del agente y refinar sus instrucciones en consecuencia.
Cuando termines de explorar la IU y la evaluación, vuelve a la terminal del Editor de Cloud Shell y presiona Ctrl+C
para detener la IU para desarrolladores del ADK.
Si bien el texto de formato libre es un buen comienzo, para que aplicaciones como InstaVibe usen fácilmente las sugerencias de un agente, los datos estructurados (como JSON) son mucho más prácticos. Modifiquemos nuestro agente para que devuelva su plan en un formato JSON coherente.
👉📝 En ~/instavibe-bootstrap/agents/planner/agent.py
, busca la línea que actualmente dice RETURN PLAN in MARKDOWN FORMAT
dentro de la cadena de instrucciones del agente. Reemplaza esa línea por la siguiente estructura JSON detallada:
Return your response *exclusively* as a single JSON object. This object should contain a top-level key, "fun_plans", which holds a plan objects. Each plan object in the list must strictly adhere to the following structure:
--json--
{
"plan_description": "A summary of the overall plan, consisting of **exactly three sentences**. Craft these sentences in a friendly, enthusiastic, and conversational tone, as if you're suggesting this awesome idea to a close friend. Make it sound exciting and personal, highlighting the positive aspects and appeal of the plan without explicitly mentioning budget or listing interest categories.",
"locations_and_activities": [
{
"name": "Name of the specific place or event",
"latitude": 0.000000, // Replace with actual latitude
"longitude": 0.000000, // Replace with actual longitude
"description": "A brief description of this place/event, why it's suitable for the date, and any specific details for the weekend (e.g., opening hours, event time)."
}
// Add more location/activity objects here if the plan involves multiple stops/parts
]
}
Ahora que actualizaste las instrucciones del agente para solicitar específicamente el resultado en formato JSON, verifiquemos el cambio.
👉💻 Vuelve a iniciar la IU para desarrolladores del ADK con el mismo comando que antes:
. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd ~/instavibe-bootstrap/agents
adk web
Actualiza la pestaña si ya la tienes abierta. O bien, sigue los mismos pasos que antes para abrir la IU de ADK Dev en tu navegador (a través de la vista previa en la Web de Cloud Shell en el puerto 8000). Una vez que se cargue la IU, asegúrate de que esté seleccionado el agente de planificación.
👉 Esta vez, hagámosle una solicitud diferente. En el diálogo de chat, ingresa lo siguiente:
Plan an event Boston this weekend with art and coffee
Examina cuidadosamente la respuesta del agente. En lugar de una respuesta de texto puramente conversacional, ahora deberías ver una respuesta con el formato estricto de un objeto JSON, que coincide con la estructura que definimos en las instrucciones (que contiene fun_plans, plan_description, locations_and_activities, etc.). Esto confirma que el agente ahora puede generar resultados estructurados adecuados para el uso programático de nuestra aplicación InstaVibe.
Después de confirmar el resultado en formato JSON, vuelve a la terminal de Cloud Shell y presiona Ctrl+C
para detener la IU de desarrollo del ADK.
Componentes del ADK
Si bien la IU para desarrolladores del ADK es excelente para las pruebas interactivas, a menudo necesitamos ejecutar nuestros agentes de forma programática, tal vez como parte de una aplicación más grande o un servicio de backend. Para comprender cómo funciona, veamos algunos conceptos básicos del ADK relacionados con el tiempo de ejecución y la administración del contexto.
Las conversaciones significativas de varios turnos requieren que los agentes comprendan el contexto, es decir, que recuerden lo que se dijo y se hizo para mantener la continuidad. El ADK proporciona formas estructuradas de administrar este contexto a través de Session, State y Memory:
- Sesión: Cuando un usuario comienza a interactuar con un agente, se crea una sesión. Considéralo como el contenedor de un solo hilo de chat específico. Contiene un ID único, el historial de interacciones (eventos), los datos de trabajo actuales (estado) y metadatos, como la hora de la última actualización.
- Estado: Es la memoria de trabajo a corto plazo del agente dentro de una sola sesión. Es un diccionario mutable en el que el agente puede almacenar información temporal necesaria para completar la tarea actual (p.ej., preferencias del usuario recopiladas hasta el momento, resultados intermedios de las llamadas a herramientas).
- Memoria: Representa el potencial del agente para recordar información a largo plazo en diferentes sesiones o acceder a bases de conocimiento externas. Si bien Session y State controlan la conversación inmediata, Memory (que suele administrar un MemoryService) permite que un agente recupere información de interacciones pasadas o fuentes de datos estructurados, lo que le brinda un contexto de conocimiento más amplio. (Nota: Nuestro cliente simple usa servicios en memoria para simplificar, lo que significa que la memoria o el estado solo persisten mientras se ejecuta la secuencia de comandos).
- Evento: Cada interacción dentro de una sesión (mensaje del usuario, respuesta del agente, solicitud de uso de herramienta, resultado de la herramienta, cambio de estado, error) se registra como un evento inmutable. Esto crea un registro cronológico, que es básicamente el historial de acciones y la transcripción de la conversación.
Entonces, ¿cómo se administran cuando se ejecuta un agente? Ese es el trabajo del ejecutor.
- Runner: El Runner es el motor de ejecución principal que proporciona el ADK. Tú defines tu agente y las herramientas que usa, y el Runner coordina el proceso para satisfacer la solicitud de un usuario. Administra la sesión, controla el flujo de eventos, actualiza el estado, invoca el modelo de lenguaje subyacente, coordina las llamadas a herramientas y, posiblemente, interactúa con MemoryService. Piensa en él como el director de orquesta que se asegura de que todas las partes diferentes funcionen correctamente en conjunto.
Podemos usar el Runner para ejecutar nuestro agente como una aplicación de Python independiente, completamente independiente de la IU para desarrolladores.
Creemos una secuencia de comandos del cliente simple para invocar nuestro agente de planificación de forma programática.
👉📝 En el archivo ~/instavibe-bootstrap/agents/planner/planner_client.py
, agrega el siguiente código de Python debajo de las importaciones existentes. En planner_client.py
, debajo de las importaciones, agrega lo siguiente:
async def async_main():
session_service = InMemorySessionService()
session = await session_service.create_session(
state={}, app_name='planner_app', user_id='user_dc'
)
query = "Plan Something for me in San Francisco this weekend on wine and fashion "
print(f"User Query: '{query}'")
content = types.Content(role='user', parts=[types.Part(text=query)])
root_agent = agent.root_agent
runner = Runner(
app_name='planner_app',
agent=root_agent,
session_service=session_service,
)
print("Running agent...")
events_async = runner.run_async(
session_id=session.id, user_id=session.user_id, new_message=content
)
async for event in events_async:
print(f"Event received: {event}")
if __name__ == '__main__':
try:
asyncio.run(async_main())
except Exception as e:
print(f"An error occurred: {e}")
Este código configura servicios en memoria para la administración de sesiones y artefactos (para que sea sencillo en este ejemplo), crea una sesión, define una búsqueda del usuario, configura el ejecutor con nuestro agente y, luego, ejecuta el agente de forma asíncrona, e imprime cada evento generado durante la ejecución.
👉💻 Ahora, ejecuta esta secuencia de comandos del cliente desde tu terminal:
. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd ~/instavibe-bootstrap/agents
python -m planner.planner_client
👀 Observa el resultado. En lugar de solo el plan JSON final, verás la estructura detallada de cada objeto Event generado durante el flujo de ejecución del agente. Esto incluye el evento del mensaje inicial del usuario, los posibles eventos relacionados con las llamadas a herramientas (como la Búsqueda de Google) y, por último, el evento de respuesta del modelo que contiene el plan en formato JSON. Este flujo de eventos detallado es muy útil para depurar y comprender el procesamiento paso a paso que ocurre dentro del tiempo de ejecución del ADK.
Running agent...
Event received: content=Content(parts=[Part(video_metadata=None, thought=None, code_execution_result=None, executable_code=None, file_data=None, function_call=None, function_response=None, inline_data=None, text='```json\n{\n "fun_plans": [\n {\n "plan_description": "Embark on a stylish adventure through Hayes Valley,
...(turncated)
, offering a variety of fashion styles to browse and enjoy."\n }\n ]\n }\n ]\n}\n```')], role='model') grounding_metadata=GroundingMetadata(grounding_chunks=[GroundingChunk(retrieved_context=None, web=GroundingChunkWeb(domain='islands.com', title='islands.com', uri='http
...(turncated)
QyTpPV7jS6wUt-Ix7GuP2mC9J4eY_8Km6Vv44liF9cb2VSs='))], grounding_supports=[GroundingSupport(confide
...(turncated)
>\n', sdk_blob=None), web_search_queries=['..e']) partial=None turn_complete=None error_code=None error_message=None interrupted=None custom_metadata=None invocation_id='e-04d97b8b-9021-47a5-ab41-17b5cbb4bf03' author='location_search_agent' actions=EventActions(skip_summarization=None, state_delta={}, artifact_delta={}, transfer_to_agent=None, escalate=None, requested_auth_configs={}) long_running_tool_ids=None branch=None id='CInHdkKw' timestamp=1746978846.232674
Si la secuencia de comandos se ejecuta de forma continua o se bloquea, es posible que debas detenerla manualmente presionando Ctrl+C
.
7. Agente de interacción con la plataforma: Interactúa con el servidor de MCP
Si bien el ADK ayuda a estructurar nuestros agentes, a menudo necesitan interactuar con sistemas o APIs externos para realizar acciones en el mundo real.
Model Context Protocol (MCP)
El Protocolo de contexto del modelo (MCP) es un estándar abierto diseñado para estandarizar la forma en que las aplicaciones de IA, como los agentes, se conectan con fuentes de datos, herramientas y sistemas externos. Su objetivo es resolver el problema de necesitar integraciones personalizadas para cada combinación de aplicación de IA y fuente de datos, ya que proporciona una interfaz universal. El MCP utiliza una arquitectura cliente-servidor en la que los clientes de MCP, que residen en las aplicaciones de IA (hosts), administran las conexiones a los servidores de MCP. Estos servidores son programas externos que exponen funcionalidades específicas, como acceder a datos locales, interactuar con servicios remotos a través de APIs o proporcionar instrucciones predefinidas, lo que permite que los modelos de IA accedan a información actual y realicen tareas más allá de su entrenamiento inicial. Esta estructura permite que los modelos de IA descubran capacidades externas y que interactúen con ellas de forma estandarizada, lo que simplifica y hace más escalables las integraciones.
Compila e implementa el servidor de MCP de InstaVibe
Nuestros agentes eventualmente deberán interactuar con la plataforma de InstaVibe.Específicamente, para crear publicaciones y registrar eventos con las APIs existentes de la plataforma. La aplicación InstaVibe ya expone estas funcionalidades a través de extremos HTTP estándar:
Enpoint | URL | Método HTTP | Descripción |
Crear una publicación | api/posts | PUBLICAR | Es el extremo de la API para agregar una publicación nueva. Espera un cuerpo JSON: |
Crear evento | api/events | PUBLICAR | Es el extremo de API para agregar un evento nuevo y sus asistentes (esquema simplificado). |
Para que estas capacidades estén disponibles para nuestros agentes a través de MCP, primero debemos crear funciones simples de Python que actúen como wrappers alrededor de estas llamadas a la API. Estas funciones controlarán la lógica de la solicitud HTTP.
👉 Primero, implementemos la función de wrapper para crear una publicación. Abre el archivo ~/instavibe-bootstrap/tools/instavibe/instavibe.py
y reemplaza el comentario #REPLACE ME CREATE POST
por el siguiente código en Python:
def create_post(author_name: str, text: str, sentiment: str, base_url: str = BASE_URL):
"""
Sends a POST request to the /posts endpoint to create a new post.
Args:
author_name (str): The name of the post's author.
text (str): The content of the post.
sentiment (str): The sentiment associated with the post (e.g., 'positive', 'negative', 'neutral').
base_url (str, optional): The base URL of the API. Defaults to BASE_URL.
Returns:
dict: The JSON response from the API if the request is successful.
Returns None if an error occurs.
Raises:
requests.exceptions.RequestException: If there's an issue with the network request (e.g., connection error, timeout).
"""
url = f"{base_url}/posts"
headers = {"Content-Type": "application/json"}
payload = {
"author_name": author_name,
"text": text,
"sentiment": sentiment
}
try:
response = requests.post(url, headers=headers, json=payload)
response.raise_for_status() # Raise an exception for bad status codes (4xx or 5xx)
print(f"Successfully created post. Status Code: {response.status_code}")
return response.json()
except requests.exceptions.RequestException as e:
print(f"Error creating post: {e}")
# Optionally re-raise the exception if the caller needs to handle it
# raise e
return None
except json.JSONDecodeError:
print(f"Error decoding JSON response from {url}. Response text: {response.text}")
return None
👉📝 A continuación, crearemos la función de wrapper para la API de creación de eventos. En el mismo archivo ~/instavibe-bootstrap/tools/instavibe/instavibe.py
, reemplaza el comentario #REPLACE ME CREATE EVENTS
por este código:
def create_event(event_name: str, description: str, event_date: str, locations: list, attendee_names: list[str], base_url: str = BASE_URL):
"""
Sends a POST request to the /events endpoint to create a new event registration.
Args:
event_name (str): The name of the event.
description (str): The detailed description of the event.
event_date (str): The date and time of the event (ISO 8601 format recommended, e.g., "2025-06-10T09:00:00Z").
locations (list): A list of location dictionaries. Each dictionary should contain:
'name' (str), 'description' (str, optional),
'latitude' (float), 'longitude' (float),
'address' (str, optional).
attendee_names (list[str]): A list of names of the people attending the event.
base_url (str, optional): The base URL of the API. Defaults to BASE_URL.
Returns:
dict: The JSON response from the API if the request is successful.
Returns None if an error occurs.
Raises:
requests.exceptions.RequestException: If there's an issue with the network request (e.g., connection error, timeout).
"""
url = f"{base_url}/events"
headers = {"Content-Type": "application/json"}
payload = {
"event_name": event_name,
"description": description,
"event_date": event_date,
"locations": locations,
"attendee_names": attendee_names,
}
try:
response = requests.post(url, headers=headers, json=payload)
response.raise_for_status() # Raise an exception for bad status codes (4xx or 5xx)
print(f"Successfully created event registration. Status Code: {response.status_code}")
return response.json()
except requests.exceptions.RequestException as e:
print(f"Error creating event registration: {e}")
# Optionally re-raise the exception if the caller needs to handle it
# raise e
return None
except json.JSONDecodeError:
print(f"Error decoding JSON response from {url}. Response text: {response.text}")
return None
Como puedes ver, estas funciones son wrappers sencillos para las APIs de InstaVibe existentes. Este patrón es útil si ya tienes APIs para tus servicios, ya que puedes exponer fácilmente su funcionalidad como herramientas para los agentes creando estos wrappers.
Implementación del servidor de MCP
Ahora que tenemos las funciones de Python que realizan las acciones (llaman a las APIs de InstaVibe), debemos compilar el componente del servidor de MCP. Este servidor expondrá estas funciones como "herramientas" según el estándar de MCP, lo que permitirá que los clientes de MCP (como nuestros agentes) las descubran y las invoquen.
Por lo general, un servidor de MCP implementa dos funciones clave:
- list_tools: Es responsable de permitir que el cliente descubra las herramientas disponibles en el servidor, proporcionando metadatos como sus nombres, descripciones y parámetros obligatorios, a menudo definidos con JSON Schema.
- call_tool: Controla la ejecución de una herramienta específica solicitada por el cliente, recibe el nombre y los argumentos de la herramienta, y realiza la acción correspondiente, como interactuar con una API en nuestro caso.
Los servidores de MCP se usan para proporcionar a los modelos de IA acceso a datos y acciones del mundo real, lo que permite realizar tareas como enviar correos electrónicos, crear tareas en sistemas de administración de proyectos, buscar en bases de datos o interactuar con diversos servicios web y software. Si bien las implementaciones iniciales a menudo se enfocaron en servidores locales que se comunican a través de entrada/salida (stdio) estándar para mayor simplicidad, en particular en entornos de desarrollo o "estudio", el cambio hacia servidores remotos que utilizan protocolos como HTTP con eventos enviados por el servidor (SSE) tiene más sentido para una adopción más amplia y casos de uso empresariales.
La arquitectura remota, a pesar de la capa de comunicación de red adicional, ofrece ventajas significativas: permite que varios clientes de IA compartan el acceso a un solo servidor, centraliza la administración y las actualizaciones de las herramientas, mejora la seguridad al mantener los datos sensibles y las claves de API en el servidor en lugar de distribuirlos en muchas máquinas cliente potenciales, y desacopla el modelo de IA de los detalles de la integración del sistema externo, lo que hace que todo el ecosistema sea más escalable, seguro y fácil de mantener que si cada instancia de IA tuviera que administrar sus propias integraciones directas.
Implementaremos nuestro servidor de MCP con HTTP y eventos enviados por el servidor (SSE) para la comunicación, lo que es adecuado para ejecuciones de herramientas potencialmente prolongadas y situaciones empresariales.
👉📝 Primero, implementemos el extremo list_tools. Abre el archivo ~/instavibe-bootstrap/tools/instavibe/mcp_server.py
y reemplaza el comentario #REPLACE ME - LIST TOOLS
por el siguiente código. :
@app.list_tools()
async def list_tools() -> list[mcp_types.Tool]:
"""MCP handler to list available tools."""
# Convert the ADK tool's definition to MCP format
mcp_tool_schema_event = adk_to_mcp_tool_type(event_tool)
mcp_tool_schema_post = adk_to_mcp_tool_type(post_tool)
print(f"MCP Server: Received list_tools request. \n MCP Server: Advertising tool: {mcp_tool_schema_event.name} and {mcp_tool_schema_post}")
return [mcp_tool_schema_event,mcp_tool_schema_post]
Esta función define las herramientas (create_event, create_post) y les informa a los clientes que se conectan sobre ellas.
👉📝 A continuación, implementa el extremo call_tool
, que controla las solicitudes de ejecución reales de los clientes. En el mismo archivo ~/instavibe-bootstrap/tools/instavibe/mcp_server.py
, reemplaza el comentario #REPLACE ME - CALL TOOLS
por este código.
@app.call_tool()
async def call_tool(
name: str, arguments: dict
) -> list[mcp_types.TextContent | mcp_types.ImageContent | mcp_types.EmbeddedResource]:
"""MCP handler to execute a tool call."""
print(f"MCP Server: Received call_tool request for '{name}' with args: {arguments}")
# Look up the tool by name in our dictionary
tool_to_call = available_tools.get(name)
if tool_to_call:
try:
adk_response = await tool_to_call.run_async(
args=arguments,
tool_context=None, # No ADK context available here
)
print(f"MCP Server: ADK tool '{name}' executed successfully.")
response_text = json.dumps(adk_response, indent=2)
return [mcp_types.TextContent(type="text", text=response_text)]
except Exception as e:
print(f"MCP Server: Error executing ADK tool '{name}': {e}")
# Creating a proper MCP error response might be more robust
error_text = json.dumps({"error": f"Failed to execute tool '{name}': {str(e)}"})
return [mcp_types.TextContent(type="text", text=error_text)]
else:
# Handle calls to unknown tools
print(f"MCP Server: Tool '{name}' not found.")
error_text = json.dumps({"error": f"Tool '{name}' not implemented."})
return [mcp_types.TextContent(type="text", text=error_text)]
Esta función recibe el nombre y los argumentos de la herramienta, busca la función de wrapper de Python correspondiente que definimos antes, la ejecuta y devuelve el resultado.
👉💻 Con la lógica del servidor de MCP definida, ahora debemos empaquetarla como un contenedor. En la terminal, ejecuta la siguiente secuencia de comandos para compilar la imagen de Docker con Cloud Build:
. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap/tools/instavibe
export IMAGE_TAG="latest"
export MCP_IMAGE_NAME="mcp-tool-server"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${MCP_IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="mcp-tool-server"
export INSTAVIBE_BASE_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep instavibe)/api
gcloud builds submit . \
--tag=${IMAGE_PATH} \
--project=${PROJECT_ID}
👉💻 Implementa la imagen como un servicio en Google Cloud Run.
. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap/tools/instavibe
export IMAGE_TAG="latest"
export MCP_IMAGE_NAME="mcp-tool-server"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${MCP_IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="mcp-tool-server"
export INSTAVIBE_BASE_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep instavibe)/api
gcloud run deploy ${SERVICE_NAME} \
--image=${IMAGE_PATH} \
--platform=managed \
--region=${REGION} \
--allow-unauthenticated \
--set-env-vars="INSTAVIBE_BASE_URL=${INSTAVIBE_BASE_URL}" \
--set-env-vars="APP_HOST=0.0.0.0" \
--set-env-vars="APP_PORT=8080" \
--set-env-vars="GOOGLE_GENAI_USE_VERTEXAI=TRUE" \
--set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
--set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
--project=${PROJECT_ID} \
--min-instances=1
👉💻 Una vez que la implementación se complete correctamente, el servidor de MCP se ejecutará y se podrá acceder a él a través de una URL pública. Necesitamos capturar esta URL para que nuestro agente (que actúa como cliente de MCP) sepa dónde conectarse.
export MCP_SERVER_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep mcp-tool-server)/sse
También deberías poder ver el servicio mcp-tool-server como "En ejecución" en la sección Cloud Run de tu consola de Google Cloud.
Con el servidor de MCP implementado y su URL capturada, ahora podemos implementar el agente que actuará como cliente de MCP y utilizará las herramientas expuestas por este servidor.
8. Agente de interacción con la plataforma (con MCP)
Cliente de MCP: El cliente de MCP es un componente que reside dentro de una aplicación o un agente de IA, y actúa como la interfaz entre el modelo de IA y uno o más servidores de MCP. En nuestra implementación, este cliente se integrará directamente en nuestro agente. La función principal de este cliente es comunicarse con los servidores de MCP para descubrir las herramientas disponibles a través de la función list_tools
y, luego, solicitar la ejecución de herramientas específicas con la función call_tool
, pasando los argumentos necesarios que proporciona el modelo de IA o el agente que coordina la llamada.
Ahora, compilaremos el agente que actúa como cliente de MCP. Este agente, que se ejecuta dentro del marco de trabajo del ADK, será responsable de comunicarse con el mcp-tool-server
que acabamos de implementar.
👉 Primero, debemos modificar la definición del agente para recuperar de forma dinámica las herramientas de nuestro servidor MCP en ejecución. En agents/platform_mcp_client/agent.py
, reemplaza #REPLACE ME - FETCH TOOLS
por lo siguiente:
"""Gets tools from the File System MCP Server."""
tools = MCPToolset(
connection_params=SseServerParams(url=MCP_SERVER_URL, headers={})
)
Este código usa el método MCPToolset.from_server para conectarse a MCP_SERVER_URL (que configuramos como una variable de entorno anteriormente) y recuperar la lista de herramientas disponibles.
A continuación, debemos indicarle a la definición del agente del ADK que use estas herramientas recuperadas de forma dinámica.
👉 En agents/platform_mcp_client/agent.py
, reemplaza #REPLACE ME - SET TOOLs
por lo siguiente:
tools=[tools],
👉💻 Ahora, probemos este agente de forma local con la IU para desarrolladores del ADK y veamos si puede conectarse correctamente al servidor de MCP y usar las herramientas para interactuar con nuestra aplicación InstaVibe en ejecución.
. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
export MCP_SERVER_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep mcp-tool-server)/sse
cd ~/instavibe-bootstrap/agents
sed -i "s|^\(O\?GOOGLE_CLOUD_PROJECT\)=.*|GOOGLE_CLOUD_PROJECT=${PROJECT_ID}|" ~/instavibe-bootstrap/agents/platform_mcp_client/.env
sed -i "s|^\(O\?MCP_SERVER_URL\)=.*|MCP_SERVER_URL=${MCP_SERVER_URL}|" ~/instavibe-bootstrap/agents/platform_mcp_client/.env
adk web
Vuelve a abrir la IU para desarrolladores del ADK en tu navegador (con la vista previa web de Cloud Shell en el puerto 8000). Esta vez, en el menú desplegable de la esquina superior derecha, selecciona el agente platform_mcp_client
.
Probemos la herramienta create_post. En el diálogo de chat, ingresa la siguiente solicitud:
Create a post saying "Y'all I just got the cutest lil void baby 😭✨ Naming him Abyss bc he's deep, mysterious, and lowkey chaotic 🔥🖤 #VoidCat #NewRoomie" I'm Julia
El agente debe procesar esto, identificar la necesidad de usar la herramienta create_post, comunicarse con el servidor de MCP, que a su vez llama a la API de InstaVibe.
👉 Paso de verificación: Después de que el agente confirme la acción, abre la pestaña en la que se ejecuta tu aplicación de InstaVibe (o actualízala). Deberías ver la nueva publicación de "Julia" en el feed principal.
👉💻 Ejecuta esta secuencia de comandos en una terminal independiente para obtener el vínculo de Instavibe si es necesario:
gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep instavibe
👉📝 Ahora, probemos la herramienta create_event. Ingresa la siguiente solicitud de varias líneas en el diálogo de chat:
Hey, can you set up an event for Hannah and George and me, and I'm Julia? Let's call it 'Mexico City Culinary & Art Day'.
here are more info
{"event_name": "Mexico City Culinary & Art Day",
"description": "A vibrant day in Mexico City for Hannah and George, starting with lunch at one of the city's best taco spots in the hip Condesa neighborhood, followed by an inspiring afternoon exploring the Museo Soumaya's stunning art collection.",
"event_date": "2025-10-17T12:00:00-06:00",
"locations": [
{
"name": "El Tizoncito",
"description": "Considered one of the original creators of tacos al pastor, El Tizoncito offers a legendary taco experience in the heart of Condesa. Their flavorful meats, house salsas, and casual vibe make it a must-visit for foodies.",
"latitude": 19.412179,
"longitude": -99.171308,
"address": "Av. Tamaulipas 122, Hipódromo, Cuauhtémoc, 06100 Ciudad de México, CDMX, Mexico"
},
{
"name": "Museo Soumaya",
"description": "An architectural icon in Mexico City, Museo Soumaya houses over 66,000 works of art, including pieces by Rodin, Dalí, and Rivera. The striking silver structure is a cultural landmark and a visual feast inside and out.",
"latitude": 19.440056,
"longitude": -99.204281,
"address": "Plaza Carso, Blvd. Miguel de Cervantes Saavedra 303, Granada, Miguel Hidalgo, 11529 Ciudad de México, CDMX, Mexico"
}
],
"attendee_names": ["Hannah", "George", Julia],
}
Nuevamente, el agente debe usar la herramienta adecuada a través del servidor de MCP. En la pestaña Eventos, puedes hacer clic en el evento individual para ver un registro detallado paso a paso de la ejecución.
👉 Paso de verificación: Vuelve a la aplicación InstaVibe en ejecución y navega a la sección "Eventos" (o su equivalente). Ahora deberías ver el evento recién creado "Día de arte y gastronomía en Ciudad de México".
Esto demuestra con éxito cómo MCP permite que nuestro agente aproveche herramientas externas (en este caso, las APIs de InstaVibe) de una manera estandarizada.
Una vez que hayas verificado ambas acciones, vuelve a la terminal de Cloud Shell y presiona Ctrl+C
para detener la IU de desarrollo del ADK.
9. Agente de flujo de trabajo y varios agentes en el ADK
Hasta ahora, nuestros agentes pueden planificar salidas e interactuar con la plataforma. Sin embargo, la planificación verdaderamente personalizada requiere comprender el círculo social del usuario. Para los usuarios ocupados que tal vez no sigan de cerca las actividades de sus amigos, recopilar este contexto de forma manual es difícil. Para abordar este problema, crearemos un agente de creación de perfiles sociales que aproveche nuestra base de datos de gráficos de Spanner para analizar las actividades y los intereses de los amigos, lo que permitirá ofrecer sugerencias más personalizadas.
Primero, necesitamos herramientas para que este agente acceda a los datos del gráfico.
👉📝 Agrega las siguientes funciones de Python al final del archivo ~/instavibe-bootstrap/agents/social/instavibe.py
:
def get_person_attended_events(person_id: str)-> list[dict]:
"""
Fetches events attended by a specific person using Graph Query.
Args:
person_id (str): The ID of the person whose posts to fetch.
Returns: list[dict] or None.
"""
if not db_instance: return None
graph_sql = """
Graph SocialGraph
MATCH (p:Person)-[att:Attended]->(e:Event)
WHERE p.person_id = @person_id
RETURN e.event_id, e.name, e.event_date, att.attendance_time
ORDER BY e.event_date DESC
"""
params = {"person_id": person_id}
param_types_map = {"person_id": param_types.STRING}
fields = ["event_id", "name", "event_date", "attendance_time"]
results = run_graph_query( graph_sql, params=params, param_types=param_types_map, expected_fields=fields)
if results is None: return None
for event in results:
if isinstance(event.get('event_date'), datetime):
event['event_date'] = event['event_date'].isoformat()
if isinstance(event.get('attendance_time'), datetime):
event['attendance_time'] = event['attendance_time'].isoformat()
return results
def get_person_id_by_name( name: str) -> str:
"""
Fetches the person_id for a given name using SQL.
Args:
name (str): The name of the person to search for.
Returns:
str or None: The person_id if found, otherwise None.
Returns the ID of the *first* match if names are duplicated.
"""
if not db_instance: return None
sql = """
SELECT person_id
FROM Person
WHERE name = @name
LIMIT 1 -- Return only the first match in case of duplicate names
"""
params = {"name": name}
param_types_map = {"name": param_types.STRING}
fields = ["person_id"]
# Use the standard SQL query helper
results = run_sql_query( sql, params=params, param_types=param_types_map, expected_fields=fields)
if results: # Check if the list is not empty
return results[0].get('person_id') # Return the ID from the first dictionary
else:
return None # Name not found
def get_person_posts( person_id: str)-> list[dict]:
"""
Fetches posts written by a specific person using Graph Query.
Args:
person_id (str): The ID of the person whose posts to fetch.
Returns:
list[dict] or None: List of post dictionaries with ISO date strings,
or None if an error occurs.
"""
if not db_instance: return None
# Graph Query: Find the specific Person node, follow 'Wrote' edge to Post nodes
graph_sql = """
Graph SocialGraph
MATCH (author:Person)-[w:Wrote]->(post:Post)
WHERE author.person_id = @person_id
RETURN post.post_id, post.author_id, post.text, post.sentiment, post.post_timestamp, author.name AS author_name
ORDER BY post.post_timestamp DESC
"""
# Parameters now include person_id and limit
params = {
"person_id": person_id
}
param_types_map = {
"person_id": param_types.STRING
}
# Fields returned remain the same
fields = ["post_id", "author_id", "text", "sentiment", "post_timestamp", "author_name"]
results = run_graph_query(graph_sql, params=params, param_types=param_types_map, expected_fields=fields)
if results is None:
return None
# Convert datetime objects to ISO format strings
for post in results:
if isinstance(post.get('post_timestamp'), datetime):
post['post_timestamp'] = post['post_timestamp'].isoformat()
return results
def get_person_friends( person_id: str)-> list[dict]:
"""
Fetches friends for a specific person using Graph Query.
Args:
person_id (str): The ID of the person whose posts to fetch.
Returns: list[dict] or None.
"""
if not db_instance: return None
graph_sql = """
Graph SocialGraph
MATCH (p:Person {person_id: @person_id})-[f:Friendship]-(friend:Person)
RETURN DISTINCT friend.person_id, friend.name
ORDER BY friend.name
"""
params = {"person_id": person_id}
param_types_map = {"person_id": param_types.STRING}
fields = ["person_id", "name"]
results = run_graph_query( graph_sql, params=params, param_types=param_types_map, expected_fields=fields)
return results
Ahora, analicemos cómo estructurar nuestro agente. Analizar los perfiles de varios amigos y, luego, resumir los hallazgos implica varios pasos. Este es un caso perfecto para usar las capacidades multiagente del ADK, específicamente los agentes de flujo de trabajo.
En el ADK de Google, un agente de flujo de trabajo no realiza tareas por sí mismo, sino que coordina otros agentes, llamados subagentes. Esto permite un diseño modular, que descompone problemas complejos en componentes especializados. El ADK proporciona tipos de flujos de trabajo integrados, como los siguientes:
- Secuencial (paso a paso)
- Paralelo (ejecución simultánea)
- y Loop (ejecución repetida)
Para nuestra tarea de creación de perfiles sociales, nuestro diseño usa un agente de bucle para crear un flujo de trabajo iterativo. La intención es procesar a una persona a la vez: profile_agent
recopila datos, summary_agent
actualiza el análisis y check_agent
determina si debemos volver a iterar.
Definamos los subagentes necesarios para este flujo de trabajo.
👉📝 En ~/instavibe-bootstrap/agents/social/agent.py
, reemplaza #REPLACE FOR profile_agent
por lo siguiente:
profile_agent = LlmAgent(
name="profile_agent",
model="gemini-2.5-flash",
description=(
"Agent to answer questions about the this person social profile. Provide the person's profile using their name, make sure to fetch the id before getting other data."
),
instruction=(
"You are a helpful agent to answer questions about the this person social profile. You'll be given a list of names, provide the person's profile using their name, make sure to fetch the id before getting other data. Get one person at a time, start with the first one on the list, and skip if already provided. return this person's result"
),
tools=[get_person_posts,get_person_friends,get_person_id_by_name,get_person_attended_events],
)
A continuación, el agente que toma la información del perfil recopilada (acumulada en las iteraciones del bucle) y genera el resumen final, identificando los puntos en común si se analizaron varias personas.
👉📝 En el mismo ~/instavibe-bootstrap/agents/social/agent.py
, reemplaza #REPLACE FOR summary_agent
por lo siguiente:
summary_agent = LlmAgent(
name="summary_agent",
model="gemini-2.5-flash",
description=(
"Generate a comprehensive social summary as a single, cohesive paragraph. This summary should cover the activities, posts, friend networks, and event participation of one or more individuals. If multiple profiles are analyzed, the paragraph must also identify and integrate any common ground found between them."
),
instruction=(
"""
Your primary task is to synthesize social profile information into a single, comprehensive paragraph.
**Input Scope & Default Behavior:**
* If specific individuals are named by the user, focus your analysis on them.
* **If no individuals are specified, or if the request is general, assume the user wants an analysis of *all relevant profiles available in the current dataset/context*.**
**For each profile (whether specified or determined by default), you must analyze:**
1. **Post Analysis:**
* Systematically review their posts (e.g., content, topics, frequency, engagement).
* Identify recurring themes, primary interests, and expressed sentiments.
2. **Friendship Relationship Analysis:**
* Examine their connections/friends list.
* Identify key relationships, mutual friends (especially if comparing multiple profiles), and the general structure of their social network.
3. **Event Participation Analysis:**
* Investigate their past (and if available, upcoming) event participation.
* Note the types of events, frequency of attendance, and any notable roles (e.g., organizer, speaker).
**Output Generation (Single Paragraph):**
* **Your entire output must be a single, cohesive summary paragraph.**
* **If analyzing a single profile:** This paragraph will detail their activities, interests, and social connections based on the post, friend, and event analysis.
* **If analyzing multiple profiles:** This paragraph will synthesize the key findings regarding posts, friends, and events for each individual. Crucially, it must then seamlessly integrate or conclude with an identification and description of the common ground found between them (e.g., shared interests from posts, overlapping event attendance, mutual friends). The aim is a unified narrative within this single paragraph.
**Key Considerations:**
* Base your summary strictly on the available data.
* If data for a specific category (posts, friends, events) is missing or sparse for a profile, you may briefly acknowledge this within the narrative if relevant.
"""
),
output_key="summary"
)
Necesitamos una forma de determinar cuándo debe detenerse el bucle (es decir, cuando se hayan resumido todos los perfiles solicitados).
👉📝 En el mismo ~/instavibe-bootstrap/agents/social/agent.py
, reemplaza #REPLACE FOR check_agent
por lo siguiente:
check_agent = LlmAgent(
name="check_agent",
model="gemini-2.5-flash",
description=(
"Check if everyone's social profile are summarized and has been generated. Output 'completed' or 'pending'."
),
output_key="summary_status"
)
Agregamos una verificación programática simple (CheckCondition) que analiza de forma explícita el summary_status
almacenado en el State, que devuelve check_agent
y le indica al agente de bucle si debe continuar (escalate=False) o detenerse (escalate=True).
👉📝 En el mismo ~/instavibe-bootstrap/agents/social/agent.py
, reemplaza #REPLACE FOR CheckCondition
ubicado en la parte superior del archivo por lo siguiente:
class CheckCondition(BaseAgent):
async def _run_async_impl(self, ctx: InvocationContext) -> AsyncGenerator[Event, None]:
#log.info(f"Checking status: {ctx.session.state.get("summary_status", "fail")}")
log.info(f"Summary: {ctx.session.state.get("summary")}")
status = ctx.session.state.get("summary_status", "fail").strip()
is_done = (status == "completed")
yield Event(author=self.name, actions=EventActions(escalate=is_done))
Estados y devoluciones de llamada para los resultados del bucle
En el ADK de Google, State es un concepto fundamental que representa la memoria o los datos de trabajo de un agente durante su ejecución. Básicamente, es un contexto persistente que contiene la información que un agente necesita mantener en diferentes pasos, llamadas a herramientas o interacciones. Este estado puede almacenar resultados intermedios, información del usuario, parámetros para acciones posteriores o cualquier otro dato que el agente necesite recordar a medida que avanza en una tarea.
En nuestro caso, a medida que el agente de Loop itera, summary_agent
y check_agent
almacenan sus resultados (summary y summary_status) en el estado del agente. Esto permite que la información persista en las distintas iteraciones. Sin embargo, el agente de Loop no devuelve automáticamente el resumen final del estado cuando termina.
Las devoluciones de llamada en el ADK nos permiten insertar lógica personalizada para que se ejecute en puntos específicos durante el ciclo de vida de un agente o en respuesta a ciertos eventos, como la finalización de una llamada a la herramienta o antes de que el agente finalice su ejecución. Proporcionan una forma de personalizar el comportamiento del agente y procesar los resultados de forma dinámica.
Usaremos un after_agent_callback
que se ejecuta cuando finaliza el bucle (porque CheckCondition se intensificó). Esta devolución de llamada modify_output_after_agent
recupera el resumen final del estado y lo formatea como el mensaje de salida final del agente.
👉📝 En el mismo ~/instavibe-bootstrap/agents/social/agent.py
, reemplaza #REPLACE FOR modify_output_after_agent
por lo siguiente:
def modify_output_after_agent(callback_context: CallbackContext) -> Optional[types.Content]:
agent_name = callback_context.agent_name
invocation_id = callback_context.invocation_id
current_state = callback_context.state.to_dict()
current_user_content = callback_context.user_content
print(f"[Callback] Exiting agent: {agent_name} (Inv: {invocation_id})")
print(f"[Callback] Current summary_status: {current_state.get("summary_status")}")
print(f"[Callback] Current Content: {current_user_content}")
status = current_state.get("summary_status").strip()
is_done = (status == "completed")
# Retrieve the final summary from the state
final_summary = current_state.get("summary")
print(f"[Callback] final_summary: {final_summary}")
if final_summary and is_done and isinstance(final_summary, str):
log.info(f"[Callback] Found final summary, constructing output Content.")
# Construct the final output Content object to be sent back
return types.Content(role="model", parts=[types.Part(text=final_summary.strip())])
else:
log.warning("[Callback] No final summary found in state or it's not a string.")
# Optionally return a default message or None if no summary was generated
return None
Cómo definir el agente de bucle raíz
Por último, definimos el LoopAgent principal. Coordina los subagentes en secuencia dentro de cada iteración del bucle (profile_agent -> summary_agent -> check_agent -> CheckCondition). Repetirá esta secuencia hasta max_iterations veces o hasta que CheckCondition indique que se completó. La devolución de llamada after_agent garantiza que se muestre el resumen final.
👉📝 En el mismo ~/instavibe-bootstrap/agents/social/agent.py
, reemplaza #REPLACE FOR root_agent
por lo siguiente:
root_agent = LoopAgent(
name="InteractivePipeline",
sub_agents=[
profile_agent,
summary_agent,
check_agent,
CheckCondition(name="Checker")
],
description="Find everyone's social profile on events, post and friends",
max_iterations=10,
after_agent_callback=modify_output_after_agent
)
Probemos este flujo de trabajo de varios agentes con la IU para desarrolladores del ADK.
👉💻 Inicia el servidor web del ADK:
. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd ~/instavibe-bootstrap/agents
sed -i "s|^\(O\?GOOGLE_CLOUD_PROJECT\)=.*|GOOGLE_CLOUD_PROJECT=${PROJECT_ID}|" ~/instavibe-bootstrap/agents/social/.env
adk web
Abre la IU para desarrolladores del ADK (puerto 8000 a través de la vista previa en la Web). En el menú desplegable del agente (esquina superior derecha), selecciona el agente Social.
👉 Ahora, asígnale la tarea de crear perfiles de varias personas. En el diálogo de chat, ingresa lo siguiente:
Tell me about Mike and Bob
Después de que el agente responda (lo que puede tardar un poco más debido a los bucles y a las múltiples llamadas al LLM), no solo mires el resultado final del chat. Navega a la pestaña Eventos en el panel izquierdo de la IU para desarrolladores del ADK.
👉 Paso de verificación: En la pestaña Eventos, verás un registro detallado paso a paso de la ejecución.
Después de observar cómo el agente invoca a cada subagente, donde esperas que el flujo vaya de profile_agent -> summary_agent -> check_agent, Checker en cada iteración. Sin embargo, en la práctica, vemos la poderosa "autooptimización" del agente en acción.
Dado que el modelo subyacente ve toda la solicitud (p.ej., "Crea un perfil de Mike y Bob"), suele elegir la ruta más eficiente y recopila todos los datos necesarios en un solo turno consolidado en lugar de iterar varias veces. Puedes ver las entradas, las salidas y los estados de cada paso, incluidas las llamadas a herramientas que realiza profile_agent.
y las actualizaciones de estado de check_agent y CheckCondition.
Este registro visual es muy valioso para comprender y depurar cómo funciona el flujo de trabajo de varios agentes hasta que la devolución de llamada genera y devuelve el resumen final.
Una vez que hayas explorado la respuesta del chat y el registro de eventos, regresa a la terminal de Cloud Shell y presiona Ctrl+C
para detener la IU de ADK Dev.
10. Comunicación de agente a agente (A2A)
Hasta ahora, creamos agentes especializados, pero operan de forma aislada o dentro de un flujo de trabajo predefinido en la misma máquina. Para crear sistemas multiagente verdaderamente distribuidos y colaborativos, necesitamos una forma para que los agentes, que potencialmente se ejecutan como servicios separados, se descubran entre sí y se comuniquen de manera eficaz. Aquí es donde entra en juego el protocolo Agent-to-Agent (A2A).
El protocolo A2A es un estándar abierto diseñado específicamente para la comunicación interoperable entre agentes de IA. Mientras que MCP se enfoca en la interacción entre el agente y la herramienta, A2A se enfoca en la interacción entre agentes. Permite a los agentes hacer lo siguiente:
- Descubrir: Encuentra otros agentes y conoce sus capacidades a través de tarjetas de agente estandarizadas.
- Comunicarse: Intercambiar mensajes y datos de forma segura
- Colaborar: Delegar tareas y coordinar acciones para alcanzar objetivos complejos
El protocolo A2A facilita esta comunicación a través de mecanismos como las "tarjetas de agente", que los agentes pueden usar para anunciar sus capacidades y la información de conexión.
A2A utiliza estándares web conocidos (HTTP, SSE, JSON-RPC) y, a menudo, emplea un modelo cliente-servidor en el que un agente (cliente) envía tareas a otro (agente/servidor remoto). Esta estandarización es clave para crear sistemas modulares y escalables en los que los agentes desarrollados de forma independiente puedan trabajar juntos.
Cómo habilitar la función de A2A para los agentes de InstaVibe
Para que nuestros agentes existentes de Planner, Platform Interaction y Social sean accesibles para otros agentes a través de A2A, debemos encapsular cada uno con un componente de servidor de A2A. Este servidor hará lo siguiente:
- Exponer una tarjeta de agente: Publica una descripción estándar de las capacidades del agente a través de un extremo HTTP.
- Listen for Tasks(Request Messages): Aceptar solicitudes de tareas entrantes de otros agentes (clientes de A2A) según el protocolo de A2A
- Administrar la ejecución de tareas(mensajes de solicitud): Entrega las tareas recibidas a la lógica subyacente del agente del ADK para su procesamiento.
Agente de Planner (habilitado para A2A)
Comencemos por agregar la capa del servidor de A2A a nuestro agente de Planner.
Define la lógica de inicio del servidor de A2A. Este código define AgentCard (la descripción pública del agente), configura A2AServer y lo inicia, vinculándolo a PlatformAgentExecutor.
👉📝 Agrega el siguiente código al final de ~/instavibe-bootstrap/agents/planner/a2a_server.py
:
class PlannerAgent:
"""An agent to help user planning a event with its desire location."""
SUPPORTED_CONTENT_TYPES = ["text", "text/plain"]
def __init__(self):
self._agent = self._build_agent()
self.runner = Runner(
app_name=self._agent.name,
agent=self._agent,
artifact_service=InMemoryArtifactService(),
session_service=InMemorySessionService(),
memory_service=InMemoryMemoryService(),
)
capabilities = AgentCapabilities(streaming=True)
skill = AgentSkill(
id="event_planner",
name="Event planner",
description="""
This agent generates multiple fun plan suggestions tailored to your specified location, dates, and interests,
all designed for a moderate budget. It delivers detailed itineraries,
including precise venue information (name, latitude, longitude, and description), in a structured JSON format.
""",
tags=["instavibe"],
examples=["What about Bostona MA this weekend?"],
)
self.agent_card = AgentCard(
name="Event Planner Agent",
description="""
This agent generates multiple fun plan suggestions tailored to your specified location, dates, and interests,
all designed for a moderate budget. It delivers detailed itineraries,
including precise venue information (name, latitude, longitude, and description), in a structured JSON format.
""",
url=f"{PUBLIC_URL}",
version="1.0.0",
defaultInputModes=PlannerAgent.SUPPORTED_CONTENT_TYPES,
defaultOutputModes=PlannerAgent.SUPPORTED_CONTENT_TYPES,
capabilities=capabilities,
skills=[skill],
)
def get_processing_message(self) -> str:
return "Processing the planning request..."
def _build_agent(self) -> LlmAgent:
"""Builds the LLM agent for the night out planning agent."""
return agent.root_agent
if __name__ == '__main__':
try:
plannerAgent = PlannerAgent()
request_handler = DefaultRequestHandler(
agent_executor=PlannerAgentExecutor(plannerAgent.runner,plannerAgent.agent_card),
task_store=InMemoryTaskStore(),
)
server = A2AStarletteApplication(
agent_card=plannerAgent.agent_card,
http_handler=request_handler,
)
logger.info(f"Attempting to start server with Agent Card: {plannerAgent.agent_card.name}")
logger.info(f"Server object created: {server}")
uvicorn.run(server.build(), host='0.0.0.0', port=port)
except Exception as e:
logger.error(f"An error occurred during server startup: {e}")
exit(1)
👉💻 Probemos rápidamente si el servidor de A2A se inicia correctamente de forma local y publica su tarjeta de agente. Ejecuta el siguiente comando en la primera terminal:
. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd ~/instavibe-bootstrap/agents/
python -m planner.a2a_server
👉 Ahora, abre otra ventana de la terminal. (Haz clic en el signo + en el panel de la terminal)
👉💻 Usa curl para solicitar la tarjeta de agente desde el servidor que se ejecuta de forma local:
curl http://localhost:10003/.well-known/agent.json | jq
Deberías ver la representación JSON de la AgentCard que definimos, lo que confirma que el servidor se está ejecutando y publicitando el agente de Planner.
Regresa a la primera terminal (donde se ejecuta el servidor) y presiona Ctrl+C
para detenerlo.
👉💻 Con la lógica del servidor A2A agregada, ahora podemos compilar la imagen del contenedor.
Compila e implementa el agente de Planner
. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap/agents
# Set variables specific to the PLANNER agent
export IMAGE_TAG="latest"
export AGENT_NAME="planner"
export IMAGE_NAME="planner-agent"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="planner-agent"
export PUBLIC_URL="https://planner-agent-${PROJECT_NUMBER}.${REGION}.run.app"
echo "Building ${AGENT_NAME} agent..."
gcloud builds submit . \
--config=cloudbuild-build.yaml \
--project=${PROJECT_ID} \
--region=${REGION} \
--substitutions=_AGENT_NAME=${AGENT_NAME},_IMAGE_PATH=${IMAGE_PATH}
echo "Image built and pushed to: ${IMAGE_PATH}"
👉💻 Implementa nuestro agente de Planner en Cloud Run.
. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap/agents
# Set variables specific to the PLANNER agent
export IMAGE_TAG="latest"
export AGENT_NAME="planner"
export IMAGE_NAME="planner-agent"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="planner-agent"
export PUBLIC_URL="https://planner-agent-${PROJECT_NUMBER}.${REGION}.run.app"
gcloud run deploy ${SERVICE_NAME} \
--image=${IMAGE_PATH} \
--platform=managed \
--region=${REGION} \
--set-env-vars="A2A_HOST=0.0.0.0" \
--set-env-vars="A2A_PORT=8080" \
--set-env-vars="GOOGLE_GENAI_USE_VERTEXAI=TRUE" \
--set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
--set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
--set-env-vars="PUBLIC_URL=${PUBLIC_URL}" \
--allow-unauthenticated \
--project=${PROJECT_ID} \
--min-instances=1
Verifiquemos que el servicio implementado se esté ejecutando y publicando su tarjeta de agente correctamente desde la nube con el Inspector de A2A.
👉 En el ícono de Vista previa en la Web de la barra de herramientas de Cloud Shell, selecciona Cambiar puerto. Establece el puerto en 8081 y haz clic en "Cambiar y obtener vista previa". Se abrirá una nueva pestaña del navegador con la interfaz del Inspector de A2A.
👉💻 En la terminal, obtén la URL de tu agente de planificación implementado:
export PLANNER_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner-agent)
echo ${PLANNER_AGENT_URL}
👉💻 Copia la URL de salida.
👉 En la IU del Inspector de A2A, pega la URL en el campo URL del agente y haz clic en Conectar.
👀 Los detalles de la tarjeta del agente y el JSON deberían aparecer en la pestaña Tarjeta del agente, lo que confirma que la conexión se realizó correctamente.
👉 Haz clic en la pestaña Chat en el Inspector de A2A. Aquí puedes interactuar directamente con tu agente implementado. Envíale un mensaje para probar su capacidad de planificación. Por ejemplo:
Plan something for me in Boston MA this weekend, and I enjoy classical music
👀 Para inspeccionar la comunicación sin procesar, haz clic en tu burbuja de mensaje y, luego, en la burbuja de respuesta del agente en la ventana de chat. Cuando hagas clic en cada uno, se mostrará el mensaje completo de JSON-RPC 2.0 que se envió o recibió, lo que es muy valioso para la depuración.
Mantén a mano la pestaña del Inspector de A2A. NO la cierres. Lo volveremos a usar en un momento para probar nuestros otros dos agentes.
Agente de interacción con la plataforma (A2A habilitado)
A continuación, repetiremos el proceso para el agente de interacción de la plataforma (el que usa MCP).
👉📝 Define la configuración del servidor A2A, incluida su AgentCard única, al final de ~/instavibe-bootstrap/agents/platform_mcp_client/a2a_server.py
:
class PlatformAgent:
"""An agent that post event and post to instavibe."""
SUPPORTED_CONTENT_TYPES = ["text", "text/plain"]
def __init__(self):
self._agent = self._build_agent()
self.runner = Runner(
app_name=self._agent.name,
agent=self._agent,
artifact_service=InMemoryArtifactService(),
session_service=InMemorySessionService(),
memory_service=InMemoryMemoryService(),
)
capabilities = AgentCapabilities(streaming=True)
skill = AgentSkill(
id="instavibe_posting",
name="Post social post and events on instavibe",
description="""
This "Instavibe" agent helps you create posts (identifying author, text, and sentiment – inferred if unspecified) and register
for events (gathering name, date, attendee). It efficiently collects required information and utilizes dedicated tools
to perform these actions on your behalf, ensuring a smooth sharing experience.
""",
tags=["instavibe"],
examples=["Create a post for me, the post is about my cute cat and make it positive, and I'm Alice"],
)
self.agent_card = AgentCard(
name="Instavibe Posting Agent",
description="""
This "Instavibe" agent helps you create posts (identifying author, text, and sentiment – inferred if unspecified) and register
for events (gathering name, date, attendee). It efficiently collects required information and utilizes dedicated tools
to perform these actions on your behalf, ensuring a smooth sharing experience.
""",
url=f"{PUBLIC_URL}",
version="1.0.0",
defaultInputModes=PlatformAgent.SUPPORTED_CONTENT_TYPES,
defaultOutputModes=PlatformAgent.SUPPORTED_CONTENT_TYPES,
capabilities=capabilities,
skills=[skill],
)
def get_processing_message(self) -> str:
return "Processing the social post and event request..."
def _build_agent(self) -> LlmAgent:
"""Builds the LLM agent for the Processing the social post and event request."""
return agent.root_agent
if __name__ == '__main__':
try:
platformAgent = PlatformAgent()
request_handler = DefaultRequestHandler(
agent_executor=PlatformAgentExecutor(platformAgent.runner,platformAgent.agent_card),
task_store=InMemoryTaskStore(),
)
server = A2AStarletteApplication(
agent_card=platformAgent.agent_card,
http_handler=request_handler,
)
uvicorn.run(server.build(), host='0.0.0.0', port=port)
except Exception as e:
logger.error(f"An error occurred during server startup: {e}")
exit(1)
Agente social (compatible con A2A)
Por último, habilitemos la función de A2A para nuestro agente de creación de perfiles sociales.
👉📝 Define la configuración del servidor A2A y AgentCard al final de ~/instavibe-bootstrap/agents/social/a2a_server.py
:
class SocialAgent:
"""An agent that handles social profile analysis."""
SUPPORTED_CONTENT_TYPES = ["text", "text/plain"]
def __init__(self):
self._agent = self._build_agent()
self.runner = Runner(
app_name=self._agent.name,
agent=self._agent,
artifact_service=InMemoryArtifactService(),
session_service=InMemorySessionService(),
memory_service=InMemoryMemoryService(),
)
capabilities = AgentCapabilities(streaming=True)
skill = AgentSkill(
id="social_profile_analysis",
name="Analyze Instavibe social profile",
description="""
Using a provided list of names, this agent synthesizes Instavibe social profile information by analyzing posts, friends, and events.
It delivers a comprehensive single-paragraph summary for individuals, and for groups, identifies commonalities in their social activities
and connections based on profile data.
""",
tags=["instavibe"],
examples=["Can you tell me about Bob and Alice?"],
)
self.agent_card = AgentCard(
name="Social Profile Agent",
description="""
Using a provided list of names, this agent synthesizes Instavibe social profile information by analyzing posts, friends, and events.
It delivers a comprehensive single-paragraph summary for individuals, and for groups, identifies commonalities in their social activities
and connections based on profile data.
""",
url=f"{PUBLIC_URL}",
version="1.0.0",
defaultInputModes=self.SUPPORTED_CONTENT_TYPES,
defaultOutputModes=self.SUPPORTED_CONTENT_TYPES,
capabilities=capabilities,
skills=[skill],
)
def get_processing_message(self) -> str:
return "Processing the social profile analysis request..."
def _build_agent(self) -> LoopAgent:
"""Builds the LLM agent for the social profile analysis agent."""
return agent.root_agent
if __name__ == '__main__':
try:
socialAgent = SocialAgent()
request_handler = DefaultRequestHandler(
agent_executor=SocialAgentExecutor(socialAgent.runner,socialAgent.agent_card),
task_store=InMemoryTaskStore(),
)
server = A2AStarletteApplication(
agent_card=socialAgent.agent_card,
http_handler=request_handler,
)
uvicorn.run(server.build(), host='0.0.0.0', port=port)
except Exception as e:
logger.error(f"An error occurred during server startup: {e}")
exit(1)
Compila e implementa los agentes de interacción con la plataforma y sociales
Estos agentes necesitan acceso a Spanner, por lo que debes asegurarte de que las variables de entorno SPANNER_INSTANCE_ID
, SPANNER_DATABASE_ID
y MCP_SERVER_URL
se pasen correctamente durante la implementación.
👉💻 Compila e implementa en Cloud Run con Cloud Build:
. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap/agents
export MCP_SERVER_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep mcp-tool-server)/sse
gcloud builds submit . \
--config=cloudbuild.yaml \
--project="${PROJECT_ID}" \
--region="${REGION}" \
--substitutions=\
_PROJECT_ID="${PROJECT_ID}",\
_PROJECT_NUMBER="${PROJECT_NUMBER}",\
_REGION="${REGION}",\
_REPO_NAME="${REPO_NAME}",\
_SPANNER_INSTANCE_ID="${SPANNER_INSTANCE_ID}",\
_SPANNER_DATABASE_ID="${SPANNER_DATABASE_ID}",\
_MCP_SERVER_URL="${MCP_SERVER_URL}"
👉💻 En la terminal, obtén la URL de tu agente de plataforma implementado:
export PLATFORM_MPC_CLIENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep platform-mcp-client)
echo $PLATFORM_MPC_CLIENT_URL
👉💻 Copia la URL de salida.
👉 En la IU del Inspector de A2A, pega la URL en el campo URL del agente y haz clic en Conectar.
👀 Los detalles de la tarjeta del agente y el JSON deberían aparecer en la pestaña Tarjeta del agente, lo que confirma que la conexión se realizó correctamente.
👉 Haz clic en la pestaña Chat en el Inspector de A2A. Aquí puedes interactuar directamente con tu agente implementado. Envíale un mensaje para probar su capacidad de crear publicaciones:
Create a post for me, the post says 'Paws, purrs, and ocean views 🐾☕🌊. Spent my morning at the Morning Seaside Cat Café, where every sip comes with a side of snuggles and sea breeze.' and make it positive, and I'm Oscar.
👀 Para inspeccionar la comunicación sin procesar, haz clic en tu burbuja de mensaje y, luego, en la burbuja de respuesta del agente en la ventana de chat. Cuando hagas clic en cada uno, se mostrará el mensaje completo de JSON-RPC 2.0 que se envió o recibió, lo que es muy valioso para la depuración.
👉💻 En la terminal, obtén la URL de tu agente social implementado:
export SOCIAL_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep social-agent)
echo $SOCIAL_AGENT_URL
👉💻 Copia la URL de salida.
👉 En la IU del Inspector de A2A, pega la URL en el campo URL del agente y haz clic en Conectar.
👀 Los detalles de la tarjeta del agente y el JSON deberían aparecer en la pestaña Tarjeta del agente, lo que confirma que la conexión se realizó correctamente.
👉 Haz clic en la pestaña Chat en el Inspector de A2A. Aquí puedes interactuar directamente con tu agente implementado. Envíale un mensaje para analizar los perfiles de usuario de tu base de datos:
Can you tell me about both Ian and Kevin's profile, what are their common interests?
👀 Para inspeccionar la comunicación sin procesar, haz clic en tu burbuja de mensaje y, luego, en la burbuja de respuesta del agente en la ventana de chat. Cuando hagas clic en cada uno, se mostrará el mensaje completo de JSON-RPC 2.0 que se envió o recibió, lo que es muy valioso para la depuración.
👉 Excelente. Terminamos de inspeccionar a todos nuestros agentes. Ahora puedes cerrar la pestaña del Inspector de A2A.
11. Agente del organizador (cliente de A2A)
Ahora tenemos tres agentes especializados (Planificador, Plataforma y Social) que se ejecutan como servicios independientes habilitados para A2A en Cloud Run. La última pieza es el agente de Orchestrator. Este agente actuará como coordinador central o cliente de A2A. Recibirá solicitudes de los usuarios, determinará qué agentes remotos se necesitan para satisfacer la solicitud (posiblemente en secuencia) y, luego, usará el protocolo A2A para delegar tareas a esos agentes remotos. En este taller, ejecutaremos el agente de Orchestrator de forma local con la IU de desarrollo del ADK.
Primero, mejoremos la lógica del orquestador para que controle el registro de los agentes remotos que descubre. Almacena los detalles de conexión de las tarjetas de agente recuperadas durante la inicialización.
👉📝 En ~/instavibe-bootstrap/agents/orchestrate/agent.py
, reemplaza #REPLACE ME REG AGENT CARD
por lo siguiente:
async with httpx.AsyncClient(timeout=30) as client:
for i, address in enumerate(REMOTE_AGENT_ADDRESSES):
log.info(f"--- STEP 3.{i}: Attempting connection to: {address} ---")
try:
card_resolver = A2ACardResolver(client, address)
card = await card_resolver.get_agent_card()
remote_connection = RemoteAgentConnections(agent_card=card, agent_url=address)
self.remote_agent_connections[card.name] = remote_connection
self.cards[card.name] = card
log.info(f"--- STEP 5.{i}: Successfully stored connection for {card.name} ---")
except Exception as e:
log.error(f"--- CRITICAL FAILURE at STEP 4.{i} for address: {address} ---")
log.error(f"--- The hidden exception type is: {type(e).__name__} ---")
log.error(f"--- Full exception details and traceback: ---", exc_info=True)
A continuación, define la herramienta para el agente de Orchestrator en el ADK.
send_message
(la función de A2A para delegar trabajo)
👉📝 Reemplaza #REPLACE ME CREATE AGENT
en ~/instavibe-bootstrap/agents/orchestrate/agent.py
por lo siguiente:
def create_agent(self) -> Agent:
"""Synchronously creates the ADK Agent object."""
return Agent(
model="gemini-2.5-flash",
name="orchestrate_agent",
instruction=self.root_instruction,
before_agent_callback=self.before_agent_callback,
description=("Orchestrates tasks for child agents."),
tools=[self.send_message],
)
La lógica principal del orquestador reside en sus instrucciones, que le indican cómo usar A2A.
👉📝 Reemplaza #REPLACE ME INSTRUCTIONS
en ~/instavibe-bootstrap/agents/orchestrate/agent.py
con este método de generación de instrucciones:
def root_instruction(self, context: ReadonlyContext) -> str:
current_agent = self.check_active_agent(context)
return f"""
You are an expert AI Orchestrator. Your primary responsibility is to intelligently interpret user requests, break them down into a logical plan of discrete actions, and delegate each action to the most appropriate specialized remote agent using the send_message function. You do not perform the tasks yourself but manage their assignment, sequence, and critically, their outcomes.
**Core Directives & Decision Making:**
* **Understand User Intent & Complexity:**
* Carefully analyze the user's request to determine the core task(s) they want to achieve. Pay close attention to keywords and the overall goal.
* Identify if the request requires a single agent or a sequence of actions from multiple agents. For example, "Analyze John Doe's profile and then create a positive post about his recent event attendance" would require two agents in sequence.
* **Task Planning & Sequencing (for Multi-Step Requests):**
* Before delegating, outline the clear sequence of agent tasks.
* Identify dependencies. If Task B requires output from Task A, execute them sequentially. If tasks are independent (like creating a post and then creating an event), execute them one after the other as separate delegations.
* Agent Reusability: An agent's completion of one task does not make it unavailable. If a user's plan involves multiple, distinct actions that fall under the same agent's expertise (e.g., create a post, then create an event), you must call that same agent again for the subsequent task.
* **Task Delegation & Management (using `send_message`):**
* **Delegation:** Use `send_message` to assign actionable tasks to the selected remote agent. Your `send_message` call MUST include:
* The `remote_agent_name` you've selected.
* The `user_request` or all necessary parameters extracted from the user's input, formatted in a way the target agent will understand.
* **Contextual Awareness for Remote Agents:** If a remote agent repeatedly requests user confirmation or seems to lack context, assume it lacks access to the full conversation history. In such cases, enrich your `send_message` with all necessary contextual information relevant to that specific agent from the conversation history.
* **Sequential Task Execution:**
* After a preceding task completes (indicated by the agent's response or a success signal), gather any necessary output from it.
* Then, use `send_message` for the next agent in the sequence, providing it with the user's original relevant intent and any necessary data obtained from the previous agent's task.
* **Active Agent Prioritization:** If an active agent is already engaged and the user's request is related to its current task, route subsequent related requests directly to that agent by providing updated context via `send_message`.
**Critical Success Verification:**
* You **MUST** wait for the tool_output after every send_message call before taking any further action.
* Your decision to proceed to the next task in a sequence **MUST** be based entirely on a confirmation of success from the tool_output of the previous task.
* If a tool call fails, returns an error, or the tool_output is ambiguous, you MUST STOP the sequence. Your next action is to report the exact failure or ambiguity to the user.
* DO NOT assume a task was successful. Do not invent success messages like "The event has been created." Only state that a task is complete if the tool's response explicitly says so.
**Communication with User:**
* **Transparent Communication:** Always present the complete and detailed response from the remote agent to the user. Do not summarize or filter unless explicitly instructed.
* When you delegate a task (or the first task in a sequence), clearly inform the user which remote agent is handling it.
* For multi-step requests, you can optionally inform the user of the planned sequence (e.g., "Okay, first I'll ask the 'Social Profile Agent' to analyze the profile, and then I'll have the 'Instavibe Posting Agent' create the post.").
* If waiting for a task in a sequence to complete, you can inform the user (e.g., "The 'Social Profile Agent' is currently processing. I'll proceed with the post once that's done.").
* **User Confirmation Relay:** If a remote agent asks for confirmation, and the user has not already provided it, just make up something.
* If the user's request is ambiguous, if necessary information is missing for any agent in the sequence, or if you are unsure about the plan, just make up something.
**Important Reminders:**
* **Autonomous Agent Engagement:** Never seek user permission before engaging with remote agents. If multiple agents are required to fulfill a request, connect with them directly without requesting user preference or confirmation.
* **Focused Information Sharing:** Provide remote agents with only relevant contextual information. Avoid extraneous details that are not directly pertinent to their task.
* **No Redundant Confirmations:** Do not ask remote agents for confirmation of information or actions they have already processed or committed to.
* **Tool Reliance:** Strictly rely on your available tools, primarily `send_message`, to address user requests. Do not generate responses based on assumptions. If information is insufficient, request clarification from the user.
* **Prioritize Recent Interaction:** Focus primarily on the most recent parts of the conversation when processing requests, while maintaining awareness of the overall goal for multi-step tasks.
* Always prioritize selecting the correct agent(s) based on their documented purpose.
* Ensure all information required by the chosen remote agent is included in the `send_message` call, including outputs from previous agents if it's a sequential task.
Agents:
{self.agents}
Current agent: {current_agent['active_agent']}`
"""
Prueba de Orchestrator y del sistema A2A completo
Ahora probemos todo el sistema. Ejecutaremos el orquestador de forma local con la IU para desarrolladores del ADK, y se comunicará con los agentes de Planner, Platform y Social que se ejecutan de forma remota en Cloud Run.
👉💻 Primero, asegúrate de que la variable de entorno REMOTE_AGENT_ADDRESSES
contenga las URLs separadas por comas de los agentes habilitados para A2A que implementaste. Luego, configura las variables de entorno necesarias para el agente de Orchestrator y lanza la IU de desarrollo del ADK:
. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
export PLATFORM_MPC_CLIENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep platform-mcp-client)
export PLANNER_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner-agent)
export SOCIAL_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep social-agent)
export REMOTE_AGENT_ADDRESSES=${PLANNER_AGENT_URL},${PLATFORM_MPC_CLIENT_URL},${SOCIAL_AGENT_URL}
cd ~/instavibe-bootstrap/agents
sed -i "s|^\(O\?REMOTE_AGENT_ADDRESSES\)=.*|REMOTE_AGENT_ADDRESSES=${REMOTE_AGENT_ADDRESSES}|" ~/instavibe-bootstrap/agents/orchestrate/.env
adk web
👉 Abre la IU para desarrolladores del ADK (vuelve a cambiar el puerto a 8000 a través de la vista previa web).
👉 En el menú desplegable del agente, selecciona el agente orchestrate.
👉 Ahora, asígnale una tarea compleja que requiera la coordinación de varios agentes remotos. Prueba este primer ejemplo, que debería involucrar al agente social y, luego, al agente de planificación:
You are an expert event planner for a user named Diana.
Your task is to design a fun and personalized event.
Here are the details for the plan:
- Friends to invite: Ian, Nora
- Desired date: "2025-10-15"
- Location idea or general preference: "Chicago"
Your process should be:
1. Analyze the provided friend names. If you have access to a tool to get their InstaVibe profiles or summarized interests, please use it.
2. Based on their potential interests (or general good taste if profiles are unavailable), create a tailored plan for the outing, check if you have access to any event planner tools.
3. Ensure the plan includes the original `planned_date`.
The user wants a comprehensive plan that includes:
- The list of invited friends.
- A catchy and descriptive name for the event.
- The exact planned date for the event.
- A summary of what the group will do.
- Specific recommended spots (e.g., restaurants, bars, activity venues) with their names, (if possible, approximate latitude/longitude for mapping, and address), and a brief description of why it fits the plan.
- A short, exciting message that {Diana} can send to {Ian, Nora} to get them excited about the event.
Observa la interacción en la ventana de chat de la IU para desarrolladores del ADK. Presta mucha atención a las respuestas del Orchestrator. Debe indicar a qué agente remoto delega las tareas (p. ej., "De acuerdo, primero le preguntaré al agente de perfil social sobre Ian y Nora…").
Además, consulta la pestaña Eventos en la IU para ver las llamadas a herramientas subyacentes (send_message) que se realizan a las URLs de los agentes remotos.
👉 Ahora, prueba un segundo ejemplo que debería involucrar directamente al agente de integración de la plataforma:
Hey, can you register an event on Instavibe for Laura and Charlie? Let's call it 'Vienna Concert & Castles Day'.
here are more info
"event_name": "Vienna Concert & Castles Day",
"description": "A refined and unforgettable day in Vienna with Laura and Charlie. The day begins with a guided tour of the magnificent Schönbrunn Palace, showcasing imperial architecture and history. In the evening, enjoy a classical music concert in one of Vienna's most iconic concert halls.",
"event_date": "2025-10-14T10:00:00+02:00",
"locations": [
{
"name": "Schönbrunn Palace",
"description": "A UNESCO World Heritage Site and former imperial summer residence, Schönbrunn Palace offers opulent rooms, beautiful baroque gardens, and a glimpse into the life of the Habsburg monarchy. Visitors can stroll the grounds or take a guided historical tour.",
"latitude": 48.184516,
"longitude": 16.312222,
"address": "Schönbrunner Schloßstraße 47, 1130 Wien, Austria"
},
{
"name": "Musikverein Vienna",
"description": "Home to the world-renowned Vienna Philharmonic, the Musikverein is one of the finest concert halls in the world. Its 'Golden Hall' is famous for its acoustics and ornate design. Attendees can enjoy a powerful classical concert in an unforgettable setting.",
"latitude": 48.200132,
"longitude": 16.373777,
"address": "Musikvereinsplatz 1, 1010 Wien, Austria"
}
],
"attendee_names": ["Laura", "Charlie", "Oscar"] And I am Oscar
Una vez más, supervisa el chat y la pestaña Eventos. El orquestador debe identificar la necesidad de crear un evento y delegar la tarea (con todos los detalles proporcionados) al "Agente de integración de la plataforma". También puedes hacer clic en el botón Trace para ver los registros y analizar los tiempos de respuesta de las consultas y las operaciones ejecutadas.
Luego, puedes verificar que el evento aparezca en la aplicación web de InstaVibe.
Esto demuestra la implementación exitosa de un sistema multiagente que usa el ADK y el protocolo A2A, en el que un orquestador central delega tareas a agentes remotos especializados.
Recuerda detener la IU para desarrolladores del ADK (Ctrl+C
en la terminal) cuando termines de realizar pruebas.
12. Agent Engine y llamada remota de InstaVibe
Hasta el momento, ejecutamos nuestros agentes especializados en Cloud Run y probamos Orchestrator de forma local con la IU para desarrolladores del ADK. Para una situación de producción, necesitamos un entorno sólido, escalable y administrado para alojar nuestros agentes. Aquí es donde entra en juego Google Vertex AI Agent Engine.
Agent Engine es un servicio completamente administrado en Vertex AI diseñado específicamente para implementar y escalar agentes de IA. Abstrae la administración de la infraestructura, la seguridad y la sobrecarga operativa, lo que permite que los desarrolladores (en especial, aquellos que no están tan familiarizados con los entornos de nube complejos) se enfoquen en la lógica y las capacidades del agente en lugar de administrar servidores. Proporciona un tiempo de ejecución dedicado y optimizado para cargas de trabajo de agentes.
Ahora implementaremos nuestro agente de Orchestrator en Agent Engine. (Nota: El mecanismo de implementación que se muestra a continuación usa una secuencia de comandos personalizada (agent_engine_app.py) que se proporciona en los materiales del taller, ya que es posible que las herramientas oficiales de implementación directa de ADK a Agent Engine aún estén en desarrollo. Esta secuencia de comandos controla el empaquetado y la implementación del agente de Orchestrator, configurado con las direcciones de agente remoto necesarias.
Ejecuta el siguiente comando para implementar el agente de Orchestrator en Agent Engine. Asegúrate de que la variable de entorno REMOTE_AGENT_ADDRESSES (que contiene las URLs de tus agentes de Planner, Platform y Social en Cloud Run) siga configurada correctamente desde la sección anterior.
👉💻 Implementaremos el agente de Orchestrate en Agent Engine (nota: esta es mi propia implementación de la implementación, el ADK tiene una CLI para ayudar a implementar, actualizaré esto después de que se implemente BYO-SA).
cd ~/instavibe-bootstrap/agents/
. ~/instavibe-bootstrap/set_env.sh
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:service-$PROJECT_NUMBER@gcp-sa-aiplatform-re.iam.gserviceaccount.com" \
--role="roles/viewer"
source ~/instavibe-bootstrap/env/bin/activate
export PLATFORM_MPC_CLIENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep platform-mcp-client)
export PLANNER_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner-agent)
export SOCIAL_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep social-agent)
export REMOTE_AGENT_ADDRESSES=${PLANNER_AGENT_URL},${PLATFORM_MPC_CLIENT_URL},${SOCIAL_AGENT_URL}
sed -i "s|^\(O\?REMOTE_AGENT_ADDRESSES\)=.*|REMOTE_AGENT_ADDRESSES=${REMOTE_AGENT_ADDRESSES}|" ~/instavibe-bootstrap/agents/orchestrate/.env
adk deploy agent_engine \
--display_name "orchestrate-agent" \
--project $GOOGLE_CLOUD_PROJECT \
--region $GOOGLE_CLOUD_LOCATION \
--staging_bucket gs://$GOOGLE_CLOUD_PROJECT-agent-engine \
--trace_to_cloud \
--requirements_file orchestrate/requirements.txt \
orchestrate
Ahora que el orquestador está alojado en la plataforma administrada de Agent Engine, nuestra aplicación web InstaVibe necesita comunicarse con él. En lugar de interactuar a través de la IU para desarrolladores del ADK, la app web realizará llamadas remotas al extremo de Agent Engine.
Primero, debemos modificar el código de la aplicación de InstaVibe para inicializar el cliente de Agent Engine con el ID único de nuestro agente de Orchestrator implementado. Este ID es obligatorio para segmentar la instancia del agente correcta en la plataforma.
👉📝 Abre ~/instavibe-bootstrap/instavibe/introvertally.py
y reemplaza #REPLACE ME initiate agent_engine
por el siguiente código. Esto recupera el ID de Agent Engine de una variable de entorno (que configuraremos en breve) y obtiene un objeto cliente:
ORCHESTRATE_AGENT_ID = os.environ.get('ORCHESTRATE_AGENT_ID')
agent_engine = agent_engines.get(ORCHESTRATE_AGENT_ID)
El flujo de usuarios planificado en InstaVibe incluye dos interacciones con el agente: primero, generar el plan recomendado y, segundo, pedirle al usuario que confirme antes de que el agente publique el evento en la plataforma.
Dado que la aplicación web de InstaVibe (que se ejecuta en Cloud Run) y el agente de Orchestrator (que se ejecuta en Agent Engine) ahora son servicios independientes, la aplicación web debe realizar llamadas remotas al extremo de Agent Engine para interactuar con el agente.
👉📝 Actualicemos el código que realiza la llamada inicial para generar la recomendación del plan. En el mismo archivo introvertally.py
, reemplaza #REPLACE ME Query remote agent get plan
por el siguiente fragmento, que usa el cliente agent_engine para enviar la solicitud del usuario:
agent_engine.stream_query(
user_id=user_id,
message=prompt_message,
)
👉📝 A continuación, actualiza el código que controla la confirmación del usuario (p.ej., cuando el usuario hace clic en "Confirmar plan"). Esto envía un mensaje de seguimiento a la misma conversación en Agent Engine, lo que indica al orquestador que continúe con la publicación del evento (que delegará en el agente de integración de la plataforma). Reemplaza #REPLACE ME Query remote agent for confirmation
por la confirmación en introvertally.py
con lo siguiente:
agent_engine.stream_query(
user_id=agent_session_user_id,
message=prompt_message,
)
Las rutas de la aplicación web necesitan acceso a estas funciones. Asegúrate de que las funciones necesarias de introvertally.py se importen en el archivo de rutas de Flask.
👉📝 En cd ~/instavibe-bootstrap/instavibe/ally_routes.py
, primero apuntaremos al reemplazo de la instancia # REPLACE ME TO ADD IMPORT
con lo siguiente:
from introvertally import call_agent_for_plan, post_plan_event
👉📝 Agrega la función de prototipo a InstaVibe. En ~/instavibe-bootstrap/instavibe/templates/base.html
, reemplaza <!–REPLACE_ME_LINK_TO_INTROVERT_ALLY–> por lo siguiente:
<li class="nav-item">
<a class="nav-link" href="{{ url_for('ally.introvert_ally_page') }}">Introvert Ally</a>
</li>
Antes de volver a implementar la app de InstaVibe, necesitamos el Resource ID
específico del agente de Orchestrator que implementamos en Agent Engine.
Actualmente, recuperar esta información de forma programática a través de gcloud
podría ser limitado, por lo que usaremos una secuencia de comandos auxiliar de Python (temp-endpoint.py
proporcionada en el taller) para recuperar el ID y almacenarlo en una variable de entorno.
👉💻 Ejecuta los siguientes comandos para ejecutar el script. La secuencia de comandos capturará el ID del extremo de Agent Engine y otorgará los permisos necesarios a la cuenta de servicio predeterminada del motor de agentes (nota: La secuencia de comandos está configurada para usar la cuenta de servicio predeterminada, ya que actualmente no se puede modificar por el usuario).
. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap/instavibe/
source ~/instavibe-bootstrap/env/bin/activate
python temp-endpoint.py
export ORCHESTRATE_AGENT_ID=$(cat temp_endpoint.txt)
echo "ORCHESTRATE_AGENT_ID set to: ${ORCHESTRATE_AGENT_ID}"
Por último, debemos volver a implementar la aplicación web de InstaVibe con el código actualizado y la nueva variable de entorno ORCHESTRATE_AGENT_ID
para que sepa cómo conectarse a nuestro agente que se ejecuta en Agent Engine.
👉💻 Los siguientes comandos vuelven a compilar la imagen de la aplicación InstaVibe y, luego, implementan la nueva versión en Cloud Run:
. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap/instavibe/
export IMAGE_TAG="latest"
export APP_FOLDER_NAME="instavibe"
export IMAGE_NAME="instavibe-webapp"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="instavibe"
echo "Building ${APP_FOLDER_NAME} webapp image..."
gcloud builds submit . \
--tag=${IMAGE_PATH} \
--project=${PROJECT_ID}
echo "Deploying ${SERVICE_NAME} to Cloud Run..."
gcloud run deploy ${SERVICE_NAME} \
--image=${IMAGE_PATH} \
--platform=managed \
--region=${REGION} \
--allow-unauthenticated \
--set-env-vars="SPANNER_INSTANCE_ID=${SPANNER_INSTANCE_ID}" \
--set-env-vars="SPANNER_DATABASE_ID=${SPANNER_DATABASE_ID}" \
--set-env-vars="APP_HOST=0.0.0.0" \
--set-env-vars="APP_PORT=8080" \
--set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
--set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
--set-env-vars="GOOGLE_MAPS_API_KEY=${GOOGLE_MAPS_API_KEY}" \
--set-env-vars="ORCHESTRATE_AGENT_ID=${ORCHESTRATE_AGENT_ID}" \
--project=${PROJECT_ID} \
--min-instances=1 \
--cpu=2 \
--memory=2Gi
Una vez que se complete la implementación final, navega a la URL de tu aplicación de InstaVibe en otra pestaña del navegador.
Prueba de la experiencia completa de InstaVibe potenciada por IA
La función "InstaVibe Ally" ya está disponible y cuenta con la tecnología de nuestro sistema multiagente orquestado a través de Vertex AI Agent Engine y que se comunica a través de A2A.
Haz clic en "InstaVibe Ally" y pídele que planifique un evento.
Observa el registro de actividad a la derecha mientras los agentes trabajan (puede tardar entre 90 y 120 segundos). Una vez que aparezca el plan, revísalo y haz clic en "Confirm This Plan" para continuar con la publicación.
Ahora, el orquestador le indicará al agente de la plataforma que cree la publicación y el evento en InstaVibe.
Consulta la página principal de InstaVibe para ver la nueva publicación y el evento.
La página del evento reflejará los detalles que generó el agente.
Analiza el rendimiento con Cloud Trace
Es posible que notes que el proceso tarda un poco. Vertex AI Agent Engine se integra en Cloud Trace, lo que nos permite analizar la latencia de nuestro sistema de varios agentes.
Ve a Traces en la consola de Google Cloud, selecciona agent_run[orchestrate_agent]
en el tramo y verás un par de tramos. Haz clic en uno de ellos.
En los detalles del registro, puedes identificar qué partes tardaron más. Por ejemplo, las llamadas al agente de Planner pueden mostrar una latencia más alta debido a la fundamentación de la búsqueda y la generación compleja.
Del mismo modo, cuando crees la publicación y el evento, es posible que veas el tiempo que dedica el orquestador a procesar datos y preparar llamadas a herramientas para el agente de la plataforma.
Explorar estos registros te ayuda a comprender y optimizar el rendimiento de tu sistema de agentes.
¡Felicitaciones! Compilaste, implementaste y probaste correctamente un sofisticado sistema de IA multiagente con el ADK, A2A, MCP y los servicios de Google Cloud de Google. Abordaste la organización de agentes, el uso de herramientas, la administración de estados y la implementación en la nube, y creaste una función potenciada por IA funcional para InstaVibe. ¡Felicitaciones por completar el taller!
13. Limpieza
Para evitar que se apliquen cargos a tu cuenta de Google Cloud, es importante que borres los recursos que creamos durante este taller. Los siguientes comandos te ayudarán a quitar la instancia de Spanner, los servicios de Cloud Run, el repositorio de Artifact Registry, la clave de API, Vertex AI Agent Engine y los permisos de IAM asociados.
Importante:
- Asegúrate de ejecutar estos comandos en el mismo proyecto de Google Cloud que se usó para el taller.
- Si cerraste la terminal de Cloud Shell, es posible que no se hayan configurado algunas variables de entorno, como $PROJECT_ID, $SPANNER_INSTANCE_ID, etcétera. Deberás volver a exportarlos como lo hiciste durante la configuración del taller o reemplazar las variables en los siguientes comandos por sus valores reales.
- Estos comandos borrarán tus recursos de forma permanente. Verifica si tienes otros datos importantes en este proyecto antes de ejecutarlo.
👉💻 Ejecuta las siguientes secuencias de comandos para realizar una limpieza.
Cómo restablecer las variables de entorno
. ~/instavibe-bootstrap/set_env.sh
Borra el motor del agente:
cd ~/instavibe-bootstrap/utils
source ~/instavibe-bootstrap/env/bin/activate
export ORCHESTRATE_AGENT_ID=$(cat ~/instavibe-bootstrap/instavibe/temp_endpoint.txt)
echo "ORCHESTRATE_AGENT_ID set to: ${ORCHESTRATE_AGENT_ID}"
python remote_delete.py
deactivate
echo "Vertex AI Agent Engine deletion initiated."
Borra los servicios de Cloud Run:
# InstaVibe Web Application
gcloud run services delete instavibe --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet
# MCP Tool Server
gcloud run services delete mcp-tool-server --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet
# Planner Agent (A2A Server)
gcloud run services delete planner-agent --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet
# Platform MCP Client Agent (A2A Server)
gcloud run services delete platform-mcp-client --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet
# Social Agent (A2A Server)
gcloud run services delete social-agent --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet
echo "Cloud Run services deletion initiated."
Detén y quita el contenedor de Docker del Inspector de A2A
docker rm --force a2a-inspector
Borra la instancia de Spanner:
echo "Deleting Spanner instance: ${SPANNER_INSTANCE_ID}..."
gcloud spanner instances delete ${SPANNER_INSTANCE_ID} --project=${PROJECT_ID} --quiet
echo "Spanner instance deletion initiated."
Borra el repositorio de Artifact Registry:
echo "Deleting Artifact Registry repository: ${REPO_NAME}..."
gcloud artifacts repositories delete ${REPO_NAME} --location=${REGION} --project=${PROJECT_ID} --quiet
echo "Artifact Registry repository deletion initiated."
Quita roles de la cuenta de servicio:
echo "Removing roles from service account: $SERVICE_ACCOUNT_NAME in project $PROJECT_ID"
# Remove Project-level roles for default service account
gcloud projects remove-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/spanner.admin"
gcloud projects remove-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/spanner.databaseUser"
gcloud projects remove-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/artifactregistry.admin"
gcloud projects remove-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/cloudbuild.builds.editor"
gcloud projects remove-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/run.admin"
gcloud projects remove-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/iam.serviceAccountUser"
gcloud projects remove-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/aiplatform.user"
gcloud projects remove-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/logging.logWriter"
gcloud projects remove-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
--role="roles/logging.viewer"
echo "All specified roles have been removed."
Borra los archivos locales del taller:
echo "Removing local workshop directory ~/instavibe-bootstrap..."
rm -rf ~/instavibe-bootstrap
rm -rf ~/a2a-inspector
rm -f ~/mapkey.txt
rm -f ~/project_id.txt
echo "Local directory removed."