Implementa tu agente en contenedores en Agent Runtime

1. Descripción general

Agent Runtime (antes conocido como Agent Engine) ofrece un entorno de ejecución administrado diseñado para implementar, ejecutar y escalar agentes de IA de manera eficaz. De forma predeterminada, la plataforma agrupa automáticamente tu código fuente y las dependencias durante el proceso de implementación.

Sin embargo, las cargas de trabajo empresariales suelen requerir la propiedad completa del entorno de ejecución. Para admitir esto, Agent Runtime proporciona la capacidad Bring Your Own Container(BYOC), que te permite implementar imágenes de contenedor personalizadas previamente compiladas.

En este codelab, se describe el proceso integral para contenerizar un agente creado con el Kit de desarrollo de agentes (ADK) de Google, configurar los permisos necesarios de Google Cloud y, luego, implementarlo en Agent Runtime con el SDK de Python o Terraform.

En este codelab, se te guiará para que realices las siguientes acciones:

  1. Crear un agente de Python con el Kit de desarrollo de agentes (ADK) de Google
  2. Unión del agente en una aplicación de FastAPI
  3. Organizar la aplicación en contenedores con Docker
  4. Configurar los permisos de Google Cloud
  5. Implementar y probar el agente alojado en contenedores en Agent Runtime

Flujo de compilación e implementación

En el siguiente diagrama, se ilustra el flujo de trabajo de los pasos de compilación y de implementación que realizarás de forma manual en este codelab:

Diagrama de flujo de CI/CD

Qué necesitas

  • Un proyecto de Google Cloud con facturación habilitada
  • Acceso a Cloud Shell (recomendado) o a un entorno de desarrollo local con gcloud y docker instalados
  • Conocimientos básicos de Python y Docker

2. Configuración del entorno

Antes de comenzar, debes habilitar las APIs necesarias y configurar tu entorno.

Paso 1: Abre Cloud Shell

Haz clic en el botón Activar Cloud Shell en la parte superior derecha de la consola de Google Cloud.

Cloud Shell

Paso 2: Configura las variables de entorno

En Cloud Shell, configura tu ID del proyecto y define las variables de entorno clave que se usan en este codelab. Reemplaza "YOUR_PROJECT_ID" por el ID de tu proyecto de Google Cloud:

gcloud config set project "YOUR_PROJECT_ID"
export PROJECT_ID=$(gcloud config get-value project)
export LOCATION="us-central1"
export MODEL="gemini-3.1-flash-lite"
export MODEL_REGION="global"

Estas variables configuran los parámetros de configuración de la implementación de destino:

  • PROJECT_ID: Es el identificador único de tu proyecto de Google Cloud en el que residirán todos los recursos de la plataforma de agentes de Gemini Enterprise y de Artifact Registry.
  • LOCATION: Es la región geográfica (p.ej., us-central1) que aloja tus repositorios y cargas de trabajo de tiempo de ejecución.
  • MODEL: Es la versión del modelo de Gemini (p.ej., gemini-3.1-flash-lite) que carga el contexto del agente.
  • MODEL_REGION: Es la región del extremo del modelo. Aquí se establece como "global" para invocar el modelo de Gemini desde los extremos globales.

Paso 3: Habilitar las API

Habilita las APIs de Google Cloud requeridas:

gcloud services enable \
    aiplatform.googleapis.com \
    cloudbuild.googleapis.com \
    compute.googleapis.com \
    artifactregistry.googleapis.com \
    storage.googleapis.com

Paso 4: Instala el SDK

Instala el SDK de Vertex AI con compatibilidad para Agent Engine y ADK:

pip install --upgrade "google-cloud-aiplatform[agent_engines,adk]>=1.144"

3. Configuración de archivos fuente

En este paso, crearás la estructura y el código de tu agente.

Descripción general de la estructura de directorios

Al final de este codelab, tus archivos se organizarán según la siguiente jerarquía del espacio de trabajo:

weather-agent-byoc/
├── Dockerfile                  # Container definition
├── deploy_byoc.py              # Python SDK deployment script
├── main.py                     # FastAPI server wrapper
├── query_agent.py              # Verify / query script
├── requirements.txt            # Python dependencies

├── weather_agent/              # Agent source module
   ├── __init__.py             # Package declaration
   ├── agent.py                # Agent & mock tools logic
   └── config.json             # Environment config variables

└── terraform/                  # Terraform configuration files
    ├── main.tf
    ├── outputs.tf
    ├── providers.tf
    ├── terraform.tfvars
    └── variables.tf

Paso 1: Crea directorios

Comienza en tu directorio principal y crea la estructura del espacio de trabajo:

cd ~
mkdir -p weather-agent-byoc/weather_agent
cd weather-agent-byoc

Paso 2: Crea el archivo de configuración

Ejecuta el siguiente comando en Cloud Shell para escribir los parámetros de configuración directamente en weather_agent/config.json. Este comando reemplaza automáticamente las variables por los valores de tu entorno:

cat <<EOF > weather_agent/config.json
{
    "PROJECT_ID": "${PROJECT_ID}",
    "LOCATION": "${LOCATION}",
    "MODEL": "${MODEL}",
    "MODEL_REGION": "${MODEL_REGION}"
}
EOF

Paso 3: Define el agente

Ejecuta la siguiente secuencia de comandos para escribir la configuración del agente y simular la lógica de la herramienta en weather_agent/agent.py:

cat << 'EOF' > weather_agent/agent.py
import json
import random
from google.adk.agents import Agent
from google.adk.models.google_llm import Gemini
from functools import cached_property
from google.genai import Client

# Load config
llm_config = json.load(open("weather_agent/config.json"))
PROJECT_ID = llm_config["PROJECT_ID"]
MODEL = llm_config["MODEL"]
MODEL_REGION = llm_config["MODEL_REGION"]

# Override Gemini class for global endpoint compatibility
class GlobalGemini(Gemini):
  @cached_property
  def api_client(self) -> Client:
    return Client(vertexai=True, location="global")

# Define Tool
def get_temperature(place: str) -> str:
    '''Returns the current temperature of a given place.

    Args:
        place: The name of the city or location.

    Returns:
        str: A string describing the temperature.
    '''
    temp = random.randint(-10, 40)
    return f"The current temperature in {place} is {temp}°C."

# Initialize LLM
llm_model = GlobalGemini(model=MODEL) if MODEL_REGION == "global" else Gemini(model=MODEL)

# Initialize Agent
root_agent = Agent(
    model=llm_model,
    name='weather_agent',
    description='An agent that provides temperature information for locations.',
    instruction='You are a helpful assistant that can provide the current temperature for any given place using the get_temperature tool.',
    tools=[get_temperature],
)
EOF

Crea un __init__.py vacío para convertir weather_agent en un paquete de Python:

touch weather_agent/__init__.py

Paso 4: Crea el wrapper de FastAPI

Ejecuta la siguiente secuencia de comandos para escribir la configuración del punto de entrada del servidor de FastAPI en main.py:

cat << 'EOF' > main.py
import inspect
import json
import logging
import os
from typing import Any, Dict, Optional
import uvicorn
import vertexai
from weather_agent.agent import root_agent
from fastapi import FastAPI, encoders, responses
from pydantic import BaseModel
from vertexai import agent_engines

app = FastAPI()

config_json = json.load(open("weather_agent/config.json"))
PROJECT_ID = config_json["PROJECT_ID"]
LOCATION = config_json["LOCATION"]
MODEL_REGION = config_json["MODEL_REGION"]

class QueryRequest(BaseModel):
    input: Optional[Dict[str, Any]] = None
    class_method: Optional[str] = None

vertexai.init(project=PROJECT_ID, location=MODEL_REGION)
adk_app = agent_engines.AdkApp(agent=root_agent)

