Cloud Run'da Evcil Hayvan Pasaportu Aracısı Oluşturma ve Dağıtma

1. Genel Bakış

Bu kod laboratuvarında, veri analizi ve konum hizmetlerini birleştirmek için Model Context Protocol (MCP) kullanan bir yapay zeka ajanı olan Pet Passport uygulamasını nasıl dağıtacağınızı öğreneceksiniz.

Uygulama, kullanıcıların New York City'deki cins popülerliğine göre köpekleriyle mükemmel bir gün geçirmelerini planlamalarına yardımcı oluyor. Aracı, "makrodan mikroya" akıl yürütme zinciri kullanır:

  1. Stratejik Keşif (BigQuery): Belirli bir tür için en yüksek nüfusa sahip NYC posta kodunu tanımlar.
  2. Yerel Yürütme (Haritalar): "Evcil hayvan dostu kafeler" ve "köpek parkları" bulmak için bu posta kodunu konum önyargısı olarak kullanır.
  3. Seyahat planı oluşturma: Verileri birleştirerek tıklanabilir bağlantılar ve resimler içeren bir "Evcil Hayvan Pasaportu" seyahat planı oluşturur.

Ajan, google-adk çerçevesi kullanılarak oluşturulur ve Gemini tarafından desteklenir.

Not: Ön uç kullanıcı arayüzü de dahil olmak üzere projenin tam kodu GitHub'da mevcuttur. Bu codelab'de temel aracı mantığına ve altyapı kurulumuna odaklanacağız.

2. Kurulum ve şartlar

Öncelikle, geliştirme ortamınızın doğru şekilde ayarlandığından emin olalım.

1. Google Cloud ile kimlik doğrulama

Etkin Google Cloud projenizi ayarlayın ve kimliğinizi doğrulayın. Ajanın BigQuery'ye ve diğer hizmetlere erişmesi için bu gereklidir.

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

Not: Kimlik doğrulama sırasında farklı bir projeyle ilgili hatalarla karşılaşırsanız kota projesini devre dışı bırakıp manuel olarak ayarlayarak bu hatayı atlayabilirsiniz:

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

2. Yazılım Gereksinimleri

Yerel makinenizde aşağıdaki yazılımların yüklü olması gerekir:

  • Python (3.13 veya sonraki sürümler gereklidir)
  • Git (depoyu indirmek için)

Depoyu indirme

Bu projenin kodu Google MCP deposunda mevcuttur. Depoyu klonlayın ve proje klasörüne gidin:

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

3. Kurulum

Dosyalarınız hazır olduğuna göre Python ortamını ayarlayalım.

  1. Sanal ortam oluşturun: Bu, bağımlılıklarınızı izole tutar.
    python3 -m venv .venv
    
  2. Sanal ortamı etkinleştirin:
    • Linux/macOS'te:
      source .venv/bin/activate
      
    • Windows'da:
      .venv\Scripts\activate
      
  3. Bağımlılıkları yükleyin:
    pip install google-adk==1.28.0 python-dotenv google-genai pillow uvicorn
    

Cloud API'lerini etkinleştirme

Projenizde aşağıdaki API'leri etkinleştirin:

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

Bölge seçin

Bölgeyi kabuğunuzda ortam değişkeni olarak ayarlayın:

export REGION=us-central1

4. API anahtarları edinme

Haritalar ve Gemini hizmetlerini kullanmak için API anahtarları edinmeniz ve bunları proje kökündeki bir .env dosyasında saklamanız gerekir.

1. Google Maps API anahtarı

  1. Google Cloud Console'a gidin.
  2. API'ler ve Hizmetler > Kimlik Bilgileri'ne gidin.
  3. Kimlik bilgileri oluştur > API anahtarı'nı tıklayın.
  4. Oluşturulan anahtarı kopyalayın ve .env dosyanıza MAPS_API_KEY=[YOUR_KEY] olarak ekleyin.
  5. (Önerilir) Anahtarı, yalnızca MCP sunucusu tarafından kullanılan Haritalar API'lerinin kullanılmasına izin verecek şekilde kısıtlayın.

2. Gemini API anahtarı (AI Studio)

  1. Google AI Studio'ya gidin.
  2. API anahtarını al'ı tıklayın veya API anahtarları bölümüne gidin.
  3. Create API key'i (API anahtarı oluştur) tıklayın.
  4. Anahtarı kopyalayın ve .env dosyanıza GEMINI_API_KEY=[YOUR_KEY] olarak ekleyin.

5. Bağımlılıkları yükleme

petpassport/ klasöründe requirements.txt dosyası oluşturma:

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

6. MCP sunucularının kimliğini doğrulama

Bu uygulama, Google Haritalar ve BigQuery ile etkileşim kurmak için Model Context Protocol (MCP) sunucularını kullanır. Bu sunucuları kimlik doğrulamak için uygun ortam değişkenlerini ve üstbilgileri yapılandırmanız gerekir.

  1. Google Haritalar MCP: X-Goog-Api-Key üstbilgisinde geçerli bir Maps API anahtarının iletilmesi gerekir.
  2. BigQuery MCP: BigQuery hizmetine erişimi olan OAuth kimlik bilgilerini gerektirir. Aracı, Cloud Run'da çalışırken varsayılan işlem hizmeti hesabını, yerel olarak çalışırken ise yerel kimlik bilgilerinizi kullanır.

Depoda, setup/setup_env.sh kurulum komut dosyası bulunur. Bu komut dosyası, .env dosyanızdaki bu değişkenleri yapılandırmanıza yardımcı olur.

7. BigQuery tablosunu oluşturma

