Ir al contenido

GUION v4 — 7 Agentes ADK en PRODUCCIÓN: Cloud Run + Protocolo A2A

GUION v4 — 7 Agentes ADK en PRODUCCIÓN: Cloud Run + Protocolo A2A

Sección titulada «GUION v4 — 7 Agentes ADK en PRODUCCIÓN: Cloud Run + Protocolo A2A»

“Del local a producción real: 7 agentes ADK como servidores A2A independientes en Cloud Run”

Duración objetivo: 70-80 minutos Patrón: Resultado primero → open loops → contexto serie → concepto (problema→solución) → refactor → setup GCP → deploy en vivo → cadena A2A → portal → casos reales → lecciones → cierre Fecha: 2026-04-05 (v4 — fusión guiones ADK + Skills + Cloud Run)


[PANTALLA: Split screen — 7 terminales con logs en tiempo real, cada una de un agente diferente en Cloud Run]

TÚ: “Lo que ves ahí son 7 agentes de IA corriendo en Google Cloud. No en local. No en Docker. Producción real.”

[CORTE A: Browser mostrando https://idp.nicolasneira.dev — portal con 7 agentes healthy]

TÚ: “Siete servicios independientes, cada uno con su propia URL, corriendo el protocolo A2A de Google. Y al final, un portal web en un dominio real mostrando el estado de todo el sistema.”

[TÍTULO: “7 Agentes ADK en PRODUCCIÓN — Cloud Run + Protocolo A2A”]


TÚ: “Tres cosas que vas a ver en este video:”

“Primero — el problema que nadie te muestra: por qué un sistema que funciona perfecto en local revienta en Cloud Run. Y no es un error de código — es un error de arquitectura.”

“Segundo — los siete deploys en vivo, uno por uno, con los tiempos reales. Y después vas a ver algo que nunca has visto: la cadena completa de 7 agentes respondiendo desde Cloud Run, en tiempo real, con los logs de cada servicio en pantalla.”

“Tercero — al final, un portal web en un dominio real. No localhost. Una URL que puedes abrir desde tu teléfono ahora mismo.”

[PAUSA]

“Y hay algo más que quiero mostrar: lo que Google recomienda en su documentación versus lo que realmente funciona en producción. Hay diferencias que no están documentadas en ningún lado.”


[MOSTRAR: Screenshots de los 3 videos anteriores del canal]

TÚ: “Contexto rápido para los que llegan nuevos. Este es el cuarto video de una serie sobre agentes de IA en producción.”

[MOSTRAR: Thumbnail video ADK — “7 agentes, 80 segundos, 21 archivos”]

“En el primero construí un IDP completo con 7 agentes ADK. Platform Architect, Infrastructure, Security, CI/CD, Observability, DevEx, Web Portal. 21 archivos generados en 80 segundos. Todo en local.”

[MOSTRAR: Thumbnail video Skills — “Agent Skills: 40 años de historia”]

“En el segundo hablé de Agent Skills — cómo controlar lo que un agente puede y no puede hacer. La diferencia entre instrucciones que el agente puede ignorar y capacidades que son imposibles de saltarse. Ese patrón tiene 40 años y viene de la robótica.”

[MOSTRAR: Thumbnail video Claude Code Teams]

“Y en el tercero construí una fábrica de skills con Claude Code Agent Teams — 6 agentes especializados que producen skills automáticamente.”

[PAUSA]

“Hoy toca lo que faltaba: llevar los 7 agentes ADK del primer video a producción real. Google Cloud Run, protocolo A2A, URLs públicas.”

[DIAGRAMA: Evolución — Local → Docker → Cloud Run]

“Si no viste los videos anteriores, no importa. Este se entiende solo. Pero si los viste, lo que viene es el paso lógico.”


CONCEPTO — POR QUÉ EL SISTEMA LOCAL NO ESCALA (6:00 - 18:00)

Sección titulada «CONCEPTO — POR QUÉ EL SISTEMA LOCAL NO ESCALA (6:00 - 18:00)»

El problema del filesystem compartido (6:00 - 9:00)

Sección titulada «El problema del filesystem compartido (6:00 - 9:00)»

TÚ: “El sistema del primer video funcionaba así.”

[DIAGRAMA: Un solo proceso, 7 agentes en secuencia, filesystem compartido]

“Un solo contenedor Docker. Los 7 agentes corriendo en secuencia. El Platform Architect escribía un YAML al disco. El Infrastructure lo leía del mismo disco. Y así en cascada.”

[MOSTRAR: Código — OUTPUT_DIR = os.getenv(‘ADK_OUTPUT_DIR’, ‘/app/outputs’)]

“Cada agente leía y escribía en este directorio. Funcionaba porque todos vivían en el mismo contenedor, compartiendo el mismo filesystem.”

[PAUSA DRAMÁTICA]

“Pero eso es como tener una empresa donde todos los departamentos comparten un solo escritorio. Funciona cuando son 3 personas. Cuando son 700, es un desastre.”

[DIAGRAMA: 7 contenedores independientes en Cloud Run, cada uno aislado]

“En Cloud Run, cada servicio es un contenedor independiente. Si el Platform Architect escribe en /app/outputs, el Infrastructure NO puede leer ese archivo. Está en otro contenedor. En otra máquina. Posiblemente en otra zona de disponibilidad.”

“Y sí — Cloud Run permite montar un filesystem compartido. GCS FUSE, Filestore NFS. Existen. Pero Google no los recomienda para comunicación entre servicios. El patrón correcto es que el estado viaje en los mensajes. El modelo mental cambia: de centralizado a distribuido.”

La solución — protocolo A2A (9:00 - 14:00)

Sección titulada «La solución — protocolo A2A (9:00 - 14:00)»

TÚ: “La solución es A2A — Agent-to-Agent. Un protocolo abierto bajo la Linux Foundation, creado originalmente por Google, diseñado para exactamente este problema.”

[DIAGRAMA — flujo A2A]

El orquestador (yo)
↓ le manda la tarea al primer agente
Platform Architect → toma la tarea, decide el stack, responde
↓ el orquestador toma esa respuesta y se la pasa al siguiente
Infrastructure → toma las decisiones del Platform Architect, genera el docker-compose, responde
↓ el orquestador toma todo lo anterior y se lo pasa al siguiente
Security → toma el contexto acumulado, analiza, responde
↓ ... y así hasta Web Portal

“El contexto fluye automáticamente de un agente al siguiente. Tú lanzas la tarea, y los agentes se la van pasando entre ellos hasta completar la cadena.”

“Más adelante les voy a mostrar la analogía completa con una constructora. Pero por ahora quédense con esto: cada agente tiene su propia URL, y se comunican por mensajes HTTP — nada más.”

Cada agente necesita tres cosas nuevas (14:00 - 18:00)

Sección titulada «Cada agente necesita tres cosas nuevas (14:00 - 18:00)»

TÚ: “Para que un agente ADK funcione como servidor A2A independiente en Cloud Run, necesita tres cosas que no tenía en local.”

[MOSTRAR: Estructura de carpetas — antes vs después]

[MOSTRAR: Dockerfile]

FROM python:3.13-slim
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
EXPOSE 8080
WORKDIR /app
COPY pyproject.toml uv.lock ./
RUN uv sync --no-dev --frozen
COPY . /app/platform_architect/
ENTRYPOINT ["uv", "run", "adk", "api_server", "--a2a", "--host", "0.0.0.0", "--port", "8080"]

“Tres decisiones que importan aquí. Primera: python:3.13-slim, NO Alpine. Alpine rompe con grpcio — una dependencia de ADK — porque usa musl libc en lugar de glibc. Eso no está en la documentación de ADK — lo encuentras en los issues de grpcio.”

“Segunda: uv en lugar de pip. uv es lo que Google recomienda en su propia documentación de Cloud Run para ADK. Lock file determinístico, builds más rápidos.”

“Tercera: el flag --a2a en el ENTRYPOINT. Sin ese flag, adk api_server solo expone la REST API nativa de ADK. Con --a2a habilita el protocolo A2A — los endpoints /a2a/{nombre} que otros agentes pueden descubrir y llamar.”

2. agent.json — la tarjeta de presentación A2A

Sección titulada «2. agent.json — la tarjeta de presentación A2A»

[MOSTRAR: agent.json]

{
"name": "Platform Architect Agent",
"description": "Analiza tareas y decide el stack tecnológico óptimo para un Internal Developer Platform (IDP).",
"url": "https://platform-architect-r7xydrdtta-uc.a.run.app/a2a/platform_architect",
"version": "1.0.0",
"capabilities": { "streaming": false, "pushNotifications": false },
"defaultInputModes": ["text/plain"],
"defaultOutputModes": ["text/plain"],
"skills": [{
"id": "platform-design",
"name": "Platform Design",
"description": "Diseña y persiste la configuración de plataforma: stack tecnológico, decisiones de arquitectura.",
"tags": ["idp", "platform", "architecture", "infrastructure"]
}]
}

“Este archivo es OBLIGATORIO para A2A. Si el flag --a2a está activo pero no hay agent.json, el servidor simplemente ignora el agente. Sin error, sin warning. Lo descubrí en el dry run — me costó una hora.”

“Noten skills — exactamente el patrón del video de Agent Skills. El agente publica lo que sabe hacer. Otros agentes leen esta tarjeta y deciden si delegarle trabajo.”

TÚ: “El cambio clave del refactor. Miren la firma de esta función en Infrastructure — el agente que lee las decisiones del Platform Architect:”

[MOSTRAR: repo original vs refactor]

# Antes (github.com/nneira/google-adk-a2a-idp — infrastructure/agent.py:69)
def get_platform_config() -> dict:
# Después (refactor A2A — infrastructure/agent.py:66)
def get_platform_config(context_json: str = "") -> dict:

“Un parámetro nuevo: context_json. Si llega con datos, parsea el contexto del mensaje A2A. Si llega vacío, lee del disco como antes — eso es el fallback. O sea, si lo corres en local sin A2A, sigue funcionando igual que antes. No rompe nada.”

“Este patrón se repite en los 6 agentes que leen contexto. El único que no lo necesita es Platform Architect — es el primero de la cadena, no tiene agente anterior del cual leer.”


APUNTE: adk deploy cloud_run vs lo que hacemos nosotros (18:00 - 20:00)

Sección titulada «APUNTE: adk deploy cloud_run vs lo que hacemos nosotros (18:00 - 20:00)»

TÚ: “Antes de entrar al setup de GCP, un apunte importante. Google ADK tiene un comando oficial para deployar:”

adk deploy cloud_run --project=mi-proyecto --region=us-central1 --a2a path/to/my_agent

“Lo probé. Funciona para un demo rápido. Pero tiene limitaciones.”

[MOSTRAR: Dockerfile generado por adk deploy cloud_run]

FROM python:3.11-slim
RUN pip install google-adk==1.28.0
COPY agents/platform_architect/ /app/agents/platform_architect/
CMD adk api_server --a2a --port=8000 /app/agents

“Python 3.11, pip sin lock file, el Dockerfile se genera en un folder temporal y se borra después del deploy. No tienes control del build, no puedes revisar qué se deployó.”

[MOSTRAR: Nuestro Dockerfile al lado]

“Nosotros usamos Python 3.13, uv con lock file, Dockerfile versionado en el repo. Que es lo que recomienda la propia documentación de Google Cloud Run — no la de ADK, la de Cloud Run.”

“Si quieres probar algo rápido, adk deploy cloud_run te sirve. Si vas a producción, arma tu propio Dockerfile.”


TÚ: “Ahora sí, el setup de GCP. Voy a mostrar cada paso — esto es lo que la mayoría de tutoriales omite o hace en background.”

[MOSTRAR: terminal]

NOTA PARA MÍ: Ir paso a paso, explicar cada comando antes de correrlo. Mostrar el browser cuando se abra para el login. Mostrar la lista de proyectos para que la audiencia vea cómo se elige uno.

gcloud auth login

“Me autentico con mi cuenta de Gmail — la misma donde tengo Google Cloud Console. Se abre el browser, elijo la cuenta, autorizo.”

gcloud projects list

“Listo los proyectos que tengo en esta cuenta. Mi recomendación: creen un proyecto nuevo para esto. Así tienen las APIs aisladas y no mezclan costos con otros servicios.”

gcloud config set project TU_PROJECT_ID

“Seteo el proyecto donde vamos a deployar los 7 agentes.”

gcloud config get-value project
gcloud config get-value account

“Verifico: proyecto correcto, cuenta correcta. Listo.”

TÚ: “GCP tiene todo deshabilitado por defecto. Necesito habilitar cuatro APIs.”

gcloud services enable \
run.googleapis.com \
cloudbuild.googleapis.com \
artifactregistry.googleapis.com \
secretmanager.googleapis.com

“Cloud Run para correr los servicios. Cloud Build para construir las imágenes Docker desde el Dockerfile. Artifact Registry para guardarlas. Secret Manager para las credenciales.”

Permisos del Service Account (22:30 - 24:30)

Sección titulada «Permisos del Service Account (22:30 - 24:30)»

TÚ: “En GCP, cuando deployás un servicio, Cloud Run usa un service account para ejecutar. El nombre sigue siempre este patrón:”

[MOSTRAR en pantalla]

{PROJECT_NUMBER}-compute@developer.gserviceaccount.com

“Noten: PROJECT_NUMBER, no PROJECT_ID. Son dos cosas distintas. Para service accounts se usa el número — el PROJECT_NUMBER.”

gcloud projects describe $(gcloud config get-value project) --format="table(projectId,projectNumber,name)"

“Ahí ven los dos: PROJECT_ID y PROJECT_NUMBER. Son distintos. Para service accounts se usa el PROJECT_NUMBER.”

PROJECT_ID=$(gcloud config get-value project)
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")
echo "PROJECT_ID: $PROJECT_ID"
echo "PROJECT_NUMBER: $PROJECT_NUMBER"

“Guardo los dos en variables porque los vamos a usar en los siguientes comandos.”

“Cuando habilitaste las APIs, Google creó automáticamente una cuenta de servicio por defecto. Es la que Cloud Run va a usar para correr los agentes. Se llama así:”

{PROJECT_NUMBER}-compute@developer.gserviceaccount.com

“Esa cuenta necesita dos permisos para que el deploy funcione.”

# Permiso para guardar las imágenes Docker construidas
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
--role="roles/storage.objectAdmin"
# Permiso para ejecutar builds
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
--role="roles/cloudbuild.builds.builder"

Gemini API Key en Secret Manager (24:30 - 28:00)

Sección titulada «Gemini API Key en Secret Manager (24:30 - 28:00)»

TÚ: “Los agentes usan Gemini como modelo. Necesitan una API key. La sacas de Google AI Studio — aistudio.google.com. Entras con tu misma cuenta de Gmail, vas a ‘Get API Key’, creas una key, y la copias.”

[MOSTRAR: browser → aistudio.google.com → Get API Key → Create API Key]

NOTA PARA MÍ: Mostrar el proceso en AI Studio. No mostrar la key en pantalla — taparla o cortarla en edición.

TÚ: “Y aquí viene algo importante.”

[PAUSA — mirar a cámara]

“Nunca pongas una API key en un Dockerfile. Nunca en el código. Nunca en una variable de entorno hardcodeada. Cloud Run tiene integración nativa con Secret Manager — úsala.”

echo -n "TU_GEMINI_API_KEY" | gcloud secrets create GEMINI_API_KEY --data-file=-

“Le doy permiso al service account para que pueda leer la API key. Sin esto, los agentes no pueden acceder al secret.”

gcloud secrets add-iam-policy-binding GEMINI_API_KEY \
--member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"

NOTA PARA MÍ: Para verificar que el secret se guardó bien:

gcloud secrets list
gcloud secrets versions access latest --secret=GEMINI_API_KEY

“En el deploy voy a usar --set-secrets para inyectar la key como variable de entorno. El contenedor nunca toca la key directamente — la lee de Secret Manager en runtime.”

TÚ: “Un detalle: Google también te permite usar Vertex AI en vez de API key. Con Vertex AI no necesitas secrets — el service account de Cloud Run se autentica directo con su identidad. Solo necesitas habilitar la API de Vertex AI y darle el rol aiplatform.user al service account. Para este video uso API key porque es más directo y el free tier de AI Studio es generoso.”


TÚ: “El comando de deploy. Lo explico antes de correrlo.”

NOTA PARA MÍ: Asegurarme de estar en la carpeta correcta antes de correr el deploy:

cd /Users/nicolasneiragarcia/NICOLASNEIRA/yt/videos/13-agentes-adk-a2a-cloud-run/scripts/proyecto-video-idp/

El --source es ruta relativa, si no estoy ahí, falla.

gcloud run deploy platform-architect \
--source=agents_adk/platform_architect/ \
--region=us-central1 \
--no-allow-unauthenticated \
--memory=1Gi \
--cpu=1 \
--min-instances=0 \
--max-instances=1 \
--cpu-throttling \
--timeout=300 \
--set-secrets="GEMINI_API_KEY=GEMINI_API_KEY:latest" \
--port=8080

--source apunta a la carpeta del agente — Cloud Build construye la imagen automáticamente desde el Dockerfile.”

--no-allow-unauthenticated — el servicio requiere un IAM identity token. Sin token, rechaza la conexión.”

--min-instances=0 — escala a cero cuando no hay tráfico. No pagas nada en idle. Cuando llega un request, Cloud Run levanta un contenedor en segundos.”

--cpu-throttling — la CPU solo se asigna durante requests. Más ahorro.”

“¿Ven este --set-secrets? El formato es VARIABLE=SECRETO:versión. La izquierda es el nombre de la variable de entorno que va a ver el contenedor. La derecha es el nombre del secret que guardamos en Secret Manager. Y latest significa ‘dame la versión más reciente’. O sea, Cloud Run va a Secret Manager, lee el valor, y se lo inyecta al contenedor cuando arranca. La API key nunca aparece en el comando.”

“Y un detalle: los nombres de servicios en Cloud Run solo aceptan letras minúsculas, números y guiones. Sin underscores. Por eso web_portal se deploya como web-portal.”

[DEPLOY 1 — platform-architect]

TÚ: “Platform Architect. El que decide el stack. Es el comando que acabo de explicar — lo corro.”

TÚ (mientras corre): “Validando la configuración… subiendo el código fuente… ahora Cloud Build está construyendo la imagen desde el Dockerfile… la sube a Artifact Registry… crea la revisión… rutea el tráfico… y configura los permisos IAM.”

[URL aparece]

TÚ: “Ahí está la URL. Ese es el endpoint del agente en Cloud Run. Todos los agentes van a seguir el mismo patrón — solo cambia el nombre del servicio.”


[DEPLOY 2 — infrastructure]

TÚ: “Infrastructure. Lee lo que decidió el Platform Architect y genera el docker-compose — servicios, healthchecks, networks, volumes.”

gcloud run deploy infrastructure \
--source=agents_adk/infrastructure/ \
--region=us-central1 \
--no-allow-unauthenticated \
--memory=1Gi --cpu=1 \
--min-instances=0 --max-instances=1 \
--cpu-throttling --timeout=300 \
--set-secrets="GEMINI_API_KEY=GEMINI_API_KEY:latest" \
--port=8080

[DEPLOY 3 — security]

TÚ: “Security. Escanea las configuraciones y genera un reporte de vulnerabilidades con recomendaciones.”

gcloud run deploy security \
--source=agents_adk/security/ \
--region=us-central1 \
--no-allow-unauthenticated \
--memory=1Gi --cpu=1 \
--min-instances=0 --max-instances=1 \
--cpu-throttling --timeout=300 \
--set-secrets="GEMINI_API_KEY=GEMINI_API_KEY:latest" \
--port=8080

[DEPLOY 4 — cicd]

TÚ: “CI/CD. Genera los scripts de build, test y deploy. Tres scripts bash ejecutables.”

gcloud run deploy cicd \
--source=agents_adk/cicd/ \
--region=us-central1 \
--no-allow-unauthenticated \
--memory=1Gi --cpu=1 \
--min-instances=0 --max-instances=1 \
--cpu-throttling --timeout=300 \
--set-secrets="GEMINI_API_KEY=GEMINI_API_KEY:latest" \
--port=8080

[DEPLOY 5 — observability]

TÚ: “Observability. Configura Prometheus, genera dashboards de Grafana con queries reales.”

gcloud run deploy observability \
--source=agents_adk/observability/ \
--region=us-central1 \
--no-allow-unauthenticated \
--memory=1Gi --cpu=1 \
--min-instances=0 --max-instances=1 \
--cpu-throttling --timeout=300 \
--set-secrets="GEMINI_API_KEY=GEMINI_API_KEY:latest" \
--port=8080

[DEPLOY 6 — devex]

TÚ: “DevEx. Genera un CLI tool — el comando idp con init, build, test, deploy, status, logs.”

gcloud run deploy devex \
--source=agents_adk/devex/ \
--region=us-central1 \
--no-allow-unauthenticated \
--memory=1Gi --cpu=1 \
--min-instances=0 --max-instances=1 \
--cpu-throttling --timeout=300 \
--set-secrets="GEMINI_API_KEY=GEMINI_API_KEY:latest" \
--port=8080

[DEPLOY 7 — web-portal]

TÚ: “Web Portal. El último. Genera un portal web completo con FastAPI + TailwindCSS. Y ojo — web_portal con underscore en el código se deploya como web-portal con guión.”

gcloud run deploy web-portal \
--source=agents_adk/web_portal/ \
--region=us-central1 \
--no-allow-unauthenticated \
--memory=1Gi --cpu=1 \
--min-instances=0 --max-instances=1 \
--cpu-throttling --timeout=300 \
--set-secrets="GEMINI_API_KEY=GEMINI_API_KEY:latest" \
--port=8080

TÚ: “Vamos a verificar que los siete están arriba.”

gcloud run services list --region=us-central1 --format='table(metadata.name, status.url)'

[7 URLs en pantalla]

platform-architect → https://platform-architect-r7xydrdtta-uc.a.run.app
infrastructure → https://infrastructure-r7xydrdtta-uc.a.run.app
security → https://security-r7xydrdtta-uc.a.run.app
cicd → https://cicd-r7xydrdtta-uc.a.run.app
observability → https://observability-r7xydrdtta-uc.a.run.app
devex → https://devex-r7xydrdtta-uc.a.run.app
web-portal → https://web-portal-r7xydrdtta-uc.a.run.app

TÚ: “Siete de siete. Todos online.”

TÚ: “Verifico que responden. Le pregunto a cada agente qué apps tiene — si responde 200, está vivo.”

TOKEN=$(gcloud auth print-identity-token)
for SERVICE in platform-architect infrastructure security cicd observability devex web-portal; do
echo -n "$SERVICE → "
curl -s -o /dev/null -w "%{http_code}" \
"https://${SERVICE}-r7xydrdtta-uc.a.run.app/list-apps" \
-H "Authorization: Bearer $TOKEN"
echo
done
platform-architect → 200
infrastructure → 200
security → 200
cicd → 200
observability → 200
devex → 200
web-portal → 200

“Siete de siete. Todos respondiendo 200. Ojo con el Authorization: Bearer $TOKEN — ese token es lo que les da permiso de hablar con los agentes. Lo generamos con gcloud auth print-identity-token. Sin ese token, Cloud Run rechaza la conexión con 403.”


PROBANDO AGENTES INDIVIDUALES (42:00 - 52:00)

Sección titulada «PROBANDO AGENTES INDIVIDUALES (42:00 - 52:00)»

TÚ: “Antes de correr la cadena completa, quiero mostrar algo importante. Voy a hablar con los agentes uno por uno, con tareas distintas, para que vean que no es un script hardcodeado — cada agente piensa y responde diferente.”

Test 1 — Platform Architect solo (42:00 - 44:30)

Sección titulada «Test 1 — Platform Architect solo (42:00 - 44:30)»

TÚ: “Empiezo con el Platform Architect. Le pido que diseñe un IDP, pero no para Python como siempre — esta vez para un stack completamente diferente.”

[MOSTRAR: terminal]

TOKEN=$(gcloud auth print-identity-token)
BASE_URL="https://platform-architect-r7xydrdtta-uc.a.run.app"
# 1. Crear una sesión
SESSION_ID=$(curl -s "$BASE_URL/apps/platform_architect/users/test/sessions" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{}' | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
echo "Session: $SESSION_ID"
# 2. Enviar la tarea
curl -s "$BASE_URL/run" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"app_name\": \"platform_architect\",
\"user_id\": \"test\",
\"session_id\": \"$SESSION_ID\",
\"new_message\": {
\"role\": \"user\",
\"parts\": [{\"text\": \"Build an IDP for a Python FastAPI application with PostgreSQL and Redis\"}]
}
}" | python3 -m json.tool

