Wdrażanie skonteneryzowanego agenta w Agent Runtime

1. Przegląd

Środowisko wykonawcze agentów (wcześniej Agent Engine) to zarządzane środowisko wykonawcze przeznaczone do skutecznego wdrażania, uruchamiania i skalowania agentów AI. Domyślnie platforma automatycznie łączy kod źródłowy i zależności podczas procesu wdrażania.

Jednak w przypadku zadań w przedsiębiorstwie często wymagane jest pełne przejęcie kontroli nad środowiskiem wykonawczym. Aby to umożliwić, Agent Runtime udostępnia funkcję Bring Your Own Container(BYOC), która pozwala wdrażać gotowe niestandardowe obrazy kontenerów.

To ćwiczenie zawiera opis kompleksowego procesu konteneryzacji agenta utworzonego za pomocą pakietu Agent Development Kit (ADK), konfigurowania niezbędnych uprawnień Google Cloud i wdrażania go w Agent Runtime za pomocą pakietu Python SDK lub Terraform.

W tym ćwiczeniu dowiesz się, jak:

  1. utworzyć agenta w Pythonie za pomocą pakietu Google Agent Development Kit (ADK);
  2. opakować agenta w aplikację FastAPI ;
  3. skonteneryzować aplikację za pomocą Dockera ;
  4. skonfigurować uprawnienia Google Cloud;
  5. wdrożyć i przetestować skonteneryzowanego agenta w środowisku wykonawczym agentów.

Proces tworzenia i wdrażania

Poniższy diagram przedstawia proces tworzenia i wdrażania, który wykonasz ręcznie w tym ćwiczeniu:

Schemat procesu CI/CD

Wymagania

  • Projekt Google Cloud z włączonym rozliczeniem.
  • Dostęp do Cloud Shell (zalecane) lub lokalnego środowiska programistycznego z zainstalowanymi narzędziami gcloud i docker.
  • Podstawowa znajomość Pythona i Dockera.

2. Konfiguracja środowiska

Zanim zaczniesz, musisz włączyć niezbędne interfejsy API i skonfigurować środowisko.

Krok 1. Otwórz Cloud Shell

W prawym górnym rogu konsoli Google Cloud kliknij przycisk Aktywuj Cloud Shell.

Cloud Shell

Krok 2. Skonfiguruj zmienne środowiskowe

W Cloud Shell ustaw identyfikator projektu i zdefiniuj kluczowe zmienne środowiskowe używane w tym ćwiczeniu. Zastąp "YOUR_PROJECT_ID" rzeczywistym identyfikatorem projektu 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"

Te zmienne konfigurują ustawienia wdrożenia docelowego:

  • PROJECT_ID: unikalny identyfikator projektu Google Cloud, w którym będą się znajdować wszystkie zasoby platformy agentów Gemini Enterprise i Artifact Registry.
  • LOCATION: region geograficzny (np. us-central1), w którym znajdują się Twoje repozytoria i zadania środowiska wykonawczego.
  • MODEL: wersja modelu Gemini (np. gemini-3.1-flash-lite) wczytywana przez kontekst agenta.
  • MODEL_REGION: region punktu końcowego modelu. Ustaw tutaj wartość "global", aby wywoływać model Gemini z globalnych punktów końcowych.

Krok 3. Włącz interfejsy API

Włącz wymagane Google Cloud APIs:

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

Krok 4. Zainstaluj SDK

Zainstaluj pakiet Vertex AI SDK z obsługą Agent Engine i ADK:

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

3. Konfiguracja plików źródłowych

W tym kroku utworzysz strukturę i kod agenta.

Omówienie struktury katalogu

Po zakończeniu tego ćwiczenia pliki będą uporządkowane w następującej hierarchii obszaru roboczego:

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

Krok 1. Utwórz katalogi

Zacznij w katalogu głównym i utwórz strukturę obszaru roboczego:

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

Krok 2. Utwórz plik konfiguracji

Aby zapisać parametry konfiguracji bezpośrednio w weather_agent/config.json, uruchom w Cloud Shell to polecenie. To polecenie automatycznie zastępuje zmienne wartościami środowiska:

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

Krok 3. Zdefiniuj agenta

Aby zapisać konfigurację agenta i logikę narzędzia pozorowanego w weather_agent/agent.py, uruchom ten skrypt:

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

Utwórz pusty plik __init__.py, aby przekształcić weather_agent w pakiet Pythona:

touch weather_agent/__init__.py

Krok 4. Utwórz otokę FastAPI

Aby zapisać konfigurację punktu wejścia serwera FastAPI w main.py, uruchom ten skrypt:

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

Krok 5. Zdefiniuj zależności

Zapisz wymagane zależności Pythona w requirements.txt:

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

4. Konteneryzacja

Teraz określ, jak agent będzie pakowany w kontener.

Krok 1. Utwórz Dockerfile

Utwórz Dockerfile w katalogu głównym projektu, aby określić, jak ma być tworzona aplikacja 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. Konfigurowanie Artifact Registry i Cloud Build

Potrzebujesz repozytorium do przechowywania obrazu kontenera i uprawnień do jego przekazywania.

