Administración centralizada de Agent Gateway con Agent Registry entre proyectos para Agent Runtime

1. Introducción

A medida que las organizaciones empresariales adoptan la IA generativa, las arquitecturas evolucionan con rapidez de chatbots monolíticos independientes a sistemas multiagente distribuidos (de agente a agente o A2A). En estas topologías modernas, los agentes de organización de alto nivel coordinan flujos de trabajo empresariales complejos delegando tareas a agentes de trabajo de dominio especializados, servidores de herramientas del Protocolo de contexto del modelo (MCP) y bases de datos empresariales de backend en proyectos independientes de Google Cloud.

Sin embargo, operar sistemas multiagente a gran escala presenta desafíos críticos de seguridad, administración y operaciones:

  • Proliferación de agentes y herramientas en la sombra: Cuando los equipos de desarrollo implementan agentes en proyectos aislados sin un catálogo centralizado, las organizaciones pierden visibilidad sobre qué herramientas y subagentes existen.
  • Salida entre proyectos sin supervisión: Permitir que los agentes tengan rutas de red directas y sin inspección crea riesgos de robo de datos y omite los perímetros de seguridad.
  • Integraciones codificadas de forma rígida frágiles: Codificar de forma rígida las URLs de los agentes downstream y los IDs de motor de razonamiento crea dependencias frágiles que se rompen durante las actualizaciones o las reimplementaciones.
  • Falta de identidad con privilegios mínimos: Las cuentas de servicio compartidas no proporcionan un no repudio criptográfico a nivel de la instancia de agente individual.