TÚ (narrando mientras espera): “Dos pasos. Primero creo una sesión — ADK necesita una sesión para mantener contexto. Después envío la tarea al endpoint /run. Esto es la REST API nativa de ADK, no A2A todavía.”

[RESPUESTA aparece en terminal]

TÚ: “Miren lo que responde. No solo eligió el stack — Python 3.11, FastAPI, PostgreSQL, Redis — sino que también decidió Prometheus+Grafana para monitoreo, Trivy para seguridad, Jenkins para CI/CD. Y llamó a save_platform_config automáticamente para guardar la decisión.”

“Eso es un agente con tools reales. No es un chatbot respondiendo texto — ejecutó una función que guarda un YAML y un JSON con la configuración completa.”

[MOSTRAR en pantalla: el tool call save_platform_config en la respuesta JSON]

Test 2 — Security SIN contexto A2A (44:30 - 47:00)

Sección titulada «Test 2 — Security SIN contexto A2A (44:30 - 47:00)»

TÚ: “Ahora algo interesante. Voy a llamar al agente de Security directamente, sin pasarle el contexto de los agentes anteriores. Como si alguien lo llamara solo, sin la cadena.”

BASE_URL="https://security-r7xydrdtta-uc.a.run.app"
SESSION_ID=$(curl -s "$BASE_URL/apps/security/users/test/sessions" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{}' | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
curl -s "$BASE_URL/run" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"app_name\": \"security\",
\"user_id\": \"test\",
\"session_id\": \"$SESSION_ID\",
\"new_message\": {
\"role\": \"user\",
\"parts\": [{\"text\": \"Analiza la seguridad de este stack: Node.js 20 con Express, PostgreSQL 16, Redis 7, Nginx como reverse proxy. Docker Compose en producción. Ejecuta el scan de seguridad completo.\"}]
}
}" | python3 -m json.tool