def _encode_chunk_to_json(chunk):
  try:
    json_chunk = encoders.jsonable_encoder(chunk)
    return json.dumps(json_chunk) + "\n"
  except Exception:
    logging.exception("Failed to encode chunk")
    return None

async def json_generator(output):
  async for chunk in output:
    encoded_chunk = _encode_chunk_to_json(chunk)
    if encoded_chunk is None:
      break
    yield encoded_chunk

async def _invoke_callable_or_raise(invocation_callable, invocation_payload):
  if inspect.iscoroutinefunction(invocation_callable):
    return await invocation_callable(**invocation_payload)
  else:
    return invocation_callable(**invocation_payload)

@app.post("/api/reasoning_engine")
async def query(request: QueryRequest) -> responses.JSONResponse:
    method = getattr(adk_app, request.class_method)
    output = await _invoke_callable_or_raise(method, request.input or {})
    try:
      json_serialized_content = encoders.jsonable_encoder({"output": output})
    except ValueError as encoding_error:
      logging.exception("Failed to encode response")
      raise encoding_error
    return responses.JSONResponse(content=json_serialized_content)

@app.post("/api/stream_reasoning_engine")
async def stream_query(request: QueryRequest) -> responses.StreamingResponse:
    method = getattr(adk_app, request.class_method)
    output = await _invoke_callable_or_raise(method, request.input or {})
    return responses.StreamingResponse(
        content=json_generator(output),
        media_type="application/json",
    )

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", 8080)))
EOF

Paso 5: Define las dependencias

Escribe las dependencias de Python necesarias en requirements.txt:

cat << 'EOF' > requirements.txt
fastapi
uvicorn
vertexai
google-cloud-aiplatform[agent_engines,adk]>=1.144
pydantic
EOF

4. Creación de contenedores

Ahora, define cómo se empaquetará tu agente en un contenedor.

Paso 1: Crea el Dockerfile

Crea el archivo Dockerfile en la raíz del directorio de tu proyecto para especificar cómo se compila tu aplicación de FastAPI:

cat << 'EOF' > Dockerfile
FROM python:3.11-slim

WORKDIR /app

COPY weather_agent/ /app/weather_agent/
COPY main.py .
COPY requirements.txt .
RUN pip install -r requirements.txt

CMD ["sh", "-c", "uvicorn main:app --host 0.0.0.0 --port $PORT"]
EOF

5. Configura Artifact Registry y Cloud Build

Necesitas un repositorio para almacenar la imagen del contenedor y permisos para enviarla.

Paso 1: Crea un repositorio

Define el nombre del repositorio y crea un repositorio de Docker dentro de Artifact Registry con las variables de entorno definidas durante la configuración:

export REPOSITORY_NAME="agents-repo"

gcloud artifacts repositories create $REPOSITORY_NAME \
    --project=$PROJECT_ID \
    --repository-format=docker \
    --location=$LOCATION \
    --description="Docker repository for Agents"

Paso 2: Configura los permisos de la cuenta de servicio

Otorga permiso a la cuenta de servicio predeterminada de Compute para enviar imágenes a Artifact Registry.

Primero, obtén el número de tu proyecto:

export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")

Otorga los roles:

# Allow pushing to Artifact Registry
gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member="serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com" \
    --role="roles/artifactregistry.writer" \
    --condition=None

# Allow Cloud Build to read storage objects
gcloud projects add-iam-policy-binding $PROJECT_NUMBER \
    --member="serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com" \
    --role="roles/storage.objectViewer" \
    --condition=None

Paso 3: Otorga permisos a los agentes de servicio

Otorga acceso de lectura de Artifact Registry a los agentes de servicio de AI Platform y Reasoning Engine:

gcloud projects add-iam-policy-binding $PROJECT_NUMBER \
    --member="serviceAccount:service-$PROJECT_NUMBER@gcp-sa-aiplatform-re.iam.gserviceaccount.com" \
    --role="roles/artifactregistry.reader"  --condition=None