Para resolver estos desafíos, Gemini Enterprise Agent Platform proporciona un plano de control de conectividad y administración unificado que consta de cuatro pilares principales:

  1. Agent Gateway (networkservices.googleapis.com): Es un proxy de aplicación de políticas y red regional administrado. Cuando opera en el modo de salida AGENT_TO_ANYWHERE, intercepta el tráfico saliente del agente, delega las evaluaciones de autorización a las extensiones de seguridad y enruta las solicitudes a través de los perímetros del proyecto.
  2. Agent Registry (agentregistry.googleapis.com): El catálogo de servicios empresariales único. Proporciona un directorio centralizado y verificado de todas las herramientas, los servidores de MCP y los agentes pares disponibles en la organización, lo que permite el descubrimiento automático dinámico en el entorno de ejecución sin extremos codificados.
  3. Identidad del agente y administración de IAP v2 (iap.googleapis.com y iam.googleapis.com): Un marco de trabajo de identidad y acceso criptográfico. Los agentes en ejecución reciben URN de máquina de SPIFFE únicas y certificadas (principal://...). La salida se evalúa en función de las políticas de acceso unificado de IAM (UAP/IAP v2) centralizadas que verifican el permiso universal iap.googleapis.com/resources.egressViaIAP con condiciones de catálogo enriquecidas de Common Expression Language (CEL) (destination.agent_registry.*).
  4. Agent Runtime (motores de razonamiento): Es una plataforma de ejecución sin servidores completamente administrada para aplicaciones de agentes basadas en Python, que incluye vinculaciones de configuración nativas (agent_gateway_config) a puertas de enlace centrales.

Situación empresarial del codelab: Compras de alimentos y bebidas en varios proyectos

En este codelab, compilarás y administrarás un ecosistema de compras de varios proyectos del mundo real que abarca tres proyectos de Google Cloud distintos:

  • Proyecto de administración central (PROJECT_GOVERNANCE): Es propiedad de los equipos de TI y SecOps centrales, y aloja la Agent Gateway central, el Agent Registry central y las Políticas de acceso unificado de IAM.
  • Proyecto de organización de consumidores (PROJECT_CONCIERGE): Es propiedad del equipo de adquisiciones y aloja el agente de Concierge de compras, que descubre proveedores de forma dinámica y enruta los pedidos de los clientes.
  • Proyecto de proveedor de dominio (PROJECT_SELLERS): Es propiedad de proveedores externos o departamentales, y aloja el agente de vendedor de hamburguesas y el agente de vendedor de pizzas.

figure1

Fig. 1: Arquitectura de administración centralizada de varios proyectos

¿Por qué usar la administración centralizada entre proyectos?

En las grandes organizaciones empresariales, los equipos de productos y los grupos de ciencia de datos crean agentes de IA en decenas de proyectos independientes de Google Cloud. Darle a cada equipo control directo sobre el registro de herramientas, las rutas de red de salida y las protecciones de seguridad crea una proliferación de herramientas no verificadas, políticas de DLP inconsistentes, salidas de VPC no supervisadas y registros de auditoría fragmentados.

La administración centralizada entre proyectos separa la creación de políticas de la ejecución de agentes:

  • Los equipos de TI central y SecOps crean políticas de seguridad, aprueban herramientas y supervisan la salida dentro de un único proyecto de administración centralizada.
  • Los equipos de productos y aplicaciones se enfocan exclusivamente en la lógica empresarial en sus proyectos de Agent Runtime independientes, vinculándose directamente a la puerta de enlace central sin la sobrecarga operativa de administrar VPC locales, interconexiones o motores de políticas fragmentados.

figure2

Fig. 2. Arquitectura y límites de administración de tres niveles entre proyectos

Modelo de alcance de identidad de dos niveles en las políticas de acceso unificado

Cuando los agentes se comunican a través de Central Agent Gateway, Identity-Aware Proxy (IAP v2) evalúa el acceso en función de la identidad del agente de la persona que llama, una identidad basada en SPIFFE y certificada criptográficamente que se emite de forma automática para el contenedor del entorno de ejecución, en comparación con una política de acceso de IAM global:

  • Nivel 1: API de Google Cloud de referencia (grano grueso a través de principalSet:// en la regla 1): Autorización de salida a nivel de proyecto que permite que todos los tiempos de ejecución de agentes en los proyectos secundarios accedan a las API estándar de Google (aiplatform, iamcredentials, telemetry, agentregistry) para descubrimiento, generación de tokens e inferencia.
  • Nivel 2: Herramientas empresariales y servicios de agente a agente (precisos a través de principal:// en las reglas 2 y 3): Acceso estricto con privilegio mínimo vinculado a instancias individuales de motor de razonamiento, aplicado con condiciones de Common Expression Language (CEL) orientadas a servicios específicos registrados en el registro de agentes (destination.agent_registry.agent.name).

Qué compilarás

  • Agent Gateway centralizada (centralized-agw) en PROJECT_GOVERNANCE
  • Extensión de servicio de autorización de IAP v2 y política de Authz en modo de aplicación forzosa estricto (failOpen: false)
  • Política de acceso unificado de IAM (uap-rules.json) y vinculación de políticas de proyecto fundamentales
  • Permisos de IAM del agente de servicio entre proyectos (ar_agw_cross_project_sa)
  • Bucket de etapa de pruebas compartido y central de Google Cloud Storage (GCS)
  • Agentes de venta de hamburguesas y pizzas aislados en PROJECT_SELLERS
  • Agente de conserjería de compras con detección automática dinámica de REST en PROJECT_CONCIERGE
  • Registros de servicios en Central Agent Registry con URLs de mTLS entre proyectos
  • Actualizaciones dinámicas de la política de salida de IAP v2 con verificación en vivo y auditorías de Cloud Logging

figure3

Fig. 3. Secuencia de implementación paso a paso

Qué aprenderá

  • Cómo configurar los permisos de IAM del agente de servicio entre proyectos para las puertas de enlace centralizadas
  • Cómo enrutar la salida de Agent Runtime a través de un Agent Gateway central en entornos de varios proyectos
  • Cómo delegar la autorización de Agent Gateway a Identity-Aware Proxy (IAP v2) con extensiones de servicio (iapPolicyVersion: "V2")
  • Cómo crear y vincular políticas de acceso unificado (UAP) de IAM con reglas de Common Expression Language (CEL) que rigen los destinos registrados de Agent Registry (destination.agent_registry.*)
  • Cómo eliminar los IDs y las URLs de agentes codificados de forma rígida con la detección automática del entorno de ejecución en Agent Registry
  • Cómo probar el bloqueo de confianza cero del perímetro real (HTTP 403 Forbidden) y verificar las actualizaciones de políticas en vivo en Cloud Logging

Requisitos

  • 3 proyectos de Google Cloud con la facturación habilitada:
    • PROJECT_GOVERNANCE: Políticas de acceso de IAM, registro, puerta de enlace y administración central
    • PROJECT_CONCIERGE: Agente organizador de conserjería de compras
    • PROJECT_SELLERS: Agentes vendedores especializados en hamburguesas y pizzas
  • Un usuario o una cuenta de servicio de IAM con roles/owner o permisos de administrador en los 3 proyectos
  • Una organización de Google Cloud (para la asignación de dominios de confianza de SPIFFE)
  • Google Cloud Shell o una máquina local con la CLI de gcloud, python (3.11 o superior) y uv instalados

Con esto, concluye la parte de la introducción. A continuación, pasaremos a la sección Configuración y entorno.

2. Configuración

Aunque esta arquitectura abarca 3 proyectos de Google Cloud distintos, puedes ejecutar el 100% de los comandos de implementación de la terminal, las descargas de repositorios y las operaciones de etapa de pruebas desde una sola terminal de Cloud Shell configurada en PROJECT_GOVERNANCE. Cada secuencia de comandos de implementación y comando de gcloud se orienta de forma explícita al proyecto de destino adecuado a través de marcas de la CLI (--project).

Para comenzar, accede a la línea de comandos de tu proyecto de Google Cloud:

Establece el contexto de tu proyecto

# set terminal project context to Central Governance Project
gcloud config set project SET_YOUR_GOVERNANCE_PROJECT_ID_HERE
# login to gcloud cli
gcloud auth login
# login for application default credentials
gcloud auth application-default login
# update gcloud components
gcloud components update --quiet

Establece variables de entorno de shell

Ingresa los identificadores específicos de tu proyecto.

# 1. Project Identifiers
export PROJECT_GOVERNANCE="SET_YOUR_GOVERNANCE_PROJECT_ID_HERE"
export PROJECT_CONCIERGE="SET_YOUR_CONCIERGE_PROJECT_ID_HERE"
export PROJECT_SELLERS="SET_YOUR_SELLERS_PROJECT_ID_HERE"

Estas variables de shell se derivarán automáticamente.

# 2. Regional & Gateway Settings
export REGION="us-central1"
export AGW_NAME="centralized-agw"
export UAP_POLICY_NAME="uap-policy-${AGW_NAME}"
export UAP_BINDING_NAME="uap-binding-${AGW_NAME}"

# 3. Retrieve Project Numbers
export PROJECT_NUMBER_GOVERNANCE=$(gcloud projects describe ${PROJECT_GOVERNANCE} --format="value(projectNumber)")
export PROJECT_NUMBER_CONCIERGE=$(gcloud projects describe ${PROJECT_CONCIERGE} --format="value(projectNumber)")
export PROJECT_NUMBER_SELLERS=$(gcloud projects describe ${PROJECT_SELLERS} --format="value(projectNumber)")

# 4. Obtain Organization ID
export ORG_ID=$(gcloud projects get-ancestors ${PROJECT_GOVERNANCE} --format="value(id, type)" | grep organization | awk '{print $1}')

# 5. Set Application Default Credentials (ADC) Quota Project
gcloud auth application-default set-quota-project ${PROJECT_GOVERNANCE}

echo "Governance Project: ${PROJECT_GOVERNANCE} (${PROJECT_NUMBER_GOVERNANCE})"
echo "Concierge Project:  ${PROJECT_CONCIERGE} (${PROJECT_NUMBER_CONCIERGE})"
echo "Sellers Project:    ${PROJECT_SELLERS} (${PROJECT_NUMBER_SELLERS})"
echo "Organization ID:    ${ORG_ID}"
echo "UAP Policy Name:    ${UAP_POLICY_NAME}"
echo "UAP Binding Name:   ${UAP_BINDING_NAME}"

Crea un directorio local para los archivos de configuración

# create config folder
mkdir -p cfg

Asigna el rol de administrador de políticas de acceso para las políticas de acceso unificado

# grant Access Policy Admin and Project IAM Admin to current user in Governance Project
for ROLE in "roles/iam.accessPolicyAdmin" "roles/resourcemanager.projectIamAdmin"; do
  gcloud projects add-iam-policy-binding ${PROJECT_GOVERNANCE} \
    --member="user:$(gcloud config get-value account)" \
    --role="${ROLE}" \
    --condition=None
done

Habilita los registros de auditoría de acceso a los datos de Cloud para IAP v2

De forma predeterminada, Google Cloud inhabilita los registros de auditoría de acceso a los datos para evitar costos de almacenamiento no deseados. Debido a que IAP v2 emite decisiones de autorización (granted=true y granted=false) como registros de auditoría de acceso a los datos, habilita los registros de ADMIN_READ, DATA_READ y DATA_WRITE para iap.googleapis.com en PROJECT_GOVERNANCE:

# 1. export current IAM policy for PROJECT_GOVERNANCE
gcloud projects get-iam-policy ${PROJECT_GOVERNANCE} \
  --format=json > cfg/gov_iam_policy.json
# 2. append auditConfigs for iap.googleapis.com
python3 -c "
import json
with open('cfg/gov_iam_policy.json') as f:
    policy = json.load(f)
audit_configs = [c for c in policy.get('auditConfigs', []) if c.get('service') != 'iap.googleapis.com']
audit_configs.append({
    'service': 'iap.googleapis.com',
    'auditLogConfigs': [
        {'logType': 'ADMIN_READ'},
        {'logType': 'DATA_READ'},
        {'logType': 'DATA_WRITE'}
    ]
})
policy['auditConfigs'] = audit_configs
with open('cfg/gov_iam_policy.json', 'w') as f:
    json.dump(policy, f, indent=2)
"
# 3. apply updated policy
gcloud projects set-iam-policy ${PROJECT_GOVERNANCE} cfg/gov_iam_policy.json
# 4. verify auditConfigs applied
gcloud projects get-iam-policy ${PROJECT_GOVERNANCE} --format="yaml(auditConfigs)"

Habilita las APIs de Google Cloud requeridas

# enable google apis (agent platform & security bundle, part 1)
for PROJ in ${PROJECT_GOVERNANCE} ${PROJECT_CONCIERGE} ${PROJECT_SELLERS}; do
  gcloud services enable \
    agentregistry.googleapis.com \
    aiplatform.googleapis.com \
    apphub.googleapis.com \
    apptopology.googleapis.com \
    cloudapiregistry.googleapis.com \
    cloudtrace.googleapis.com \
    compute.googleapis.com \
    dataform.googleapis.com \
    iam.googleapis.com \
    agentidentity.googleapis.com \
    iap.googleapis.com \
    logging.googleapis.com \
    modelarmor.googleapis.com \
    monitoring.googleapis.com \
    networksecurity.googleapis.com \
    networkservices.googleapis.com \
    notebooks.googleapis.com \
    observability.googleapis.com \
    --project=${PROJ}
done
# enable google apis (agent platform bundle, part 2)
for PROJ in ${PROJECT_GOVERNANCE} ${PROJECT_CONCIERGE} ${PROJECT_SELLERS}; do
  gcloud services enable \
    securitycenter.googleapis.com \
    saasservicemgmt.googleapis.com \
    storage.googleapis.com \
    telemetry.googleapis.com \
    texttospeech.googleapis.com \
    --project=${PROJ}
done
# enable google apis (foundational & agent runtime build bundle, part 3)
for PROJ in ${PROJECT_GOVERNANCE} ${PROJECT_CONCIERGE} ${PROJECT_SELLERS}; do
  gcloud services enable \
    artifactregistry.googleapis.com \
    cloudbuild.googleapis.com \
    cloudresourcemanager.googleapis.com \
    iamcredentials.googleapis.com \
    serviceusage.googleapis.com \
    run.googleapis.com \
    orgpolicy.googleapis.com \
    --project=${PROJ}
done

Valida la habilitación de la API en todos los proyectos

Asegurarse de que los tres proyectos (PROJECT_GOVERNANCE, PROJECT_CONCIERGE y PROJECT_SELLERS) tengan exactamente las mismas APIs habilitadas establece la coherencia operativa y evita fallas en la generación de tokens en el entorno de ejecución, errores de catalogación de esquemas o pérdidas de telemetría.

Ejecuta la siguiente secuencia de comandos de validación en Cloud Shell para verificar la paridad de la API en los tres proyectos:

# validate that all required APIs are enabled across all 3 projects
python3 - << 'EOF'
import subprocess
import os
import sys

REQUIRED_APIS = [
    "agentregistry.googleapis.com",
    "aiplatform.googleapis.com",
    "apphub.googleapis.com",
    "apptopology.googleapis.com",
    "cloudapiregistry.googleapis.com",
    "cloudtrace.googleapis.com",
    "compute.googleapis.com",
    "dataform.googleapis.com",
    "iam.googleapis.com",
    "agentidentity.googleapis.com",
    "iap.googleapis.com",
    "logging.googleapis.com",
    "modelarmor.googleapis.com",
    "monitoring.googleapis.com",
    "networksecurity.googleapis.com",
    "networkservices.googleapis.com",
    "notebooks.googleapis.com",
    "observability.googleapis.com",
    "securitycenter.googleapis.com",
    "saasservicemgmt.googleapis.com",
    "storage.googleapis.com",
    "telemetry.googleapis.com",
    "texttospeech.googleapis.com",
    "artifactregistry.googleapis.com",
    "cloudbuild.googleapis.com",
    "cloudresourcemanager.googleapis.com",
    "iamcredentials.googleapis.com",
    "serviceusage.googleapis.com",
    "run.googleapis.com",
    "orgpolicy.googleapis.com"
]

projects = {
    "GOVERNANCE": os.environ.get("PROJECT_GOVERNANCE", ""),
    "CONCIERGE": os.environ.get("PROJECT_CONCIERGE", ""),
    "SELLERS": os.environ.get("PROJECT_SELLERS", "")
}

enabled = {}
for role, proj in projects.items():
    if not proj:
        print(f"Error: Environment variable for {role} is not set.")
        sys.exit(1)
    res = subprocess.run(
        ["gcloud", "services", "list", "--enabled", f"--project={proj}", "--format=value(config.name)"],
        capture_output=True, text=True, check=True
    )
    enabled[role] = set(res.stdout.strip().splitlines())

print(f"\n{'API Name':<36} | {'GOVERNANCE':<12} | {'CONCIERGE':<12} | {'SELLERS':<12}")
print("-" * 78)

all_synced = True
for api in REQUIRED_APIS:
    g_status = "ENABLED" if api in enabled["GOVERNANCE"] else "MISSING"
    c_status = "ENABLED" if api in enabled["CONCIERGE"] else "MISSING"
    s_status = "ENABLED" if api in enabled["SELLERS"] else "MISSING"
    if "MISSING" in (g_status, c_status, s_status):
        all_synced = False
    print(f"{api:<36} | {g_status:<12} | {c_status:<12} | {s_status:<12}")

print("-" * 78)
if all_synced:
    print("✅ All 29 required APIs are ENABLED and synchronized across all three projects.\n")
else:
    print("❌ Discrepancies detected. Please re-run the enablement commands for missing services.\n")
    sys.exit(1)
EOF

Resultado de la validación de muestra:

Deberías ver todas las APIs habilitadas.

✅ All 30 required APIs are ENABLED and synchronized across all three projects.

Configura las políticas de la organización

Las políticas predeterminadas de la organización de Google Cloud aplican restricciones que limitan las vinculaciones de políticas de acceso de IAM v3 a los recursos (constraints/iam.managed.disableAccessPolicyBinding).

Anula cualquier restricción heredada de la política de la organización a nivel del proyecto configurando de forma explícita enforce: false como permitido.

# disable iam v3 constraint (allow v3 access policies)
gcloud org-policies set-policy /dev/stdin << EOF
name: projects/${PROJECT_NUMBER_GOVERNANCE}/policies/iam.managed.disableAccessPolicyBinding
spec:
  rules:
  - enforce: false
EOF
# verify org policy constraints on project
gcloud org-policies describe iam.managed.disableAccessPolicyBinding \
  --project=${PROJECT_GOVERNANCE} --effective

Con esto concluye la parte de configuración… Ahora, pasemos a la sección Registra las APIs principales de Google.

3. Agent Registry

Registra el servicio de extremo de las APIs de Google principales

Agent Gateway requiere que las URLs de la API de Google se registren en el Registro central de agentes para que los agentes configurados con agent_gateway_config puedan enrutar el tráfico de salida de forma segura a los servicios de backend principales de Google Cloud (como aiplatform, credenciales de IAM y telemetría).

Crea core-gapi-services en Agent Registry

# register core google api endpoints in agent registry with standard and :443 port variants
gcloud agent-registry services create core-gapi-services \
  --project=${PROJECT_GOVERNANCE} \
  --location=${REGION} \
  --display-name="gapi.core.services" \
  --description="Core Google Cloud APIs and Service Endpoints" \
  --endpoint-spec-type=no-spec \
  --interfaces=protocolBinding=JSONRPC,url=https://telemetry.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://telemetry.mtls.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com:443 \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com:443 \
  --interfaces=protocolBinding=JSONRPC,url=https://aiplatform.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://aiplatform.googleapis.com:443 \
  --interfaces=protocolBinding=JSONRPC,url=https://aiplatform.mtls.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://aiplatform.mtls.googleapis.com:443 \
  --interfaces=protocolBinding=JSONRPC,url=https://cloudresourcemanager.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://cloudresourcemanager.googleapis.com:443 \
  --interfaces=protocolBinding=JSONRPC,url=https://cloudresourcemanager.mtls.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://cloudresourcemanager.mtls.googleapis.com:443 \
  --interfaces=protocolBinding=JSONRPC,url=https://iamcredentials.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://iamcredentials.googleapis.com:443 \
  --interfaces=protocolBinding=JSONRPC,url=https://iamcredentials.mtls.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://iamcredentials.mtls.googleapis.com:443 \
  --interfaces=protocolBinding=JSONRPC,url=https://agentregistry.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://agentregistry.mtls.googleapis.com \
  --interfaces=protocolBinding=JSONRPC,url=https://agentregistry.googleapis.com:443 \
  --interfaces=protocolBinding=JSONRPC,url=https://agentregistry.mtls.googleapis.com:443

Captura el ID del recurso del extremo de las APIs principales

# capture the underlying Agent Registry endpoint ID
export ENDPOINT_ID=$(gcloud agent-registry services describe core-gapi-services \
  --project=${PROJECT_GOVERNANCE} \
  --location=${REGION} \
  --format="value(registryResource)" | awk -F'/' '{print $NF}')
echo "Core APIs Endpoint ID: ${ENDPOINT_ID}"

Información sobre principalSet y principal en la identidad de agentes

En Google Cloud IAM y Gemini Enterprise Agent Platform, las identidades de máquina emitidas para ejecutar contenedores de agentes usan URN de SPIFFE con certificación criptográfica evaluados por Identity-Aware Proxy (IAP v2). Cuando configuras las políticas de acceso unificado de IAM, puedes orientar un principal específico o un principalSet basado en atributos:

Dimensión

principal:// (identidad de máquina única)

principalSet:// (grupo basado en atributos)

Sintaxis de IAM

principal://...

principalSet://...

Nivel de detalle

Detallado (nivel de instancia): Identifica una sola instancia de contenedor de motor de razonamiento específica.

De nivel general (nivel de proyecto): Identifica todos los motores de razonamiento que comparten un atributo de proyecto común.

Patrón de URN

principal://agents.global.org-${ORG_ID}.system.id.goog/resources/aiplatform/projects/${PROJECT_NUMBER}/locations/${REGION}/reasoningEngines/${ENGINE_ID}

principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER}

Caso de uso en Agent Platform

Nivel 2 (herramientas empresariales y A2A): Autoriza a agentes de organización específicos a invocar herramientas de dominio de destino (p.ej., Purchasing Concierge $\rightarrow$ Burger Seller).

Nivel 1 (infraestructura básica): Otorga a todos los agentes de un proyecto acceso de salida a las APIs de Google Cloud (core-gapi-services).

Impacto en el ciclo de vida

Si se borra y vuelve a crear un agente, su nuevo ID de Engine requiere una vinculación de política de IAM actualizada.

Se aplica automáticamente a los agentes recién implementados en ese proyecto sin actualizaciones adicionales de IAM.

Administración declarativa con políticas de acceso unificado (UAP/IAP v2)

En la versión heredada de IAP v1, las políticas de salida se adjuntaban directamente a los recursos individuales de Agent Registry con gcloud beta iap web add-iam-policy-binding. En IAP v2 y las políticas de acceso unificado, se eliminan las vinculaciones por recurso en favor de una única política de acceso de IAM centralizada (cfg/uap-rules.json).

La autorización de salida básica para core-gapi-services se configurará como la Regla 1 en la política de acceso unificado de la sección 5, lo que garantizará que todos los contenedores de agentes tengan rutas de salida básicas establecidas antes de la implementación.

Para obtener detalles técnicos más detallados sobre los identificadores de principales y los mecanismos de Workload Identity, consulta los siguientes recursos:

Con esto, concluye el registro de endpoints de las APIs principales. A continuación, se encuentra la sección Implementa la puerta de enlace de agentes centralizada.

4. Agent Gateway

Implementa Agent Gateway centralizada

Implementa la Agent Gateway centralizada (centralized-agw) en modo de salida AGENT_TO_ANYWHERE dentro del proyecto $PROJECT_GOVERNANCE.

Define el manifiesto de configuración de la puerta de enlace

Crea cfg/${AGW_NAME}.yaml para la administración del tráfico de salida:

# generate agent gateway config yaml
cat > cfg/${AGW_NAME}.yaml << EOF
name: ${AGW_NAME}
protocols:
  - MCP
googleManaged:
  governedAccessPath: AGENT_TO_ANYWHERE
registries:
  - "//agentregistry.googleapis.com/projects/${PROJECT_GOVERNANCE}/locations/${REGION}"
EOF

Importa la configuración de Agent Gateway

# import and create agent gateway
gcloud network-services agent-gateways import ${AGW_NAME} \
  --source="cfg/${AGW_NAME}.yaml" \
  --location=${REGION} \
  --project=${PROJECT_GOVERNANCE}

Verifica los detalles de Agent Gateway

# show agent gateway status
gcloud network-services agent-gateways describe ${AGW_NAME} \
  --location=${REGION} \
  --project=${PROJECT_GOVERNANCE}

Resultado de muestra:

agentGatewayCard:
  mtlsEndpoint: projects/${AGW_TP_ID}/regions/us-central1/serviceAttachments/unitkind1-swp-mtls-psc-sa
  rootCertificates:
  - |
    -----BEGIN CERTIFICATE-----
    MIIDwzCCAqugAwIBAgITNQuWGopdOZaHdcK7r7AYFhonqDANBgkqhkiG9w0BAQsF
    ...
    -----END CERTIFICATE-----
  serviceExtensionsServiceAccount: service-${PROJ_NO}@gcp-sa-dep.iam.gserviceaccount.com
createTime: 'YYYY-MM-DDT12:34:56.789098765Z'
googleManaged:
  governedAccessPath: AGENT_TO_ANYWHERE
name: projects/${PROJECT_GOVERNANCE}/locations/us-central1/agentGateways/centralized-agw
protocols:
- MCP
registries:
- //agentregistry.googleapis.com/projects/${PROJECT_GOVERNANCE}/locations/us-central1
updateTime: 'YYYY-MM-DDT12:34:56.789098765Z'

Con esto concluye la implementación de la puerta de enlace. A continuación, ve a la sección Configura la autorización.

5. Autorización

Configura la autorización de Agent Gateway y la UAP básica

Agent Gateway protege y administra el tráfico saliente de herramientas y agentes con políticas de autorización (networksecurity.authzPolicies) integradas con las políticas de acceso unificado (UAP) de Identity-Aware Proxy (IAP v2).

Descripción general de la arquitectura de autorización

figure4

Fig. 4. Descripción general de la arquitectura de autorización

La arquitectura de autorización se compone de tres capas interconectadas:

  1. Extensión de servicio IAP (authzExtension): Recurso regional configurado con service: iap.googleapis.com, metadata: iapPolicyVersion: "V2" y failOpen: false para la aplicación estricta de confianza cero en el perímetro.
  2. Política de autorización de la puerta de enlace (authzPolicy): Recurso regional que se orienta a tu Agent Gateway con policyProfile: REQUEST_AUTHZ y action: CUSTOM, y enruta las verificaciones de autorización a la extensión de autorización de IAP.
  3. Políticas de acceso unificado y vinculación de IAM (accessPolicy y policyBinding): Recurso global de IAM v3 evaluado por IAP. Verifica el permiso universal iap.googleapis.com/resources.egressViaIAP con las identidades de SPIFFE del emisor y las condiciones del catálogo de CEL.

Paso 1: Crea e importa la extensión de autorización de IAP v2

Crea el manifiesto de la extensión de servicio con iapPolicyVersion: "V2" y failOpen: false en el modo estricto ENFORCE:

# create authz extension config file in ENFORCE mode
cat > cfg/${AGW_NAME}-svc-ext-authz-iap.yaml << EOF
name: ${AGW_NAME}-svc-ext-authz-iap
service: iap.googleapis.com
failOpen: false
timeout: 1s
metadata:
  iapPolicyVersion: "V2"
EOF

Importa la extensión de Authz:

# import IAP v2 authz extension
gcloud service-extensions authz-extensions import ${AGW_NAME}-svc-ext-authz-iap \
  --source=cfg/${AGW_NAME}-svc-ext-authz-iap.yaml \
  --location=${REGION} \
  --project=${PROJECT_GOVERNANCE}

Verifica que la extensión de Authz esté activa:

# describe authz extension
gcloud service-extensions authz-extensions describe ${AGW_NAME}-svc-ext-authz-iap \
  --location=${REGION} \
  --project=${PROJECT_GOVERNANCE}

Resultado de muestra:

createTime: 'YYYY-MM-DDT12:34:56.789098765Z'
failOpen: false
metadata:
  iapPolicyVersion: V2
name: projects/${PROJECT_GOVERNANCE}/locations/us-central1/authzExtensions/centralized-agw-svc-ext-authz-iap
service: iap.googleapis.com
timeout: 1s

Paso 2: Crea e importa la política de autorización de la puerta de enlace

Crea una configuración de política de autorización que se adjunte a Agent Gateway y delegue la verificación de solicitudes a la extensión de autorización de IAP:

# create authz policy manifest
cat > cfg/${AGW_NAME}-authz-policy-profile-iap.yaml << EOF
name: ${AGW_NAME}-authz-policy-profile-iap
target:
  resources:
    - "projects/${PROJECT_GOVERNANCE}/locations/${REGION}/agentGateways/${AGW_NAME}"
policyProfile: REQUEST_AUTHZ
action: CUSTOM
customProvider:
  authzExtension:
    resources:
      - "projects/${PROJECT_GOVERNANCE}/locations/${REGION}/authzExtensions/${AGW_NAME}-svc-ext-authz-iap"
EOF

Importa la política de autorización:

# import authz policy
gcloud beta network-security authz-policies import ${AGW_NAME}-authz-policy-profile-iap \
  --source=cfg/${AGW_NAME}-authz-policy-profile-iap.yaml \
  --location=${REGION} \
  --project=${PROJECT_GOVERNANCE}

Verifica la política de autorización activa:

# describe authz policy
gcloud beta network-security authz-policies describe ${AGW_NAME}-authz-policy-profile-iap \
  --location=${REGION} \
  --project=${PROJECT_GOVERNANCE}

Paso 3: Crea la política de acceso unificado inicial (regla 1: APIs principales de Google)

Crea cfg/uap-rules.json con la regla 1 que autoriza a los tres principalSet del proyecto a llegar a core-gapi-services:

# create initial unified access policy rules manifest
cat > cfg/uap-rules.json << EOF
[
  {
    "description": "Rule 1: Allow agent runtimes across all 3 projects to reach Core Google APIs",
    "effect": "ALLOW",
    "principals": [
      "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_GOVERNANCE}",
      "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_CONCIERGE}",
      "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_SELLERS}"
    ],
    "operation": {
      "permissions": [
        "iap.googleapis.com/resources.egressViaIAP"
      ]
    },
    "conditions": {
      "iap.googleapis.com": {
        "expression": \
        "destination.is_registered == true && \
         destination.agent_registry.resource_type == 'ENDPOINT' && ( \
         destination.agent_registry.endpoint.name == 'projects/${PROJECT_GOVERNANCE}/locations/${REGION}/endpoints/core-gapi-services' || \
         destination.agent_registry.endpoint.name == 'projects/${PROJECT_GOVERNANCE}/locations/${REGION}/endpoints/${ENDPOINT_ID}' || \
         destination.agent_registry.endpoint.name == 'projects/${PROJECT_NUMBER_GOVERNANCE}/locations/${REGION}/endpoints/${ENDPOINT_ID}')"
      }
    }
  }
]
EOF

Paso 4: Crea y vincula la política de acceso de IAM

Crea la política de acceso de IAM global:

# create global IAM access policy
gcloud iam access-policies create ${UAP_POLICY_NAME} \
  --details-rules=cfg/uap-rules.json \
  --project=${PROJECT_GOVERNANCE} \
  --location=global

Vincula la política de acceso a PROJECT_GOVERNANCE:

# bind access policy to governance project
gcloud iam policy-bindings create ${UAP_BINDING_NAME} \
  --policy="projects/${PROJECT_GOVERNANCE}/locations/global/accessPolicies/${UAP_POLICY_NAME}" \
  --target-resource="//cloudresourcemanager.googleapis.com/projects/${PROJECT_GOVERNANCE}" \
  --project=${PROJECT_GOVERNANCE} \
  --location=global

Verifica que la vinculación de la política esté activa:

# verify policy binding
gcloud iam policy-bindings describe ${UAP_BINDING_NAME} \
  --project=${PROJECT_GOVERNANCE} \
  --location=global

Resultado de muestra:

name: projects/${PROJECT_GOVERNANCE}/locations/global/policyBindings/uap-binding-centralized-agw
policy: projects/${PROJECT_GOVERNANCE}/locations/global/accessPolicies/uap-policy-centralized-agw
policyKind: ACCESS_POLICY
target:
  resource: //cloudresourcemanager.googleapis.com/projects/${PROJECT_GOVERNANCE}

La salida de las APIs de Cloud fundamentales de Google Cloud ahora está autorizada de forma segura en los tres proyectos en el modo estricto de ENFORCE.

Con esto concluye la configuración de la autorización de la puerta de enlace. A continuación, pasaremos a la sección Configura los permisos de IAM entre proyectos.

6. IAM entre proyectos

Configura los permisos de IAM entre proyectos

En esta topología de varios proyectos, los Agent Runtimes residen en proyectos de radio (PROJECT_CONCIERGE y PROJECT_SELLERS), mientras que el Agent Gateway central y el Agent Registry residen en PROJECT_GOVERNANCE.

Debido a que los proyectos de Google Cloud son perímetros de seguridad aislados, el acceso entre proyectos se debe otorgar de forma explícita en dos capas operativas:

  1. Plano de control (tiempo de implementación): Cuando se implementa un contenedor de agente configurado con --agent-gateway-config, el agente de servicio de Agent Runtime (service-@gcp-sa-aiplatform.iam.gserviceaccount.com) del proyecto de radio debe adjuntar el contenedor a la puerta de enlace central. Creamos un rol personalizado mínimo (ar_agw_cross_project_sa) que otorga networkservices.agentGateways.use, get y operations.get en PROJECT_GOVERNANCE.
  2. Plano de datos (ejecución del entorno de ejecución):
    • Detección de catálogos: Las identidades de radio necesitan roles/agentregistry.viewer en PROJECT_GOVERNANCE para resolver los extremos de agentes de destino de forma dinámica.
    • Invocación de destino: El agente de Concierge necesita roles/aiplatform.user en PROJECT_SELLERS para ejecutar consultas en los motores de razonamiento del vendedor.

Crea un rol de IAM personalizado en PROJECT_GOVERNANCE

# create custom role in central governance project
gcloud iam roles create ar_agw_cross_project_sa \
  --project=${PROJECT_GOVERNANCE} \
  --title="Runtime Agent Gateway Cross-Project SA" \
  --description="Custom role for cross-project service agents to access Central Agent Gateway" \
  --permissions="networkservices.agentGateways.get,networkservices.agentGateways.use,networkservices.operations.get" \
  --stage="GA"

Asigna un rol personalizado a los agentes de servicio de Agent Runtime

# 1. ensure aiplatform service identities are provisioned across all projects
for PROJ in ${PROJECT_GOVERNANCE} ${PROJECT_CONCIERGE} ${PROJECT_SELLERS}; do
  gcloud beta services identity create --service=aiplatform.googleapis.com --project=${PROJ}
done
# 2. derive aiplatform service agent emails
export CONCIERGE_AI_SA="service-${PROJECT_NUMBER_CONCIERGE}@gcp-sa-aiplatform.iam.gserviceaccount.com"
export CONCIERGE_RE_SA="service-${PROJECT_NUMBER_CONCIERGE}@gcp-sa-aiplatform-re.iam.gserviceaccount.com"
export CONCIERGE_COMPUTE_SA="${PROJECT_NUMBER_CONCIERGE}-compute@developer.gserviceaccount.com"

export SELLERS_AI_SA="service-${PROJECT_NUMBER_SELLERS}@gcp-sa-aiplatform.iam.gserviceaccount.com"
export SELLERS_RE_SA="service-${PROJECT_NUMBER_SELLERS}@gcp-sa-aiplatform-re.iam.gserviceaccount.com"
export SELLERS_COMPUTE_SA="${PROJECT_NUMBER_SELLERS}-compute@developer.gserviceaccount.com"
# 3. grant custom role & network viewer to Concierge and Sellers Service Agents
for SA in ${CONCIERGE_AI_SA} ${SELLERS_AI_SA}; do
  gcloud projects add-iam-policy-binding ${PROJECT_GOVERNANCE} \
    --member="serviceAccount:${SA}" \
    --role="projects/${PROJECT_GOVERNANCE}/roles/ar_agw_cross_project_sa" \
    --condition=None

  gcloud projects add-iam-policy-binding ${PROJECT_GOVERNANCE} \
    --member="serviceAccount:${SA}" \
    --role="roles/networkservices.viewer" \
    --condition=None
done
# 4. grant agent registry viewer on Governance Project for dynamic autodiscovery
for MEMBER in "serviceAccount:${CONCIERGE_AI_SA}" "serviceAccount:${CONCIERGE_RE_SA}" "serviceAccount:${CONCIERGE_COMPUTE_SA}" "serviceAccount:${SELLERS_AI_SA}" "serviceAccount:${SELLERS_RE_SA}" "serviceAccount:${SELLERS_COMPUTE_SA}" "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_CONCIERGE}" "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_SELLERS}"; do
  gcloud projects add-iam-policy-binding ${PROJECT_GOVERNANCE} \
    --member="${MEMBER}" \
    --role="roles/agentregistry.viewer" \
    --condition=None
done
# 5. grant agent project viewer on Governance Project for dynamic autodiscovery
for SA in ${CONCIERGE_COMPUTE_SA} ${CONCIERGE_AI_SA}; do
  gcloud projects add-iam-policy-binding ${PROJECT_GOVERNANCE} \
    --member="serviceAccount:${SA}" \
    --role="roles/viewer" \
    --condition=None
done
# 6. grant aitplatform user on Sellers project to Concierge for cross-project A2A invocation
for MEMBER in "serviceAccount:${CONCIERGE_AI_SA}" "serviceAccount:${CONCIERGE_RE_SA}" "serviceAccount:${CONCIERGE_COMPUTE_SA}" "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_CONCIERGE}"; do
  gcloud projects add-iam-policy-binding ${PROJECT_SELLERS} \
    --member="${MEMBER}" \
    --role="roles/aiplatform.user" \
    --condition=None
done

Con esto concluye la configuración de IAM entre proyectos. A continuación, pasaremos a la sección Implementa los agentes de vendedor y de conserje.

7. Agent Runtime

Implementa agentes de ventas y conserjería

La base de código de la aplicación de varios agentes y las secuencias de comandos de implementación que se usan para este codelab se mantienen en un repositorio de GitHub de Google Cloud remoto. En los siguientes pasos, se clonará el repositorio de forma local, se copiarán los archivos necesarios en la estructura del directorio de trabajo actual, se limpiarán los archivos temporales y se instalarán las dependencias con uv.

Recupera artefactos remotos

# clone remote repository to temp local dir
git clone https://github.com/GoogleCloudPlatform/cloud-networking-solutions.git ./temp_agw_multiproject
# copy multi-agent application files to current working directory
cp -r temp_agw_multiproject/codelabs/agw-multiproject ./cross-project-multiagent
# remove temporary directory
rm -rf temp_agw_multiproject
# install dependencies
uv sync --directory ./cross-project-multiagent

Crea un bucket de etapa de pruebas central compartido

# create shared central staging bucket
gcloud storage buckets create gs://${PROJECT_GOVERNANCE}-shared-staging \
  --project=${PROJECT_GOVERNANCE} \
  --location=${REGION}
# grant cross-project read/write access to runtime service agents
gcloud storage buckets add-iam-policy-binding gs://${PROJECT_GOVERNANCE}-shared-staging \
  --member="serviceAccount:service-${PROJECT_NUMBER_CONCIERGE}@gcp-sa-aiplatform.iam.gserviceaccount.com" \
  --role="roles/storage.objectAdmin"

gcloud storage buckets add-iam-policy-binding gs://${PROJECT_GOVERNANCE}-shared-staging \
  --member="serviceAccount:service-${PROJECT_NUMBER_SELLERS}@gcp-sa-aiplatform.iam.gserviceaccount.com" \
  --role="roles/storage.objectAdmin"

Cómo funciona la vinculación de Agent Gateway entre proyectos

En este paso, implementarás los agentes de vendedores en el proyecto de radio (PROJECT_SELLERS) y, al mismo tiempo, los configurarás para que enruten la salida a través de la Agent Gateway central en PROJECT_GOVERNANCE:

# !-- for example purposes -- NOT a command to execute --!
# snippet from deploy_burger.py
burger_config = {
    "staging_bucket": staging_bucket_uri,
    "gcs_dir_name": "burger_agent",
    "display_name": "burger-seller-agent-adk",
    "identity_type": "AGENT_IDENTITY",
    "agent_gateway_config": {
        "agent_to_anywhere_config": {
            "agent_gateway": f"projects/{args.governance_project}/locations/{args.region}/agentGateways/{args.gateway}"
        }
    },
}
deployed_burger = client.agent_engines.create(agent=burger_playground, config=burger_config)

Como la Regla 1 se estableció antes en nuestra Política de acceso unificado, las solicitudes de inicialización de contenedores a las APIs de Google Cloud se permiten a través de la puerta de enlace sin interrupciones.

Implementa agentes de venta de hamburguesas y pizzas en PROJECT_SELLERS

# 1. deploy Burger Seller Agent to PROJECT_SELLERS
uv run --directory ./cross-project-multiagent python deploy_burger.py \
  --project=${PROJECT_SELLERS} \
  --region=${REGION} \
  --governance-project=${PROJECT_GOVERNANCE} \
  --gateway=projects/${PROJECT_GOVERNANCE}/locations/${REGION}/agentGateways/${AGW_NAME}
# 2. deploy Pizza Seller Agent to PROJECT_SELLERS
uv run --directory ./cross-project-multiagent python deploy_pizza.py \
  --project=${PROJECT_SELLERS} \
  --region=${REGION} \
  --governance-project=${PROJECT_GOVERNANCE} \
  --gateway=projects/${PROJECT_GOVERNANCE}/locations/${REGION}/agentGateways/${AGW_NAME}

Valida el enrutamiento de la puerta de enlace del vendedor

# retrieve deployed seller reasoning engine IDs
export BURGER_ENGINE_ID=$(grep BURGER_SELLER_AGENT_ID cross-project-multiagent/burger_agent.env | awk -F'/' '{print $NF}')
export PIZZA_ENGINE_ID=$(grep PIZZA_SELLER_AGENT_ID cross-project-multiagent/pizza_agent.env | awk -F'/' '{print $NF}')

echo "Burger Engine ID: ${BURGER_ENGINE_ID}"
echo "Pizza Engine ID:  ${PIZZA_ENGINE_ID}"
# inspect runtime configuration for both Seller Agents
for ENGINE_ID in ${BURGER_ENGINE_ID} ${PIZZA_ENGINE_ID}; do
  curl -s -X GET "https://${REGION}-aiplatform.googleapis.com/v1beta1/projects/${PROJECT_SELLERS}/locations/${REGION}/reasoningEngines/${ENGINE_ID}" \
    -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
    -H "Content-Type: application/json" \
    | jq '{displayName: .displayName, identityType: .spec.identityType, effectiveIdentity: .spec.effectiveIdentity, agentGatewayConfig: .spec.deploymentSpec.agentGatewayConfig}'
done

Implementa el agente de conserjería de compras en PROJECT_CONCIERGE

# deploy Purchasing Concierge to PROJECT_CONCIERGE
uv run --directory ./cross-project-multiagent python deploy_concierge_adk.py \
  --project=${PROJECT_CONCIERGE} \
  --region=${REGION} \
  --staging-bucket=gs://${PROJECT_GOVERNANCE}-shared-staging \
  --gateway-name=${AGW_NAME} \
  --gateway-project=${PROJECT_GOVERNANCE}

Valida el enrutamiento de la puerta de enlace de compras

# retrieve Concierge engine ID
export CONCIERGE_ENGINE_ID=$(grep CONCIERGE_AGENT_ID cross-project-multiagent/concierge_agent.env | awk -F'/' '{print $NF}')
echo "Concierge Engine ID: ${CONCIERGE_ENGINE_ID}"
# inspect runtime configuration for Purchasing Concierge
curl -s -X GET "https://${REGION}-aiplatform.googleapis.com/v1beta1/projects/${PROJECT_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "Content-Type: application/json" \
  | jq '{displayName: .displayName, identityType: .spec.identityType, effectiveIdentity: .spec.effectiveIdentity, agentGatewayConfig: .spec.deploymentSpec.agentGatewayConfig}'

En el resultado, se debería mostrar la identidad y el proyecto del Agent Runtime del agente de Concierge, y la vinculación a la Agent Gateway del proyecto de administración.

{
  "displayName": "purchasing-concierge-adk",
  "identityType": "AGENT_IDENTITY",
  "effectiveIdentity": "agents.global.org-${ORG_ID}.system.id.goog/resources/aiplatform/projects/${PROJECT_CONCIERGE}/locations/us-central1/reasoningEngines/${CONCIERGE_ENGINE_ID}",
  "agentGatewayConfig": {
    "agentToAnywhereConfig": {
      "agentGateway": "projects/${PROJECT_GOVERNANCE}/locations/us-central1/agentGateways/centralized-agw"
    }
  }
}

Con esto concluyen las implementaciones de agentes. A continuación, pasaremos a la sección Registra agentes en el registro central de agentes.

8. Registro multiproyecto

Registra agentes en el registro central de agentes

Registra los tres agentes en el registro central de agentes en PROJECT_GOVERNANCE con extremos de mTLS regionales entre proyectos y números de proyecto numéricos.

Registra servicios como agentes que no son A2A en Agent Registry

# 1. register Burger Seller Agent
gcloud agent-registry services create burger-seller-agent \
  --project=${PROJECT_GOVERNANCE} \
  --location=${REGION} \
  --display-name="Burger Seller Agent" \
  --description="Specialist agent that sells burgers and fries" \
  --agent-spec-type=no-spec \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${BURGER_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com/v1beta1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${BURGER_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com:443/v1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${BURGER_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com:443/v1beta1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${BURGER_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com/v1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${BURGER_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com/v1beta1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${BURGER_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com:443/v1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${BURGER_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com:443/v1beta1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${BURGER_ENGINE_ID}:query
# 2. register Pizza Seller Agent
gcloud agent-registry services create pizza-seller-agent \
  --project=${PROJECT_GOVERNANCE} \
  --location=${REGION} \
  --display-name="Pizza Seller Agent" \
  --description="Specialist agent that sells pizzas and pasta" \
  --agent-spec-type=no-spec \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${PIZZA_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com/v1beta1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${PIZZA_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com:443/v1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${PIZZA_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com:443/v1beta1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${PIZZA_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com/v1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${PIZZA_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com/v1beta1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${PIZZA_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com:443/v1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${PIZZA_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com:443/v1beta1/projects/${PROJECT_NUMBER_SELLERS}/locations/${REGION}/reasoningEngines/${PIZZA_ENGINE_ID}:query
# 3. register Purchasing Concierge Agent
gcloud agent-registry services create purchasing-concierge-adk \
  --project=${PROJECT_GOVERNANCE} \
  --location=${REGION} \
  --display-name="Purchasing Concierge Agent" \
  --description="Orchestrator concierge agent that routes purchasing requests" \
  --agent-spec-type=no-spec \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com/v1beta1/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com:443/v1/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.googleapis.com:443/v1beta1/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com/v1/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com/v1beta1/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com:443/v1/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}:query \
  --interfaces=protocolBinding=JSONRPC,url=https://${REGION}-aiplatform.mtls.googleapis.com:443/v1beta1/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}:query

Captura los IDs de Agent Registry subyacentes

# capture underlying Agent Registry Agent UUIDs
export BURGER_AGENT_ID=$(gcloud agent-registry services describe burger-seller-agent --project=${PROJECT_GOVERNANCE} --location=${REGION} --format="value(registryResource)" | awk -F'/' '{print $NF}')
export PIZZA_AGENT_ID=$(gcloud agent-registry services describe pizza-seller-agent --project=${PROJECT_GOVERNANCE} --location=${REGION} --format="value(registryResource)" | awk -F'/' '{print $NF}')
export CONCIERGE_AGENT_ID=$(gcloud agent-registry services describe purchasing-concierge-adk --project=${PROJECT_GOVERNANCE} --location=${REGION} --format="value(registryResource)" | awk -F'/' '{print $NF}')

echo "Burger Agent ID:    ${BURGER_AGENT_ID}"
echo "Pizza Agent ID:     ${PIZZA_AGENT_ID}"
echo "Concierge Agent ID: ${CONCIERGE_AGENT_ID}"

Con esto, concluye la configuración del registro. A continuación, se encuentra la sección Configura las políticas de salida de A2A.

9. Políticas de UAP

Configura políticas de salida de A2A en la política de acceso unificado

En la arquitectura de rechazo predeterminado de Agent Gateway en el modo estricto ENFORCE, ocurre lo siguiente:

  1. Regla 1 (APIs de Google Cloud de referencia): Permite que los contenedores de agentes en los 3 proyectos lleguen a core-gapi-services.
  2. Regla 2 (Agente de ventas de hamburguesas: PERMITIR): Permite que la instancia del agente de Concierge de compras invoque específicamente al agente de ventas de hamburguesas.
  3. Agente de vendedor de pizzas (RECHAZADO de forma predeterminada): Se excluyó de forma intencional de las reglas de la política. En el modo ENFORCE (failOpen: false), cualquier intento de Concierge de invocar al vendedor de pizzas se finalizará de inmediato en el perímetro de la puerta de enlace con HTTP 403 Forbidden.

Formular la identidad del agente de conserjería

# formulate the exact SPIFFE machine identity for the Concierge Agent
export CONCIERGE_SPIFFE_PRINCIPAL="principal://agents.global.org-${ORG_ID}.system.id.goog/resources/aiplatform/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}"
echo "Concierge SPIFFE Principal: ${CONCIERGE_SPIFFE_PRINCIPAL}"

Actualiza el manifiesto con las reglas 1 y 2

Para actualizar de forma segura la política de acceso sin alterar las reglas existentes, define el manifiesto consolidado completo que contiene la Regla 1 (APIs de Google principales) y la Regla 2 (Agente de Burger Seller):

# create consolidated policy manifest with Rule 1 and Rule 2
cat > cfg/uap-rules-consolidated.json << EOF
[
  {
    "description": "Rule 1: Allow agent runtimes across all 3 projects to reach Core Google APIs",
    "effect": "ALLOW",
    "principals": [
      "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_GOVERNANCE}",
      "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_CONCIERGE}",
      "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_SELLERS}"
    ],
    "operation": {
      "permissions": [
        "iap.googleapis.com/resources.egressViaIAP"
      ]
    },
    "conditions": {
      "iap.googleapis.com": {
        "expression": \
        "destination.is_registered == true && \
         destination.agent_registry.resource_type == 'ENDPOINT' && ( \
         destination.agent_registry.endpoint.name == 'projects/${PROJECT_GOVERNANCE}/locations/${REGION}/endpoints/core-gapi-services' || \
         destination.agent_registry.endpoint.name == 'projects/${PROJECT_GOVERNANCE}/locations/${REGION}/endpoints/${ENDPOINT_ID}' || \
         destination.agent_registry.endpoint.name == 'projects/${PROJECT_NUMBER_GOVERNANCE}/locations/${REGION}/endpoints/${ENDPOINT_ID}')"
      }
    }
  },
  {
    "description": "Rule 2: Allow Purchasing Concierge to invoke Burger Seller Agent via Central Gateway",
    "effect": "ALLOW",
    "principals": [
      "${CONCIERGE_SPIFFE_PRINCIPAL}",
      "principal://agents.global.org-${ORG_ID}.system.id.goog/attribute.agentIdentity/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}",
      "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.agentIdentity/projects/${PROJECT_NUMBER_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}"
    ],
    "operation": {
      "permissions": [
        "iap.googleapis.com/resources.egressViaIAP"
      ]
    },
    "conditions": {
      "iap.googleapis.com": {
        "expression": \
        "destination.is_registered == true && \
         destination.agent_registry.resource_type == 'AGENT' && ( \
         destination.agent_registry.agent.name == 'projects/${PROJECT_GOVERNANCE}/locations/${REGION}/agents/burger-seller-agent' || \
         destination.agent_registry.agent.name == 'projects/${PROJECT_GOVERNANCE}/locations/${REGION}/agents/${BURGER_AGENT_ID}' || \
         destination.agent_registry.agent.name == 'projects/${PROJECT_NUMBER_GOVERNANCE}/locations/${REGION}/agents/${BURGER_AGENT_ID}')"
      }
    }
  }
]
EOF

Aplica la política de acceso actualizada

Aplica el manifiesto de reglas consolidadas de forma declarativa con --details-rules:

# update IAM access policy with consolidated manifest (Rule 1 + Rule 2)
gcloud iam access-policies update ${UAP_POLICY_NAME} \
  --details-rules=cfg/uap-rules-consolidated.json \
  --project=${PROJECT_GOVERNANCE} \
  --location=global

Verifica los detalles de la política de acceso de IAM

# inspect updated access policy
gcloud iam access-policies describe ${UAP_POLICY_NAME} \
  --project=${PROJECT_GOVERNANCE} \
  --location=global

Resultado de muestra:

details:
  rules:
  - conditions:
      iap.googleapis.com:
        expression: destination.is_registered == true && destination.agent_registry.resource_type
          == 'ENDPOINT' && (destination.agent_registry.endpoint.name == 'projects/${PROJECT_GOVERNANCE}/locations/us-central1/endpoints/core-gapi-services'
          || destination.agent_registry.endpoint.name == 'projects/${PROJECT_NUMBER_GOVERNANCE}/locations/us-central1/endpoints/${ENDPOINT_ID}')
    description: 'Rule 1: Allow agent runtimes across all 3 projects to reach Core
      Google APIs'
    effect: ALLOW
    operation:
      permissions:
      - iap.googleapis.com/resources.egressViaIAP
    principals:
    - principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_GOVERNANCE}
    - principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_CONCIERGE}
    - principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_SELLERS}
  - conditions:
      iap.googleapis.com:
        expression: (destination.is_registered == true) && (destination.agent_registry.resource_type
          == 'AGENT') && (destination.agent_registry.agent.name == 'projects/${PROJECT_GOVERNANCE}/locations/us-central1/agents/burger-seller-agent'
          || destination.agent_registry.agent.name == 'projects/${PROJECT_NUMBER_GOVERNANCE}/locations/us-central1/agents/${BURGER_AGENT_ID}')
    description: 'Rule 2: Allow Purchasing Concierge to invoke Burger Seller Agent
      via Central Gateway'
    effect: ALLOW
    operation:
      permissions:
      - iap.googleapis.com/resources.egressViaIAP
    principals:
    - principal://agents.global.org-${ORG_ID}.system.id.goog/resources/aiplatform/projects/${PROJECT_NUMBER_CONCIERGE}/locations/us-central1/reasoningEngines/${CONCIERGE_ENGINE_ID}
name: projects/${PROJECT_GOVERNANCE}/locations/global/accessPolicies/uap-policy-centralized-agw

Con esto concluye la configuración de la política. A continuación, se encuentra la sección Prueba y verifica las políticas de administración.

10. Verifica las políticas

Prueba y verifica las políticas de administración con Cloud Logging

En esta sección, probarás las interacciones entre agentes (A2A) de diferentes proyectos en el AI Playground de Agent Runtime, observarás el bloqueo real de HTTP 403 Forbidden en el modo estricto de ENFORCE, modificarás la política de acceso unificado en vivo y validarás la aprobación inmediata de pedidos.

Paso 1: Abre la zona de pruebas de IA de Agent Runtime en PROJECT_CONCIERGE

  1. Abre la consola de Google Cloud.
  2. En la barra superior del selector de proyectos, cambia a PROJECT_CONCIERGE.
  3. En el menú de navegación, ve a Agent Platform > Agentes > Implementaciones.
  4. Haz clic en purchasing-concierge-adk.
  5. Selecciona Playground para abrir la interfaz de chat interactiva en el lado derecho de la pantalla.

Paso 2: Prueba el pedido de hamburguesa (coincidencia con la regla 2 -> 200 OK)

En la ventana de chat de Playground, envía la siguiente instrucción de pedido:

I would like 10 Classic Cheeseburgers. Place this order now.

Si se necesita una respuesta de confirmación, envía la siguiente:

Confirmed, please place the order.

Como alternativa, realiza la prueba de manera programática desde Cloud Shell o la terminal:

uv run --directory ./cross-project-multiagent python -c "
import vertexai
from vertexai.preview import reasoning_engines
vertexai.init(project='${PROJECT_CONCIERGE}', location='${REGION}')
agent = reasoning_engines.ReasoningEngine('projects/${PROJECT_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}')
response = agent.query(input={'message': 'I would like 22 Spicy Cajun Burgers please. Place this order now.'})
print(response)
"

Y si se necesita una respuesta de confirmación, usa este comando:

uv run --directory ./cross-project-multiagent python -c "
import vertexai
from vertexai.preview import reasoning_engines
vertexai.init(project='${PROJECT_CONCIERGE}', location='${REGION}')
agent = reasoning_engines.ReasoningEngine('projects/${PROJECT_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}')
response = agent.query(input='Yes please place the order now.')
print(response['text'])
"

Qué sucede tras bambalinas:

  1. Descubrimiento dinámico: Durante el inicio de la sesión, el Concierge de compras consultó el Agent Registry central en PROJECT_GOVERNANCE (a través de core-gapi-services por medio de Agent Gateway autorizado por la regla 1) para descubrir el extremo de mTLS regional para burger-seller-agent.
  2. Resolución de intents e invocación de A2A: Gemini dentro del asistente de compras analiza el intent del pedido de comida y, luego, invoca al agente de venta de hamburguesas a través de una RPC saliente a https://${REGION}-aiplatform.mtls.googleapis.com/.../reasoningEngines/${BURGER_ENGINE_ID}.
  3. Intercepción de la puerta de enlace y propagación de SPIFFE: agent_gateway_config captura el tráfico de salida y lo dirige a la puerta de enlace central del agente en PROJECT_GOVERNANCE, que lleva la identidad criptográfica de SPIFFE de Concierge (principal://...).
  4. Evaluación de la política de IAP v2: El Agent Gateway central invoca la extensión de autorización de IAP (authzExtension). IAP v2 evalúa la regla 2 en la política de acceso unificado de IAM. Debido a que el emisor coincide con ${CONCIERGE_SPIFFE_PRINCIPAL} y el destino coincide con burger-seller-agent, IAP devuelve ALLOW (granted: true).
  5. Ejecución entre proyectos: Agent Gateway envía la solicitud autorizada entre proyectos a PROJECT_SELLERS, donde el motor de razonamiento de Burger Seller procesa el pedido y devuelve la confirmación.

Respuesta esperada:

Your order for 10 Classic Cheeseburger(s) has been placed!
Here is a summary of your order:
- 10x Classic Cheeseburger @ IDR 85,000/each = IDR 850,000

Total: IDR 850,000
Your Order ID is: e8f9c732-f347-4cc4-acff-cfe09ccbeddd

Paso 3: Inspecciona los registros de auditoría de Agent Gateway y de IAP v2 (HTTP 200/ALLOWED)

Consulta los registros de solicitudes de Agent Gateway en PROJECT_GOVERNANCE:

# query Agent Gateway logs for successful 200 OK requests
gcloud logging read "
  logName=\"projects/${PROJECT_GOVERNANCE}/logs/networkservices.googleapis.com%2Fgateway_requests\"
  AND jsonPayload.authzPolicyInfo.result=\"ALLOWED\"
" \
  --project="${PROJECT_GOVERNANCE}" \
  --limit=10 \
  --format="table(
    timestamp.date('%H:%M:%S'):label=TIME,
    httpRequest.requestMethod:label=METHOD,
    httpRequest.status:label=STATUS,
    jsonPayload.authzPolicyInfo.result:label=AUTHZ,
    httpRequest.requestUrl:label=URL
  )"

Los registros deben capturar el tráfico saliente que se origina en ambos proyectos de radio (PROJECT_CONCIERGE y PROJECT_SELLERS) con campos de salida para las llamadas de razonamiento de Gemini (generateContent), la telemetría de Cloud Trace (/v1/traces) y las búsquedas de credenciales de IAM, que se interceptan y autorizan de forma transparente por la regla 1 (core-gapi-services).

Consulta los registros de acceso a los datos de auditoría de Cloud de IAP v2 para verificar la versión de la política POLICY_VERSION_V2:

# query IAP v2 audit logs with shortened principal and resource fields
gcloud logging read "
  logName=\"projects/${PROJECT_GOVERNANCE}/logs/cloudaudit.googleapis.com%2Fdata_access\"
  AND protoPayload.serviceName=\"iap.googleapis.com\"
" \
  --project="${PROJECT_GOVERNANCE}" \
  --limit=5 \
  --format="table(
    timestamp.date('%H:%M:%S'):label=TIME,
    protoPayload.authenticationInfo.principalSubject.sub('\.global\..*\/reasoningEngines\/', '.[...]/reasoningEngines/'):label=CALLER,
    protoPayload.authorizationInfo[0].granted:label=GRANTED,
    protoPayload.metadata.destination.agent_registry.resource_type.basename():label=TYPE,
    protoPayload.metadata.destination.agent_registry.resource_id.basename():label=RESOURCE_ID,
    protoPayload.authorizationInfo[0].permission.basename():label=PERMISSION
  )"

Resultado de muestra:

TIME      CALLER                                                            GRANTED  TYPE      RESOURCE_ID     PERMISSION
HH:MM:SS  principal://agents.[...]/reasoningEngines/${CONCIERGE_ENGINE_ID}  True     Endpoint  ${ENDPOINT_ID}  resources.egressViaIAP
HH:MM:SS  principal://agents.[...]/reasoningEngines/${BURGER_ENGINE_ID}     True     Endpoint  ${ENDPOINT_ID}  resources.egressViaIAP
HH:MM:SS  principal://agents.[...]/reasoningEngines/${CONCIERGE_ENGINE_ID}  True     Endpoint  ${ENDPOINT_ID}  resources.egressViaIAP
HH:MM:SS  principal://agents.[...]/reasoningEngines/${BURGER_ENGINE_ID}     True     Endpoint  ${ENDPOINT_ID}  resources.egressViaIAP

Paso 4: Prueba el pedido de pizza (Default Deny -> HTTP 403 Forbidden ENFORCED)

En la misma ventana de chat de Playground, envía la siguiente instrucción de pedido de pizza:

I would like 10 BBQ Chicken Pizzas. Place this order now.

Si se necesita una respuesta de confirmación, envía la siguiente:

Confirmed, please place the order.

Como alternativa, realiza la prueba de manera programática desde Cloud Shell o la terminal:

uv run --directory ./cross-project-multiagent python -c "
import vertexai
from vertexai.preview import reasoning_engines
vertexai.init(project='${PROJECT_CONCIERGE}', location='${REGION}')
agent = reasoning_engines.ReasoningEngine('projects/${PROJECT_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}')
response = agent.query(input='I would like 8 Hawaiian pizzas, please. Place this order now.')
print(response)
"

Y si se necesita una respuesta de confirmación, usa este comando:

uv run --directory ./cross-project-multiagent python -c "
import vertexai
from vertexai.preview import reasoning_engines
vertexai.init(project='${PROJECT_CONCIERGE}', location='${REGION}')
agent = reasoning_engines.ReasoningEngine('projects/${PROJECT_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}')
response = agent.query(input='Yes please place the order now.')
print(response['text'])
"

Respuesta esperada:

I apologize, but I am unable to process that request at the moment. It seems
there was an issue connecting to the pizza seller agent. Please try again later.

Qué sucede tras bambalinas:

  1. Descubrimiento dinámico: El Concierge de compras resolvió el extremo pizza-seller-agent del Agent Registry central durante el inicio.
  2. Resolución de intents e invocación de A2A: Gemini dentro del Concierge de compras intenta enviar la solicitud de pedido de pizza al extremo del vendedor de pizzas en PROJECT_SELLERS.
  3. Intercepción de Agent Gateway: agent_gateway_config captura la RPC saliente y la dirige al Agent Gateway central.
  4. Evaluación de políticas de IAP v2 (denegación predeterminada): El Agent Gateway central invoca a IAP v2. Debido a que no existe ninguna regla en la política de acceso unificado que coincida con pizza-seller-agent, IAP devuelve DENY (granted: false).
  5. Bloqueo estricto del perímetro: Debido a que la extensión de autorización está en modo de aplicación forzosa (failOpen: false), el Agent Gateway central finaliza de inmediato la conexión saliente y devuelve HTTP 403 Forbidden. El tráfico nunca sale de la puerta de enlace ni llega a PROJECT_SELLERS.

Paso 5: Inspecciona los registros de Agent Gateway para ver las solicitudes bloqueadas (HTTP 403/RECHAZADA)

# query Agent Gateway logs for blocked 403 requests
gcloud logging read "
  logName=\"projects/${PROJECT_GOVERNANCE}/logs/networkservices.googleapis.com%2Fgateway_requests\"
  AND httpRequest.status=403
" \
  --project="${PROJECT_GOVERNANCE}" \
  --limit=5 \
  --format="table(
    timestamp.date('%H:%M:%S'):label=TIME,
    httpRequest.requestMethod:label=METHOD,
    httpRequest.status:label=STATUS,
    jsonPayload.authzPolicyInfo.result:label=AUTHZ,
    httpRequest.requestUrl:label=URL
  )"

Resultado de muestra de un registro de denegación:

TIME      METHOD  STATUS  AUTHZ   URL
HH:MM:SS  POST    403     DENIED  https://us-central1-aiplatform.mtls.googleapis.com/v1beta1/projects/${PROJECT_SELLERS}/locations/us-central1/reasoningEngines/${PIZZA_ENGINE_ID}:query

Consulta los registros de auditoría de acceso a los datos de IAP v2 para ver la decisión de denegación:

# query IAP v2 audit logs with shortened principal and resource fields
gcloud logging read "
  logName=\"projects/${PROJECT_GOVERNANCE}/logs/cloudaudit.googleapis.com%2Fdata_access\"
  AND protoPayload.serviceName=\"iap.googleapis.com\"
" \
  --project="${PROJECT_GOVERNANCE}" \
  --limit=5 \
  --format="table(
    timestamp.date('%H:%M:%S'):label=TIME,
    protoPayload.authenticationInfo.principalSubject.sub('\.global\..*\/reasoningEngines\/', '.[...]/reasoningEngines/'):label=CALLER,
    protoPayload.authorizationInfo[0].granted:label=GRANTED,
    protoPayload.metadata.destination.agent_registry.resource_type.basename():label=TYPE,
    protoPayload.metadata.destination.agent_registry.resource_id.basename():label=RESOURCE_ID,
    protoPayload.authorizationInfo[0].permission.basename():label=PERMISSION
  )"

Resultado de ejemplo de un registro de auditoría de denegación:

TIME      CALLER                                                            GRANTED  TYPE      RESOURCE_ID     PERMISSION
HH:MM:SS  principal://agents.[...]/reasoningEngines/${PIZZA_ENGINE_ID}      True     Endpoint  ${REGISTRY_ID}  resources.egressViaIAP
HH:MM:SS  principal://agents.[...]/reasoningEngines/${PIZZA_ENGINE_ID}      True     Endpoint  ${REGISTRY_ID}  resources.egressViaIAP
HH:MM:SS  principal://agents.[...]/reasoningEngines/${CONCIERGE_ENGINE_ID}  False    Agent     ${REGISTRY_ID}  resources.egressViaIAP
HH:MM:SS  principal://agents.[...]/reasoningEngines/${PIZZA_ENGINE_ID}      True     Endpoint  ${REGISTRY_ID}  resources.egressViaIAP

Paso 6: Otorga acceso de salida de forma dinámica al agente de pizzas

Crea un cfg/uap-rules-update-3.json nuevo para incluir la Regla 1 (APIs principales), la Regla 2 (Agente de vendedor de hamburguesas) y, ahora, la Regla 3 (Agente de vendedor de pizzas).

# create addendum to update policy manifest with Rule 3 for Pizza Agent
cat > cfg/uap-rules-update-3.json << EOF
[
  {
    "description": "Rule 3: Allow Purchasing Concierge to invoke Pizza Seller Agent via Central Gateway",
    "effect": "ALLOW",
    "principals": [
      "${CONCIERGE_SPIFFE_PRINCIPAL}"
    ],
    "operation": {
      "permissions": [
        "iap.googleapis.com/resources.egressViaIAP"
      ]
    },
    "conditions": {
      "iap.googleapis.com": {
        "expression": \
        "destination.is_registered == true && \
         destination.agent_registry.resource_type == 'AGENT' && ( \
         destination.agent_registry.agent.name == 'projects/${PROJECT_GOVERNANCE}/locations/${REGION}/agents/pizza-seller-agent' || \
         destination.agent_registry.agent.name == 'projects/${PROJECT_GOVERNANCE}/locations/${REGION}/agents/${PIZZA_AGENT_ID}' || \
         destination.agent_registry.agent.name == 'projects/${PROJECT_NUMBER_GOVERNANCE}/locations/${REGION}/agents/${PIZZA_AGENT_ID}')"
      }
    }
  }
]
EOF

Aplica la actualización de la política en vivo:

# update IAM access policy with Pizza rule
gcloud iam access-policies update ${UAP_POLICY_NAME} \
  --add-details-rules=cfg/uap-rules-update-3.json \
  --project=${PROJECT_GOVERNANCE} \
  --location=global

Paso 7: Vuelve a consultar al agente de pizzas (éxito inmediato con 200 OK)

En la ventana de chat de Playground, vuelve a enviar la instrucción para pedir pizza:

I would like 10 BBQ Chicken Pizzas. Place this order now.

Si se necesita una respuesta de confirmación, envía la siguiente:

Confirmed, please place the order.

Como alternativa, realiza la prueba de manera programática desde Cloud Shell o la terminal:

uv run --directory ./cross-project-multiagent python -c "
import vertexai
from vertexai.preview import reasoning_engines
vertexai.init(project='${PROJECT_CONCIERGE}', location='${REGION}')
agent = reasoning_engines.ReasoningEngine('projects/${PROJECT_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}')
response = agent.query(input='I would like 11 Veggie pizzas, please. Place this order now.')
print(response)
"

Y si se necesita una respuesta de confirmación, usa este comando:

uv run --directory ./cross-project-multiagent python -c "
import vertexai
from vertexai.preview import reasoning_engines
vertexai.init(project='${PROJECT_CONCIERGE}', location='${REGION}')
agent = reasoning_engines.ReasoningEngine('projects/${PROJECT_CONCIERGE}/locations/${REGION}/reasoningEngines/${CONCIERGE_ENGINE_ID}')
response = agent.query(input='Yes please place the order now.')
print(response['text'])
"

Respuesta esperada:

Your order has been placed!

**Order ID:** 8d6c13d7-31dc-4d80-b6a7-80d1e50b6411

**Order Details:**
*   10 x BBQ Chicken Pizza @ IDR 130,000 each = IDR 1,300,000

**Total: IDR 1,300,000**

Qué sucede tras bambalinas:

  1. Actualización dinámica de políticas: La actualización de la política de acceso unificado de IAM se aplica de inmediato en el motor de evaluación de IAP sin tiempo de inactividad y sin volver a implementar ningún contenedor.
  2. Invocación de A2A: El Concierge envía la solicitud a través de Central Agent Gateway.
  3. Evaluación de la política de IAP v2 (aprobación): IAP v2 coincide con la regla 3, verifica la identidad del emisor y la expresión CEL de destino, y devuelve ALLOW (granted: true).
  4. Ejecución entre proyectos: El Agent Gateway central envía el tráfico autorizado a PROJECT_SELLERS, donde el vendedor de pizzas procesa el pedido.

Paso 8: Inspecciona los registros de Agent Gateway para ver las solicitudes de pizza otorgadas

# query Agent Gateway logs for successful 200 OK requests
gcloud logging read "
  logName=\"projects/${PROJECT_GOVERNANCE}/logs/networkservices.googleapis.com%2Fgateway_requests\"
  AND jsonPayload.authzPolicyInfo.result=\"ALLOWED\"
" \
  --project="${PROJECT_GOVERNANCE}" \
  --limit=10 \
  --format="table(
    timestamp.date('%H:%M:%S'):label=TIME,
    httpRequest.requestMethod:label=METHOD,
    httpRequest.status:label=STATUS,
    jsonPayload.authzPolicyInfo.result:label=AUTHZ,
    httpRequest.requestUrl:label=URL
  )"

Resultado de ejemplo del registro de acceso otorgado:

TIME      METHOD  STATUS  AUTHZ    URL
HH:MM:SS  POST    200     ALLOWED  https://aiplatform.mtls.googleapis.com/v1beta1/projects/${PROJECT_SELLERS}/locations/global/publishers/google/models/gemini-3.5-flash:generateContent
HH:MM:SS  POST    200     ALLOWED  https://us-central1-aiplatform.mtls.googleapis.com/v1beta1/projects/${PROJECT_SELLERS}/locations/us-central1/reasoningEngines/${PIZZA_ENGINE_ID}:query

Con esto concluyen las pruebas y la verificación. A continuación, ve a la sección Limpieza.

11. Limpieza

Para evitar que se apliquen cargos a tu cuenta de Google Cloud por los recursos que usaste en este codelab, ejecuta los pasos de eliminación en orden inverso estricto de dependencia:

1. Limpia las implementaciones de motor de razonamiento

Ejecuta la secuencia de comandos cleanup_old_deployments.py incluida en ambos proyectos de entorno de ejecución para borrar los motores de razonamiento y esperar a que se completen sus operaciones de larga duración:

# delete all Reasoning Engines deployed in Concierge and Sellers projects
uv run --directory ./cross-project-multiagent python cleanup_old_deployments.py --project=${PROJECT_CONCIERGE} --region=${REGION}
uv run --directory ./cross-project-multiagent python cleanup_old_deployments.py --project=${PROJECT_SELLERS} --region=${REGION}

Como alternativa, puedes enumerar y borrar motores de razonamiento de forma intercalada:

uv run --directory ./cross-project-multiagent python -c '
import vertexai
import os
from vertexai.preview import reasoning_engines

region = os.environ.get("REGION", "us-central1")
for proj in [os.environ.get("PROJECT_CONCIERGE"), os.environ.get("PROJECT_SELLERS")]:
    if not proj:
        continue
    print(f"Cleaning reasoning engines in {proj}...")
    vertexai.init(project=proj, location=region)
    for eng in reasoning_engines.ReasoningEngine.list():
        print(f"  Deleting {eng.resource_name} ({eng.display_name})...")
        eng.delete()
'

2. Borrar servicios de Agent Registry

# delete agent registry services in Central Governance Project
for SERVICE in burger-seller-agent pizza-seller-agent purchasing-concierge-adk core-gapi-services; do
  gcloud agent-registry services delete ${SERVICE} \
    --project=${PROJECT_GOVERNANCE} \
    --location=${REGION} \
    --quiet || true
done

3. Borra la vinculación de la política de acceso unificado de IAM y la política de acceso

# 1. delete IAM policy binding
gcloud -q iam policy-bindings delete ${UAP_BINDING_NAME} \
  --project=${PROJECT_GOVERNANCE} \
  --location=global || true

# 2. delete IAM access policy
gcloud -q iam access-policies delete ${UAP_POLICY_NAME} \
  --project=${PROJECT_GOVERNANCE} \
  --location=global || true

4. Borra Agent Gateway y las políticas de seguridad

# 1. delete authorization policy
gcloud beta network-security authz-policies delete ${AGW_NAME}-authz-policy-profile-iap \
  --location=${REGION} \
  --project=${PROJECT_GOVERNANCE} --quiet || true

# 2. delete authorization extension
gcloud service-extensions authz-extensions delete ${AGW_NAME}-svc-ext-authz-iap \
  --location=${REGION} \
  --project=${PROJECT_GOVERNANCE} --quiet || true
# 3. delete agent gateway
gcloud network-services agent-gateways delete ${AGW_NAME} \
  --project=${PROJECT_GOVERNANCE} \
  --location=${REGION} --quiet || true

5. Quita las vinculaciones de IAM entre proyectos y el rol personalizado

# 1. remove custom role and network viewer bindings for spoke service agents
for NUM in "${PROJECT_NUMBER_CONCIERGE}" "${PROJECT_NUMBER_SELLERS}"; do
  SA="service-${NUM}@gcp-sa-aiplatform.iam.gserviceaccount.com"
  
  gcloud projects remove-iam-policy-binding ${PROJECT_GOVERNANCE} \
    --member="serviceAccount:${SA}" \
    --role="projects/${PROJECT_GOVERNANCE}/roles/ar_agw_cross_project_sa" --quiet || true

  gcloud projects remove-iam-policy-binding ${PROJECT_GOVERNANCE} \
    --member="serviceAccount:${SA}" \
    --role="roles/networkservices.viewer" --quiet || true
done
# 2. remove registry viewer permissions across both spoke projects
for NUM in "${PROJECT_NUMBER_CONCIERGE}" "${PROJECT_NUMBER_SELLERS}"; do
  for MEMBER in \
    "serviceAccount:service-${NUM}@gcp-sa-aiplatform.iam.gserviceaccount.com" \
    "serviceAccount:service-${NUM}@gcp-sa-aiplatform-re.iam.gserviceaccount.com" \
    "serviceAccount:${NUM}-compute@developer.gserviceaccount.com" \
    "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${NUM}"; do
      gcloud projects remove-iam-policy-binding ${PROJECT_GOVERNANCE} \
        --member="${MEMBER}" \
        --role="roles/agentregistry.viewer" --quiet || true
  done
done
# 3. remove project viewer permissions
for MEMBER in \
  "serviceAccount:${PROJECT_NUMBER_CONCIERGE}-compute@developer.gserviceaccount.com" \
  "serviceAccount:service-${PROJECT_NUMBER_CONCIERGE}@gcp-sa-aiplatform.iam.gserviceaccount.com"; do
    gcloud projects remove-iam-policy-binding ${PROJECT_GOVERNANCE} \
      --member="${MEMBER}" \
      --role="roles/viewer" --quiet || true
done
# 4. remove spoke-to-spoke delegation in Sellers project
for MEMBER in \
  "serviceAccount:service-${PROJECT_NUMBER_CONCIERGE}@gcp-sa-aiplatform.iam.gserviceaccount.com" \
  "serviceAccount:service-${PROJECT_NUMBER_CONCIERGE}@gcp-sa-aiplatform-re.iam.gserviceaccount.com" \
  "serviceAccount:${PROJECT_NUMBER_CONCIERGE}-compute@developer.gserviceaccount.com" \
  "principalSet://agents.global.org-${ORG_ID}.system.id.goog/attribute.platformContainer/aiplatform/projects/${PROJECT_NUMBER_CONCIERGE}"; do
    gcloud projects remove-iam-policy-binding ${PROJECT_SELLERS} \
      --member="${MEMBER}" \
      --role="roles/aiplatform.user" --quiet || true
done
# 5. delete custom IAM role after all bindings have been unlinked
gcloud iam roles delete ar_agw_cross_project_sa \
  --project=${PROJECT_GOVERNANCE} --quiet || true

Si asignaste roles/iam.accessPolicyAdmin y roles/resourcemanager.projectIamAdmin durante la fase de configuración, quítalos de tu cuenta de usuario activa para restablecer el privilegio mínimo:

# 6. remove Access Policy Admin and Project IAM Admin roles from user
for ROLE in "roles/iam.accessPolicyAdmin" "roles/resourcemanager.projectIamAdmin"; do
  gcloud projects remove-iam-policy-binding ${PROJECT_GOVERNANCE} \
    --member="user:$(gcloud config get-value account)" \
    --role="${ROLE}" \
    --condition=None --quiet || true
done

6. Revierte el registro de datos de auditoría y las restricciones de la política de la organización

# 1. Export current Central Governance IAM policy
gcloud projects get-iam-policy ${PROJECT_GOVERNANCE} --format=json > cfg/gov_iam_policy.json
# 2. Filter out iap.googleapis.com from auditConfigs
python3 -c "
import json
with open('cfg/gov_iam_policy.json') as f:
    policy = json.load(f)

if 'auditConfigs' in policy:
    # Remove iap.googleapis.com; if nothing else remains, clear the list
    policy['auditConfigs'] = [
        ac for ac in policy['auditConfigs'] if ac.get('service') != 'iap.googleapis.com'
    ]

with open('cfg/gov_iam_policy.json', 'w') as f:
    json.dump(policy, f, indent=2)
"
# 3. Apply the updated policy to revert audit logging to default
gcloud projects set-iam-policy ${PROJECT_GOVERNANCE} cfg/gov_iam_policy.json

7. Revierte las restricciones de la política de la organización

# revert iam v3 access policy binding org policy on project to org level setting
gcloud org-policies delete iam.managed.disableAccessPolicyBinding --project=${PROJECT_GOVERNANCE}

8. Borra el bucket de etapa de pruebas de GCS compartido y los artefactos locales

# delete central staging bucket
gcloud storage rm -r gs://${PROJECT_GOVERNANCE}-shared-staging
# remove local configuration manifests, environment files, and application
rm -rf cfg/ cross-project-multiagent/ *.env

Aquí concluye la parte de limpieza… ¡A continuación, la Conclusión!

12. Conclusión

¡Felicitaciones! Implementaste y gobernaste una arquitectura de agente a agente (A2A) de varios proyectos en Google Cloud con Vertex AI Agent Runtime, Central Agent Gateway, Agent Registry y las políticas de acceso unificado (UAP) de IAM.

Resumen de los conceptos clave

  • Perímetro de salida centralizado: Se enrutaron los contenedores de entorno de ejecución de radio (PROJECT_CONCIERGE, PROJECT_SELLERS) a través de una Agent Gateway central en PROJECT_GOVERNANCE con agentGatewayConfig.
  • Gobernanza declarativa (UAP): Se reemplazaron las vinculaciones fragmentadas por recurso por una única política de acceso de IAM auditable que evalúa IAP v2 en la puerta de enlace.
  • Identidad criptográfica: Se aplicó la salida con privilegios mínimos usando identidades SPIFFE de contenedor (principal://...) en lugar de claves de larga duración.
  • Descubrimiento dinámico de servicios: Se resolvieron los extremos de los agentes pares en el entorno de ejecución a través del registro central de agentes, lo que eliminó las URLs y los IDs de proyectos codificados.
  • Agilidad de la política de entorno de ejecución: Se cambió pizza-seller-agent de Denegación predeterminada (403 Forbidden) a Permitido (200 OK) en tiempo real a través de la actualización de la política, sin reinicios de contenedores.

cosmopup

Cosmopup dice: "Los agentes son geniales: hacen todo el trabajo entre proyectos mientras yo me concentro en mi objetivo principal: ¡dormir la siesta!".

Próximos pasos y documentación