[RESPUESTA aparece — el agente se queja]

TÚ: “Miren lo que dice: ‘me encuentro con las manos atadas… las bases de nuestra cadena de agentes A2A no me han proporcionado los datos necesarios’. No pudo encontrar la configuración del Platform Architect ni las decisiones de Infrastructure.”

[PAUSA — mirar a cámara]

“Esto es exactamente el punto. El agente de Security está diseñado para trabajar en cadena. Necesita el contexto de los agentes anteriores — qué scanner eligió el Platform Architect, qué servicios configuró Infrastructure. Sin eso, no puede hacer su trabajo.”

“Es como pedirle al inspector de seguridad que revise un edificio sin darle los planos. Técnicamente puede mirar las paredes, pero no tiene la información completa para hacer un análisis real.”

Test 3 — CI/CD CON contexto manual (47:00 - 50:00)

Sección titulada «Test 3 — CI/CD CON contexto manual (47:00 - 50:00)»

TÚ: “Ahora lo contrario. Voy a llamar al agente de CI/CD pero esta vez le paso contexto manualmente — como si fuera un IDP para una fintech con Java y Spring Boot.”

BASE_URL="https://cicd-r7xydrdtta-uc.a.run.app"
SESSION_ID=$(curl -s "$BASE_URL/apps/cicd/users/test/sessions" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{}' | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
curl -s "$BASE_URL/run" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"app_name\": \"cicd\",
\"user_id\": \"test\",
\"session_id\": \"$SESSION_ID\",
\"new_message\": {
\"role\": \"user\",
\"parts\": [{\"text\": \"Contexto: IDP para una fintech. Stack: Java 21, Spring Boot 3.2, PostgreSQL 16, Redis, Kafka. CI/CD runner: GitHub Actions. El Infrastructure Agent ya generó el docker-compose con 5 servicios. Genera los scripts de CI/CD para build, test y deploy.\"}]
}
}" | python3 -m json.tool

