Déployer votre agent conteneurisé sur Agent Runtime

1. Présentation

Agent Runtime (anciennement Agent Engine) offre un environnement d'exécution géré conçu pour déployer, exécuter et faire évoluer efficacement des agents d'IA. Par défaut, la plate-forme regroupe automatiquement votre code source et vos dépendances lors du processus de déploiement.

Toutefois, les charges de travail d'entreprise nécessitent souvent une propriété complète de l'environnement d'exécution. Pour ce faire, Agent Runtime fournit la fonctionnalité Bring Your Own Container(BYOC), qui vous permet de déployer des images de conteneurs personnalisées prédéfinies.

Cet atelier de programmation décrit le processus de bout en bout pour conteneuriser un agent créé avec l'Agent Development Kit (ADK) Google, configurer les autorisations Google Cloud nécessaires et le déployer sur Agent Runtime à l'aide du SDK Python ou de Terraform.

Cet atelier de programmation vous guide à travers les étapes suivantes :

  1. Créer un agent Python à l'aide de Google Agent Development Kit (ADK).
  2. Encapsuler l'agent dans une application FastAPI.
  3. Conteneurisez l'application avec Docker.
  4. Configurer les autorisations Google Cloud.
  5. Déployer et tester l'agent conteneurisé sur Agent Runtime

Flux de compilation et de déploiement

Le schéma suivant illustre le workflow des étapes de compilation et de déploiement que vous effectuerez manuellement dans cet atelier de programmation :

Diagramme de flux CI/CD

Ce dont vous avez besoin

  • Projet Google Cloud avec facturation activée
  • Accès à Cloud Shell (recommandé) ou à un environnement de développement local avec gcloud et docker installés.
  • Connaissances de base de Python et Docker

2. Configuration de l'environnement

Avant de commencer, vous devez activer les API nécessaires et configurer votre environnement.

Étape 1 : Ouvrez Cloud Shell

Cliquez sur le bouton Activer Cloud Shell en haut à droite de la console Google Cloud.

Cloud Shell

Étape 2 : Configurez les variables d'environnement

Dans Cloud Shell, définissez l'ID de votre projet et définissez les principales variables d'environnement utilisées tout au long de cet atelier de programmation. Remplacez "YOUR_PROJECT_ID" par l'ID de votre projet 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"

Ces variables configurent les paramètres de déploiement cible :

  • PROJECT_ID : identifiant unique de votre projet Google Cloud dans lequel résideront toutes les ressources Gemini Enterprise Agent Platform et Artifact Registry.
  • LOCATION : région géographique (par exemple, us-central1) hébergeant vos dépôts et vos charges de travail d'exécution.
  • MODEL : version du modèle Gemini (par exemple, gemini-3.1-flash-lite) chargée par le contexte de l'agent.
  • MODEL_REGION : région du point de terminaison du modèle. Définissez ici la valeur "global" pour appeler le modèle Gemini à partir des points de terminaison mondiaux.

Étape 3 : Activer les API

Activez les API Google Cloud requises :

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

Étape 4 : Installer le SDK

Installez le SDK Vertex AI avec la prise en charge d'Agent Engine et de l'ADK :

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

3. Configurer les fichiers sources

Dans cette étape, vous allez créer la structure et le code de votre agent.

Présentation de la structure des répertoires

À la fin de cet atelier de programmation, vos fichiers seront organisés selon la hiérarchie d'espace de travail suivante :

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

Étape 1 : Créer des répertoires

Commencez dans votre répertoire d'accueil et créez la structure de l'espace de travail :

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

Étape 2 : Créez le fichier de configuration

Exécutez la commande suivante dans Cloud Shell pour écrire les paramètres de configuration directement dans weather_agent/config.json. Cette commande remplace automatiquement les variables par les valeurs de votre environnement :

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

Étape 3 : Définir l'agent

Exécutez le script suivant pour écrire la configuration de l'agent et simuler la logique de l'outil dans 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

Créez un __init__.py vide pour faire de weather_agent un package Python :

touch weather_agent/__init__.py

Étape 4 : Créer un wrapper FastAPI

Exécutez le script suivant pour écrire la configuration du point d'entrée du serveur FastAPI dans 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

Étape 5 : Définir les dépendances

Écrivez les dépendances Python requises dans requirements.txt :

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

4. Conteneurisation

Définissez maintenant comment votre agent sera empaqueté dans un conteneur.

Étape 1 : Créez un fichier Dockerfile

Créez le fichier Dockerfile à la racine du répertoire de votre projet pour spécifier la façon dont votre application FastAPI est créée :

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. Configurer Artifact Registry et Cloud Build

