Einen Pet Passport-Agenten in Cloud Run erstellen und bereitstellen

1. Übersicht

In diesem Codelab erfahren Sie, wie Sie die App Pet Passport bereitstellen. Dabei handelt es sich um einen KI-Agenten, der das Model Context Protocol (MCP) verwendet, um Datenanalysen und Standortdienste zu kombinieren.

Die App hilft Nutzern, einen perfekten Tag mit ihrem Hund zu planen, basierend auf der Beliebtheit der Rasse in New York City. Der Agent verwendet eine „Macro-to-Micro“-Kette:

  1. Strategische Erkundung (BigQuery) : Ermittelt die Postleitzahl von New York City mit der höchsten Population für eine bestimmte Rasse.
  2. Lokale Ausführung (Maps) : Verwendet diese Postleitzahl als Standortbias, um „haustierfreundliche Cafés“ und „Hundeparks“ zu finden.
  3. Routenplanung:Kombiniert die Daten, um eine „Pet Passport“-Route mit anklickbaren Links und Bildern zu erstellen.

Der Agent wird mit dem google-adk-Framework erstellt und von Gemini unterstützt.

Hinweis: Der vollständige Projektcode, einschließlich der Frontend-UI, ist auf GitHub verfügbar. In diesem Codelab konzentrieren wir uns auf die Kernlogik des Agenten und die Einrichtung der Infrastruktur.

2. Einrichtung und Anforderungen

Prüfen Sie zuerst, ob Ihre Entwicklungsumgebung richtig eingerichtet ist.

1. Mit Google Cloud authentifizieren

Legen Sie Ihr aktives Google Cloud-Projekt fest und authentifizieren Sie sich. Dies ist erforderlich, damit der Agent auf BigQuery und andere Dienste zugreifen kann.

gcloud config set project [YOUR-PROJECT-ID]
gcloud auth application-default login --project [YOUR-PROJECT-ID]

Hinweis: Wenn bei der Authentifizierung Fehler zu einem anderen Projekt auftreten, können Sie diese umgehen, indem Sie das Kontingentprojekt deaktivieren und manuell festlegen:

gcloud auth application-default login --disable-quota-project
gcloud auth application-default set-quota-project [YOUR-PROJECT-ID]

2. Softwareanforderungen

Auf Ihrem lokalen Computer muss die folgende Software installiert sein:

  • Python (Version 3.13 oder höher erforderlich)
  • Git (zum Herunterladen des Repositorys)

Repository herunterladen

Der Code für dieses Projekt ist im Google MCP-Repository verfügbar. Klonen Sie das Repository und rufen Sie den Projektordner auf:

git clone https://github.com/google/mcp.git
cd examples/petpassport

3. Installation

Nachdem Sie die Dateien haben, richten wir die Python-Umgebung ein.

  1. Virtuelle Umgebung erstellen:Dadurch werden Ihre Abhängigkeiten isoliert.
    python3 -m venv .venv
    
  2. Virtuelle Umgebung aktivieren
    • Unter Linux/macOS:
      source .venv/bin/activate
      
    • Unter Windows:
      .venv\Scripts\activate
      
  3. Abhängigkeiten installieren :
    pip install google-adk==1.28.0 python-dotenv google-genai pillow uvicorn
    

Cloud APIs aktivieren

Aktivieren Sie die folgenden APIs in Ihrem Projekt:

gcloud services enable \
  bigquery.googleapis.com \
  aiplatform.googleapis.com \
  artifactregistry.googleapis.com \
  cloudbuild.googleapis.com \
  run.googleapis.com \
  storage.googleapis.com

Region auswählen

Legen Sie die Region als Umgebungsvariable in Ihrer Shell fest:

export REGION=us-central1

4. API-Schlüssel abrufen

Wenn Sie die Maps- und Gemini-Dienste verwenden möchten, müssen Sie API-Schlüssel abrufen und in einer .env-Datei im Projektstammverzeichnis speichern.

