Implantar seu agente em contêiner no Agent Runtime

1. Visão geral

Agent Runtime (antigo Agent Engine) oferece um ambiente de execução gerenciado projetado para implantar, executar e escalonar agentes de IA com eficiência. Por padrão, a plataforma agrupa automaticamente o código-fonte e as dependências durante o processo de implantação.

No entanto, as cargas de trabalho empresariais geralmente exigem propriedade completa do ambiente de execução. Para oferecer suporte a isso, o Agent Runtime oferece o recurso Bring Your Own Container(BYOC, na sigla em inglês), que permite implantar imagens de contêiner personalizadas pré-criadas.

Este codelab descreve o processo completo de conteinerização de um agente criado com o Kit de Desenvolvimento de Agente (ADK, na sigla em inglês) do Google, a configuração das permissões necessárias do Google Cloud e a implantação no Agent Runtime usando o SDK do Python ou o Terraform.

Este codelab vai ajudar você a:

  1. Criar um agente do Python usando o Kit de Desenvolvimento de Agente (ADK, na sigla em inglês) do Google.
  2. Encapsular o agente em um aplicativo FastAPI.
  3. Conteinerizar o aplicativo com o Docker.
  4. Configurar permissões do Google Cloud.
  5. Implantar e testar o agente conteinerizado no Agent Runtime.

Fluxo de criação e implantação

O diagrama a seguir ilustra o fluxo de trabalho das etapas de criação e implantação que você vai realizar manualmente neste codelab:

Diagrama de fluxo de CI/CD

O que você precisa

  • um projeto do Google Cloud com faturamento ativado
  • Acesso ao Cloud Shell (recomendado) ou um ambiente de desenvolvimento local com gcloud e docker instalados.
  • Conhecimento básico de Python e Docker.

2. Configuração do ambiente

Antes de começar, ative as APIs necessárias e configure seu ambiente.

Etapa 1: abrir o Cloud Shell

Clique no botão Ativar Cloud Shell no canto superior direito do console do Google Cloud.

Cloud Shell

Etapa 2: configurar variáveis de ambiente

No Cloud Shell, defina o ID do projeto e as principais variáveis de ambiente usadas neste codelab. Substitua "YOUR_PROJECT_ID" pelo ID do projeto do 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"

Essas variáveis configuram as configurações de implantação de destino:

  • PROJECT_ID: o identificador exclusivo do projeto do Google Cloud em que todos os recursos da plataforma de agente do Gemini Enterprise e do Artifact Registry vão residir.
  • LOCATION: a região geográfica (por exemplo, us-central1) que hospeda seus repositórios e cargas de trabalho de ambiente de execução.
  • MODEL: a versão do modelo do Gemini (por exemplo, gemini-3.1-flash-lite) carregada pelo contexto do agente.
  • MODEL_REGION: a região do endpoint do modelo. Defina como "global" para invocar o modelo do Gemini nos endpoints globais.

Etapa 3: ativar APIs

Ative as APIs do Google Cloud necessárias:

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

Etapa 4: instalar o SDK

Instale o SDK da Vertex AI com suporte ao Agent Engine e ao ADK:

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

3. Configuração de arquivos de origem

Nesta etapa, você vai criar a estrutura e o código do agente.

Visão geral da estrutura de diretórios

Ao final deste codelab, seus arquivos serão organizados na seguinte hierarquia de espaço de trabalho:

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

Etapa 1: criar diretórios

Comece no diretório principal e crie a estrutura do espaço de trabalho:

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

Etapa 2: criar o arquivo de configuração

Execute o comando a seguir no Cloud Shell para gravar os parâmetros de configuração diretamente em weather_agent/config.json. Esse comando substitui automaticamente as variáveis pelos valores do ambiente:

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

Etapa 3: definir o agente

Execute o script a seguir para gravar a configuração do agente e simular a lógica da ferramenta em 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

Crie um __init__.py vazio para tornar weather_agent um pacote do Python:

touch weather_agent/__init__.py

Etapa 4: criar o wrapper do FastAPI

Execute o script a seguir para gravar a configuração do ponto de entrada do servidor FastAPI em 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

Etapa 5: definir dependências

Grave as dependências necessárias do Python em requirements.txt:

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

4. Conteinerização

Agora, defina como o agente será empacotado em um contêiner.

Etapa 1: criar o Dockerfile