[RESPUESTA aparece — CI/CD genera todo]

TÚ: “Cinco segundos. Generó tres cosas: un workflow de GitHub Actions completo, una app dummy para probar el pipeline, y los tres scripts locales — build, test, deploy. Todo adaptado a Java + Spring Boot.”

“Noten que le dije ‘fintech con Java’ — no Python. Y respondió acorde. No está hardcodeado. El agente entiende el contexto y adapta su output.”

[MOSTRAR en pantalla: los tool calls — generate_github_actions_workflow, generate_dummy_app, save_cicd_scripts]

TÚ: “¿Qué acabamos de ver?”

“Primero: cada agente es un servicio independiente con su propia URL. Puedes hablarle directo con un curl.”

“Segundo: algunos agentes funcionan solos — Platform Architect, CI/CD, DevEx. Otros necesitan el contexto de la cadena — Security, Web Portal.”

“Tercero: hacer esto a mano — crear sesión, copiar respuesta, pegarla en el siguiente agente — funciona pero no escala. Con 7 agentes serían 14 curls manuales, copiando JSON de uno a otro.”

[PAUSA]

“Para eso existe el orchestrator.”


TÚ: “Antes de correr el orchestrator, quiero que veas lo que pasa en cada servicio. Abro los logs de los 7 agentes en tiempo real.”