Krok 1. Utwórz repozytorium

Zdefiniuj nazwę repozytorium i utwórz repozytorium Dockera w Artifact Registry za pomocą zmiennych środowiskowych zdefiniowanych podczas konfiguracji:

export REPOSITORY_NAME="agents-repo"

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

Krok 2. Skonfiguruj uprawnienia konta usługi

Przyznaj domyślnemu kontu usługi Compute uprawnienia do przekazywania obrazów do Artifact Registry.

Najpierw znajdź numer projektu:

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

Przyznaj role:

# 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

Krok 3. Przyznaj uprawnienia agentom usługi

Przyznaj agentom usługi AI Platform i Reasoning Engine dostęp do Artifact Registry w roli czytelnika:

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

Krok 4. Utwórz i prześlij obraz

Użyj Cloud Build, aby utworzyć i przesłać obraz kontenera:

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

6. Wdrażanie agenta za pomocą pakietu SDK

Teraz, gdy uprawnienia są skonfigurowane, możesz wdrożyć niestandardowy kontener.

Krok 1. Wdróż agenta BYOC

Aby wdrożyć kontener hostowany w rejestrze w Agent Runtime, utwórz plik Pythona deploy_byoc.py w katalogu głównym projektu:

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

Aby wdrożyć agenta w Agent Runtime, uruchom skrypt wdrożenia:

python3 deploy_byoc.py

7. Wdrażanie agenta za pomocą Terraform

Możesz też wdrożyć tego samego skonteneryzowanego agenta za pomocą Terraform. Jest to zalecane w środowiskach produkcyjnych, aby zarządzać infrastrukturą jako kodem.

Krok 1. Przejdź do katalogu Terraform

Utwórz katalog terraform w katalogu głównym projektu i przejdź do niego:

mkdir -p terraform
cd terraform

Krok 2. Utwórz konfigurację dostawców

Aby zapisać mapowanie dostawców w providers.tf, uruchom ten skrypt:

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

Krok 3. Utwórz definicję zmiennych

Zapisz blok opisu danych wejściowych w 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

Krok 4. Utwórz konfigurację główną

Zapisz parametry definicji zasobów głównych w 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

Krok 5. Utwórz definicję danych wyjściowych

Zapisz blok danych wyjściowych w 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

Krok 6. Utwórz plik wartości zmiennych (tfvars)

Aby wdrożyć dynamicznie bez edytowania symboli zastępczych, przekaż zmienne środowiskowe bezpośrednio do terraform.tfvars:

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

Krok 7. Zainicjuj i zastosuj

Zainicjuj Terraform i zastosuj konfigurację:

terraform init
terraform apply

Gdy pojawi się odpowiedni komunikat, potwierdź zastosowanie, wpisując yes.

Po zakończeniu Terraform wyświetli nazwę zasobu. Aby programowo przechwycić nazwę zasobu do agent_resource_name.txt i wrócić do folderu głównego, użyj tego polecenia:

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

8. Wysyłanie zapytań do agenta

Sprawdź, czy agent działa i odpowiada.

Krok 1. Utwórz skrypt zapytania

Aby pobrać współrzędne lokalizacji, zapisz skrypt weryfikacji w query_agent.py za pomocą dynamicznego sprawdzania konfiguracji:

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

Uruchom skrypt zapytania:

python3 query_agent.py

Powinny pojawić się dane wyjściowe przesyłane z agenta, w tym symulowana temperatura w Tokio.

Krok 2. Użyj konsoli

  1. Aby przefiltrować listę agentów, przejdź do wdrożonego agenta, klikając kolejno Agent Platform > Agenci > Deployment.

Zdjęcie agenta

  1. Na panelu agenta kliknij Plac zabaw.

Panel agenta

  1. Utwórz nową sesję i wpisz zapytanie, aby sprawdzić, czy agent odpowiada na żądania zgodnie z oczekiwaniami.

Interakcja z agentem

9. Czyszczenie

Aby uniknąć obciążenia konta opłatami, usuń utworzone przez siebie zasoby.

Jeśli wdrożono za pomocą Terraform, przejdź do katalogu terraform i wykonaj działanie usuwania:

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

Jeśli wdrożono za pomocą pakietu SDK, utwórz skrypt usuwania wdrożonego agenta:

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

Aby usunąć agenta, uruchom skrypt:

python3 delete_agent.py

Aby usunąć pozostałe zasoby, wróć do katalogu głównego i uruchom w Cloud Shell te polecenia:

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. Podsumowanie

Gratulacje! Udało Ci się skonteneryzować i wdrożyć agenta AI w Agent Runtime za pomocą BYOC.

W tym ćwiczeniu dowiedziałeś się, jak:

  • używać ADK do definiowania agenta i opakowywania go za pomocą FastAPI;
  • tworzyć Dockerfile i tworzyć obrazy za pomocą Cloud Build;
  • Zarządzaj uprawnieniami IAM dla Agent Runtime.
  • wdrażać niestandardowy kontener za pomocą pakietu Python SDK i Terraform ;
  • testować wdrożonego agenta i wysyłać do niego zapytania.