1. Google Maps API-Schlüssel

  1. Gehen Sie zur Google Cloud Console.
  2. Rufen Sie APIs & Dienste > Anmeldedaten auf.
  3. Klicken Sie auf Anmeldedaten erstellen > API-Schlüssel.
  4. Kopieren Sie den generierten Schlüssel und fügen Sie ihn Ihrer .env-Datei als MAPS_API_KEY=[YOUR_KEY] hinzu.
  5. (Empfohlen) Beschränken Sie den Schlüssel so, dass nur die Maps APIs verwendet werden können, die vom MCP-Server verwendet werden.

2. Gemini API-Schlüssel (AI Studio)

  1. Gehen Sie zu Google AI Studio.
  2. Klicken Sie auf API-Schlüssel abrufen oder rufen Sie den Abschnitt „API-Schlüssel“ auf.
  3. Klicken Sie auf API-Schlüssel erstellen.
  4. Kopieren Sie den Schlüssel und fügen Sie ihn Ihrer .env-Datei als GEMINI_API_KEY=[YOUR_KEY] hinzu.

5. Abhängigkeiten installieren

Erstellen Sie im Ordner petpassport/ eine Datei requirements.txt:

google-adk==1.28.0
python-dotenv
google-genai
pillow

6. MCP-Server authentifizieren

Diese Anwendung verwendet MCP-Server (Model Context Protocol), um mit Google Maps und BigQuery zu interagieren. Um diese Server zu authentifizieren, müssen Sie die entsprechenden Umgebungsvariablen und Header konfigurieren.

  1. Google Maps MCP:Erfordert einen gültigen Maps API-Schlüssel, der im Header X-Goog-Api-Key übergeben wird.
  2. BigQuery MCP:Erfordert OAuth-Anmeldedaten mit Zugriff auf den BigQuery-Dienst. Der Agent verwendet das Standarddienstkonto für Compute, wenn er in Cloud Run ausgeführt wird, oder Ihre lokalen Anmeldedaten, wenn er lokal ausgeführt wird.

Im Repository finden Sie ein Setupscript setup/setup_env.sh, mit dem Sie diese Variablen in Ihrer .env-Datei konfigurieren können.

7. BigQuery-Tabelle erstellen

Bevor der Agent Daten zu Hundelizenzen abfragen kann, müssen wir das Dataset und die Tabelle in BigQuery erstellen und die Daten laden.

Wir stellen ein Setupscript setup/setup_bigquery.sh bereit, das die folgenden Schritte ausführt:

  1. Erstellt einen Cloud Storage-Bucket mit dem Namen pet-passport-data-[PROJECT_ID] zum Speichern der Rohdaten.
  2. Lädt das öffentliche NYC Dog Licensing-Dataset (CSV) herunter.
  3. Lädt die CSV-Datei in den Bucket hoch.
  4. Erstellt ein BigQuery-Dataset mit dem Namen nyc_dogs.
  5. Lädt die Daten aus dem Bucket in eine Tabelle mit dem Namen licenses im Dataset.

Führen Sie den folgenden Befehl im Terminal aus, um das Setupscript auszuführen:

bash setup/setup_bigquery.sh

8. Verbindung mit MCP-Servern herstellen

Ein wichtiger Teil dieser App ist die Verwendung von MCP, um eine Verbindung zu Daten und Diensten herzustellen. In diesem Abschnitt konfigurieren Sie die MCP-Toolsets für BigQuery und Google Maps in einer Datei namens petpassport/tools.py.

Vollständiger tools.py-Code

Hier ist die vollständige Implementierung für tools.py, einschließlich MCP-Toolsets und benutzerdefinierter Tools für die Bild- und Datenpersistenz. Wir haben diesen Code optimiert, um Redundanzen zu reduzieren, indem wir die Bucket-Auflösung auf Modulebene verschoben haben:

import os
import dotenv
import google.auth
import time
import datetime
from google.cloud import storage
from PIL import Image
from google import genai
from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset
from google.adk.tools.mcp_tool.mcp_session_manager import StreamableHTTPConnectionParams 

MAPS_MCP_URL = "https://mapstools.googleapis.com/mcp" 
BIGQUERY_MCP_URL = "https://bigquery.googleapis.com/mcp" 

PROJECT_ID = os.getenv('GOOGLE_CLOUD_PROJECT', 'project_not_set')
BUCKET_NAME = f"pet-passport-data-{PROJECT_ID}" 