[ABRIR 7 TERMINALES en split screen]

NOTA PARA MÍ: Correr ./logs-all.sh — hace tail de los logs de los 7 agentes en una sola terminal, con prefijo de color por agente. Para cerrar: Ctrl+C.

./logs-all.sh

TÚ: “Silencio total. Los 7 agentes están en cero — no hay ningún contenedor corriendo. Escalan a cero cuando no los usan. Eso es Cloud Run. No hay servidor encendido, no hay costo. Hasta que alguien los llama.”


MOMENTO WOW — LA CADENA COMPLETA (54:00 - 64:00)

Sección titulada «MOMENTO WOW — LA CADENA COMPLETA (54:00 - 64:00)»

TÚ: “Este es el momento. El orchestrator.”

[MOSTRAR: orchestrator_a2a.py — la parte del pipeline]

for agent_name in AGENT_SEQUENCE:
message = build_message(agent_name, user_task, accumulated_context)
result = await send_task(session, agent_name, message)
# Acumular contexto para el siguiente agente
accumulated_context[agent_name] = {
"output": result["text"],
"tool_output": result.get("tool_output", {}),
}

“El orchestrator llama a cada agente en orden via HTTP. Lo clave: después de cada agente, acumula la respuesta como contexto y se la pasa al siguiente. Así el contexto fluye sin filesystem compartido — solo mensajes.”

