Créer et déployer un agent Pet Passport sur Cloud Run

1. Présentation

Dans cet atelier de programmation, vous allez apprendre à déployer l'application Pet Passport, un agent d'IA qui utilise le protocole MCP (Model Context Protocol) pour combiner l'analyse de données et les services de localisation.

L'application aide les utilisateurs à planifier une journée parfaite avec leur chien en fonction de la popularité de la race à New York. L'agent utilise une chaîne de raisonnement "macro vers micro" :

  1. Découverte stratégique (BigQuery) : identifie le code postal de New York où la population d'une race spécifique est la plus élevée.
  2. Exécution locale (Maps) : utilise ce code postal comme biais de localisation pour trouver des "cafés acceptant les animaux" et des "parcs pour chiens".
  3. Génération d'itinéraires : combine les données pour créer un itinéraire "Passeport pour animaux de compagnie" avec des liens cliquables et des images.

L'agent est créé à l'aide du framework google-adk et optimisé par Gemini.

Remarque : Le code complet du projet, y compris l'UI frontend, est disponible sur GitHub. Dans cet atelier de programmation, nous nous concentrerons sur la logique de l'agent principal et la configuration de l'infrastructure.

2. Préparation

Tout d'abord, assurez-vous que votre environnement de développement est correctement configuré.

1. Authentification avec Google Cloud

Définissez votre projet Google Cloud actif et authentifiez-vous. Cela est nécessaire pour que l'agent puisse accéder à BigQuery et à d'autres services.

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

Remarque : Si vous rencontrez des erreurs concernant un autre projet lors de l'authentification, vous pouvez les ignorer en désactivant le projet de quota et en le définissant manuellement :

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

2. Logiciels requis

Vous devez avoir installé les logiciels suivants sur votre ordinateur local :

  • Python (version 3.13 ou ultérieure requise)
  • Git (pour télécharger le dépôt)

Télécharger le dépôt

Le code de ce projet est disponible dans le dépôt Google MCP. Clonez le dépôt et accédez au dossier du projet :

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

3. Installation

Maintenant que vous avez les fichiers, configurons l'environnement Python.

  1. Créez un environnement virtuel : cela permet d'isoler vos dépendances.
    python3 -m venv .venv
    
  2. Activez l'environnement virtuel :
    • Sous Linux/macOS :
      source .venv/bin/activate
      
    • Sous Windows :
      .venv\Scripts\activate
      
  3. Installez les dépendances :
    pip install google-adk==1.28.0 python-dotenv google-genai pillow uvicorn
    

Activer les API Cloud

Activez les API suivantes dans votre projet :

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

Sélectionner une région

Définissez la région en tant que variable d'environnement dans votre shell :

export REGION=us-central1

4. Obtenir des clés API

Pour utiliser les services Maps et Gemini, vous devez obtenir des clés API et les stocker dans un fichier .env à la racine du projet.

1. Clé API Google Maps

  1. Accédez à la console Google Cloud.
  2. Accédez à API et services > Identifiants.
  3. Cliquez sur Créer des identifiants > Clé API.
  4. Copiez la clé générée et ajoutez-la à votre fichier .env en tant que MAPS_API_KEY=[YOUR_KEY].
  5. (Recommandé) Restreignez la clé pour n'autoriser que les API Maps utilisées par le serveur MCP.

2. Clé API Gemini (AI Studio)

  1. Accédez à Google AI Studio.
  2. Cliquez sur Obtenir une clé API ou accédez à la section "Clés API".
  3. Cliquez sur Créer une clé API.
  4. Copiez la clé et ajoutez-la à votre fichier .env en tant que GEMINI_API_KEY=[YOUR_KEY].

5. Installer des dépendances

Créez un fichier requirements.txt dans le dossier petpassport/ :

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

6. Authentifier les serveurs MCP

Cette application s'appuie sur des serveurs MCP (Model Context Protocol) pour interagir avec Google Maps et BigQuery. Pour authentifier ces serveurs, vous devez configurer les variables d'environnement et les en-têtes appropriés.

  1. Google Maps MCP : nécessite une clé API Maps valide transmise dans l'en-tête X-Goog-Api-Key.
  2. MCP BigQuery : nécessite des identifiants OAuth avec accès au service BigQuery. L'agent utilise le compte de service Compute par défaut lorsqu'il s'exécute sur Cloud Run, ou vos identifiants locaux lorsqu'il s'exécute en local.

Nous fournissons un script de configuration setup/setup_env.sh dans le dépôt qui permet de configurer ces variables dans votre fichier .env.

7. Créer la table BigQuery

Avant que l'agent puisse interroger les données sur les licences pour chiens, nous devons créer l'ensemble de données et la table dans BigQuery, puis charger les données.