def get_maps_mcp_toolset():
    dotenv.load_dotenv()
    maps_api_key = os.getenv('MAPS_API_KEY', 'no_api_found')
    
    tools = MCPToolset(
        connection_params=StreamableHTTPConnectionParams(
            url=MAPS_MCP_URL,
            headers={    
                "X-Goog-Api-Key": maps_api_key
            },
            timeout=30.0,          
            sse_read_timeout=300.0
        )
    )
    print("Maps MCP Toolset configured.")
    return tools


def get_bigquery_mcp_toolset():   
    credentials, project_id = google.auth.default(
            scopes=["https://www.googleapis.com/auth/bigquery"]
    )

    credentials.refresh(google.auth.transport.requests.Request())
    oauth_token = credentials.token
        
    HEADERS_WITH_OAUTH = {
        "Authorization": f"Bearer {oauth_token}",
        "x-goog-user-project": project_id
    }

    tools = MCPToolset(
        connection_params=StreamableHTTPConnectionParams(
            url=BIGQUERY_MCP_URL,
            headers=HEADERS_WITH_OAUTH,
            timeout=30.0,          
            sse_read_timeout=300.0
        )
    )
    print("BigQuery MCP Toolset configured.")
    return tools

def generate_pet_passport_photo(prompt: str, image_path: str = None) -> str:
    """Generates an image using gemini-3.1-flash-image-preview based on a prompt and a reference image."""
    client = genai.Client()
    output_path = f"/tmp/pet_passport_{int(time.time())}.png"
    
    try:
        image = Image.open(image_path)
        response = client.models.generate_content(
            model="gemini-3.1-flash-image-preview",
            contents=[prompt, image],
        )
        
        for part in response.parts:
            if part.inline_data is not None:
                generated_image = part.as_image()
                generated_image.save(output_path)
                
                # Upload to GCS and generate signed URL
                try:
                    storage_client = storage.Client()
                    bucket = storage_client.bucket(BUCKET_NAME)
                    blob_name = os.path.basename(output_path)
                    blob = bucket.blob(blob_name)
                    
                    blob.upload_from_filename(output_path)
                    
                    url = blob.generate_signed_url(
                        version="v4",
                        expiration=datetime.timedelta(hours=24),
                        method="GET",
                    )
                    return url
                except Exception as e:
                    print(f"Error uploading image to GCS: {e}")
                    return output_path
                
        raise ValueError("No image was returned by the model.")
    except Exception as e:
        print(f"Error generating image: {e}")
        raise

def save_pet_passport(user_id: str, breed: str, postal_code: str, route_details: str, image_paths: list[str] = None) -> str:
    """Appends the generated itinerary to the user's history in GCS."""
    try:
        storage_client = storage.Client()
        bucket = storage_client.bucket(BUCKET_NAME)
        blob = bucket.blob(f"user-{user_id}.json")
        
        # Download existing or start fresh
        # ... (Implementation details hidden for brevity) ...
        return "Success"
    except Exception as e:
        print(f"Error saving path: {e}")
        raise

Codeerklärung: tools.py

  • get_maps_mcp_toolset und get_bigquery_mcp_toolset konfigurieren die MCP-Clients mit den richtigen Endpunkten und Authentifizierungsheadern.
  • generate_pet_passport_photo verwendet Gemini, um eine Szene zu erstellen, und lädt das Ergebnis in Google Cloud Storage hoch. Es wird eine signierte URL an das Frontend zurückgegeben, damit Serverneustarts überstanden werden.

9. KI-Agenten erstellen

Nachdem Sie Ihre Tools konfiguriert haben, ist es an der Zeit, das „Gehirn“ des Agenten zu erstellen. Mit dem Agent Development Kit (ADK) erstellen Sie einen Agenten in einer Datei namens petpassport/agent.py.

Vollständiger agent.py-Code

Hier ist die vollständige Implementierung für agent.py, in der wir den Agenten und seine Anweisungen definieren:

import os
import dotenv
import tools
from google.adk.agents import LlmAgent

dotenv.load_dotenv()

PROJECT_ID = os.getenv('GOOGLE_CLOUD_PROJECT', 'project_not_set')

maps_toolset = tools.get_maps_mcp_toolset()
bigquery_toolset = tools.get_bigquery_mcp_toolset()