Aracı, köpek lisansı verilerini sorgulamadan önce BigQuery'de veri kümesini ve tabloyu oluşturup verileri yüklememiz gerekir.

Aşağıdaki adımları gerçekleştiren bir kurulum komut dosyası setup/setup_bigquery.sh sağlıyoruz:

  1. Ham verileri depolamak için pet-passport-data-[PROJECT_ID] adlı bir Cloud Storage paketi oluşturur.
  2. Herkese açık NYC Dog Licensing veri kümesini (CSV) indirir.
  3. CSV'yi pakete yükler.
  4. nyc_dogs adlı bir BigQuery veri kümesi oluşturur.
  5. Paketteki verileri veri kümesinde licenses adlı bir tabloya yükler.

Kurulum komut dosyasını çalıştırmak için terminalinizde aşağıdaki komutu yürütün:

bash setup/setup_bigquery.sh

8. MCP sunucularına bağlanma

Bu uygulamanın önemli bir parçası, verilere ve hizmetlere bağlanmak için MCP'yi kullanmaktır. Bu bölümde, petpassport/tools.py adlı bir dosyada BigQuery ve Google Haritalar için MCP araç setlerini yapılandıracaksınız.

tools.py kodunu tamamlama

tools.py için MCP araç setleri ve resim ile veri kalıcılığına yönelik özel araçlar da dahil olmak üzere tam uygulama aşağıda verilmiştir. Bu kodu, demet çözünürlüğünü modül düzeyine taşıyarak gereksiz tekrarları azaltacak şekilde optimize ettik:

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

Kod Açıklaması: tools.py

  • get_maps_mcp_toolset ve get_bigquery_mcp_toolset, MCP istemcilerini doğru uç noktalar ve kimlik doğrulama üstbilgileriyle yapılandırır.
  • generate_pet_passport_photo, sahne oluşturmak için Gemini'ı kullanır ve sonucu Google Cloud Storage'a yükler. Sunucu yeniden başlatıldığında çalışmaya devam etmek için ön uca imzalı bir URL döndürür.

9. Aracıyı oluşturma

Araçlarınızı yapılandırdıktan sonra temsilcinin "beynini" oluşturma zamanı gelir. petpassport/agent.py adlı bir dosyada temsilci oluşturmak için Agent Development Kit'i (ADK) kullanacaksınız.

agent.py kodunu tamamlama

Aşağıda, aracıyı ve talimatlarını tanımladığımız agent.py için tam uygulama yer almaktadır:

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

Kod Açıklaması: agent.py

  • Kapsayıcı ortamını desteklemek için tools doğrudan (düzleştirilmiş yapı) içe aktarırız.
  • Temsilci gemini-2.5-pro ile başlatılır.
  • Talimatlar, katı bir çok adımlı düşünce zinciri (önce BigQuery, sonra Haritalar) tanımlar ve karmaşaya yol açan yaya rotalarının halüsinasyonunu veya oluşturulmasını kesinlikle yasaklar.

10. Uygulamayı Yerel Olarak Çalıştırma

Cloud Run'a dağıtmadan önce uygulamayı yerel olarak test etmeniz önerilir.

  1. Proje dizininde olduğunuzdan emin olun:
    cd examples/petpassport
    
  2. FastAPI sunucusunu başlatın: Uygulamayı çalıştırmak için uvicorn kullanılır. Giriş noktası, petpassport klasöründeki main.py'dir.
    uvicorn petpassport.main:app --reload
    
  3. Kullanıcı arayüzünü açın: Evcil Hayvan Pasaportu arayüzüyle etkileşimde bulunmak için tarayıcınızda http://127.0.0.1:8000/ui/ adresine gidin.

11. Cloud Run'a dağıtma

Temsilciniz hazır olduğuna göre Cloud Run'a dağıtma zamanı geldi. Kapsayıcı ortamı üzerinde sıkı kontrol sağlamak için doğrudan standart gcloud komutunu kullanırız.

Proje dizininden aşağıdaki komutu çalıştırın:

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

Ortam değişkenlerini yapılandırma

Dağıtımdan sonra Google Cloud Console'da Cloud Run hizmetine gidin ve Değişkenler ve Gizli Diziler sekmesinde aşağıdaki ortam değişkenlerini ayarlayın:

  • MAPS_API_KEY: Google Maps API anahtarınız.
  • GOOGLE_CLOUD_PROJECT: Proje kimliğiniz.
  • PROJECT_ID: Proje kimliğiniz (eski modüller için yedeklilik desteklenir).

12. Örnek İstemler

Aşağıdaki istemleri kullanarak dağıtılan temsilciyle etkileşime geçmeyi deneyin:

  1. Standart: "New York'ta 10021 yakınlarında Golden Retriever cinsi köpeğimle yürüyüşe çıkmak istiyorum. Bize kafe içeren bir rota bul."
  2. Farklı Cins: "Fransız buldoğum var ve Upper West Side'da (10024 yakınında) yaşıyoruz. Popüler bir köpek parkında duran kısa bir yürüyüş öner."
  3. Resimle: (Köpeğinizin fotoğrafını yükleyin) "Corgi cinsi köpeğimin fotoğrafını ekliyorum. 10013 numaralı posta kodunun yakınındayız. Bize mükemmel bir gezi planla."

13. Temizleme

Bu eğiticide kullanılan kaynaklar için ücretlendirilmemek istiyorsanız:

  • Cloud Run hizmetini silin: gcloud run services delete petpassport --region=$REGION
  • GCS paketini silin: gcloud storage rm -r gs://pet-passport-data-$PROJECT_ID