TÚ: “Lo corro.”

NOTA PARA MÍ: Antes de correr el orchestrator, cargar las URLs de Cloud Run:

cd /Users/nicolasneiragarcia/NICOLASNEIRA/yt/videos/13-agentes-adk-a2a-cloud-run/scripts/proyecto-video-idp/
source ./set-urls.sh
cd agents_adk/platform_architect
PYTHONWARNINGS=ignore uv run python ../../orchestrator_a2a.py \
"Build an IDP for a Python FastAPI application with PostgreSQL and Redis"

PLOT TWIST — DE AI STUDIO A VERTEX AI (opcional, si sale 503)

Sección titulada «PLOT TWIST — DE AI STUDIO A VERTEX AI (opcional, si sale 503)»

[MOSTRAR: log con error 503 UNAVAILABLE - high demand]

TÚ: “Espera — ¿viste el 503? Esto es porque estamos usando el endpoint público de AI Studio, que comparte cuotas con millones de usuarios. En producción real nadie hace esto. Vamos a cambiar a Vertex AI, que usa el endpoint regional de nuestro proyecto GCP. Tres variables de entorno, sin tocar código.”

[MOSTRAR: logs con backend: GoogleLLMVariant.GEMINI_API]

“Mira el log: backend: GEMINI_API. Ese es AI Studio. Vamos a cambiarlo a VERTEX_AI.”

gcloud services enable aiplatform.googleapis.com

2. Dar permiso a la service account de Cloud Run

Sección titulada «2. Dar permiso a la service account de Cloud Run»
gcloud projects add-iam-policy-binding project-cbd42e2d-0a78-4c20-9df \
--member="serviceAccount:39303680563-compute@developer.gserviceaccount.com" \
--role="roles/aiplatform.user"

TÚ: “La service account por defecto de Cloud Run ahora puede invocar modelos de Vertex AI. Sin API keys — usa la identidad del propio servicio.”

3. Actualizar los 7 servicios (sin rebuild, solo env vars)

Sección titulada «3. Actualizar los 7 servicios (sin rebuild, solo env vars)»
for svc in platform-architect infrastructure security cicd observability devex web-portal; do
echo "→ Actualizando $svc..."
gcloud run services update $svc --region=us-central1 \
--update-env-vars="GOOGLE_GENAI_USE_VERTEXAI=TRUE,GOOGLE_CLOUD_PROJECT=project-cbd42e2d-0a78-4c20-9df,GOOGLE_CLOUD_LOCATION=us-central1"
done

TÚ: “Tres variables de entorno en cada servicio. GOOGLE_GENAI_USE_VERTEXAI=TRUE le dice a ADK que use Vertex AI en vez de AI Studio. GOOGLE_CLOUD_PROJECT y GOOGLE_CLOUD_LOCATION apuntan al endpoint regional de nuestro proyecto. El código de los agentes no cambia. Los Dockerfiles no cambian. Nada se rebuildea. Cloud Run crea una revisión nueva por servicio, en paralelo, sin downtime.”

for svc in platform-architect infrastructure security cicd observability devex web-portal; do
echo "=== $svc ==="
gcloud run services describe $svc --region=us-central1 \
--format="value(spec.template.spec.containers[0].env)" | tr ';' '\n' | grep GOOGLE
done

TÚ: “Los 7 servicios confirmados con las variables de Vertex AI. Ahora vuelvo a correr el orchestrator.”

cd agents_adk/platform_architect
PYTHONWARNINGS=ignore uv run python ../../orchestrator_a2a.py \
"Build an IDP for a Python FastAPI application with PostgreSQL and Redis"

[MOSTRAR: logs con backend: GoogleLLMVariant.VERTEX_AI]

TÚ: “Ahí lo tienes: backend: VERTEX_AI. Mismo modelo, mismo código, mismos contenedores — pero ahora el LLM vive en el endpoint regional de nuestro proyecto GCP, con cuota dedicada. Adiós 503.”

NOTA PARA MÍ: Diferencia clave a explicar:

  • AI Studio (generativelanguage.googleapis.com) → endpoint global, cuota compartida, free tier, ideal para prototipos. Sufre 503 UNAVAILABLE cuando hay demanda alta.
  • Vertex AI (us-central1-aiplatform.googleapis.com) → endpoint regional por proyecto, cuota dedicada, sin free tier pero los $300 de crédito GCP lo cubren sobradamente. Es lo que se usa en producción real.

[MOSTRAR: Los 7 terminales de logs encendiéndose uno por uno]

TÚ (narrando en tiempo real — segundo a segundo): “Mira los logs…”

“Platform Architect… cold start… Started server process… ahí está, decidiendo el stack…”

[LOG: platform-architect terminal se ilumina]

“Infrastructure… se enciende… leyendo el contexto del Platform Architect… generando docker-compose…”