gcloud projects add-iam-policy-binding $PROJECT_NUMBER \
    --member="serviceAccount:service-$PROJECT_NUMBER@gcp-sa-aiplatform.iam.gserviceaccount.com" \
    --role="roles/artifactregistry.reader"  --condition=None

Paso 4: Compila y envía la imagen

Usa Cloud Build para compilar y enviar la imagen del contenedor:

gcloud builds submit \
    --project=$PROJECT_ID \
    --region=$LOCATION \
    --tag $LOCATION-docker.pkg.dev/$PROJECT_ID/$REPOSITORY_NAME/weather-agent-image:latest \
    .

6. Implementa el agente con el SDK

Ahora que los permisos están configurados, puedes implementar tu contenedor personalizado.

Paso 1: Implementa el agente de BYOC

Crea el archivo de Python deploy_byoc.py dentro de la raíz del directorio de tu proyecto para implementar el contenedor alojado en el registro en el tiempo de ejecución del agente:

cat << 'EOF' > deploy_byoc.py
import json
import os
import vertexai
from google.cloud import aiplatform

config = json.load(open("weather_agent/config.json"))
PROJECT_ID = config["PROJECT_ID"]
LOCATION = config["LOCATION"]
REPOSITORY_NAME = "agents-repo"

vertexai.init(project=PROJECT_ID, location=LOCATION)
client = vertexai.Client(project=PROJECT_ID, location=LOCATION)

image_uri = f"{LOCATION}-docker.pkg.dev/{PROJECT_ID}/{REPOSITORY_NAME}/weather-agent-image:latest"

print(f"Deploying custom container agent from {image_uri}...")
remote_agent = client.agent_engines.create(
    config={
        "display_name": "byoc_weather_agent",
        "description": "BYOC weather agent from custom container",
        "container_spec": {
            "image_uri": image_uri
        },
        "class_methods": [
            # For convenience to interact with the agent through the Python SDK
            # https://docs.cloud.google.com/gemini-enterprise-agent-platform/scale/runtime/use-an-adk-agent#supported-operations
            {"api_mode": "", "name": "get_session"},
            {"api_mode": "", "name": "list_sessions"},
            {"api_mode": "", "name": "create_session"},
            {"api_mode": "", "name": "delete_session"},
            {"api_mode": "async", "name": "async_get_session"},
            {"api_mode": "async", "name": "async_list_sessions"},
            {"api_mode": "async", "name": "async_create_session"},
            {"api_mode": "async", "name": "async_delete_session"},
            {"api_mode": "async", "name": "async_add_session_to_memory"},
            {"api_mode": "async", "name": "async_search_memory"},
            {"api_mode": "stream", "name": "stream_query"},
            {"api_mode": "async_stream", "name": "async_stream_query"},
            {"api_mode": "async_stream", "name": "streaming_agent_run_with_events"},
        ],
        "agent_framework": "google-adk",
    },
)

print(f"Agent successfully deployed!")
print(f"Resource Name: {remote_agent.api_resource.name}")

# Save resource name for testing
with open("agent_resource_name.txt", "w") as f:
    f.write(remote_agent.api_resource.name)
EOF

Ejecuta la secuencia de comandos de implementación para implementar el agente en Agent Runtime:

python3 deploy_byoc.py

7. Implementa el agente con Terraform

Como alternativa, puedes implementar el mismo agente en contenedores con Terraform. Se recomienda para entornos de producción para administrar la infraestructura como código.

Paso 1: Navega al directorio de Terraform

Crea un directorio terraform en la raíz de tu proyecto y navega a él:

mkdir -p terraform
cd terraform

Paso 2: Crea la configuración de los proveedores

Ejecuta la siguiente secuencia de comandos para escribir la asignación de proveedores en providers.tf:

cat << 'EOF' > providers.tf
terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = ">= 5.28.0"
    }
  }
}

provider "google" {
  project = var.project_id
  region  = var.location
}
EOF

Paso 3: Crea la definición de variables

Escribe el bloque de descripción de las entradas en variables.tf:

