Ir al contenido

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

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

Sección titulada «GUION v3 — 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: 45-50 minutos Patrón: Resultado primero → contexto → concepto → setup GCP → deploy en vivo → momento wow → cierre Fecha: 2026-04-04 (actualizado con dry run real)


[PANTALLA: Terminal — orchestrator_a2a.py corriendo, logs de los 7 agentes respondiendo desde Cloud Run]

TÚ: “Lo que ves ahí son 7 agentes de IA respondiendo en cadena. No en local. No en Docker. Desde Google Cloud.”

[CORTE A: Terminal mostrando las 7 URLs activas]

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 URLs. Siete servicios independientes en Cloud Run. Cada uno es un agente ADK corriendo el protocolo A2A de Google.”

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


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

“Primero: por qué el sistema del video anterior no podía ir a producción y qué cambié en el código para que funcionara.”

“Segundo: el setup completo de GCP — proyecto, APIs, permisos, secrets — sin saltar nada. Esto es lo que la mayoría de tutoriales omite.”

“Tercero: los siete deploys en vivo, uno por uno, con los tiempos reales. Y al final la cadena completa corriendo desde Cloud Run.”


CONTEXTO — EL VIDEO ANTERIOR (2:30 - 6:00)

Sección titulada «CONTEXTO — EL VIDEO ANTERIOR (2:30 - 6:00)»

[MOSTRAR: Screenshot del video ADK anterior]

TÚ: “Contexto rápido. En el video anterior construí un IDP completo con 7 agentes ADK: Platform Architect, Infrastructure, Security, CI/CD, Observability, DevEx, Web Portal. Todo en local, todo en Docker.”

[MOSTRAR: Diagrama — un solo contenedor, filesystem compartido]

“Funcionaba así: un solo proceso Docker, los 7 agentes en secuencia, compartiendo un filesystem. El Platform Architect escribía un YAML al disco. El Infrastructure lo leía. Y así en cascada.”

“Funcionó. El video anduvo bien. Pero había un problema que no mencioné.”

[PAUSA]

“Todo eso es desarrollo local. En Cloud Run eso no existe.”


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 problema central es este.”