Nous fournissons un script de configuration setup/setup_bigquery.sh qui effectue les étapes suivantes :

  1. Crée un bucket Cloud Storage nommé pet-passport-data-[PROJECT_ID] pour stocker les données brutes.
  2. Télécharge l'ensemble de données publiques (CSV) sur les licences pour chiens à New York.
  3. importe le fichier CSV dans le bucket ;
  4. Crée un ensemble de données BigQuery nommé nyc_dogs.
  5. Charge les données du bucket dans une table nommée licenses dans l'ensemble de données.

Pour exécuter le script de configuration, exécutez la commande suivante dans votre terminal :

bash setup/setup_bigquery.sh

8. Se connecter aux serveurs MCP

Une partie essentielle de cette application consiste à utiliser MCP pour se connecter aux données et aux services. Dans cette section, vous allez configurer les ensembles d'outils MCP pour BigQuery et Google Maps dans un fichier appelé petpassport/tools.py.

Compléter le code tools.py

Voici l'implémentation complète de tools.py, y compris les ensembles d'outils MCP et les outils personnalisés pour la persistance des images et des données. Nous avons optimisé ce code pour réduire les redondances en déplaçant la résolution du bucket au niveau du module :

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

Explication du code : tools.py

  • get_maps_mcp_toolset et get_bigquery_mcp_toolset configurent les clients MCP avec les bons points de terminaison et les bons en-têtes d'authentification.
  • generate_pet_passport_photo utilise Gemini pour créer une scène et importe le résultat dans Google Cloud Storage. Il renvoie une URL signée au frontend pour survivre aux redémarrages du serveur.

9. Créer l'agent

Maintenant que vos outils sont configurés, il est temps de créer le "cerveau" de l'agent. Vous allez utiliser l'Agent Development Kit (ADK) pour créer un agent dans un fichier nommé petpassport/agent.py.

Compléter le code agent.py

Voici l'implémentation complète de agent.py, où nous définissons l'agent et ses instructions :

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]
)

Explication du code : agent.py

  • Nous importons tools directement (structure aplatie) pour prendre en charge l'environnement de conteneur.
  • L'agent est initialisé avec gemini-2.5-pro.
  • Les instructions définissent une chaîne de réflexion stricte en plusieurs étapes (BigQuery d'abord, puis Maps) et interdisent strictement l'hallucination ou le rendu d'itinéraires à pied qui conduisent à l'encombrement.

10. Exécuter l'application en local

Avant de déployer l'application sur Cloud Run, il est judicieux de la tester en local.

  1. Assurez-vous d'être dans le répertoire du projet :
    cd examples/petpassport
    
  2. Démarrez le serveur FastAPI : nous utilisons uvicorn pour exécuter l'application. Le point d'entrée est main.py dans le dossier petpassport.
    uvicorn petpassport.main:app --reload
    
  3. Ouvrez l'interface utilisateur : accédez à http://127.0.0.1:8000/ui/ dans votre navigateur pour interagir avec l'interface Passeport pour animaux de compagnie.

11. Déployer sur Cloud Run

Maintenant que votre agent est prêt, il est temps de le déployer sur Cloud Run. Nous utilisons directement la commande gcloud standard pour contrôler strictement l'environnement du conteneur.

Depuis le répertoire du projet, exécutez la commande suivante :

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

Configurer les variables d'environnement

Après le déploiement, accédez au service Cloud Run dans la console Google Cloud et définissez les variables d'environnement suivantes dans l'onglet Variables et secrets :

  • MAPS_API_KEY : votre clé API Google Maps.
  • GOOGLE_CLOUD_PROJECT : ID de votre projet
  • PROJECT_ID : ID de votre projet (la redondance est acceptée pour les anciens modules).

12. Exemples de requêtes

Essayez d'interagir avec l'agent déployé à l'aide des prompts suivants :

  1. Standard : "Je veux aller me promener avec mon Golden Retriever à New York, près du code postal 10021. Trouve-nous un itinéraire qui passe par un café."
  2. Race différente : "J'ai un bouledogue français et nous sommes dans l'Upper West Side (près de 10024). Suggère une courte promenade qui s'arrête dans un parc à chiens populaire."
  3. Avec une image : (importez une photo de votre chien) "Voici une photo de mon corgi ! Nous sommes près de 10013. Organise-nous une journée parfaite."

13. Effectuer un nettoyage

Pour éviter que les ressources utilisées dans ce tutoriel soient facturées :

  • Supprimez le service Cloud Run : gcloud run services delete petpassport --region=$REGION
  • Supprimez le bucket GCS : gcloud storage rm -r gs://pet-passport-data-$PROJECT_ID