[LOG: infrastructure terminal se ilumina]

“Security… CI/CD… Observability…”

[seguir narrando cada uno mientras los logs aparecen]

“DevEx… generando el CLI…”

“Y el último… Web Portal…”

[PAUSA cuando los 7 terminan]

TÚ: “Siete de siete. La cadena completa. Cada agente corrió en Cloud Run, respondió via A2A, y el contexto fluyó de uno al siguiente. Sin estado compartido entre servicios. Solo mensajes.”

[PAUSA]

“Pero hay algo más.”


TÚ: “Ahora quiero ser transparente con algo. Lo que voy a mostrar es un dashboard de monitoreo que yo construí. No es algo que los agentes generaron — es una interfaz que hace health checks en tiempo real a los 7 servicios en Cloud Run.”

“¿Por qué? Porque los agentes generan sus outputs en la sesión — los ves en la terminal, en los logs. Pero no los persisten en ningún lado todavía. Lo que sí puedo mostrarte es que los 7 están vivos, respondiendo, y el sistema funciona como un todo.”

[MOSTRAR: deploy del portal]

gcloud run deploy idp-portal \
--source=outputs/portal/ \
--region=us-central1 \
--allow-unauthenticated \
--memory=512Mi --cpu=1 \
--min-instances=0 --max-instances=1 \
--port=8080

“Este servicio es público — --allow-unauthenticated. Los agentes son privados, el portal es público. El portal hace los health checks desde su backend usando el metadata server de GCP — obtiene el IAM token automáticamente sin credenciales hardcodeadas.”

TÚ: “Y lo mapeo a un dominio real.”

gcloud beta run domain-mappings create \
--service=idp-portal \
--domain=idp.nicolasneira.dev \
--region=us-central1

[MOSTRAR: CNAME en Cloudflare]

idp CNAME ghs.googlehosted.com.

“Un CNAME en Cloudflare. Google provisiona el certificado SSL automáticamente.”