cat << 'EOF' > variables.tf
variable "project_id" {
  type        = string
  description = "The Google Cloud Project ID"
}

variable "location" {
  type        = string
  description = "The region to deploy the reasoning engine"
  default     = "us-central1"
}

variable "repository_name" {
  type        = string
  description = "The Artifact Registry repository name"
  default     = "agents-repo"
}

variable "image_tag" {
  type        = string
  description = "The tag of the container image to deploy"
  default     = "latest"
}
EOF

Paso 4: Crea la configuración principal

Escribe los parámetros de definición de recursos principales en main.tf:

cat << 'EOF' > main.tf
locals {
  class_methods = [
    {"api_mode" = "", "name" = "get_session"},
    {"api_mode" = "", "name" = "list_sessions"},
    {"api_mode" = "", "name" = "create_session"},
    {"api_mode" = "", "name" = "delete_session"},
    {"api_mode" = "async", "name" = "async_get_session"},
    {"api_mode" = "async", "name" = "async_list_sessions"},
    {"api_mode" = "async", "name" = "async_create_session"},
    {"api_mode" = "async", "name" = "async_delete_session"},
    {"api_mode" = "async", "name" = "async_add_session_to_memory"},
    {"api_mode" = "async", "name" = "async_search_memory"},
    {"api_mode" = "stream", "name" = "stream_query"},
    {"api_mode" = "async_stream", "name" = "async_stream_query"},
    {"api_mode" = "async_stream", "name" = "streaming_agent_run_with_events"}
  ]
}

# define the resource with the BYOC configuration, set agent_framework to "google-adk" to enable interactive features on the console.
resource "google_vertex_ai_reasoning_engine" "byoc_weather_agent" {
  display_name = "byoc_weather_agent_tf"
  description  = "BYOC weather agent deployed via Terraform"
  project      = var.project_id
  location     = var.location

  spec {
    class_methods = jsonencode(local.class_methods)
    agent_framework = "google-adk"
    container_spec {
      image_uri = "${var.location}-docker.pkg.dev/${var.project_id}/${var.repository_name}/weather-agent-image:${var.image_tag}"
    }
  }
}
EOF

Paso 5: Crea la definición de salidas

Escribe el bloque de salidas en outputs.tf:

cat << 'EOF' > outputs.tf
output "reasoning_engine_id" {
  value       = google_vertex_ai_reasoning_engine.byoc_weather_agent.id
  description = "The ID of the deployed reasoning engine"
}

output "reasoning_engine_resource_name" {
  value       = google_vertex_ai_reasoning_engine.byoc_weather_agent.id
  description = "The resource name of the deployed reasoning engine"
}
EOF

Paso 6: Crea el archivo de valores de variables (tfvars)

Realiza la implementación de forma dinámica sin editar marcadores de posición. Para ello, ingresa variables de entorno directamente en terraform.tfvars:

cat <<EOF > terraform.tfvars
project_id      = "${PROJECT_ID}"
location        = "${LOCATION}"
repository_name = "agents-repo"
image_tag       = "latest"
EOF

Paso 7: Inicializa y aplica

Inicializa Terraform y aplica la configuración:

terraform init
terraform apply

Para confirmar la aplicación, escribe yes cuando se te solicite.

Una vez completado, Terraform mostrará el nombre del recurso. Captúralo de forma programática en agent_resource_name.txt y vuelve a la carpeta raíz:

terraform output -raw reasoning_engine_resource_name > ../agent_resource_name.txt
cd ..

8. Cómo consultar al agente

Verifica que tu agente se esté ejecutando y respondiendo.

Paso 1: Crea un lenguaje de consulta

Escribe la secuencia de comandos de verificación en query_agent.py con una verificación de configuración dinámica para recuperar las coordenadas de ubicación:

cat << 'EOF' > query_agent.py
import json
import os
import requests
from google import auth as google_auth
from google.auth.transport import requests as google_requests