[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 corrían en el mismo contenedor, compartiendo el mismo filesystem.”

[MOSTRAR: Diagrama — 7 contenedores independientes en Cloud Run, sin filesystem compartido]

“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 región. El filesystem compartido no existe.”

“El modelo mental cambia completamente. 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 que Google donó a la Linux Foundation.”

[MOSTRAR: a2a-protocol.org]

“En lugar de compartir estado via filesystem, el estado viaja en los mensajes. El orquestador toma el output de cada agente y lo inyecta como contexto en el siguiente.”

[DIAGRAMA — flujo A2A]

Orchestrator → POST /run → Platform Architect
Platform Architect responde → "Python, FastAPI, PostgreSQL, Redis"
Orchestrator toma esa respuesta → POST /run → Infrastructure
con el contexto: "el Platform Architect decidió este stack..."
Infrastructure responde → docker-compose generado
Y así con los 7 en cadena.

“JSON sobre HTTPS. Stateless. Cada agente recibe su tarea con el contexto necesario, procesa, responde. Sin estado compartido.”

TÚ: “Para que esto funcionara en Cloud Run, hice tres cambios al proyecto del video anterior.”

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

“Primero: cada agente necesitaba su propio servidor HTTP. Agregué un __main__.py que inicia adk api_server — el servidor HTTP que viene incluido en el paquete de Google ADK.”

[MOSTRAR: main.py]

ENTRYPOINT ["uv", "run", "adk", "api_server", "--host", "0.0.0.0", "--port", "8080"]

“Segundo: cada agente necesitaba su propio Dockerfile.”

[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", "--host", "0.0.0.0", "--port", "8080"]

“Noten: python:3.13-slim. No Alpine. Alpine rompe con grpcio — una dependencia de ADK — porque usa musl libc en lugar de glibc. Ese detalle no está documentado claramente en ningún lado.”

“Tercero: los tools que leían del filesystem cambiaron para aceptar contexto via mensaje, con fallback al disco para modo local.”

[MOSTRAR: cambio en get_platform_config()]

# Antes — solo lee del disco
def get_platform_config() -> dict:
config_path = Path(OUTPUT_DIR) / "platform-config.yaml"
with open(config_path, 'r') as f:
return yaml.safe_load(f)
# Después — acepta contexto A2A, fallback al disco para local
def get_platform_config(context_json: str = "") -> dict:
if context_json:
data = json.loads(context_json)
return data.get("platform_config") or data
config_path = Path(OUTPUT_DIR) / "platform-config.yaml"
with open(config_path, 'r') as f:
return yaml.safe_load(f)

“Este cambio mantiene compatibilidad. En local sigue funcionando igual. En Cloud Run el contexto llega via mensaje.”


TÚ: “Antes del deploy, hay que preparar GCP. Voy a mostrar cada paso — esto es lo que la mayoría de tutoriales omite o hace en background.”

[MOSTRAR: terminal]

gcloud auth login
gcloud config set project TU_PROJECT_ID
gcloud config get-value project

“Me autentico con mi cuenta de Google, seteo el proyecto, verifico. Tres comandos.”

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. Artifact Registry para guardarlas. Secret Manager para las credenciales — ahora explico por qué.”

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

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

TÚ: “En GCP, cuando deploys un servicio, Cloud Run usa un service account para ejecutar. El nombre de ese service account sigue siempre el mismo patrón.”

[MOSTRAR en pantalla]

{PROJECT_NUMBER}-compute@developer.gserviceaccount.com

“Noten: PROJECT_NUMBER, no PROJECT_ID. Son distintos. El PROJECT_ID es el nombre que tú le pusiste. El PROJECT_NUMBER lo asigna Google automáticamente. Para obtenerlo:”

PROJECT_NUMBER=$(gcloud projects describe TU_PROJECT_ID --format="value(projectNumber)")
echo $PROJECT_NUMBER

“Con ese número, doy permisos al service account para que Cloud Build pueda construir las imágenes:“

gcloud projects add-iam-policy-binding TU_PROJECT_ID \
--member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
--role="roles/storage.objectAdmin"
gcloud projects add-iam-policy-binding TU_PROJECT_ID \
--member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
--role="roles/cloudbuild.builds.builder"

Gemini API Key en Secret Manager (22:30 - 26:00)

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

TÚ: “Los agentes usan Gemini 2.5 Flash como modelo. Necesitan una API key. Y aquí viene algo importante:”

[PAUSA]

“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.”

“Primero creo el secret:”

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

“Luego doy acceso al service account de Cloud Run al secret:”

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

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


TÚ: “El comando de deploy. Lo explico antes de correrlo para que entiendas cada flag.”

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 para recibir requests. Seguridad básica.”

--min-instances=0 — escala a cero cuando no hay tráfico. No pagas nada en idle.”

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

--set-secrets — inyecta la API key desde Secret Manager. El contenedor la recibe como variable de entorno en runtime, sin que esté hardcodeada en ningún lado.”

“Una cosa más — los nombres de servicios en Cloud Run solo aceptan letras minúsculas, números y guiones. Sin underscores. Por eso el agente web_portal se deploya como web-portal.”

[DEPLOY 1 — platform-architect]

TÚ: “Platform Architect. El que decide el stack. Cloud Build va a construir la imagen desde el Dockerfile, subirla a Artifact Registry, y deployar el servicio. Voy a narrar mientras corre.”

[narrar el proceso: validación → build → push → deploy → routing]

[URL aparece]

TÚ: “Ahí está la URL. Noten el patrón: {nombre-servicio}-{hash}-uc.a.run.app. Google genera este hash automáticamente — es único por proyecto y región. Todos sus agentes van a compartir el mismo hash r7xydrdtta, lo que hace fácil identificarlos.”

https://platform-architect-r7xydrdtta-uc.a.run.app

[DEPLOY 2-7 — narrar cada uno brevemente]

TÚ para infrastructure: “Infrastructure. Genera el docker-compose basado en lo que decida el Platform Architect.” TÚ para security: “Security. Escanea el código y genera las configuraciones de seguridad.” TÚ para cicd: “CI/CD. Los pipelines de build, test y deploy.” TÚ para observability: “Observability. Prometheus, Grafana, alertas.” TÚ para devex: “DevEx. El CLI tool para los desarrolladores.” TÚ para web-portal: “Web Portal. El último. Y ojo — el nombre tiene underscore en el código pero Cloud Run no acepta underscores, así que se deploya como web-portal con guión.”

[7 URLs en pantalla]

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

TÚ: “Verifico que responden antes de correr la cadena.”

TOKEN=$(gcloud auth print-identity-token)
curl -s https://platform-architect-r7xydrdtta-uc.a.run.app/list-apps \
-H "Authorization: Bearer $TOKEN"
# Responde: ["platform_architect"]

“El endpoint /list-apps confirma que el agente está activo. El Bearer token es el IAM identity token — sin él el servicio rechaza la conexión.”


TÚ: “Antes de correr el orchestrator, abro los logs de cada agente en tiempo real. Así pueden ver exactamente lo que pasa en cada servicio de Cloud Run.”

[ABRIR 7 TERMINALES en split screen]

gcloud run services logs tail platform-architect --region=us-central1
gcloud run services logs tail infrastructure --region=us-central1
gcloud run services logs tail security --region=us-central1
gcloud run services logs tail cicd --region=us-central1
gcloud run services logs tail observability --region=us-central1
gcloud run services logs tail devex --region=us-central1
gcloud run services logs tail web-portal --region=us-central1

TÚ: “Por ahora silencio. Los agentes están en cero — no hay ningún contenedor corriendo. Escalan a cero cuando no los usan. Eso es Cloud Run.”


MOMENTO WOW — LA CADENA COMPLETA (44:00 - 52:00)

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

TÚ: “Ahora el momento que importa. Este es el orchestrator — pero no es un script HTTP manual. Es un SequentialAgent de ADK con 7 RemoteA2aAgent como sub-agentes. ADK maneja todo el protocolo A2A por nosotros.”

[MOSTRAR: orchestrator_sequential_a2a.py — la parte del SequentialAgent]

pipeline = SequentialAgent(
name="idp_orchestrator",
sub_agents=[
RemoteA2aAgent(name="platform_architect", agent_card=PA_CARD_URL),
RemoteA2aAgent(name="infrastructure", agent_card=INFRA_CARD_URL),
RemoteA2aAgent(name="security", agent_card=SEC_CARD_URL),
RemoteA2aAgent(name="cicd", agent_card=CICD_CARD_URL),
RemoteA2aAgent(name="observability", agent_card=OBS_CARD_URL),
RemoteA2aAgent(name="devex", agent_card=DEV_CARD_URL),
RemoteA2aAgent(name="web_portal", agent_card=WP_CARD_URL),
]
)

TÚ: “Cada agente tiene un agent-card.json — su tarjeta de presentación A2A. El RemoteA2aAgent la lee y sabe exactamente cómo comunicarse con él. Y el SequentialAgent garantiza el orden de ejecución.”

[MOSTRAR: agent-card.json de uno de los agentes]

TÚ: “Corro el orchestrator.”

cd agents_adk/platform_architect
uv run python ../../orchestrator_sequential_a2a.py \
"Build IDP for a Python FastAPI application"

[MOSTRAR: logs en tiempo real — cada agente respondiendo desde Cloud Run]

TÚ (narrando): “Platform Architect… decidió el stack…” “Infrastructure… generó el docker-compose…” “Security, CI/CD, Observability, DevEx, Web Portal…”

[RESPUESTA FINAL]

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

[PAUSA]

“Pero hay algo más.”


BONUS — EL PORTAL EN PRODUCCIÓN (50:00 - 55:00)

Sección titulada «BONUS — EL PORTAL EN PRODUCCIÓN (50:00 - 55:00)»

TÚ: “El web portal agent generó el código de un dashboard. Lo deployamos como un servicio más en Cloud Run.”

gcloud run deploy idp-portal \
--source=outputs/portal/ \
--region=us-central1 \
--allow-unauthenticated

TÚ: “Y ahora lo exponemos en un dominio real.”

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

[MOSTRAR: CNAME record en Cloudflare]

idp CNAME ghs.googlehosted.com.

TÚ: “Google provisiona el certificado SSL automáticamente. Y ahora…”

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

TÚ: “Este portal está mostrando el estado real de los 7 agentes en Cloud Run. Los health checks los hace el backend del portal con IAM auth — el browser nunca toca los agentes directamente. Todo privado, todo en producción.”

[PAUSA]

“Esto no era posible con el sistema del video anterior. El filesystem compartido no existe en Cloud Run. El protocolo A2A lo resuelve — y el resultado es un sistema real, con URL, con SSL, accesible desde cualquier lugar.”


TÚ: “Seis cosas que me llevé:”

[LECCIÓN 1 — python:3.13-slim] “No Alpine. grpcio — que es una dependencia de ADK — no compila en Alpine porque usa musl libc. python:3.13-slim usa glibc. Ese detalle no está claro en ninguna documentación.”

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

[LECCIÓN 3 — PROJECT_NUMBER vs PROJECT_ID] “Son distintos. El PROJECT_ID es el nombre que tú le pones. El PROJECT_NUMBER lo asigna Google. Para los service accounts necesitas el número, no el ID. Obtenlo dinámicamente con gcloud projects describe.”

[LECCIÓN 4 — Cloud Run y underscores] “Los nombres de servicios en Cloud Run solo aceptan letras minúsculas, números y guiones. Sin underscores. web_portalweb-portal. Ese error lo encuentras en el primer deploy.”

[LECCIÓN 5 — A2A requiere —a2a + agent.json]adk api_server sin --a2a no expone el protocolo A2A — solo la REST API propia de ADK. Para A2A real necesitas dos cosas: el flag --a2a en el Dockerfile y un archivo agent.json en la carpeta del agente. Sin el agent.json el servidor ignora el agente para A2A aunque el flag esté activo.”

[LECCIÓN 6 — IAM auth en cadena de servicios] “El portal es público pero los agentes son privados. El portal hace los health checks desde su backend usando el metadata server de GCP — obtiene el IAM token automáticamente sin credenciales hardcodeadas. roles/run.invoker en la service account del portal es todo lo que necesitas.”

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

[PAUSA]

“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 exactamente para lo que fue diseñado A2A.”

“En el próximo video voy a construir la interfaz web para interactuar con estos agentes. Nada de terminal — una UI real conectada al sistema en producción.”

[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

URLs reales (ya deployadas — usar estas en la grabación)

Sección titulada «URLs reales (ya deployadas — usar estas en la grabación)»
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
  1. Las 7 URLs apareciendo una por una
  2. /list-apps respondiendo con el nombre del agente
  3. El orchestrator corriendo con logs de cada agente
  4. Diagrama local (filesystem) vs producción (A2A messages)

Errores conocidos del dry run (mostrarlos en vivo o mencionar)

Sección titulada «Errores conocidos del dry run (mostrarlos en vivo o mencionar)»
  • Permiso storage.objectAdmin faltaba → se resuelve con gcloud projects add-iam-policy-binding
  • Secret Manager API deshabilitada → gcloud services enable secretmanager.googleapis.com
  • web_portal con underscore → renombrar a web-portal
  • gcloud config get-value project — proyecto correcto
  • Variables de entorno con las 7 URLs seteadas
  • orchestrator_a2a.py listo para correr

  • v1 — descartado (tenía Claude Code Agent Teams)
  • v2 — estructura base
  • v3 — actualizado con dry run real (2026-04-04)
  • Dry run completado — 7 agentes deployados y funcionando
  • Grabación