[ABRIR BROWSER: https://idp.nicolasneira.dev]

TÚ: “Ahí está. Un dominio real. SSL. Los 7 agentes con sus health checks en verde, en tiempo real. Esto es monitoreo — verifico que cada servicio responde y cuánto tarda.”

[PAUSA]

“En el primer video esto era localhost:8080. Ahora es idp.nicolasneira.dev. Y sí — falta persistir lo que los agentes generan. Para eso existe MCP, el Model Context Protocol. Pero eso viene en el próximo video. Hoy el logro es que los 7 agentes están en producción, con URLs reales, hablando A2A entre ellos.”


TÚ: “Antes del cierre, quiero que entiendas lo que acabamos de hacer con una analogía.”

“Imaginate una constructora. Tienes un arquitecto, un ingeniero estructural, un especialista en seguridad, un encargado de instalaciones eléctricas, uno de plomería, uno de terminaciones, y uno que entrega el proyecto final al cliente.”

[PAUSA]

“En una constructora chica, todos trabajan en la misma oficina. Se pasan los planos de mano en mano. Si el arquitecto cambia algo, le grita al ingeniero que está al lado. Eso es nuestro sistema local — un solo contenedor, filesystem compartido.”

[DIAGRAMA: Oficina chica → todos comparten escritorio]

“Pero cuando la constructora crece y tiene proyectos en 5 ciudades distintas, no puedes tener a todos en la misma oficina. El arquitecto está en Santiago, el ingeniero en Medellín, el de seguridad en Buenos Aires. Cada uno tiene su propia oficina, su propio teléfono, su propia dirección.”

[DIAGRAMA: Oficinas distribuidas → cada una con su dirección]

“¿Cómo se comunican? Con documentos formales. El arquitecto envía el plano por correo certificado. El ingeniero lo recibe, lo procesa, y envía su respuesta. Protocolo. Cada documento tiene un formato estándar que todos entienden.”

[PAUSA — mirar a cámara]

“Eso es exactamente lo que acabamos de hacer. Cada agente es una oficina independiente con su propia dirección URL. El protocolo A2A es el correo certificado — formato estándar, JSON sobre HTTPS. Y el orchestrator es la oficina central que coordina el flujo de documentos.”

“La gracia es que al ingeniero de Medellín no le importa si el arquitecto en Santiago usa AutoCAD o Revit. Solo le importa que el plano llegue en el formato acordado. A2A hace exactamente eso — el agente puede estar hecho en ADK, en LangChain, en CrewAI, lo que sea. Si habla A2A, funciona.”

TÚ: “Tres ejemplos concretos:”

Startup — 3 devs, cero DevOps. Corren el IDP: en 5 minutos tienen docker-compose, CI/CD, monitoreo. Lo que toma 2 semanas de un DevOps senior.”

Agencia con 15 clientes. Cada cliente con stack diferente. El Platform Architect genera un IDP distinto para cada uno. 15 clientes, 15 configuraciones, un solo sistema.”

Enterprise con 50+ microservicios. El IDP integrado en GitHub Actions. El developer llena 3 campos — lenguaje, framework, tipo de servicio — y recibe un PR con todo configurado. Self-service real.”

[MOSTRAR: ejemplo de GitHub Actions YAML]

on:
workflow_dispatch:
inputs:
language:
description: 'Runtime'
required: true
type: choice
options: [python, go, node]
framework:
description: 'Framework'
required: true

“El developer no necesita saber cómo se configura Prometheus. Solo pide lo que necesita.”


TÚ: “Cierro con las lecciones reales de este proyecto.”

[LECCIÓN 1 — python:3.13-slim, no Alpine] “No Alpine. grpcio — dependencia de ADK — no compila en Alpine porque usa musl libc. python:3.13-slim usa glibc. No está en la documentación de ADK — lo encuentras en los issues de grpcio.”

[LECCIÓN 2 — Secret Manager] “Nunca credenciales en el Dockerfile ni en variables hardcodeadas. --set-secrets en Cloud Run inyecta desde Secret Manager en runtime. Una línea más en el deploy que elimina un riesgo real.”

[LECCIÓN 3 — PROJECT_NUMBER vs PROJECT_ID] “Son distintos. Para service accounts necesitas el número, no el ID. Obtenlo con gcloud projects describe.”

[LECCIÓN 4 — Cloud Run y underscores] “Sin underscores en nombres de servicios. web_portalweb-portal. Lo descubres en el primer deploy.”

[LECCIÓN 5 — A2A requiere —a2a + agent.json] “Sin el flag --a2a, solo tienes la REST API de ADK. Con el flag pero sin agent.json, el servidor ignora el agente silenciosamente. Necesitas los dos.”

[LECCIÓN 6 — IAM auth en cadena] “Portal público, agentes privados. El portal usa el metadata server de GCP para autenticarse con los agentes. roles/run.invoker en el service account del portal. Sin credenciales hardcodeadas.”

[LECCIÓN 7 — La doc del CLI no coincide con la doc de Cloud Run]adk deploy cloud_run genera Python 3.11 + pip. La documentación de Google Cloud Run para ADK muestra Python 3.13 + uv. Cuando haya contradicción, sigue la doc de Cloud Run — es más reciente.”

TÚ: “Y siendo honesto, las limitaciones:”

Costo — cada agente consume tokens de Gemini. La cadena completa gasta unos centavos, pero si la corres 1000 veces al día, eso escala. Monitoréalo.”

Non-determinismo — si corres la misma tarea dos veces, los agentes pueden decidir stacks diferentes. Eso es una feature y un riesgo al mismo tiempo.”

Cold start — con min-instances=0, el primer request tarda 5-10 segundos mientras Cloud Run levanta el contenedor. Después de eso es instantáneo.”

“Las ventajas superan las limitaciones. Pero necesitabas saber la verdad completa.”

TÚ: “El código completo está en GitHub — los 7 Dockerfiles, los 7 agent.json, el orchestrator, el portal, todo. Link en la descripción.”

[PAUSA — mirar a cámara]

“Estos 7 agentes están en producción. Tienen URLs. Responden al protocolo A2A. Cualquier otro agente — de cualquier framework, de cualquier empresa — puede llamarlos. Eso es lo que hace A2A diferente: interoperabilidad real.”

TÚ: “Pero hay algo que falta. Lo mencioné antes: los outputs de los agentes se pierden cuando la sesión termina. No se guardan en ningún lado.”

“En el próximo video voy a resolver eso con MCP — Model Context Protocol. Un servidor MCP conectado a Cloud Storage donde los agentes persisten todo lo que generan. ADK para el framework, A2A para la comunicación entre agentes, MCP para el acceso a herramientas y recursos. El stack completo.”

[MOSTRAR: Diagrama — ADK + A2A + MCP = full stack]

“Si en este video montamos la constructora con 7 oficinas comunicándose por protocolo… en el siguiente les damos un archivo central donde guardan todos los planos.”

[PAUSA — mirar a cámara directamente]

TÚ: “Si llegaste hasta acá y te sirvió — suscríbete. En serio. No es por el algoritmo.”

“Esta serie avanza video a video. Cada uno construye sobre el anterior. Si no te suscribes, el siguiente video donde resolvemos la persistencia con MCP no te va a aparecer. YouTube no te lo va a recomendar si no le dices que te interesa.”

“Suscríbete y activa la campanita. Es gratis y me ayuda a seguir haciendo esto.”

[PAUSA]

“Nos vemos en el próximo.”

[FADE OUT]


  • Tiempos reales de cada gcloud run deploy (~3-4 min por agente)
  • Si hay un error — resolverlo en vivo (ej: el underscore en web_portal)
  • Los tiempos de respuesta de la cadena completa
  • El cold start visible en los logs (“Started server process”)
platform-architect → https://platform-architect-r7xydrdtta-uc.a.run.app
infrastructure → https://infrastructure-r7xydrdtta-uc.a.run.app
security → https://security-r7xydrdtta-uc.a.run.app
cicd → https://cicd-r7xydrdtta-uc.a.run.app
observability → https://observability-r7xydrdtta-uc.a.run.app
devex → https://devex-r7xydrdtta-uc.a.run.app
web-portal → https://web-portal-r7xydrdtta-uc.a.run.app
idp-portal → https://idp-portal-39303680563.us-central1.run.app
dominio → https://idp.nicolasneira.dev
  1. Split screen con 7 terminales de logs encendiéndose en secuencia
  2. Las 7 URLs apareciendo una por una
  3. /list-apps respondiendo con el nombre del agente
  4. Diagrama local (filesystem compartido) vs producción (A2A messages)
  5. Portal en idp.nicolasneira.dev con health checks en verde
  6. Comparación Dockerfile adk deploy vs Dockerfile propio

Errores conocidos del dry run (mostrarlos en vivo si ocurren)

Sección titulada «Errores conocidos del dry run (mostrarlos en vivo si ocurren)»
  • Permiso storage.objectAdmin faltaba → gcloud projects add-iam-policy-binding
  • Secret Manager API deshabilitada → gcloud services enable secretmanager.googleapis.com
  • web_portal con underscore → renombrar a web-portal
  • agent.json faltante → A2A silenciosamente ignorado
Open Loop (minuto)Payoff (minuto)
“por qué revienta en Cloud Run” (1:30)filesystem compartido no recomendado → A2A (6:00)
“cadena completa en el minuto 54” (2:00)7 agentes en split screen (54:00)
“portal en dominio real” (2:30)idp.nicolasneira.dev (64:00)
“lo que Google recomienda vs lo que funciona” (2:30)sección adk deploy vs manual (18:00)
  • gcloud config get-value project — proyecto correcto
  • Variables de entorno con las 7 URLs seteadas
  • orchestrator_a2a.py listo para correr
  • 7 terminales de logs pre-abiertas
  • Browser con idp.nicolasneira.dev en pestaña

  • v1 — descartado (tenía Claude Code Agent Teams)
  • v2 — estructura base
  • v3 — actualizado con dry run real (2026-04-04)
  • v4 — fusión completa: guiones ADK + Skills + patrón retención (2026-04-05)
  • Dry run completado — 7 agentes deployados y funcionando
  • Grabación