# Load config coordinates directly
config_json = json.load(open("weather_agent/config.json"))
LOCATION = config_json["LOCATION"]
PROJECT_ID = config_json["PROJECT_ID"]

# Load agent resource name
with open("agent_resource_name.txt", "r") as f:
    agent_resource_name = f.read().strip()

def get_identity_token():
    credentials, _ = google_auth.default()
    auth_request = google_requests.Request()
    credentials.refresh(auth_request)
    return credentials.token

# Access the agent at the fastapi endpoint that was specified in main.py
url = f"https://{LOCATION}-aiplatform.googleapis.com/v1/{agent_resource_name}/api/stream_reasoning_engine"

payload = {
    "class_method": "async_stream_query",
    "input": {
        "user_id": "codelab_test_user",
        "message": "What is the temperature in Tokyo?",
    },
}

print(f"Sending query to {url}...")
response = requests.post(
    url,
    headers={
        "Content-Type": "application/json",
        "Authorization": f"Bearer {get_identity_token()}",
    },
    data=json.dumps(payload),
    stream=True,
)

for chunk in response.iter_content(chunk_size=8192):
    if chunk:
        print(chunk.decode('utf-8'))
EOF

Ejecuta la secuencia de comandos de consulta:

python3 query_agent.py

Deberías ver el resultado que transmite el agente, incluida la temperatura simulada de Tokio.

Paso 2: Usa la consola

  1. Para navegar al agente implementado, selecciona Agent Platform > Agents > Deployments para filtrar la lista de agentes.

Imagen del agente

  1. Selecciona Playground en el panel del agente.

Panel del agente

  1. Crea una sesión nueva y escribe tu búsqueda para verificar si el agente responde a las solicitudes como se muestra.

Interacción con el agente

9. Limpieza

Para evitar que se generen cargos, limpia los recursos que creaste.

Si realizaste la implementación con Terraform, cambia al directorio terraform y ejecuta la acción de destrucción:

cd ~/weather-agent-byoc/terraform
terraform destroy
cd ..

Si realizaste la implementación con el SDK, crea la secuencia de comandos para borrar el agente implementado:

cat << 'EOF' > delete_agent.py
import json
import os
import vertexai
from google.cloud import aiplatform

config = json.load(open("weather_agent/config.json"))
PROJECT_ID = config["PROJECT_ID"]
LOCATION = config["LOCATION"]

vertexai.init(project=PROJECT_ID, location=LOCATION)
client = vertexai.Client(project=PROJECT_ID, location=LOCATION)

with open("agent_resource_name.txt", "r") as f:
    agent_resource_name = f.read().strip()

# 1. Delete the Agent
# Note: We retrieve the list first to ensure we delete the ones created in this session
try:
    page_size = 100
    reasoning_engines = client.agent_engines.list()
    for engine in reasoning_engines:
        if agent_resource_name in engine.api_resource.name:
            print(f"Deleting Reasoning Engine: {engine.api_resource.name}")
            engine.delete(force=True)
except Exception as e:
    print(f"Error deleting reasoning engines: {e}")
EOF

Ejecuta la secuencia de comandos para borrar el agente:

python3 delete_agent.py

Para limpiar el resto de los recursos, vuelve al directorio principal y ejecuta los siguientes comandos en Cloud Shell:

cd ~

# 1. Delete the Artifact Registry Repository
gcloud artifacts repositories delete $REPOSITORY_NAME --location=$LOCATION --quiet

# 2. Clean up files (Optional)
rm -rf ~/weather-agent-byoc

10. Conclusión

¡Felicitaciones! Creaste un contenedor y, luego, implementaste correctamente un agente de IA en Agent Runtime con BYOC.

Aprendiste todo esto:

  • Usa el ADK para definir un agente y encapsularlo con FastAPI.
  • Crea un Dockerfile y compila imágenes con Cloud Build.
  • Administrar permisos de IAM para Agent Runtime
  • Implementa tu contenedor personalizado con el SDK de Python y Terraform.
  • Prueba tu agente implementado y hazle preguntas.