Crie o Dockerfile na raiz do diretório do projeto para especificar como o aplicativo FastAPI é criado:

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. Configurar o Artifact Registry e o Cloud Build

Você precisa de um repositório para armazenar a imagem do contêiner e permissões para enviá-la.

Etapa 1: criar o repositório

Defina o nome do repositório e crie um repositório do Docker no Artifact Registry usando as variáveis de ambiente definidas durante a configuração:

export REPOSITORY_NAME="agents-repo"

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

Etapa 2: configurar permissões da conta de serviço

Conceda permissão à conta de serviço padrão do Compute para enviar imagens ao Artifact Registry.

Primeiro, receba o número do projeto:

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

Conceda os papéis:

# 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

Etapa 3: conceder permissões a agentes de serviço

Conceda acesso de leitura do Artifact Registry aos agentes de serviço do AI Platform e do 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

Etapa 4: criar e enviar a imagem

Use o Cloud Build para criar e enviar a imagem do contêiner:

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

6. Implantar o agente com o SDK

Agora que as permissões estão configuradas, você pode implantar seu contêiner personalizado.

Etapa 1: implantar o agente BYOC

Crie o arquivo Python deploy_byoc.py na raiz do diretório do projeto para implantar o contêiner hospedado no registro no Agent Runtime:

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

Execute o script de implantação para implantar o agente no Agent Runtime:

python3 deploy_byoc.py

7. Implantar o agente com o Terraform

Como alternativa, você pode implantar o mesmo agente conteinerizado usando o Terraform. Isso é recomendado para ambientes de produção para gerenciar a infraestrutura como código.

Etapa 1: navegar até o diretório do Terraform

Crie um diretório terraform na raiz do projeto e navegue até ele:

mkdir -p terraform
cd terraform

Etapa 2: criar a configuração de provedores

Execute o script a seguir para gravar o mapeamento de provedores em 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

Etapa 3: criar a definição de variáveis

Grave o bloco de descrição de entradas em 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

Etapa 4: criar a configuração principal

Grave os parâmetros de definição de recursos principais em 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

Etapa 5: criar a definição de saídas

Grave o bloco de saídas em 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

Etapa 6: criar o arquivo de valores de variáveis (tfvars)

Implante dinamicamente sem edição de marcador de posição, transmitindo variáveis de ambiente diretamente para terraform.tfvars:

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

Etapa 7: inicializar e aplicar

Inicialize o Terraform e aplique a configuração:

terraform init
terraform apply

Confirme a aplicação digitando yes quando solicitado.

Quando concluído, o Terraform gera o nome do recurso. Capture-o programaticamente para agent_resource_name.txt e retorne à pasta raiz:

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

8. Consultar o agente

Verifique se o agente está em execução e respondendo.

Etapa 1: criar o script de consulta

Grave o script de verificação em query_agent.py usando uma verificação de configuração dinâmica para buscar coordenadas de localização:

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

Execute o script de consulta:

python3 query_agent.py

Você verá a saída transmitida do agente, incluindo a temperatura simulada para Tóquio.

Etapa 2: usar o console

  1. Navegue até o agente implantado selecionando Plataforma de agente > Agentes > Implantações para filtrar a lista de agentes.

Imagem do agente

  1. Selecione Playground no painel do agente.

Painel do agente

  1. Crie uma nova sessão e digite sua consulta para verificar se o agente está respondendo às solicitações, conforme mostrado.

Interação do agente

9. Revisão dos dados

Para evitar cobranças, limpe os recursos criados.

Se você implantou usando o Terraform, mude para o diretório terraform e execute a ação de exclusão:

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

Se você implantou usando o SDK, crie o script para excluir o agente implantado:

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

Execute o script para excluir o agente:

python3 delete_agent.py

Para limpar o restante dos recursos, volte ao diretório principal e execute os seguintes comandos no 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. Conclusão

Parabéns! Você conteinerizou e implantou um agente de IA no Agent Runtime usando o BYOC.

Você aprendeu a:

  • Usar o ADK para definir um agente e encapsulá-lo usando o FastAPI.
  • Criar um Dockerfile e criar imagens usando o Cloud Build.
  • Gerenciar permissões do IAM para o Agent Runtime.
  • Implante seu contêiner personalizado usando o SDK do Python e o Terraform.
  • Teste e consulte o agente implantado.