root_agent = LlmAgent(
    model='gemini-2.5-pro',
    name='root_agent',
    instruction=f"""
        You are the Pet Passport Agent. Your goal is to help users find a fun walking route for their dog in NYC.
        
        When given a breed and a postal code, follow this flow:
        1. **Strategic Discovery:** Use BigQuery to find the most popular neighborhood for that breed in NYC.
        2. **Local Execution:** Use Maps to build a walking route with specific places (parks, cafes) in that area.
        
        **NO DIRECTIONS LINKS:** You must NOT include a Google Maps directions link (e.g., `https://www.google.com/maps/dir/...`) in your final response. Only provide links to individual places.
        
        After generating the itinerary, you MUST call the `save_pet_passport` tool to save this path to the user's profile. Pass a clean summary of the itinerary as `route_details`. The summary should include details (like rating, description from maps).
    """,
    tools=[maps_toolset, bigquery_toolset, tools.generate_pet_passport_photo, tools.save_pet_passport]
)

Codeerklärung: agent.py

  • Wir importieren tools direkt (vereinfachte Struktur), um die Containerumgebung zu unterstützen.
  • Der Agent wird mit gemini-2.5-pro initialisiert.
  • Die Anweisungen definieren eine strenge mehrstufige Kette von Gedanken (zuerst BigQuery, dann Maps) und verbieten ausdrücklich die Halluzination oder das Rendern von Fußgängerrouten, die zu Unordnung führen.

10. Anwendung lokal ausführen

Bevor Sie die Anwendung in Cloud Run bereitstellen, sollten Sie sie lokal testen.

  1. Achten Sie darauf, dass Sie sich im Projektverzeichnis befinden :
    cd examples/petpassport
    
  2. FastAPI-Server starten:Wir verwenden uvicorn, um die App auszuführen. Der Einstiegspunkt ist main.py im Ordner petpassport.
    uvicorn petpassport.main:app --reload
    
  3. UI öffnen:Rufen Sie in Ihrem Browser http://127.0.0.1:8000/ui/ auf, um mit der Pet Passport-Oberfläche zu interagieren.

11. In Cloud Run bereitstellen

Nachdem Sie den Agenten erstellt haben, können Sie ihn in Cloud Run bereitstellen. Wir verwenden den Standardbefehl gcloud direkt, um die Containerumgebung streng zu kontrollieren.

Führen Sie im Projektverzeichnis den folgenden Befehl aus:

gcloud run deploy petpassport \
  --source petpassport \
  --region $REGION \
  --allow-unauthenticated \
  --labels dev-tutorial=google-mcp

Umgebungsvariablen konfigurieren

Rufen Sie nach der Bereitstellung den Cloud Run-Dienst in der Google Cloud Console auf und legen Sie auf dem Tab Variablen und Secrets die folgenden Umgebungsvariablen fest:

  • MAPS_API_KEY: Ihr Google Maps API-Schlüssel.
  • GOOGLE_CLOUD_PROJECT: Ihre Projekt-ID.
  • PROJECT_ID: Ihre Projekt-ID (Redundanz wird für Legacy-Module unterstützt).

12. Beispiel-Prompts

Versuchen Sie, mit diesen Prompts mit dem bereitgestellten Agenten zu interagieren:

  1. Standard : „Ich möchte mit meinem Golden Retriever in New York City in der Nähe von 10021 spazieren gehen. Suche eine Route für uns mit einem Café.“
  2. Andere Rasse : „Ich habe eine französische Bulldogge und wir sind in der Upper West Side (in der Nähe von 10024). Schlage einen kurzen Spaziergang vor, der an einem beliebten Hundepark endet.“
  3. Mit Bild : (Laden Sie ein Foto Ihres Hundes hoch) „Hier ist ein Foto meines Corgis! Wir sind in der Nähe von 10013. Plane einen perfekten Tag für uns.“

13. Bereinigen

So vermeiden Sie, dass Ihnen die in dieser Anleitung verwendeten Ressourcen in Rechnung gestellt werden:

  • Löschen Sie den Cloud Run-Dienst: gcloud run services delete petpassport --region=$REGION
  • Löschen Sie den GCS-Bucket: gcloud storage rm -r gs://pet-passport-data-$PROJECT_ID