Vous avez besoin d'un dépôt pour stocker l'image de conteneur et des autorisations pour la transférer.

Étape 1 : Créer un dépôt

Définissez le nom du dépôt et créez un dépôt Docker dans Artifact Registry à l'aide des variables d'environnement définies lors de la configuration :

export REPOSITORY_NAME="agents-repo"

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

Étape 2 : Configurer les autorisations du compte de service

Accordez au compte de service Compute par défaut l'autorisation de transférer des images vers Artifact Registry.

Tout d'abord, obtenez le numéro de votre projet :

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

Attribuez les rôles :

# 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

Étape 3 : Accorder des autorisations aux agents de service

Accordez l'accès en lecture Artifact Registry aux agents de service AI Platform et 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

Étape 4 : Créez et transférez l'image

Utilisez Cloud Build pour créer et transférer l'image de conteneur :

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

6. Déployer l'agent avec le SDK

Maintenant que les autorisations sont configurées, vous pouvez déployer votre conteneur personnalisé.

Étape 1 : Déployer l'agent BYOC

Créez le fichier Python deploy_byoc.py à la racine du répertoire de votre projet pour déployer le conteneur hébergé dans le registre sur 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

Exécutez le script de déploiement pour déployer l'agent sur Agent Runtime :

python3 deploy_byoc.py

7. Déployer l'agent avec Terraform

Vous pouvez également déployer le même agent conteneurisé à l'aide de Terraform. Cette méthode est recommandée pour les environnements de production afin de gérer l'infrastructure en tant que code.

Étape 1 : Accéder au répertoire Terraform

Créez un répertoire terraform à la racine de votre projet et accédez-y :

mkdir -p terraform
cd terraform

Étape 2 : Créez la configuration des fournisseurs

Exécutez le script suivant pour écrire le mappage des fournisseurs dans 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

Étape 3 : Créer une définition de variables

Écrivez le bloc de description des entrées dans 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

Étape 4 : Créez la configuration principale

Écrivez les principaux paramètres de définition des ressources dans 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

Étape 5 : Créer une définition des sorties

Écrivez le bloc de sorties dans 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

Étape 6 : Créer un fichier de valeurs de variables (tfvars)

Déployez de manière dynamique sans modifier les espaces réservés en transmettant les variables d'environnement directement à terraform.tfvars :

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

Étape 7 : Initialiser et appliquer

Initialisez Terraform et appliquez la configuration :

terraform init
terraform apply

Confirmez l'application en saisissant yes lorsque vous y êtes invité.

Une fois l'opération terminée, Terraform affiche le nom de la ressource. Capturez-le de manière programmatique dans agent_resource_name.txt et revenez au dossier racine :

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

8. Interroger l'agent

Vérifiez que votre agent est en cours d'exécution et qu'il répond.

Étape 1 : Créer un script de requête

Écrivez le script de validation dans query_agent.py à l'aide d'une vérification de la configuration dynamique pour récupérer les coordonnées de l'emplacement :

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

Exécutez le script de requête :

python3 query_agent.py

Vous devriez voir le résultat renvoyé par l'agent, y compris la température simulée pour Tokyo.

Étape 2 : Utilisez la console

  1. Accédez à l'agent déployé en sélectionnant Plate-forme d'agent > Agents > Déploiements pour filtrer la liste des agents.

Image de l&#39;agent

  1. Sélectionnez Playground (Terrain de jeu) dans le tableau de bord de l'agent.

Tableau de bord des agents

  1. Créez une session et saisissez votre requête pour vérifier si l'agent répond aux demandes comme indiqué.

Interaction avec l&#39;agent

9. Nettoyage

Pour éviter que des frais ne vous soient facturés, nettoyez les ressources que vous avez créées.

Si vous avez effectué le déploiement à l'aide de Terraform, accédez au répertoire terraform et exécutez l'action de suppression :

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

Si vous avez effectué le déploiement à l'aide du SDK, créez le script permettant de supprimer l'agent déployé :

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

Exécutez le script pour supprimer l'agent :

python3 delete_agent.py

Pour nettoyer le reste des ressources, revenez à votre répertoire d'accueil et exécutez les commandes suivantes dans 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. Conclusion

Félicitations ! Vous avez réussi à conteneuriser et à déployer un agent d'IA sur Agent Runtime à l'aide de BYOC.

Vous avez appris à :

  • Utilisez ADK pour définir un agent et l'encapsuler à l'aide de FastAPI.
  • Créez un fichier Dockerfile et générez des images à l'aide de Cloud Build.
  • Gérez les autorisations IAM pour Agent Runtime.
  • Déployez votre conteneur personnalisé à l'aide du SDK Python et de Terraform.
  • Testez et interrogez votre agent déployé.