Google'ın Temsilci Yığını'nın Kullanımı: Google Cloud'da ADK, A2A, MCP

1. Öğrenecekleriniz

Hoş geldiniz! Bugün oldukça keyifli bir yolculuğa çıkacağız. Popüler bir sosyal etkinlik platformu olan InstaVibe'ı düşünerek başlayalım. Bu özellik başarılı olsa da bazı kullanıcılar için grup etkinliklerinin planlanması sıkıcı bir iş olabilir. Tüm arkadaşlarınızın ilgi alanlarını öğrenmeye çalıştığınızı, ardından etkinlikler veya mekanlarla ilgili sonsuz seçenek arasından eleme yaptığınızı ve son olarak her şeyi koordine ettiğinizi hayal edin. Çok fazla! İşte tam da bu noktada yapay zekayı ve daha spesifik olarak akıllı aracıları devreye sokarak gerçek bir fark yaratabiliriz.

Bu fikrin amacı, kullanıcı ve arkadaş tercihlerini anlamak için akıllıca "dinlemek" gibi zorlu görevleri yerine getirebilecek ve ardından proaktif olarak harika ve kişiye özel etkinlikler önerebilecek bir sistem oluşturmaktır. Amacımız, InstaVibe'daki sosyal planlamayı sorunsuz ve keyifli bir deneyime dönüştürmektir. Bu akıllı asistanları oluşturmaya başlamak için doğru araçlarla güçlü bir temel oluşturmamız gerekiyor.

Göreceğiniz konsept:

Başlık Sayfası

Google'ın ADK'sı ile temeller: Google'ın Agent Development Kit'ini (ADK) kullanarak ilk akıllı aracınızı oluşturmanın temellerinde uzmanlaşın. Temel bileşenleri, aracı yaşam döngüsünü ve çerçevenin yerleşik araçlarından nasıl etkili bir şekilde yararlanacağınızı öğrenin.

Model Context Protocol (MCP) ile Asistan Özelliklerini Genişletme: Asistanlarınıza özel araçlar ve bağlam ekleyerek uzmanlık gerektiren görevleri yerine getirmelerini ve belirli bilgilere erişmelerini sağlayın. Model Context Protocol (MCP) kavramını tanıtın. Bu bağlamı sağlamak için MCP sunucusu ayarlamayı öğreneceksiniz.

Aracı Etkileşimlerini ve Düzenlemeyi Tasarlama: Aracı düzenlemesini anlamak için tek bir aracının ötesine geçin. Basit sıralı iş akışlarından döngüler, koşullu mantık ve paralel işlemeyi içeren karmaşık senaryolara kadar çeşitli etkileşim kalıpları tasarlayın. Modüler görevleri yönetmek için ADK çerçevesinde alt aracı kavramını kullanıma sunma.

İşbirlikçi Çoklu Aracı Sistemleri Oluşturma: Birden fazla aracının karmaşık hedeflere ulaşmak için işbirliği yaptığı sistemleri nasıl tasarlayacağınızı keşfedin. Dağıtılmış aracıların (farklı makinelerde veya hizmetlerde çalışıyor olabilir) güvenilir bir şekilde etkileşim kurması için standartlaştırılmış bir yol oluşturan Aracıdan Aracıya (A2A) iletişim protokolünü öğrenin ve uygulayın.

Google Cloud'da aracıları üretime alma: Aracı uygulamalarınızı geliştirme ortamlarından buluta taşıyın. Google Cloud Platform'da (GCP) ölçeklenebilir ve sağlam çok aracı sistemler tasarlayıp dağıtmaya yönelik en iyi uygulamaları öğrenin. Cloud Run gibi GCP hizmetlerinden yararlanma hakkında bilgi edinin ve aracılarınızı barındırmak ve yönetmek için en yeni Google Agent Engine'in özelliklerini keşfedin.

2. Mimari

InstaVibe ile yapay zeka destekli sosyal medya planlaması

Sosyal dinleme nedir?

Sosyal medya dinleme, bir konu, marka veya sektör hakkında insanların ne söylediğini anlamak için sosyal medya, forumlar ve haber siteleri gibi platformlardaki dijital sohbetleri izleme sürecidir. Kamuoyu duyguları, trendler ve kullanıcı ihtiyaçları hakkında değerli bilgiler sağlar. Bu atölye çalışmasında, bu kavramı aracı tabanlı bir sistemde kullanacağız.

You're on the Team at InstaVibe

Genç yetişkinlere yönelik popüler bir sosyal etkinlik platformu olan başarılı bir startup şirketi "InstaVibe"da çalıştığınızı hayal edin. İşler iyi gidiyor ancak ekibiniz, birçok teknoloji şirketi gibi yatırımcıların yapay zekayı kullanarak yenilik yapma baskısıyla karşı karşıya. Ayrıca, diğer kullanıcılar kadar etkileşimde bulunmayan bir kullanıcı segmenti olduğunu da fark ettiniz. Bu kullanıcılar, grup etkinlikleri başlatmaya daha az istekli olabilir veya planlama sürecini zor bulabilir. Bu durum, şirketiniz için bu önemli kullanıcı grubu arasında platforma bağlılığın azalması anlamına gelir.

Ekibinizin araştırması, yapay zeka destekli yardımın bu kullanıcıların deneyimini önemli ölçüde iyileştirebileceğini gösteriyor. Bu uygulamanın amacı, kullanıcının ve arkadaşlarının ilgi alanlarına göre alakalı etkinlikleri proaktif olarak önererek sosyal gezileri planlama sürecini kolaylaştırmaktır. Sizin ve iş arkadaşlarınızın karşılaştığı soru şudur: Yapay zeka temsilcileri, ilgi alanlarını keşfetme, etkinlik araştırması yapma ve potansiyel olarak ilk koordinasyonu sağlama gibi genellikle zaman alan görevleri nasıl otomatikleştirebilir?

Aracı Tabanlı Çözüm (Prototip Kavramı)

Çoklu temsilci sistemiyle desteklenen bir prototip özellik geliştirmenizi öneriyoruz. Kavramsal bir döküm:

Usecase

  • Sosyal profilleme aracısı: Bu aracı, kullanıcı bağlantılarını, etkileşimlerini ve kullanıcının tercihleriyle ilgili olabilecek daha geniş kamu trendlerini analiz etmek için sosyal dinleme tekniklerini kullanır. Bu özelliğin amacı, ortak ilgi alanlarını ve uygun etkinlik özelliklerini (ör. daha sakin toplantılarla ilgili tercihler, belirli hobiler) belirlemektir.
  • Etkinlik Planlama Aracısı: Bu aracı, Sosyal Profil Oluşturma Aracısı'nın sunduğu analizlerden yararlanarak belirlenen ölçütlere (ör. konum, ilgi alanları) uygun belirli etkinlikler, mekanlar veya fikirler için online kaynaklarda arama yapar.
  • Platform Etkileşimi Temsilcisi (MCP kullanılarak): Bu temsilci, Etkinlik Planlama Temsilcisi'nden son planı alır. Temel işlevi, önceden tanımlanmış bir MCP (Model Context Protocol) aracı kullanarak doğrudan InstaVibe platformuyla etkileşim kurmaktır. Bu araç, temsilciye etkinlik önerisi taslağı oluşturma ve planı özetleyen bir gönderi oluşturma gibi belirli özellikler sunar.
  • Orchestrator Agent: Bu aracı, merkezi koordinatör olarak işlev görür. InstaVibe platformundan ilk kullanıcı isteğini alır, genel hedefi anlar (ör. "plan a event for me and my friends" [Ben ve arkadaşlarım için bir etkinlik planla]) ve ardından belirli görevleri mantıksal bir sırayla uygun uzmanlaşmış aracılara devreder. Aracılar arasındaki bilgi akışını yönetir ve nihai sonucun kullanıcıya geri iletilmesini sağlar.

Temel Mimari Öğeler ve Teknolojiler

Mimari

Google Cloud Platform (GCP):

  • Vertex AI:
    • Gemini Modelleri: Google'ın en gelişmiş Büyük Dil Modelleri'ne (LLM) erişim sağlar. Bu modeller, temsilcilerimizin akıl yürütme ve karar verme özelliklerini destekler.
    • Vertex AI Agent Engine: Orkestratör aracımızı dağıtmak, barındırmak ve ölçeklendirmek için kullanılan yönetilen bir hizmettir. Üretime geçişi basitleştirir ve altyapı karmaşıklıklarını soyutlar.
  • Cloud Run: Container mimarisine alınmış uygulamaları dağıtmak için kullanılan sunucusuz platform. Bu bilgileri şu amaçlarla kullanırız:
    • Ana InstaVibe web uygulamasını barındırır.
    • A2A özellikli bağımsız aracıları (Planlayıcı, Sosyal Profil Oluşturma, Platform Etkileşimi) bağımsız mikro hizmetler olarak dağıtın.
    • MCP Tool Server'ı çalıştırarak InstaVibe'ın dahili API'lerini temsilcilerin kullanımına sunun.
  • Spanner: Tümden yönetilen, küresel olarak dağıtılmış ve son derece tutarlı bir ilişkisel veritabanı. Bu atölyede, GRAPH DDL ve sorgu özelliklerini kullanarak aşağıdaki işlemleri yapmak için bu hizmetin Grafik Veritabanı olarak sunduğu özelliklerden yararlanacağız:
    • Karmaşık sosyal ilişkileri (kullanıcılar, arkadaşlıklar, etkinlik katılımı, gönderiler) modelleme ve depolama.
    • Sosyal Profil Oluşturma temsilcileri için bu ilişkilerin verimli bir şekilde sorgulanmasını sağlar.
  • Artifact Registry: Container görüntülerini depolamak, yönetmek ve güvenliğini sağlamak için tümüyle yönetilen bir hizmettir.
  • Cloud Build: Derlemelerinizi Google Cloud'da yürüten bir hizmettir. Bu hizmeti, aracımızın ve uygulamamızın kaynak kodundan Docker container görüntülerini otomatik olarak oluşturmak için kullanıyoruz.
  • Cloud Storage: Cloud Build gibi hizmetler tarafından derleme yapılarını depolamak ve Agent Engine tarafından operasyonel ihtiyaçları için kullanılır.
  • Temel Aracı Çerçeveleri ve Protokolleri:
    • Google'ın Agent Development Kit'i (ADK): Aşağıdakiler için birincil çerçeve:
      • Her bir akıllı aracının temel mantığını, davranışını ve talimat kümelerini tanımlama.
      • Aracıların yaşam döngülerini, durumunu ve belleğini (kısa süreli oturum durumu ve potansiyel olarak uzun süreli bilgi) yönetme.
      • Temsilcilerin dünyayla etkileşim kurmak için kullanabileceği araçları (ör. Google Arama veya özel olarak geliştirilmiş araçlar) entegre etme
      • Alt aracıların sıralı, döngü ve paralel yürütülmesi de dahil olmak üzere çoklu aracı iş akışlarını düzenleme
    • Temsilciden Temsilciye (A2A) İletişim Protokolü: Aşağıdakileri sağlayan açık bir standarttır:
      • Farklı yapay zeka temsilcileri arasında, ayrı hizmetler olarak veya farklı makinelerde çalışıyor olsalar bile doğrudan ve standartlaştırılmış iletişim ve ortak çalışma.
      • Aracıların birbirlerinin özelliklerini (Aracı Kartları aracılığıyla) keşfetmesi ve görevleri devretmesi. Bu, Orchestrator temsilcimizin uzmanlaşmış Planner, Social ve Platform temsilcileriyle etkileşim kurması için çok önemlidir.
    • A2A Python Kitaplığı (a2a-python): ADK aracılarımızın A2A protokolünü konuşmasını sağlamak için kullanılan somut kitaplık. Aşağıdakiler için gereken sunucu tarafı bileşenlerini sağlar:
      • Aracıları A2A uyumlu sunucular olarak kullanıma sunma.
      • Keşif için "Aracı Kartı"nın yayınlanmasını otomatik olarak yönetir.
      • Diğer aracılardan (ör. Orchestrator) gelen görev isteklerini alıp yönetme
    • Model Context Protocol (MCP): Temsilcilerin şunları yapmasına olanak tanıyan açık bir standarttır:
      • Harici araçlar, veri kaynakları ve sistemlerle standart bir şekilde bağlantı kurup bunları kullanın.
      • Platform Etkileşimi Aracımız, bir MCP sunucusuyla iletişim kurmak için bir MCP istemcisi kullanır. Bu istemci de InstaVibe platformunun mevcut API'leriyle etkileşim kurmak için araçlar sunar.
  • Hata Ayıklama Araçları:
    • A2A Inspector: A2A Inspector, A2A özellikli aracılarımıza bağlanmak, onları incelemek ve onlarla etkileşim kurmak için bu atölye çalışması boyunca kullanılan web tabanlı bir hata ayıklama aracıdır. Nihai üretim mimarisinin bir parçası olmasa da geliştirme iş akışımızın önemli bir parçasıdır. Bu hizmet şunları sağlar:
      • Agent Card Viewer: Bir aracının herkese açık özelliklerini getirmek ve doğrulamak için kullanılır.
      • Canlı Sohbet Arayüzü: Dağıtılan bir temsilciye doğrudan mesaj göndererek anında test yapabilirsiniz.
      • Hata Ayıklama Konsolu: İnceleyici ile aracı arasında değiştirilen ham JSON-RPC mesajlarını görüntülemek için kullanılır.
  • Dil modelleri (LLM'ler): Sistemin "beyinleri":
    • Google'ın Gemini Modelleri: Özellikle gemini-2.0-flash gibi sürümleri kullanırız. Bu modeller şu amaçlarla seçilir:
      • Gelişmiş Akıl Yürütme ve Talimatları Uygulama: Karmaşık istemleri anlama, ayrıntılı talimatları uygulama ve görevler hakkında akıl yürütme becerileri sayesinde bu modeller, aracıların karar verme sürecini desteklemek için uygundur.
      • Araç Kullanımı (İşlev Çağırma): Gemini modelleri, ADK aracılığıyla sağlanan araçların ne zaman ve nasıl kullanılacağını belirlemede mükemmeldir. Bu sayede temsilciler bilgi toplayabilir veya işlem gerçekleştirebilir.
      • Verimlilik (Flash Modelleri): "Flash" varyantları, performans ve maliyet açısından iyi bir denge sunar. Hızlı yanıt gerektiren birçok etkileşimli aracı görevinde kullanılabilir.

Google Cloud kredisine mi ihtiyacınız var?

3. Başlamadan önce

👉Google Cloud Console'un üst kısmındaki Cloud Shell'i etkinleştir'i tıklayın (Cloud Shell bölmesinin üst kısmındaki terminal şeklindeki simge). Cloud Shell

👉 "Open Editor" (Düzenleyiciyi Aç) düğmesini tıklayın (kalemli açık bir klasöre benzer). Bu işlemle pencerede Cloud Shell kod düzenleyici açılır. Sol tarafta bir dosya gezgini görürsünüz. Cloud Shell

👉Gösterildiği gibi, alt durum çubuğundaki Cloud Code ile oturum açma düğmesini tıklayın. Eklentiyi talimatlara göre yetkilendirin. Durum çubuğunda Cloud Code - no project (Cloud Code - proje yok) ifadesini görüyorsanız bunu seçin, ardından açılır listede "Select a Google Cloud Project" (Bir Google Cloud projesi seçin) seçeneğini ve oluşturduğunuz projeler listesinden belirli bir Google Cloud projesini seçin. Cloud Shell

👉 Google Cloud proje kimliğinizi bulun:

  • Google Cloud Console'u açın: https://console.cloud.google.com
  • Sayfanın üst kısmındaki proje açılır listesinden bu atölye çalışması için kullanmak istediğiniz projeyi seçin.
  • Proje kimliğiniz, kontrol panelindeki Proje bilgileri kartında gösterilir.

Cloud Shell

👉Bulut IDE'sinde terminali açın. Cloud Shell

👉💻 Terminalde, aşağıdaki komutu kullanarak kimliğinizin zaten doğrulandığını ve projenin proje kimliğinize ayarlandığını doğrulayın:

gcloud auth list

👉💻 instavibe-bootstrap projesini GitHub'dan kopyalayın:

git clone -b adk-1.2.1-a2a-0.2.7 https://github.com/weimeilin79/instavibe-bootstrap.git
chmod +x ~/instavibe-bootstrap/init.sh
chmod +x ~/instavibe-bootstrap/set_env.sh

Proje Yapısını Anlama

İnşa etmeye başlamadan önce, az önce klonladığınız instavibe-bootstrap projenin düzenini anlamak için biraz zaman ayıralım. Bu sayede, atölye boyunca dosyaları nerede bulacağınızı ve nasıl düzenleyeceğinizi öğrenebilirsiniz.

instavibe-bootstrap/
├── agents/
   ├── orchestrate/
   ├── planner/
   ├── platform_mcp_client/
   └── social/
├── instavibe/
   ├── static/
   └── templates/
├── tools/
   └── instavibe/
├── utils/
├── init.sh
└── set_env.sh

Önemli dizinlerin dökümü:

  • agents/: Bu, yapay zeka sistemimizin kalbidir. Her alt dizin (planner/, social/ vb.), belirli bir akıllı aracının kaynak kodunu içerir.
    • agent.py: Her aracının klasöründe, aracının mantığının bulunduğu ana dosyadır.
    • a2a_server.py: Bu dosya, ADK aracısını bir Aracıdan Aracıya (A2A) sunucusuyla sarmalar.
    • Dockerfile: Aracıyı Cloud Run veya Agent Engine'e dağıtmak için container görüntüsünün nasıl oluşturulacağını tanımlar.
  • instavibe/: Bu dizin, InstaVibe web uygulamasının kaynak kodunun tamamını içerir.
  • tools/: Bu dizin, temsilcilerimizin kullanabileceği harici araçlar oluşturmak için kullanılır.
    • instavibe/, Model Context Protocol (MCP) sunucusunu içerir.

Bu modüler yapı, web uygulamasını çeşitli yapay zeka bileşenlerinden ayırarak tüm sistemin yönetilmesini, test edilmesini ve dağıtılmasını kolaylaştırır.

👉💻 İlk kullanıma hazırlama komut dosyasını çalıştırın:

Bu komut dosyası, Google Cloud proje kimliğinizi girmenizi ister.

init.sh komut dosyası tarafından istendiğinde son adımda bulduğunuz Google Cloud proje kimliğini girin:

cd ~/instavibe-bootstrap
./init.sh

👉💻 Gerekli proje kimliğini ayarlayın:

gcloud config set project $(cat ~/project_id.txt) --quiet

👉💻 Gerekli Google Cloud API'lerini etkinleştirmek için aşağıdaki komutu çalıştırın:

gcloud services enable  run.googleapis.com \
                        cloudfunctions.googleapis.com \
                        cloudbuild.googleapis.com \
                        artifactregistry.googleapis.com \
                        spanner.googleapis.com \
                        apikeys.googleapis.com \
                        iam.googleapis.com \
                        compute.googleapis.com \
                        aiplatform.googleapis.com \
                        cloudresourcemanager.googleapis.com \
                        maps-backend.googleapis.com

👉💻 Gerekli tüm ortam değişkenlerini ayarlayın:

export PROJECT_ID=$(gcloud config get project)
export PROJECT_NUMBER=$(gcloud projects describe ${PROJECT_ID} --format="value(projectNumber)")
export SERVICE_ACCOUNT_NAME=$(gcloud compute project-info describe --format="value(defaultServiceAccount)")
export SPANNER_INSTANCE_ID="instavibe-graph-instance"
export SPANNER_DATABASE_ID="graphdb"
export GOOGLE_CLOUD_PROJECT=$(gcloud config get project)
export GOOGLE_GENAI_USE_VERTEXAI=TRUE
export GOOGLE_CLOUD_LOCATION="us-central1"

İzinleri ayarlama

👉💻 İzin verin. Terminalde şunu çalıştırın :

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/spanner.admin"

# Spanner Database User
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/spanner.databaseUser"

# Artifact Registry Admin
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/artifactregistry.admin"

# Cloud Build Editor
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/cloudbuild.builds.editor"

# Cloud Run Admin
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/run.admin"

# IAM Service Account User
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/iam.serviceAccountUser"

# Vertex AI User
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/aiplatform.user"

# Logging Writer (to allow writing logs)
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/logging.logWriter"


gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/logging.viewer"


👉 Sonucu IAM konsolunuzda doğrulayın.Cloud Shell

👉💻 Artifact Registry deposu oluşturmak için terminalde aşağıdaki komutları çalıştırın. Aracılarımız, MCP sunucusu ve InstaVibe uygulaması için tüm Docker görüntüleri, Cloud Run veya Agent Engine'e dağıtılmadan önce burada depolanır.

export REPO_NAME="introveally-repo"
gcloud artifacts repositories create $REPO_NAME \
  --repository-format=docker \
  --location=us-central1 \
  --description="Docker repository for InstaVibe workshop"

API anahtarları için Harita Platformu'nu ayarlama

InstaVibe uygulamanızda Google Haritalar hizmetlerini kullanmak için bir API anahtarı oluşturmanız ve bu anahtarı uygun şekilde kısıtlamanız gerekir.

👉 Yeni bir sekmede API'ler ve Hizmetler > Kimlik Bilgileri'ne gidin. "Kimlik bilgileri" sayfasında üstteki + KİMLİK BİLGİLERİ OLUŞTUR düğmesini tıklayın. Açılır menüden API anahtarı'nı seçin. alt text

👉 Yeni oluşturduğunuz API anahtarını gösteren bir iletişim kutusu açılır. Uygulama yapılandırmanız için daha sonra bu değere ihtiyacınız olacak.

👉 "API anahtarı oluşturuldu" iletişim kutusunda KAPAT'ı tıklayın.

👉 Yeni API anahtarınızın listelendiğini görürsünüz (ör. "API anahtarı 1"). Sağdaki üç noktayı tıklayın ve "API anahtarını kısıtla ve yeniden adlandır" sayfasını açmak için API anahtarını düzenle'yi seçin. alt text

👉 En üstteki Ad alanında varsayılan adı şu şekilde değiştirin: Maps Platform API Key (🚨🚨ÖNEMLİ🚨🚨 Lütfen bu adı kullanın.)

Maps Platform API Key

👉 "Uygulama kısıtlamaları" bölümünde Yok'un seçili olduğundan emin olun.

👉 "API kısıtlamaları" bölümünde Anahtarı kısıtla radyo düğmesini seçin.

👉 API'leri seç açılır menüsünü tıklayın. Görüntülenen arama kutusuna Maps JavaScript API yazın ve listeden seçin. alt text

👉 Tamam'ı tıklayın.

👉 Sayfanın alt kısmındaki KAYDET düğmesini tıklayın.

Anahtar Sonuç

Artık "Maps Platform API Key" adlı bir API anahtarını başarıyla oluşturdunuz, yalnızca "Maps JavaScript API" kullanımına izin verecek şekilde kısıtladınız ve API'nin projeniz için etkinleştirildiğinden emin oldunuz.

4. Grafik veritabanı oluşturma

Akıllı aracılarımızı oluşturmadan önce InstaVibe sosyal ağımızdaki zengin bağlantıları depolayabileceğimiz ve anlayabileceğimiz bir yöntem bulmamız gerekiyor. Bu noktada bir grafik veritabanı devreye girer. Verileri satır ve sütun tablolarında depolayan geleneksel ilişkisel veritabanlarının aksine, grafik veritabanı özellikle verileri düğümler (ör. kişiler, etkinlikler veya yayınlar) ve bunları birbirine bağlayan ilişkiler (kenarlar) (ör. arkadaşlıklar, etkinlik katılımı veya bahsetmeler) açısından temsil etmek ve sorgulamak için tasarlanmıştır. Bu yapı, gerçek dünyadaki sosyal ağların yapısını yansıttığı için sosyal medya uygulamaları açısından son derece güçlüdür. Böylece, farklı öğelerin nasıl birbirine bağlı olduğunu keşfetmek kolaylaşır.

Bu grafik veritabanını Google Cloud Spanner kullanarak uyguluyoruz. Spanner, öncelikle küresel olarak dağıtılmış, güçlü tutarlılığa sahip bir ilişkisel veritabanı olarak bilinse de grafik yapılarını doğrudan ilişkisel tablolarımızın üzerinde tanımlamamıza ve sorgulamamıza da olanak tanır.

Bu sayede, Spanner'ın ölçeklenebilirliği, işlemsel tutarlılığı ve tanıdık SQL arayüzünün yanı sıra yapay zeka destekli özelliklerimiz için çok önemli olan karmaşık sosyal dinamikleri analiz etmeye yönelik grafik sorgularının etkileyici gücünden yararlanabiliyoruz.

👉💻 Cloud Shell IDE terminalinde. Google Cloud'da gerekli altyapıyı sağlayın. Öncelikle, veritabanlarımız için özel bir kapsayıcı görevi gören bir Spanner örneği oluşturacağız. Örnek hazır olduğunda, içinde tüm tablolarımızı ve InstaVibe'ın grafik verilerini barındıracak olan gerçek Spanner veritabanını oluşturacağız:

. ~/instavibe-bootstrap/set_env.sh

gcloud spanner instances create $SPANNER_INSTANCE_ID \
  --config=regional-us-central1 \
  --description="GraphDB Instance InstaVibe" \
  --processing-units=100 \
  --edition=ENTERPRISE

gcloud spanner databases create $SPANNER_DATABASE_ID \
  --instance=$SPANNER_INSTANCE_ID \
  --database-dialect=GOOGLE_STANDARD_SQL

👉💻 Spanner'a varsayılan hizmet hesabına okuma/yazma erişimi verme

echo "Granting Spanner read/write access to ${SERVICE_ACCOUNT_NAME} for database ${SPANNER_DATABASE_ID}..."

gcloud spanner databases add-iam-policy-binding ${SPANNER_DATABASE_ID} \
  --instance=${SPANNER_INSTANCE_ID} \
  --member="serviceAccount:${SERVICE_ACCOUNT_NAME}" \
  --role="roles/spanner.databaseUser" \
  --project=${PROJECT_ID}

👉💻 Hemen. Python sanal ortamı oluşturacak, gerekli Python paketlerini yükleyecek, ardından Spanner'da grafik veritabanı şemasını oluşturup ilk verilerle yükleyecek ve setup.py komut dosyasını çalıştıracağız.

. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap
python -m venv env
source env/bin/activate
pip install -r requirements.txt
cd instavibe
python setup.py

👉 Yeni bir tarayıcı sekmesinde Google Cloud Console'a gidin ve Spanner'a gidin. Spanner örneklerinizin listesini görürsünüz. instavibe-graph-instance simgesini tıklayın. Spanner örneği 👉 Örnek genel bakış sayfasında, söz konusu örnekteki veritabanlarının listesini görürsünüz. graphdbspanner db simgesini tıklayın.

👉 Veritabanınızın sol gezinme bölmesinde Spanner Studio'yu spanner studio tıklayın.

👉 Sorgu düzenleyicide (Untitled query sekmesi) aşağıdaki Graph SQL sorgusunu yapıştırın. Bu sorgu, tüm Kişi düğümlerini ve diğer Kişi düğümleriyle olan doğrudan Arkadaşlık ilişkilerini bulur. Sonucu görmek için ÇALIŞTIR'ı tıklayın.

Graph SocialGraph
MATCH result_paths = ((p:Person)-[f:Friendship]-(friend:Person))
RETURN SAFE_TO_JSON(result_paths) AS result_paths

Spanner Graph

👉 Aynı sorgu düzenleyicide, aynı etkinliğe katılan kullanıcıları bulmak için önceki DDL'yi değiştirin. Bu, paylaşılan bir etkinlik aracılığıyla dolaylı bir bağlantı olduğunu gösterir.

Graph SocialGraph
MATCH result_paths =  (p1:Person)-[:Attended]->(e:Event)<-[:Attended]-(p2:Person)
WHERE p1.person_id < p2.person_id
RETURN SAFE_TO_JSON(result_paths) AS result_paths

Spanner Graph

👉 Bu sorgu, farklı bir bağlantı türünü inceler. Belirli bir kişinin arkadaşları tarafından yazılan gönderilerde bahsedilen kişiler, sorgu düzenleyicide aşağıdaki sorguyu çalıştırır.

Graph SocialGraph
MATCH result_paths =  (user:Person {name: "Alice"})-[:Friendship]-(friend:Person)-[:Wrote]->(post:Post)-[:Mentioned]->(mentioned_person:Person)
WHERE user <> mentioned_person AND friend <> mentioned_person -- Avoid self-mentions or friend mentioning themselves in their own post if not intended
RETURN SAFE_TO_JSON(result_paths) AS result_paths

Spanner Graph

Bu sorgular, InstaVibe uygulamamız için Spanner'ı grafik veritabanı olarak kullanmanın gücüne dair sadece bir bakış sunmaktadır. Sosyal verilerimizi birbirine bağlı bir grafik olarak modelleyerek ilişkilerin ve etkinliklerin ayrıntılı analizini mümkün kılıyoruz. Bu analiz, yapay zeka aracılarımızın kullanıcı bağlamını anlaması, ilgi alanlarını keşfetmesi ve sonuç olarak akıllı sosyal planlama yardımı sunması için temel olacaktır.

Temel veri yapımız artık hazır ve test edildi. Şimdi de temsilcilerimizin etkileşimde bulunacağı mevcut InstaVibe uygulamasına odaklanalım.

5. InstaVibe'ın mevcut durumu

Yapay zeka temsilcilerimizin nerede yer alacağını anlamak için öncelikle mevcut InstaVibe web uygulamasını dağıtıp çalıştırmamız gerekir. Bu uygulama, kullanıcı arayüzünü ve daha önce oluşturduğumuz Spanner grafik veritabanına bağlanan temel işlevleri sağlar.

ana sayfa

InstaVibe uygulaması, etkinlik ayrıntıları sayfalarında etkinlik konumlarını görsel olarak göstermek için Google Haritalar'ı kullanır. Bu işlevin etkinleştirilmesi için uygulamanın, daha önce oluşturduğumuz API anahtarına ihtiyacı vardır. Aşağıdaki komut dosyası, atadığımız görünen adı ("Haritalar Platformu API Anahtarı") kullanarak gerçek anahtar dizesini alır.

etkinlik sayfası

👉💻 Cloud Shell IDE'ye geri dönün. Aşağıdaki komut dosyasını çalıştırın. Ardından, gösterilen GOOGLE_MAPS_API_KEY'nin, daha önce Google Cloud Console'da oluşturup kopyaladığınız anahtarla eşleştiğinden emin olmak için çıkışı dikkatlice kontrol edin.

. ~/instavibe-bootstrap/set_env.sh
export KEY_DISPLAY_NAME="Maps Platform API Key"

GOOGLE_MAPS_KEY_ID=$(gcloud services api-keys list \
  --project="${PROJECT_ID}" \
  --filter="displayName='${KEY_DISPLAY_NAME}'" \
  --format="value(uid)" \
  --limit=1)

GOOGLE_MAPS_API_KEY=$(gcloud services api-keys get-key-string "${GOOGLE_MAPS_KEY_ID}" \
    --project="${PROJECT_ID}" \
    --format="value(keyString)")

echo "${GOOGLE_MAPS_API_KEY}" > ~/mapkey.txt

echo "Retrieved GOOGLE_MAPS_API_KEY: ${GOOGLE_MAPS_API_KEY}"

anahtar sonuç

👉💻 Şimdi InstaVibe web uygulaması için container görüntüsünü oluşturup Artifact Registry depomuza aktaralım.

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/instavibe/
export IMAGE_TAG="latest"
export APP_FOLDER_NAME="instavibe"
export IMAGE_NAME="instavibe-webapp"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="instavibe"

gcloud builds submit . \
  --tag=${IMAGE_PATH} \
  --project=${PROJECT_ID}

👉💻 Yeni derleme InstaVibe web uygulaması resmini Cloud Run'a dağıtın.

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/instavibe/
export IMAGE_TAG="latest"
export APP_FOLDER_NAME="instavibe"
export IMAGE_NAME="instavibe-webapp"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="instavibe"

gcloud run deploy ${SERVICE_NAME} \
  --image=${IMAGE_PATH} \
  --platform=managed \
  --region=${REGION} \
  --allow-unauthenticated \
  --set-env-vars="SPANNER_INSTANCE_ID=${SPANNER_INSTANCE_ID}" \
  --set-env-vars="SPANNER_DATABASE_ID=${SPANNER_DATABASE_ID}" \
  --set-env-vars="APP_HOST=0.0.0.0" \
  --set-env-vars="APP_PORT=8080" \
  --set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
  --set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
  --set-env-vars="GOOGLE_MAPS_API_KEY=${GOOGLE_MAPS_API_KEY}" \
  --project=${PROJECT_ID} \
  --min-instances=1

Dağıtım başarıyla tamamlandıktan sonra Cloud Run günlüklerinde, çalışan InstaVibe uygulamanızın herkese açık URL'si gösterilir.

URL

Bu URL'yi Google Cloud Console'da Cloud Run bölümüne gidip instavibe hizmetini seçerek de bulabilirsiniz. ListeURL

Temel InstaVibe platformunu keşfetmek için bu URL'yi web tarayıcınızda açın. Oluşturduğumuz grafik veritabanı tarafından desteklenen gönderileri, etkinlikleri ve kullanıcı bağlantılarını görün.

Hedef uygulamamız çalıştığına göre, özelliklerini geliştirmek için ilk akıllı temsilciyi oluşturmaya başlayalım.

6. Basic Agent,Event Planner with ADK

ADK Çerçevesi

Google'ın ADK çerçevesine giriş Temelimiz (InstaVibe uygulaması ve veritabanı) hazır olduğuna göre Google'ın Agent Development Kit'ini (ADK) kullanarak ilk akıllı aracımızı oluşturmaya başlayabiliriz.

Agent Development Kit (ADK), yapay zeka aracı geliştirme ve dağıtma için özel olarak tasarlanmış esnek ve modüler bir çerçevedir. Tasarım ilkesi, geleneksel yazılım geliştirmeye daha çok benzeyen bir aracı geliştirme deneyimi sunmaktır. Bu sayede geliştiricilerin, basit ve tek amaçlı görevlerden karmaşık ve çok aracılı iş akışlarına kadar her şeyi yönetebilen aracı mimarileri oluşturması, dağıtması ve düzenlemesi önemli ölçüde kolaylaşır.

ADK, temel olarak Agent kavramı etrafında döner. Bu kavram, talimatları, yapılandırmayı (ör. seçilen dil modeli, istemler, yanıtlar) ve değerlendirme ölçütlerini kapsar. Gemini) ve işlem yapmak ya da bilgi toplamak için kullanabileceği bir dizi Tools.

06-agent.png

İlk temsilcimiz "Etkinlik Planlayıcı" olacak. Temel amacı, sosyal etkinlikler için kullanıcı isteklerini (konum, tarihler ve ilgi alanlarını belirterek) almak ve yaratıcı, kişiye özel öneriler oluşturmaktır. Önerilerin alakalı ve güncel bilgilere (ör. o hafta sonu gerçekleşen belirli etkinlikler) dayalı olmasını sağlamak için ADK'nın yerleşik araçlarından biri olan Google Arama'yı kullanacağız. Bu sayede, kullanıcıların ölçütleriyle eşleşen mekanlar, etkinlikler ve aktivitelerle ilgili en son ayrıntıları alarak yanıtlarını gerçek zamanlı web sonuçlarına dayandırabilir.

👉📝 Cloud Shell IDE'ye geri dönün. ~/instavibe-bootstrap/agents/planner/agent.py bölümünde, aracı oluşturmak için aşağıdaki istemi ve talimatı ekleyin.

from google.adk.agents import Agent
from google.adk.tools import google_search

root_agent = Agent(
    name="planner_agent",
    model="gemini-2.0-flash",
    description="Agent tasked with generating creative and fun dating plan suggestions",
    instruction="""

        You are a specialized AI assistant tasked with generating creative and fun plan suggestions.

        Request:
        For the upcoming weekend, specifically from **[START_DATE_YYYY-MM-DD]** to **[END_DATE_YYYY-MM-DD]**, in the location specified as **[TARGET_LOCATION_NAME_OR_CITY_STATE]** (if latitude/longitude are provided, use these: Lat: **[TARGET_LATITUDE]**, Lon: **[TARGET_LONGITUDE]**), please generate a distinct dating plan suggestions.

        Constraints and Guidelines for Suggestions:
        1.  Creativity & Fun: Plans should be engaging, memorable, and offer a good experience for a date.
        2.  Budget: All generated plans should aim for a moderate budget (conceptually "$$"), meaning they should be affordable yet offer good value, without being overly cheap or extravagant. This budget level should be *reflected in the choice of activities and venues*, but **do not** explicitly state "Budget: $$" in the `plan_description`.
        3.  Interest Alignment:
               Consider the following user interests: **[COMMA_SEPARATED_LIST_OF_INTERESTS, e.g., outdoors, arts & culture, foodie, nightlife, unique local events, live music, active/sports]**. Tailor suggestions specifically to these where possible. The plan should *embody* these interests.
               Fallback: If specific events or venues perfectly matching all listed user interests cannot be found for the specified weekend, you should create a creative and fun generic dating plan that is still appealing, suitable for the location, and adheres to the moderate budget. This plan should still sound exciting and fun, even if it's more general.
        4.  Current & Specific: Prioritize finding specific, current events, festivals, pop-ups, or unique local venues operating or happening during the specified weekend dates. If exact current events cannot be found, suggest appealing evergreen options or implement the fallback generic plan.
        5.  Location Details: For each place or event mentioned within a plan, you MUST provide its name, precise latitude, precise longitude, and a brief, helpful description.
        6.  Maximum Activities: The plan must contain a maximum of 3 distinct activities.

        RETURN PLAN in MARKDOWN FORMAT 
    """,
    tools=[google_search]
)

İlk aracımızı tanımladık. ADK'nın en iyi özelliklerinden biri, sezgisel yapısı ve sunduğu kullanışlı araçlardır. Özellikle yararlı olanlardan biri, aracınızı etkileşimli olarak test etmenize ve yanıtlarını anlık olarak görmenize olanak tanıyan ADK Dev UI'dir.

👉💻 Başlayalım. Aşağıdaki komutlar ADK DEV kullanıcı arayüzünü başlatır:

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd  ~/instavibe-bootstrap/agents
sed -i "s|^\(O\?GOOGLE_CLOUD_PROJECT\)=.*|GOOGLE_CLOUD_PROJECT=${PROJECT_ID}|" ~/instavibe-bootstrap/agents/planner/.env
adk web

Komutları çalıştırdıktan sonra terminalinizde ADK Web Sunucusu'nun başlatıldığını belirten bir çıkış görmelisiniz. Bu çıkış şuna benzer:

+-----------------------------------------------------------------------------+
| ADK Web Server started                                                      |
|                                                                             |
| For local testing, access at http://localhost:8000.                         |
+-----------------------------------------------------------------------------+

INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)

👉 Ardından, tarayıcınızdan ADK Dev kullanıcı arayüzüne erişmek için:

Cloud Shell araç çubuğundaki (genellikle sağ üstte) Web önizleme simgesinden (genellikle bir göz veya ok içeren bir kare gibi görünür) Bağlantı noktasını değiştir'i seçin. Pop-up pencerede bağlantı noktasını 8000 olarak ayarlayın ve "Değiştir ve Önizle"yi tıklayın. Ardından Cloud Shell, ADK Dev kullanıcı arayüzünü gösteren yeni bir tarayıcı sekmesi veya penceresi açar.

web önizlemesi

ADK Dev UI tarayıcınızda açıldıktan sonra: Kullanıcı arayüzünün sağ üst kısmındaki açılır menüden etkileşim kurmak istediğiniz aracı olarak planner'ı seçin. Şimdi sağdaki sohbet iletişim kutusunda temsilcinize bir görev vermeyi deneyin. Örneğin, temsilciyle sohbet etme:

Search and plan something in Seattle for me this weekend
This weekend and I enjoy food and anime

Tarih öner (Tercihiniz)

July 12 2025

Temsilcinin isteğinizi işlediğini ve Google Arama sonuçlarına dayalı bir plan sunduğunu görürsünüz.

adk dev ui

Bir temsilciyle etkileşim kurmak bir şeydir, ancak özellikle değişiklik yaparken temsilcinin her zaman beklendiği gibi davrandığını nasıl bilebiliriz?

Geleneksel yazılım testi yöntemleri, üretken ve deterministik olmayan doğaları nedeniyle yapay zeka aracıları için genellikle yetersiz kalır. Harika bir demodan güvenilir bir üretim aracına geçiş yapmak için sağlam bir değerlendirme stratejisi şarttır. Bir üretken modelin nihai çıktısını kontrol etmekten farklı olarak, bir aracıyı değerlendirmek genellikle karar verme sürecini ve çeşitli senaryolarda araçları doğru kullanma veya talimatları uygulama becerisini değerlendirmeyi içerir. ADK, bu konuda yardımcı olacak özellikler sunar.

Eval

👉 ADK Dev UI'de soldaki gezinme panelinde "Eval" (Değerlendirme) sekmesini tıklayın. plan_eval adlı önceden yüklenmiş bir test dosyası görürsünüz. Bu dosya, planlayıcı aracımızı test etmek için önceden tanımlanmış girişler ve ölçütler içerir.

👉 "boston" gibi bir senaryo seçin ve Değerlendirmeyi Çalıştır düğmesini tıklayın. Açılan pop-up pencerede eşleşme puanını 0, 3'e düşürün ve Başlat'ı tıklayın.

Eşleşme skoru

Bu işlem, test girişiyle birlikte aracıyı çalıştırır ve çıktısının tanımlanan beklentileri karşılayıp karşılamadığını kontrol eder. Bu sayede, temsilcinizin performansını sistematik olarak test edebilirsiniz.

adk dev ui evaluation

👉 Şimdi daha katı bir eşik kullanıldığında ne olacağına bakalım. "nyc" senaryosunu seçin ve Değerlendirmeyi Çalıştır'ı tekrar tıklayın. Bu kez eşleşme puanını varsayılan değerinde (Yanıta eşleşme puanı: 0, 7) bırakın ve Başlat'ı tıklayın. Sonucun "Başarısız" olduğunu görürsünüz. Bu durum, aracının yaratıcı çıktısı önceden tanımlanmış "altın" yanıtla tam olarak eşleşmediği için beklenir.

adk dev ui evaluation fail

👉 Neden başarısız olduğunu anlamak için "nyc" satırındaki başarısız simgesini tıklayın. Kullanıcı arayüzünde artık aracının Gerçek yanıtı ile test senaryosunun Beklenen yanıtı yan yana karşılaştırılıyor. Bu görünüm, hata ayıklama için çok önemlidir. Temsilcinin çıktısının tam olarak nerede farklılaştığını görmenize ve talimatlarını buna göre iyileştirmenize olanak tanır.

Kullanıcı arayüzünü ve değerlendirmeyi incelemeyi tamamladığınızda Cloud Shell Editor terminalinize dönün ve ADK Dev UI'yi durdurmak için Ctrl+C tuşuna basın.

Serbest biçimli metin çıkışı iyi bir başlangıç olsa da InstaVibe gibi uygulamaların bir aracının önerilerini kolayca kullanabilmesi için yapılandırılmış veriler (ör. JSON) çok daha pratiktir. Planını tutarlı bir JSON biçiminde döndürmek için aracımızı değiştirelim.

👉📝 ~/instavibe-bootstrap/agents/planner/agent.py içinde, aracının talimat dizesinde şu anda RETURN PLAN in MARKDOWN FORMAT yazan satırı bulun. Bu satırı aşağıdaki ayrıntılı JSON yapısıyla değiştirin:

Return your response *exclusively* as a single JSON object. This object should contain a top-level key, "fun_plans", which holds a plan objects. Each plan object in the list must strictly adhere to the following structure:

        --json--
        {
          "plan_description": "A summary of the overall plan, consisting of **exactly three sentences**. Craft these sentences in a friendly, enthusiastic, and conversational tone, as if you're suggesting this awesome idea to a close friend. Make it sound exciting and personal, highlighting the positive aspects and appeal of the plan without explicitly mentioning budget or listing interest categories.",
          "locations_and_activities": [
              {
              "name": "Name of the specific place or event",
              "latitude": 0.000000,  // Replace with actual latitude
              "longitude": 0.000000, // Replace with actual longitude
              "description": "A brief description of this place/event, why it's suitable for the date, and any specific details for the weekend (e.g., opening hours, event time)."
              }
              // Add more location/activity objects here if the plan involves multiple stops/parts
          ]
        }

Aracının talimatlarını JSON çıkışı isteyecek şekilde güncellediğinize göre şimdi değişikliği doğrulayalım.

👉💻 ADK Geliştirici Kullanıcı Arayüzü'nü yeniden başlatmak için önceki komutu kullanın:

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd  ~/instavibe-bootstrap/agents
adk web

Sekme zaten açıksa yenileyin. Alternatif olarak, ADK Dev UI'yi tarayıcınızda açmak için (Cloud Shell'in 8000 bağlantı noktasındaki Web Önizlemesi aracılığıyla) daha önce yaptığınız gibi aynı adımları uygulayın. Kullanıcı arayüzü yüklendikten sonra planlayıcı aracısının seçildiğinden emin olun.

👉 Bu kez farklı bir istekte bulunalım. Sohbet iletişim kutusuna şunu girin:

Plan an event Boston this weekend with art and coffee

Aracının yanıtını dikkatlice inceleyin. Artık tamamen sohbet odaklı bir metin yanıtı yerine, talimatlarda tanımladığımız yapıya (fun_plans, plan_description, locations_and_activities vb. içeren) uygun şekilde kesinlikle JSON nesnesi olarak biçimlendirilmiş bir yanıt göreceksiniz. Bu, aracının artık InstaVibe uygulamamız tarafından programatik kullanıma uygun yapılandırılmış çıkış üretebileceğini onaylar.

adk dev ui json

JSON çıkışını onayladıktan sonra Cloud Shell terminalinize dönün ve ADK Dev UI'yi durdurmak için Ctrl+C tuşuna basın.

ADK Bileşenleri

ADK Geliştirici Kullanıcı Arayüzü, etkileşimli test için harika olsa da genellikle aracıları programatik olarak çalıştırmamız gerekir. Bu, daha büyük bir uygulamanın veya arka uç hizmetinin parçası olarak yapılabilir. Bunun nasıl çalıştığını anlamak için çalışma zamanı ve bağlam yönetimiyle ilgili bazı temel ADK kavramlarına bakalım.

Anlamlı ve çok turlu sohbetler için aracıların bağlamı anlaması, yani sürekliliği korumak için söylenenleri ve yapılanları hatırlaması gerekir. ADK, Oturum, Durum ve Bellek aracılığıyla bu bağlamı yönetmek için yapılandırılmış yöntemler sunar:

  • Oturum: Kullanıcı bir temsilciyle etkileşim kurmaya başladığında oturum oluşturulur. Bunu tek ve belirli bir sohbet dizisinin kapsayıcısı olarak düşünebilirsiniz. Benzersiz bir kimlik, etkileşim geçmişi (Etkinlikler), mevcut çalışma verileri (Durum) ve son güncelleme zamanı gibi meta veriler içerir.
  • Durum: Bu, tek bir oturumdaki aracının kısa süreli çalışma belleğidir. Aracının, mevcut görevi tamamlamak için gereken geçici bilgileri (ör. şimdiye kadar toplanan kullanıcı tercihleri, araç çağrılarından elde edilen ara sonuçlar) depolayabileceği değiştirilebilir bir sözlüktür.
  • Bellek: Bu, aracının farklı oturumlarda uzun süreli hatırlama veya harici bilgi tabanlarına erişme potansiyelini gösterir. Oturum ve Durum, anlık görüşmeyi yönetirken Bellek (genellikle MemoryService tarafından yönetilir), bir aracının geçmiş etkileşimlerden veya yapılandırılmış veri kaynaklarından bilgi almasına olanak tanıyarak daha geniş bir bilgi bağlamı sunar. (Not: Basit istemcimiz, basitlik için bellek içi hizmetleri kullanır. Bu nedenle, bellek/durum yalnızca komut dosyası çalışırken kalıcı olur.)
  • Etkinlik: Bir oturumdaki her etkileşim (kullanıcı mesajı, aracı yanıtı, araç kullanım isteği, araç sonucu, durum değişikliği, hata) değiştirilemez bir etkinlik olarak kaydedilir. Bu işlem, kronolojik bir günlük oluşturur. Bu günlük, sohbetin transkripti ve işlem geçmişidir.

Peki, bir aracı çalıştırıldığında bunlar nasıl yönetilir? Bu, Runner'ın sorumluluğundadır.

  • Runner: Runner, ADK tarafından sağlanan temel yürütme motorudur. Aracınızı ve kullandığı araçları siz tanımlarsınız. Runner ise kullanıcının isteğini yerine getirme sürecini düzenler. Oturumu yönetir, etkinlik akışını işler, durumu günceller, temel dil modelini çağırır, araç çağrılarını koordine eder ve MemoryService ile etkileşime girebilir. Bunu, tüm farklı parçaların doğru şekilde birlikte çalıştığından emin olan orkestra şefi olarak düşünebilirsiniz.

Aracı, geliştirme kullanıcı arayüzünden tamamen bağımsız olarak, aracımızı bağımsız bir Python uygulaması şeklinde çalıştırmak için kullanabiliriz.

Planlayıcı aracımızı programatik olarak çağırmak için basit bir istemci komut dosyası oluşturalım.

👉📝 ~/instavibe-bootstrap/agents/planner/planner_client.py dosyasında, mevcut içe aktarma işlemlerinin altına aşağıdaki Python kodunu ekleyin. planner_client.py içinde, içe aktarmaların altına aşağıdakileri ekleyin:

async def async_main():
  session_service = InMemorySessionService()

  session = await session_service.create_session(
      state={}, app_name='planner_app', user_id='user_dc'
  )

  query = "Plan Something for me in San Francisco this weekend on wine and fashion "
  print(f"User Query: '{query}'")
  content = types.Content(role='user', parts=[types.Part(text=query)])

  root_agent = agent.root_agent
  runner = Runner(
        app_name='planner_app',
        agent=root_agent,
        session_service=session_service,
  )
  print("Running agent...")
  events_async =  runner.run_async(
    session_id=session.id, user_id=session.user_id, new_message=content
  )

  async for event in events_async:
    print(f"Event received: {event}")


if __name__ == '__main__':
  try:
    asyncio.run(async_main())
  except Exception as e:
    print(f"An error occurred: {e}")

Bu kod, oturum ve yapay nesne yönetimi için bellek içi hizmetler oluşturur (bu örnekte basit tutulur), bir oturum oluşturur, kullanıcı sorgusunu tanımlar, Runner'ı aracımızla yapılandırır ve ardından aracıyı eşzamansız olarak çalıştırarak yürütme sırasında oluşturulan her etkinliği yazdırır.

👉💻 Şimdi bu istemci komut dosyasını terminalinizden çalıştırın:

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd  ~/instavibe-bootstrap/agents
python -m planner.planner_client

👀 Çıkışı inceleyin. Yalnızca son JSON planı yerine, aracının yürütme akışı sırasında oluşturulan her Event nesnesinin ayrıntılı yapısını görürsünüz. Bu kapsamda, ilk kullanıcı mesajı etkinliği, araç çağrılarıyla (ör. Google Arama) ilgili olası etkinlikler ve son olarak JSON planını içeren modelin yanıt etkinliği yer alır. Bu ayrıntılı etkinlik akışı, hata ayıklama ve ADK çalışma zamanında gerçekleşen adım adım işlemeyi anlamak için çok yararlıdır.

Running agent...
Event received: content=Content(parts=[Part(video_metadata=None, thought=None, code_execution_result=None, executable_code=None, file_data=None, function_call=None, function_response=None, inline_data=None, text='```json\n{\n "fun_plans": [\n  {\n   "plan_description": "Embark on a stylish adventure through Hayes Valley, 
...(turncated)
, offering a variety of fashion styles to browse and enjoy."\n    }\n   ]\n  }\n ]\n}\n```')], role='model') grounding_metadata=GroundingMetadata(grounding_chunks=[GroundingChunk(retrieved_context=None, web=GroundingChunkWeb(domain='islands.com', title='islands.com', uri='http
...(turncated)
QyTpPV7jS6wUt-Ix7GuP2mC9J4eY_8Km6Vv44liF9cb2VSs='))], grounding_supports=[GroundingSupport(confide
...(turncated)
>\n', sdk_blob=None), web_search_queries=['..e']) partial=None turn_complete=None error_code=None error_message=None interrupted=None custom_metadata=None invocation_id='e-04d97b8b-9021-47a5-ab41-17b5cbb4bf03' author='location_search_agent' actions=EventActions(skip_summarization=None, state_delta={}, artifact_delta={}, transfer_to_agent=None, escalate=None, requested_auth_configs={}) long_running_tool_ids=None branch=None id='CInHdkKw' timestamp=1746978846.232674

Komut dosyası sürekli çalışıyorsa veya askıda kalıyorsa Ctrl+C tuşuna basarak komut dosyasını manuel olarak durdurmanız gerekebilir.

7. Platform Interaction Agent - MCP sunucusuyla etkileşim kurar

ADK, aracıların yapısını oluşturmaya yardımcı olsa da gerçek dünyadaki işlemleri gerçekleştirmek için genellikle harici sistemlerle veya API'lerle etkileşim kurmaları gerekir.

Model Context Protocol (MCP)

Model Bağlam Protokolü (MCP), aracı gibi yapay zeka uygulamalarının harici veri kaynakları, araçlar ve sistemlerle nasıl bağlantı kurduğunu standartlaştırmak için tasarlanmış açık bir standarttır. Evrensel bir arayüz sağlayarak her yapay zeka uygulaması ve veri kaynağı kombinasyonu için özel entegrasyonlara ihtiyaç duyma sorununu çözmeyi amaçlar. MCP, AI uygulamalarında (ana makineler) bulunan MCP istemcilerinin MCP sunucularına bağlantıları yönettiği bir istemci-sunucu mimarisini kullanır. Bu sunucular, yerel verilere erişme, API'ler aracılığıyla uzak hizmetlerle etkileşim kurma veya önceden tanımlanmış istemler sağlama gibi belirli işlevleri kullanıma sunan harici programlardır. Bu sayede yapay zeka modelleri güncel bilgilere erişebilir ve ilk eğitimlerinin ötesinde görevler gerçekleştirebilir. Bu yapı, yapay zeka modellerinin harici özellikleri standartlaştırılmış bir şekilde keşfetmesini ve bunlarla etkileşim kurmasını sağlayarak entegrasyonları daha basit ve ölçeklenebilir hale getirir.

InstaVibe MCP sunucusunu oluşturma ve dağıtma

07-mcp-server.png

Temsilcilerimizin sonunda InstaVibe platformuyla etkileşime geçmesi gerekecek.Özellikle, platformun mevcut API'lerini kullanarak gönderi oluşturmak ve etkinlik kaydetmek için. InstaVibe uygulaması, bu işlevleri standart HTTP uç noktaları üzerinden kullanıma sunar:

Enpoint

URL

HTTP yöntemi

Açıklama

Gönderi oluştur

api/posts

POST

Yeni bir gönderi eklemek için kullanılan API uç noktası. JSON gövdesi bekleniyor:
{"author_name": "...", "text": "...", "sentiment": "..." (optional)}

Etkinlik Oluştur

api/events

POST

Yeni bir etkinlik ve katılımcılarını eklemek için kullanılan API uç noktası (basitleştirilmiş şema).
JSON gövdesi bekleniyor: { "event_name": "...", "description": "...", "event_date": "YYYY-MM-DDTHH:MM:SSZ", "locations": [ {"name": "...", "description": "...", "latitude": 0.0, "longitude": 0.0, "address": "..."} ], "attendee_names": ["...", "..."] }

Bu özellikleri MCP aracılığıyla temsilcilerimizin kullanımına sunmak için öncelikle bu API çağrıları etrafında sarmalayıcı görevi gören basit Python işlevleri oluşturmamız gerekir. Bu işlevler, HTTP isteği mantığını işler.

👉 Öncelikle, gönderi oluşturmak için sarmalayıcı işlevi uygulayalım. ~/instavibe-bootstrap/tools/instavibe/instavibe.py dosyasını açın ve #REPLACE ME CREATE POST yorumunu aşağıdaki Python koduyla değiştirin:

def create_post(author_name: str, text: str, sentiment: str, base_url: str = BASE_URL):
    """
    Sends a POST request to the /posts endpoint to create a new post.

    Args:
        author_name (str): The name of the post's author.
        text (str): The content of the post.
        sentiment (str): The sentiment associated with the post (e.g., 'positive', 'negative', 'neutral').
        base_url (str, optional): The base URL of the API. Defaults to BASE_URL.

    Returns:
        dict: The JSON response from the API if the request is successful.
              Returns None if an error occurs.

    Raises:
        requests.exceptions.RequestException: If there's an issue with the network request (e.g., connection error, timeout).
    """
    url = f"{base_url}/posts"
    headers = {"Content-Type": "application/json"}
    payload = {
        "author_name": author_name,
        "text": text,
        "sentiment": sentiment
    }

    try:
        response = requests.post(url, headers=headers, json=payload)
        response.raise_for_status()  # Raise an exception for bad status codes (4xx or 5xx)
        print(f"Successfully created post. Status Code: {response.status_code}")
        return response.json()
    except requests.exceptions.RequestException as e:
        print(f"Error creating post: {e}")
        # Optionally re-raise the exception if the caller needs to handle it
        # raise e
        return None
    except json.JSONDecodeError:
        print(f"Error decoding JSON response from {url}. Response text: {response.text}")
        return None

👉📝 Ardından, etkinlik oluşturma API'si için sarmalayıcı işlevi oluşturacağız. Aynı ~/instavibe-bootstrap/tools/instavibe/instavibe.py dosyasında, #REPLACE ME CREATE EVENTS yorumunu aşağıdaki kodla değiştirin:

def create_event(event_name: str, description: str, event_date: str, locations: list, attendee_names: list[str], base_url: str = BASE_URL):
    """
    Sends a POST request to the /events endpoint to create a new event registration.

    Args:
        event_name (str): The name of the event.
        description (str): The detailed description of the event.
        event_date (str): The date and time of the event (ISO 8601 format recommended, e.g., "2025-06-10T09:00:00Z").
        locations (list): A list of location dictionaries. Each dictionary should contain:
                          'name' (str), 'description' (str, optional),
                          'latitude' (float), 'longitude' (float),
                          'address' (str, optional).
        attendee_names (list[str]): A list of names of the people attending the event.
        base_url (str, optional): The base URL of the API. Defaults to BASE_URL.

    Returns:
        dict: The JSON response from the API if the request is successful.
              Returns None if an error occurs.

    Raises:
        requests.exceptions.RequestException: If there's an issue with the network request (e.g., connection error, timeout).
    """
    url = f"{base_url}/events"
    headers = {"Content-Type": "application/json"}
    payload = {
        "event_name": event_name,
        "description": description,
        "event_date": event_date,
        "locations": locations,
        "attendee_names": attendee_names,
    }

    try:
        response = requests.post(url, headers=headers, json=payload)
        response.raise_for_status()  # Raise an exception for bad status codes (4xx or 5xx)
        print(f"Successfully created event registration. Status Code: {response.status_code}")
        return response.json()
    except requests.exceptions.RequestException as e:
        print(f"Error creating event registration: {e}")
        # Optionally re-raise the exception if the caller needs to handle it
        # raise e
        return None
    except json.JSONDecodeError:
        print(f"Error decoding JSON response from {url}. Response text: {response.text}")
        return None

Gördüğünüz gibi bu işlevler, mevcut InstaVibe API'lerinin basit sarmalayıcılarıdır. Bu kalıp, hizmetleriniz için API'leriniz varsa kullanışlıdır. Bu tür sarmalayıcılar oluşturarak işlevlerini kolayca aracıların araçları olarak kullanıma sunabilirsiniz.

MCP Sunucusu Uygulaması

İşlemleri gerçekleştiren (InstaVibe API'lerini çağıran) Python işlevlerimiz olduğuna göre artık MCP Sunucusu bileşenini oluşturmamız gerekiyor. Bu sunucu, bu işlevleri MCP standardına göre "araçlar" olarak kullanıma sunar. Böylece MCP istemcileri (ör. temsilcilerimiz) bunları keşfedip çağırabilir.

Bir MCP sunucusu genellikle iki temel işlevi uygular:

  • list_tools: İstemcinin sunucudaki mevcut araçları keşfetmesine olanak tanımaktan, adları, açıklamaları ve gerekli parametreleri gibi meta verileri sağlamaktan sorumludur. Bu meta veriler genellikle JSON şeması kullanılarak tanımlanır.
  • call_tool: İstemci tarafından istenen belirli bir aracın yürütülmesini işler, aracın adını ve bağımsız değişkenlerini alır ve karşılık gelen işlemi (ör. bizim durumumuzda bir API ile etkileşim kurma) gerçekleştirir.

MCP sunucuları, yapay zeka modellerine gerçek dünya verilerine ve işlemlerine erişim sağlamak için kullanılır. Bu sayede e-posta gönderme, proje yönetimi sistemlerinde görev oluşturma, veritabanlarında arama yapma veya çeşitli yazılımlar ve web hizmetleriyle etkileşim kurma gibi görevler gerçekleştirilebilir. İlk uygulamalar genellikle basitlik için, özellikle geliştirme veya "stüdyo" ortamlarında standart giriş/çıkış (stdio) üzerinden iletişim kuran yerel sunuculara odaklanmış olsa da, HTTP gibi protokolleri kullanan uzak sunuculara geçiş, daha geniş bir benimseme ve kurumsal kullanım alanları için daha mantıklıdır.

Uzak mimari, eklenen ağ iletişimi katmanına rağmen önemli avantajlar sunar: Birden fazla yapay zeka istemcisinin tek bir sunucuya erişimi paylaşmasına olanak tanır, araçların yönetimini ve güncellemelerini merkezileştirir, hassas verileri ve API anahtarlarını olası birçok istemci makinesine dağıtmak yerine sunucu tarafında tutarak güvenliği artırır ve yapay zeka modelini harici sistem entegrasyonunun özelliklerinden ayırarak tüm ekosistemi daha ölçeklenebilir, güvenli ve sürdürülebilir hale getirir. Bu sayede, her yapay zeka örneğinin kendi doğrudan entegrasyonlarını yönetmesi gerekmez.

07-mcp-server.png

İletişim için HTTP ve sunucu tarafından gönderilen etkinlikleri (SSE) kullanarak MCP sunucumuzu uygulayacağız. Bu yöntem, uzun sürebilecek araç yürütmeleri ve kurumsal senaryolar için uygundur.

👉📝 Öncelikle list_tools uç noktasını uygulayalım. ~/instavibe-bootstrap/tools/instavibe/mcp_server.py dosyasını açın ve #REPLACE ME - LIST TOOLS yorumunu aşağıdaki kodla değiştirin. :

@app.list_tools()
async def list_tools() -> list[mcp_types.Tool]:
  """MCP handler to list available tools."""
  # Convert the ADK tool's definition to MCP format
  mcp_tool_schema_event = adk_to_mcp_tool_type(event_tool)
  mcp_tool_schema_post = adk_to_mcp_tool_type(post_tool)
  print(f"MCP Server: Received list_tools request. \n MCP Server: Advertising tool: {mcp_tool_schema_event.name} and {mcp_tool_schema_post}")
  return [mcp_tool_schema_event,mcp_tool_schema_post]

Bu işlev, araçları (create_event, create_post) tanımlar ve bağlanan istemcilere bu araçlar hakkında bilgi verir.

👉📝 Ardından, istemcilerden gelen gerçek yürütme isteklerini işleyen call_tool uç noktasını uygulayın. Aynı ~/instavibe-bootstrap/tools/instavibe/mcp_server.py dosyasında, #REPLACE ME - CALL TOOLS yorumunu bu kodla değiştirin.

@app.call_tool()
async def call_tool(
    name: str, arguments: dict
) -> list[mcp_types.TextContent | mcp_types.ImageContent | mcp_types.EmbeddedResource]:
  """MCP handler to execute a tool call."""
  print(f"MCP Server: Received call_tool request for '{name}' with args: {arguments}")

  # Look up the tool by name in our dictionary
  tool_to_call = available_tools.get(name)
  if tool_to_call:
    try:
      adk_response = await tool_to_call.run_async(
          args=arguments,
          tool_context=None, # No ADK context available here
      )
      print(f"MCP Server: ADK tool '{name}' executed successfully.")
      
      response_text = json.dumps(adk_response, indent=2)
      return [mcp_types.TextContent(type="text", text=response_text)]

    except Exception as e:
      print(f"MCP Server: Error executing ADK tool '{name}': {e}")
      # Creating a proper MCP error response might be more robust
      error_text = json.dumps({"error": f"Failed to execute tool '{name}': {str(e)}"})
      return [mcp_types.TextContent(type="text", text=error_text)]
  else:
      # Handle calls to unknown tools
      print(f"MCP Server: Tool '{name}' not found.")
      error_text = json.dumps({"error": f"Tool '{name}' not implemented."})
      return [mcp_types.TextContent(type="text", text=error_text)]

Bu işlev, araç adını ve bağımsız değişkenleri alır, daha önce tanımladığımız ilgili Python sarmalayıcı işlevini bulur, yürütür ve sonucu döndürür.

👉💻 MCP sunucu mantığı tanımlandığına göre artık bunu bir kapsayıcı olarak paketlememiz gerekiyor. Terminalde, Cloud Build'i kullanarak Docker görüntüsünü oluşturmak için aşağıdaki komut dosyasını çalıştırın:

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/tools/instavibe

export IMAGE_TAG="latest"
export MCP_IMAGE_NAME="mcp-tool-server"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${MCP_IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="mcp-tool-server"
export INSTAVIBE_BASE_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep instavibe)/api

gcloud builds submit . \
  --tag=${IMAGE_PATH} \
  --project=${PROJECT_ID}

👉💻 Ardından, görüntüyü Google Cloud Run'da hizmet olarak dağıtın.

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/tools/instavibe

export IMAGE_TAG="latest"
export MCP_IMAGE_NAME="mcp-tool-server"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${MCP_IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="mcp-tool-server"
export INSTAVIBE_BASE_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep instavibe)/api

gcloud run deploy ${SERVICE_NAME} \
  --image=${IMAGE_PATH} \
  --platform=managed \
  --region=${REGION} \
  --allow-unauthenticated \
  --set-env-vars="INSTAVIBE_BASE_URL=${INSTAVIBE_BASE_URL}" \
  --set-env-vars="APP_HOST=0.0.0.0" \
  --set-env-vars="APP_PORT=8080" \
  --set-env-vars="GOOGLE_GENAI_USE_VERTEXAI=TRUE" \
  --set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
  --set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
  --project=${PROJECT_ID} \
  --min-instances=1

👉💻 Dağıtım başarıyla tamamlandıktan sonra MCP sunucusu çalışır ve herkese açık bir URL üzerinden erişilebilir. Temsilcimizin (MCP istemcisi olarak hareket eder) nereye bağlanacağını bilmesi için bu URL'yi yakalamamız gerekir.

export MCP_SERVER_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep mcp-tool-server)/sse

Ayrıca, mcp-tool-server hizmetinin Google Cloud Console'unuzun Cloud Run bölümünde "Çalışıyor" olarak listelendiğini de görebilirsiniz.

Cloud Run

MCP sunucusu dağıtılıp URL'si yakalandıktan sonra artık MCP istemcisi olarak hareket edecek ve bu sunucu tarafından kullanıma sunulan araçları kullanacak aracıyı uygulayabiliriz.

8. Platform Etkileşimi Aracısı (MCP kullanılarak)

MCP İstemcisi: MCP istemcisi, bir yapay zeka uygulaması veya aracısında bulunan, yapay zeka modeli ile bir veya daha fazla MCP sunucusu arasında arayüz görevi gören bir bileşendir. Uygulamamızda bu istemci doğrudan aracımıza entegre edilecektir. Bu istemcinin temel işlevi, list_tools işlevi aracılığıyla kullanılabilir araçları keşfetmek ve ardından call_tool işlevini kullanarak belirli araçların yürütülmesini istemektir. Bu sırada, yapay zeka modeli veya aramayı düzenleyen aracı tarafından sağlanan gerekli bağımsız değişkenler iletilir.

MCP İstemcisi

Şimdi MCP istemcisi gibi davranan temsilciyi oluşturacağız. ADK çerçevesinde çalışan bu aracı, yeni dağıttığımız mcp-tool-server ile iletişim kurmaktan sorumlu olacaktır.

👉 Öncelikle, çalışan MCP sunucumuzdaki araçları dinamik olarak getirmek için aracı tanımını değiştirmemiz gerekir. agents/platform_mcp_client/agent.py içinde #REPLACE ME - FETCH TOOLS yerine aşağıdakileri girin:

"""Gets tools from the File System MCP Server."""
  tools =  MCPToolset(
      connection_params=SseServerParams(url=MCP_SERVER_URL, headers={})
  )

Bu kod, MCP_SERVER_URL'ye (daha önce ortam değişkeni olarak ayarladığımız) bağlanmak ve kullanılabilir araçların listesini almak için MCPToolset.from_server yöntemini kullanır.

Ardından, ADK aracı tanımına bu dinamik olarak getirilen araçları gerçekten kullanmasını söylememiz gerekir.

👉 agents/platform_mcp_client/agent.py içinde #REPLACE ME - SET TOOLs ifadesini aşağıdakilerle değiştirin:

  tools=[tools],

👉💻 Şimdi, bu aracının MCP sunucusuna doğru şekilde bağlanıp bağlanamadığını ve çalışan InstaVibe uygulamamızla etkileşim kurmak için araçları kullanıp kullanamadığını görmek üzere ADK Geliştirici Kullanıcı Arayüzü'nü kullanarak yerel olarak test edelim.

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
export MCP_SERVER_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep mcp-tool-server)/sse

cd  ~/instavibe-bootstrap/agents
sed -i "s|^\(O\?GOOGLE_CLOUD_PROJECT\)=.*|GOOGLE_CLOUD_PROJECT=${PROJECT_ID}|" ~/instavibe-bootstrap/agents/platform_mcp_client/.env
sed -i "s|^\(O\?MCP_SERVER_URL\)=.*|MCP_SERVER_URL=${MCP_SERVER_URL}|" ~/instavibe-bootstrap/agents/platform_mcp_client/.env
adk web

Tarayıcınızda ADK Dev UI'yi tekrar açın (Cloud Shell'in 8000 bağlantı noktasındaki web önizlemesini kullanarak). Bu kez sağ üstteki açılır listeden platform_mcp_client aracısını seçin.

create_post aracını test edelim. Sohbet iletişim kutusuna aşağıdaki isteği girin:

Create a post saying "Y'all I just got the cutest lil void baby 😭✨ Naming him Abyss bc he's deep, mysterious, and lowkey chaotic 🔥🖤 #VoidCat #NewRoomie" I'm Julia

ADK Dev UI Post

Temsilci bunu işlemeli, create_post aracının kullanılması gerektiğini belirlemeli, MCP sunucusuyla iletişim kurmalı ve bu sunucu da InstaVibe API'yi çağırmalıdır.

👉 Doğrulama adımı: Temsilci işlemi onayladıktan sonra InstaVibe uygulamanızın çalıştığı sekmeyi açın (veya sekmeyi yenileyin). "Julia" adlı kullanıcının yeni gönderisini ana özet akışında görürsünüz.

InstaVibe Post

👉💻 Gerekirse Instavibe bağlantısını almak için bu komut dosyasını ayrı bir terminalde çalıştırın:

gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep instavibe

👉📝 Şimdi create_event aracını test edelim. Sohbet iletişim kutusuna aşağıdaki çok satırlı isteği girin:

Hey, can you set up an event for Hannah and George and me, and I'm Julia? Let's call it 'Mexico City Culinary & Art Day'.
here are more info
  {"event_name": "Mexico City Culinary & Art Day",
  "description": "A vibrant day in Mexico City for Hannah and George, starting with lunch at one of the city's best taco spots in the hip Condesa neighborhood, followed by an inspiring afternoon exploring the Museo Soumaya's stunning art collection.",
  "event_date": "2025-10-17T12:00:00-06:00",
  "locations": [
    {
      "name": "El Tizoncito",
      "description": "Considered one of the original creators of tacos al pastor, El Tizoncito offers a legendary taco experience in the heart of Condesa. Their flavorful meats, house salsas, and casual vibe make it a must-visit for foodies.",
      "latitude": 19.412179,
      "longitude": -99.171308,
      "address": "Av. Tamaulipas 122, Hipódromo, Cuauhtémoc, 06100 Ciudad de México, CDMX, Mexico"
    },
    {
      "name": "Museo Soumaya",
      "description": "An architectural icon in Mexico City, Museo Soumaya houses over 66,000 works of art, including pieces by Rodin, Dalí, and Rivera. The striking silver structure is a cultural landmark and a visual feast inside and out.",
      "latitude": 19.440056,
      "longitude": -99.204281,
      "address": "Plaza Carso, Blvd. Miguel de Cervantes Saavedra 303, Granada, Miguel Hidalgo, 11529 Ciudad de México, CDMX, Mexico"
    }
  ],
  "attendee_names": ["Hannah", "George", Julia],
}

Yine, temsilci MCP sunucusu üzerinden uygun aracı kullanmalıdır. Etkinlikler sekmesinde, tek tek etkinlikleri tıklayarak yürütmenin ayrıntılı ve adım adım izini görebilirsiniz.

ADK Dev UI Event

👉 Doğrulama Adımı: Çalışan InstaVibe uygulamanıza geri dönün ve "Etkinlikler" bölümüne (veya eşdeğer bir bölüme) gidin. Yeni oluşturulan "Mexico City Culinary & Art Day" etkinliğini artık listede görebilirsiniz.

InstaVibe etkinliği

Bu, MCP'nin temsilcimizin harici araçlardan (bu örnekte InstaVibe'ın API'leri) standart bir şekilde yararlanmasına nasıl olanak tanıdığını başarıyla gösteriyor.

Her iki işlemi de doğruladıktan sonra Cloud Shell terminalinize dönün ve ADK Dev UI'yı durdurmak için Ctrl+C tuşuna basın.

9. ADK'daki İş Akışı Aracısı ve Çoklu Aracı

Şu ana kadar olan deneyimlerimize göre, temsilcilerimiz gezileri planlayabiliyor ve platformla etkileşim kurabiliyor. Ancak gerçekten kişiselleştirilmiş planlama için kullanıcının sosyal çevresinin anlaşılması gerekir. Arkadaşlarının etkinliklerini yakından takip etmeyen yoğun kullanıcılar için bu bağlamı manuel olarak toplamak zordur. Bu sorunu çözmek için arkadaş etkinliklerini ve ilgi alanlarını analiz ederek daha kişiye özel öneriler sunmamızı sağlayacak, Spanner Graph Veritabanımızdan yararlanan bir Sosyal Profil Oluşturma aracısı oluşturacağız.

Sosyal Profil Oluşturma Aracısı

Öncelikle bu aracının grafik verilerine erişebilmesi için araçlara ihtiyacımız var.

👉📝 Aşağıdaki Python işlevlerini ~/instavibe-bootstrap/agents/social/instavibe.py dosyasının sonuna ekleyin:

def get_person_attended_events(person_id: str)-> list[dict]:
    """
    Fetches events attended by a specific person using Graph Query.
    Args:
       person_id (str): The ID of the person whose posts to fetch.
    Returns: list[dict] or None.
    """
    if not db_instance: return None

    graph_sql = """
        Graph SocialGraph
        MATCH (p:Person)-[att:Attended]->(e:Event)
        WHERE p.person_id = @person_id
        RETURN e.event_id, e.name, e.event_date, att.attendance_time
        ORDER BY e.event_date DESC
    """
    params = {"person_id": person_id}
    param_types_map = {"person_id": param_types.STRING}
    fields = ["event_id", "name", "event_date", "attendance_time"]

    results = run_graph_query( graph_sql, params=params, param_types=param_types_map, expected_fields=fields)

    if results is None: return None

    for event in results:
        if isinstance(event.get('event_date'), datetime):
            event['event_date'] = event['event_date'].isoformat()
        if isinstance(event.get('attendance_time'), datetime):
            event['attendance_time'] = event['attendance_time'].isoformat()
    return results

def get_person_id_by_name( name: str) -> str:
    """
    Fetches the person_id for a given name using SQL.

    Args:
       name (str): The name of the person to search for.

    Returns:
        str or None: The person_id if found, otherwise None.
                     Returns the ID of the *first* match if names are duplicated.
    """
    if not db_instance: return None

    sql = """
        SELECT person_id
        FROM Person
        WHERE name = @name
        LIMIT 1 -- Return only the first match in case of duplicate names
    """
    params = {"name": name}
    param_types_map = {"name": param_types.STRING}
    fields = ["person_id"]

    # Use the standard SQL query helper
    results = run_sql_query( sql, params=params, param_types=param_types_map, expected_fields=fields)

    if results: # Check if the list is not empty
        return results[0].get('person_id') # Return the ID from the first dictionary
    else:
        return None # Name not found


def get_person_posts( person_id: str)-> list[dict]:
    """
    Fetches posts written by a specific person using Graph Query.

    Args:
        person_id (str): The ID of the person whose posts to fetch.


    Returns:
        list[dict] or None: List of post dictionaries with ISO date strings,
                           or None if an error occurs.
    """
    if not db_instance: return None

    # Graph Query: Find the specific Person node, follow 'Wrote' edge to Post nodes
    graph_sql = """
        Graph SocialGraph
        MATCH (author:Person)-[w:Wrote]->(post:Post)
        WHERE author.person_id = @person_id
        RETURN post.post_id, post.author_id, post.text, post.sentiment, post.post_timestamp, author.name AS author_name
        ORDER BY post.post_timestamp DESC
    """
    # Parameters now include person_id and limit
    params = {
        "person_id": person_id
    }
    param_types_map = {
        "person_id": param_types.STRING
    }
    # Fields returned remain the same
    fields = ["post_id", "author_id", "text", "sentiment", "post_timestamp", "author_name"]

    results = run_graph_query(graph_sql, params=params, param_types=param_types_map, expected_fields=fields)

    if results is None:
        return None

    # Convert datetime objects to ISO format strings
    for post in results:
        if isinstance(post.get('post_timestamp'), datetime):
            post['post_timestamp'] = post['post_timestamp'].isoformat()

    return results


def get_person_friends( person_id: str)-> list[dict]:
    """
    Fetches friends for a specific person using Graph Query.
    Args:
        person_id (str): The ID of the person whose posts to fetch.
    Returns: list[dict] or None.
    """
    if not db_instance: return None

    graph_sql = """
        Graph SocialGraph
        MATCH (p:Person {person_id: @person_id})-[f:Friendship]-(friend:Person)
        RETURN DISTINCT friend.person_id, friend.name
        ORDER BY friend.name
    """
    params = {"person_id": person_id}
    param_types_map = {"person_id": param_types.STRING}
    fields = ["person_id", "name"]

    results = run_graph_query( graph_sql, params=params, param_types=param_types_map, expected_fields=fields)

    return results

Şimdi de temsilcimizi nasıl yapılandıracağımızı ele alalım. Birden fazla arkadaşınızın profilini analiz edip bulguları özetlemek için birkaç adım uygulamanız gerekir. Bu, ADK'nın çoklu aracı özelliklerini, özellikle de iş akışı aracılarını kullanmak için mükemmel bir senaryodur.

Google'ın ADK'sında bir İş Akışı Aracısı, görevleri kendisi yapmaz ancak alt aracılar adı verilen diğer aracıları yönetir. Bu sayede, karmaşık sorunları uzmanlaşmış bileşenlere ayırarak modüler bir tasarım oluşturabilirsiniz. ADK, aşağıdakiler gibi yerleşik iş akışı türleri sağlar:

  • Sıralı (adım adım)
  • Paralel (eşzamanlı yürütme)
  • ve Loop (tekrarlanan yürütme)

Sosyal Profil Oluşturma Aracısı

Sosyal profilleme görevimizde, tasarımımız yinelemeli bir iş akışı oluşturmak için Döngü Aracısı'nı kullanır. Amaç, her seferinde bir kişiyi işlemektir: profile_agent verileri toplar, summary_agent analizi günceller ve check_agent tekrar döngüye girmemiz gerekip gerekmediğini belirler.

Bu iş akışı için gereken alt aracıları tanımlayalım.

👉📝 ~/instavibe-bootstrap/agents/social/agent.py içinde #REPLACE FOR profile_agent yerine aşağıdakileri girin:

profile_agent = LlmAgent(
    name="profile_agent",
    model="gemini-2.5-flash",
    description=(
        "Agent to answer questions about the this person social profile. Provide the person's profile using their name, make sure to fetch the id before getting other data."
    ),
    instruction=(
        "You are a helpful agent to answer questions about the this person social profile. You'll be given a list of names, provide the person's profile using their name, make sure to fetch the id before getting other data. Get one person at a time, start with the first one on the list, and skip if already provided. return this person's result"
    ),
    tools=[get_person_posts,get_person_friends,get_person_id_by_name,get_person_attended_events],
)

Ardından, toplanan profil bilgilerini (döngü yinelemeleri boyunca biriktirilen) alan ve birden fazla kişi analiz edildiyse ortak noktaları belirleyerek nihai özeti oluşturan aracı.

👉📝 Aynı ~/instavibe-bootstrap/agents/social/agent.py içinde #REPLACE FOR summary_agent yerine aşağıdakileri girin:

summary_agent = LlmAgent(
    name="summary_agent",
    model="gemini-2.5-flash",
    description=(
        "Generate a comprehensive social summary as a single, cohesive paragraph. This summary should cover the activities, posts, friend networks, and event participation of one or more individuals. If multiple profiles are analyzed, the paragraph must also identify and integrate any common ground found between them."
    ),
    instruction=(
        """
        Your primary task is to synthesize social profile information into a single, comprehensive paragraph.

            **Input Scope & Default Behavior:**
            *   If specific individuals are named by the user, focus your analysis on them.
            *   **If no individuals are specified, or if the request is general, assume the user wants an analysis of *all relevant profiles available in the current dataset/context*.**

            **For each profile (whether specified or determined by default), you must analyze:**

            1.  **Post Analysis:**
                *   Systematically review their posts (e.g., content, topics, frequency, engagement).
                *   Identify recurring themes, primary interests, and expressed sentiments.

            2.  **Friendship Relationship Analysis:**
                *   Examine their connections/friends list.
                *   Identify key relationships, mutual friends (especially if comparing multiple profiles), and the general structure of their social network.

            3.  **Event Participation Analysis:**
                *   Investigate their past (and if available, upcoming) event participation.
                *   Note the types of events, frequency of attendance, and any notable roles (e.g., organizer, speaker).

            **Output Generation (Single Paragraph):**

            *   **Your entire output must be a single, cohesive summary paragraph.**
                *   **If analyzing a single profile:** This paragraph will detail their activities, interests, and social connections based on the post, friend, and event analysis.
                *   **If analyzing multiple profiles:** This paragraph will synthesize the key findings regarding posts, friends, and events for each individual. Crucially, it must then seamlessly integrate or conclude with an identification and description of the common ground found between them (e.g., shared interests from posts, overlapping event attendance, mutual friends). The aim is a unified narrative within this single paragraph.

            **Key Considerations:**
            *   Base your summary strictly on the available data.
            *   If data for a specific category (posts, friends, events) is missing or sparse for a profile, you may briefly acknowledge this within the narrative if relevant.
                """
        ),
    output_key="summary"
)

Döngünün ne zaman durdurulması gerektiğini (ör.istenen tüm profiller özetlendiğinde) belirlememiz gerekiyor.

👉📝 Aynı ~/instavibe-bootstrap/agents/social/agent.py içinde #REPLACE FOR check_agent yerine aşağıdakileri girin:

check_agent = LlmAgent(
    name="check_agent",
    model="gemini-2.5-flash",
    description=(
        "Check if everyone's social profile are summarized and has been generated. Output 'completed' or 'pending'."
    ),
    output_key="summary_status"
)

summary_status değerini State içinde açıkça arayan, check_agent tarafından döndürülen ve Loop Agent'a devam edip etmeyeceğini (escalate=False) veya durdurup durdurmayacağını (escalate=True) söyleyen basit bir programatik kontrol (CheckCondition) ekliyoruz.

👉📝 Aynı ~/instavibe-bootstrap/agents/social/agent.py içinde, dosyanın üst kısmında bulunan #REPLACE FOR CheckCondition öğesini aşağıdakilerle değiştirin:

class CheckCondition(BaseAgent):
    async def _run_async_impl(self, ctx: InvocationContext) -> AsyncGenerator[Event, None]:
        #log.info(f"Checking status: {ctx.session.state.get("summary_status", "fail")}")
        log.info(f"Summary: {ctx.session.state.get("summary")}")

        status = ctx.session.state.get("summary_status", "fail").strip()
        is_done = (status == "completed")

        yield Event(author=self.name, actions=EventActions(escalate=is_done))

Loop sonuçları için durum ve geri aramalar

Google'ın ADK'sında Durum, bir aracının yürütülmesi sırasında belleğini veya çalışma verilerini temsil eden önemli bir kavramdır. Bu, bir temsilcinin farklı adımlar, araç çağrıları veya etkileşimler boyunca koruması gereken bilgileri içeren kalıcı bir bağlamdır. Bu durum, ara sonuçları, kullanıcı bilgilerini, sonraki işlemler için parametreleri veya aracının bir görevde ilerlerken hatırlaması gereken diğer verileri depolayabilir.

Senaryomuzda, Loop Aracısı yineleme yaptığında summary_agent ve check_agent, çıkışlarını (özet ve summary_status) aracının durumunda saklar. Bu sayede bilgiler yinelemeler arasında korunur. Ancak Loop Agent, tamamlandığında durumdan otomatik olarak nihai özeti döndürmez.

Sosyal Profil Oluşturma Aracısı

ADK'daki geri çağırma işlevleri, bir aracının yaşam döngüsü sırasında belirli noktalarda veya belirli etkinliklere (ör. bir araç çağrısının tamamlanması ya da aracının yürütmeyi bitirmesinden önce) yanıt olarak yürütülecek özel mantık yerleştirmemize olanak tanır. Bu işlevler, aracının davranışını özelleştirmenin ve sonuçları dinamik olarak işlemenin bir yolunu sunar.

Döngü tamamlandığında (CheckCondition yükseltildiği için) çalışan bir after_agent_callback kullanacağız. Bu geri çağırma işlevi modify_output_after_agent, durumdan nihai özeti alır ve bunu temsilcinin nihai çıkış mesajı olarak biçimlendirir.

Geri ara

👉📝 Aynı ~/instavibe-bootstrap/agents/social/agent.py içinde #REPLACE FOR modify_output_after_agent ifadesini follow ile değiştirin:

def modify_output_after_agent(callback_context: CallbackContext) -> Optional[types.Content]:

    agent_name = callback_context.agent_name
    invocation_id = callback_context.invocation_id
    current_state = callback_context.state.to_dict()
    current_user_content = callback_context.user_content
    print(f"[Callback] Exiting agent: {agent_name} (Inv: {invocation_id})")
    print(f"[Callback] Current summary_status: {current_state.get("summary_status")}")
    print(f"[Callback] Current Content: {current_user_content}")

    status = current_state.get("summary_status").strip()
    is_done = (status == "completed")
    # Retrieve the final summary from the state

    final_summary = current_state.get("summary")
    print(f"[Callback] final_summary: {final_summary}")
    if final_summary and is_done and isinstance(final_summary, str):
        log.info(f"[Callback] Found final summary, constructing output Content.")
        # Construct the final output Content object to be sent back
        return types.Content(role="model", parts=[types.Part(text=final_summary.strip())])
    else:
        log.warning("[Callback] No final summary found in state or it's not a string.")
        # Optionally return a default message or None if no summary was generated
        return None

Kök Döngü Aracısını Tanımlama

Son olarak, ana LoopAgent'ı tanımlıyoruz. Her döngü yinelemesinde alt aracıları sırayla düzenler (profile_agent -> summary_agent -> check_agent -> CheckCondition). Bu sıra, CheckCondition tamamlanmayı işaret edene veya max_iterations kez tekrarlanana kadar devam eder. after_agent_callback, nihai özetin döndürülmesini sağlar.

👉📝 Aynı ~/instavibe-bootstrap/agents/social/agent.py içinde #REPLACE FOR root_agent ifadesini follow ile değiştirin:

root_agent = LoopAgent(
    name="InteractivePipeline",
    sub_agents=[
        profile_agent,
        summary_agent,
        check_agent,
        CheckCondition(name="Checker")
    ],
    description="Find everyone's social profile on events, post and friends",
    max_iterations=10,
    after_agent_callback=modify_output_after_agent
)

ADK Dev UI'yi kullanarak bu çoklu aracı iş akışını test edelim.

👉💻 ADK web sunucusunu başlatın:

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd  ~/instavibe-bootstrap/agents
sed -i "s|^\(O\?GOOGLE_CLOUD_PROJECT\)=.*|GOOGLE_CLOUD_PROJECT=${PROJECT_ID}|" ~/instavibe-bootstrap/agents/social/.env
adk web

ADK Dev UI'yi (Web Önizlemesi üzerinden 8000 numaralı bağlantı noktası) açın. Aracı açılır menüsünde (sağ üst) Sosyal Aracı'yı seçin.

👉 Şimdi, birden fazla kişinin profilini oluşturma görevini verin. Sohbet iletişim kutusuna şunu girin:

Tell me about Mike and Bob

Aracı yanıt verdikten sonra (döngü ve birden fazla LLM çağrısı nedeniyle bu biraz daha uzun sürebilir) yalnızca son sohbet çıkışına bakmayın. ADK Dev UI'nin sol bölmesindeki Etkinlikler sekmesine gidin.

👉 Doğrulama Adımı: Etkinlikler sekmesinde, yürütmenin adım adım ayrıntılı izini görürsünüz. 09-01-adk-dev-ui.png

Aracının her bir alt aracı nasıl çağırdığını gözlemledikten sonra, akışın her yinelemede profile_agent -> summary_agent -> check_agent -> Checker şeklinde ilerlemesini beklersiniz. Ancak uygulamada, aracının güçlü "kendi kendini optimize etme" özelliğinin çalıştığını görüyoruz.

Çünkü temel model, isteğin tamamını (ör. "Mike ve Bob'un profilini oluştur") gibi bir görev verildiğinde genellikle en verimli yolu seçer ve birden çok kez yineleme yapmak yerine gerekli tüm verileri tek bir birleştirilmiş adımda toplar. profile_agent tarafından yapılan araç çağrıları da dahil olmak üzere her adımın girişlerini, çıkışlarını ve durumlarını görebilirsiniz.

09-02-ui-graph.png

ve check_agent ile CheckCondition'dan gelen durum güncellemeleri. 09-03-ui-state.png

Bu görsel iz, çoklu aracı iş akışının son özet oluşturulup geri çağırma tarafından döndürülene kadar nasıl çalıştığını anlamak ve hatalarını ayıklamak için çok değerlidir.

Sohbet yanıtını ve etkinlik izlemeyi inceledikten sonra Cloud Shell terminaline dönün ve ADK Dev UI'yi durdurmak için Ctrl+C tuşuna basın.

10. Temsilciler Arası (A2A) İletişim

Şimdiye kadar uzmanlaşmış aracıları oluşturduk ancak bunlar yalıtılmış olarak veya aynı makinede önceden tanımlanmış bir iş akışı içinde çalışıyor. Gerçekten dağıtılmış ve ortak çalışmaya dayalı çoklu aracı sistemleri oluşturmak için, muhtemelen ayrı hizmetler olarak çalışan aracıların birbirini keşfedebileceği ve etkili bir şekilde iletişim kurabileceği bir yöntem gerekir. Bu noktada, temsilciden temsilciye (A2A) protokolü devreye girer.

A2A protokolü, yapay zeka aracıları arasında birlikte çalışabilir iletişimi için özel olarak tasarlanmış açık bir standarttır. MCP, temsilciden araca etkileşime odaklanırken A2A, temsilciden temsilciye etkileşime odaklanır. Bu özellik, temsilcilerin şunları yapmasına olanak tanır:

  • Keşfetme: Diğer aracıları bulun ve standartlaştırılmış Aracı Kartları aracılığıyla özelliklerini öğrenin.
  • İletişim kurma: Mesaj ve verileri güvenli bir şekilde paylaşın.
  • İşbirliği yapma: Karmaşık hedeflere ulaşmak için görevleri devredin ve işlemleri koordine edin.

A2A protokolü, bu iletişimi "Aracı Kartları" gibi mekanizmalar aracılığıyla kolaylaştırır. Aracı Kartları, aracıların yeteneklerini ve bağlantı bilgilerini tanıtmak için kullanabileceği bir araçtır.

10-05-agent-card

A2A, bilinen web standartlarından (HTTP, SSE, JSON-RPC) yararlanır ve genellikle bir aracının (istemci) görevleri diğerine (uzak aracı/sunucu) gönderdiği bir istemci-sunucu modeli kullanır. Bu standardizasyon, bağımsız olarak geliştirilen aracıların birlikte çalışabileceği modüler ve ölçeklenebilir sistemler oluşturmak için çok önemlidir.

InstaVibe temsilcileri için A2A'yı etkinleştirme

Mevcut Planner, Platform Interaction ve Social aracılarımızın A2A üzerinden diğer aracılara erişilebilir olması için her birini A2A sunucu bileşeniyle sarmamız gerekir. Bu sunucu:

  • Aracı Kartı Gösterme: Aracı özelliklerinin standart bir açıklamasını HTTP uç noktası üzerinden sunun.
  • Görevler için Dinleme(İstek Mesajları): A2A protokolüne göre diğer temsilcilerden (A2A istemcileri) gelen görev isteklerini kabul edin.
  • Görev(İstek Mesajları) Yürütme İşlemini Yönetme: Alınan görevleri işlenmek üzere temel ADK aracısı mantığına aktarır.

Planner Agent (A2A Etkin)

all-agent-planner

Planlayıcı Temsilcimize A2A sunucu katmanını ekleyerek başlayalım.

A2A sunucusunun başlatma mantığını tanımlayın. Bu kod, AgentCard'ı (temsilcinin herkese açık açıklaması) tanımlar, A2AServer'ı yapılandırır ve başlatır. Böylece, A2AServer PlatformAgentExecutor'a bağlanır.

👉📝 Aşağıdaki kodu ~/instavibe-bootstrap/agents/planner/a2a_server.py dosyasının sonuna ekleyin:

class PlannerAgent:
    """An agent to help user planning a event with its desire location."""
    SUPPORTED_CONTENT_TYPES = ["text", "text/plain"]

    def __init__(self):
        self._agent = self._build_agent()
        self.runner = Runner(
            app_name=self._agent.name,
            agent=self._agent,
            artifact_service=InMemoryArtifactService(),
            session_service=InMemorySessionService(),
            memory_service=InMemoryMemoryService(),
        )
        capabilities = AgentCapabilities(streaming=True)
        skill = AgentSkill(
            id="event_planner",
            name="Event planner",
            description="""
            This agent generates multiple fun plan suggestions tailored to your specified location, dates, and interests,
            all designed for a moderate budget. It delivers detailed itineraries,
            including precise venue information (name, latitude, longitude, and description), in a structured JSON format.
            """,
            tags=["instavibe"],
            examples=["What about Bostona MA this weekend?"],
        )
        self.agent_card = AgentCard(
            name="Event Planner Agent",
            description="""
            This agent generates multiple fun plan suggestions tailored to your specified location, dates, and interests,
            all designed for a moderate budget. It delivers detailed itineraries,
            including precise venue information (name, latitude, longitude, and description), in a structured JSON format.
            """,
            url=f"{PUBLIC_URL}",
            version="1.0.0",
            defaultInputModes=PlannerAgent.SUPPORTED_CONTENT_TYPES,
            defaultOutputModes=PlannerAgent.SUPPORTED_CONTENT_TYPES,
            capabilities=capabilities,
            skills=[skill],
        )

    def get_processing_message(self) -> str:
        return "Processing the planning request..."

    def _build_agent(self) -> LlmAgent:
        """Builds the LLM agent for the night out planning agent."""
        return agent.root_agent


if __name__ == '__main__':
    try:
        plannerAgent = PlannerAgent()

        request_handler = DefaultRequestHandler(
            agent_executor=PlannerAgentExecutor(plannerAgent.runner,plannerAgent.agent_card),
            task_store=InMemoryTaskStore(),
        )

        server = A2AStarletteApplication(
            agent_card=plannerAgent.agent_card,
            http_handler=request_handler,
        )
        logger.info(f"Attempting to start server with Agent Card: {plannerAgent.agent_card.name}")
        logger.info(f"Server object created: {server}")

        uvicorn.run(server.build(), host='0.0.0.0', port=port)
    except Exception as e:
        logger.error(f"An error occurred during server startup: {e}")
        exit(1)

👉💻 A2A sunucusunun yerel olarak doğru şekilde başlatılıp başlatılmadığını ve Agent Card'ını yayınlayıp yayınlamadığını hızlıca test edelim. İlk terminalinizde aşağıdaki komutu çalıştırın:

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd ~/instavibe-bootstrap/agents/
python -m planner.a2a_server

👉 Şimdi başka bir terminal penceresi açın. (Terminal panelinde + işaretini tıklayın) iki terminal

👉💻 Yerel olarak çalışan sunucudan Agent Card'ı istemek için curl'ü kullanın:

curl http://localhost:10003/.well-known/agent.json | jq

Sunucunun çalıştığını ve Planlayıcı aracısının reklamını yaptığını onaylayan, tanımladığımız AgentCard'ın JSON gösterimini görmeniz gerekir.

10-02-planner-a2a.png

İlk terminale (sunucunun çalıştığı yer) geri dönün ve durdurmak için Ctrl+C tuşuna basın.

👉💻 A2A sunucu mantığı eklendiğinden artık kapsayıcı görüntüsünü oluşturabiliriz.

Planlayıcı aracısını oluşturma ve dağıtma

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/agents

# Set variables specific to the PLANNER agent
export IMAGE_TAG="latest"
export AGENT_NAME="planner"
export IMAGE_NAME="planner-agent"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="planner-agent"
export PUBLIC_URL="https://planner-agent-${PROJECT_NUMBER}.${REGION}.run.app"

echo "Building ${AGENT_NAME} agent..."
gcloud builds submit . \
  --config=cloudbuild-build.yaml \
  --project=${PROJECT_ID} \
  --region=${REGION} \
  --substitutions=_AGENT_NAME=${AGENT_NAME},_IMAGE_PATH=${IMAGE_PATH}

echo "Image built and pushed to: ${IMAGE_PATH}"

👉💻 Planlayıcı aracımızı Cloud Run'da dağıtın.

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/agents

# Set variables specific to the PLANNER agent
export IMAGE_TAG="latest"
export AGENT_NAME="planner"
export IMAGE_NAME="planner-agent"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="planner-agent"
export PUBLIC_URL="https://planner-agent-${PROJECT_NUMBER}.${REGION}.run.app"


gcloud run deploy ${SERVICE_NAME} \
  --image=${IMAGE_PATH} \
  --platform=managed \
  --region=${REGION} \
  --set-env-vars="A2A_HOST=0.0.0.0" \
  --set-env-vars="A2A_PORT=8080" \
  --set-env-vars="GOOGLE_GENAI_USE_VERTEXAI=TRUE" \
  --set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
  --set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
  --set-env-vars="PUBLIC_URL=${PUBLIC_URL}" \
  --allow-unauthenticated \
  --project=${PROJECT_ID} \
  --min-instances=1

A2A Inspector'ı kullanarak dağıtılan hizmetin çalıştığını ve buluttan Agent Card'ını doğru şekilde sunduğunu doğrulayalım.

👉 Cloud Shell araç çubuğundaki Web önizlemesi simgesinden Bağlantı noktasını değiştir'i seçin. Bağlantı noktasını 8081 olarak ayarlayın ve "Değiştir ve Önizle"yi tıklayın. A2A Inspector arayüzünü içeren yeni bir tarayıcı sekmesi açılır.

10-08-web-preview.png

👉💻 Terminalde, dağıtılan planlayıcı aracınızın URL'sini alın:

export PLANNER_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner-agent)
echo ${PLANNER_AGENT_URL}

👉💻 Çıkış URL'sini kopyalayın.

👉 A2A Inspector kullanıcı arayüzünde URL'yi Agent URL (Aracı URL'si) alanına yapıştırıp Connect (Bağlan) seçeneğini tıklayın.

👀 Temsilcinin kart bilgileri ve JSON'u, başarılı bir bağlantıyı onaylayarak Temsilci Kartı sekmesinde görünmelidir.

10-03-planner-a2a.png

👉 A2A Inspector'da Chat sekmesini tıklayın. Burada, dağıtılan temsilcinizle doğrudan etkileşim kurabilir ve planlama özelliğini test etmek için ona mesaj gönderebilirsiniz. Örneğin:

Plan something for me in Boston MA this weekend, and I enjoy classical music

👀 Ham iletişimi incelemek için mesaj balonunuzu, ardından sohbet penceresinde temsilcinin yanıt balonunu tıklayın. Her birini tıkladığınızda, gönderilen veya alınan tam JSON-RPC 2.0 mesajı gösterilir. Bu, hata ayıklama için çok değerlidir.

A2A Inspector sekmesini elinizin altında bulundurun. KAPATMAYIN. Diğer iki aracımızı test etmek için biraz sonra tekrar kullanacağız.

10-06-a2a-inspector.png

Platform Interaction Agent (A2A Etkin)

all-agent-platform

Ardından, Platform Interaction Agent (MCP'yi kullanan) için işlemi tekrarlayacağız.

👉📝 ~/instavibe-bootstrap/agents/platform_mcp_client/a2a_server.py bölümünün sonunda, benzersiz AgentCard'ı da dahil olmak üzere A2A sunucu kurulumunu tanımlayın:

class PlatformAgent:
  """An agent that post event and post to instavibe."""

  SUPPORTED_CONTENT_TYPES = ["text", "text/plain"]

  def __init__(self):
    self._agent = self._build_agent()
    self.runner = Runner(
        app_name=self._agent.name,
        agent=self._agent,
        artifact_service=InMemoryArtifactService(),
        session_service=InMemorySessionService(),
        memory_service=InMemoryMemoryService(),
    )
    capabilities = AgentCapabilities(streaming=True)
    skill = AgentSkill(
            id="instavibe_posting",
            name="Post social post and events on instavibe",
            description="""
            This "Instavibe" agent helps you create posts (identifying author, text, and sentiment – inferred if unspecified) and register
            for events (gathering name, date, attendee). It efficiently collects required information and utilizes dedicated tools
            to perform these actions on your behalf, ensuring a smooth sharing experience.
            """,
            tags=["instavibe"],
            examples=["Create a post for me, the post is about my cute cat and make it positive, and I'm Alice"],
        )
    self.agent_card = AgentCard(
            name="Instavibe Posting Agent",
            description="""
            This "Instavibe" agent helps you create posts (identifying author, text, and sentiment – inferred if unspecified) and register
            for events (gathering name, date, attendee). It efficiently collects required information and utilizes dedicated tools
            to perform these actions on your behalf, ensuring a smooth sharing experience.
            """,
            url=f"{PUBLIC_URL}",
            version="1.0.0",
            defaultInputModes=PlatformAgent.SUPPORTED_CONTENT_TYPES,
            defaultOutputModes=PlatformAgent.SUPPORTED_CONTENT_TYPES,
            capabilities=capabilities,
            skills=[skill],
        )


  def get_processing_message(self) -> str:
      return "Processing the social post and event request..."

  def _build_agent(self) -> LlmAgent:
    """Builds the LLM agent for the Processing the social post and event request."""
    return agent.root_agent


if __name__ == '__main__':
    try:
        platformAgent = PlatformAgent()

        request_handler = DefaultRequestHandler(
            agent_executor=PlatformAgentExecutor(platformAgent.runner,platformAgent.agent_card),
            task_store=InMemoryTaskStore(),
        )

        server = A2AStarletteApplication(
            agent_card=platformAgent.agent_card,
            http_handler=request_handler,
        )

        uvicorn.run(server.build(), host='0.0.0.0', port=port)
    except Exception as e:
        logger.error(f"An error occurred during server startup: {e}")
        exit(1)

Sosyal Medya Temsilcisi (A2A Etkin)

all-agent-social

Son olarak, sosyal profilleme aracımız için A2A'yı etkinleştirelim.

👉📝 ~/instavibe-bootstrap/agents/social/a2a_server.py bölümünün sonunda A2A sunucu kurulumunu ve AgentCard'ı tanımlayın:

class SocialAgent:
  """An agent that handles social profile analysis."""

  SUPPORTED_CONTENT_TYPES = ["text", "text/plain"]

  def __init__(self):
    self._agent = self._build_agent()
    self.runner = Runner(
        app_name=self._agent.name,
        agent=self._agent,
        artifact_service=InMemoryArtifactService(),
        session_service=InMemorySessionService(),
        memory_service=InMemoryMemoryService(),
    )
    capabilities = AgentCapabilities(streaming=True)
    skill = AgentSkill(
                id="social_profile_analysis",
                name="Analyze Instavibe social profile",
                description="""
                Using a provided list of names, this agent synthesizes Instavibe social profile information by analyzing posts, friends, and events.
                It delivers a comprehensive single-paragraph summary for individuals, and for groups, identifies commonalities in their social activities
                and connections based on profile data.
                """,
                tags=["instavibe"],
                examples=["Can you tell me about Bob and Alice?"],
    )
    self.agent_card = AgentCard(
                name="Social Profile Agent",
                description="""
                Using a provided list of names, this agent synthesizes Instavibe social profile information by analyzing posts, friends, and events.
                It delivers a comprehensive single-paragraph summary for individuals, and for groups, identifies commonalities in their social activities
                and connections based on profile data.
                """,
                url=f"{PUBLIC_URL}",
                version="1.0.0",
                defaultInputModes=self.SUPPORTED_CONTENT_TYPES,
                defaultOutputModes=self.SUPPORTED_CONTENT_TYPES,
                capabilities=capabilities,
                skills=[skill],
    )

  def get_processing_message(self) -> str:
      return "Processing the social profile analysis request..."

  def _build_agent(self) -> LoopAgent:
    """Builds the LLM agent for the social profile analysis agent."""
    return agent.root_agent

if __name__ == '__main__':
    try:
        socialAgent = SocialAgent()

        request_handler = DefaultRequestHandler(
            agent_executor=SocialAgentExecutor(socialAgent.runner,socialAgent.agent_card),
            task_store=InMemoryTaskStore(),
        )

        server = A2AStarletteApplication(
            agent_card=socialAgent.agent_card,
            http_handler=request_handler,
        )

        uvicorn.run(server.build(), host='0.0.0.0', port=port)
    except Exception as e:
        logger.error(f"An error occurred during server startup: {e}")
        exit(1)

Platform Etkileşimi ve Sosyal medya aracılarını oluşturma ve dağıtma

Bu aracıların Spanner'a erişmesi gerekir. Bu nedenle, dağıtım sırasında SPANNER_INSTANCE_ID, SPANNER_DATABASE_ID ve MCP_SERVER_URL ortam değişkenlerinin doğru şekilde iletildiğinden emin olun.

👉💻 Cloud Build ile Cloud Run'da derleme ve dağıtım yapma:

. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap/agents
export MCP_SERVER_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep mcp-tool-server)/sse


gcloud builds submit . \
  --config=cloudbuild.yaml \
  --project="${PROJECT_ID}" \
  --region="${REGION}" \
  --substitutions=\
_PROJECT_ID="${PROJECT_ID}",\
_PROJECT_NUMBER="${PROJECT_NUMBER}",\
_REGION="${REGION}",\
_REPO_NAME="${REPO_NAME}",\
_SPANNER_INSTANCE_ID="${SPANNER_INSTANCE_ID}",\
_SPANNER_DATABASE_ID="${SPANNER_DATABASE_ID}",\
_MCP_SERVER_URL="${MCP_SERVER_URL}"

👉💻 Terminalde, dağıtılan platform aracınızın URL'sini alın:

export PLATFORM_MPC_CLIENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep platform-mcp-client)
echo $PLATFORM_MPC_CLIENT_URL

👉💻 Çıkış URL'sini kopyalayın.

👉 A2A Inspector kullanıcı arayüzünde URL'yi Agent URL (Aracı URL'si) alanına yapıştırıp Connect (Bağlan) seçeneğini tıklayın.

👀 Temsilcinin kart bilgileri ve JSON'u, başarılı bir bağlantıyı onaylayarak Temsilci Kartı sekmesinde görünmelidir.

10-05-platform-a2a.png

👉 A2A Inspector'da Chat sekmesini tıklayın. Burada, dağıtılan temsilcinizle doğrudan etkileşim kurabilir, ona mesaj göndererek temsilcinin gönderi oluşturma becerisini test edebilirsiniz:

Create a post for me, the post says 'Paws, purrs, and ocean views 🐾☕🌊. Spent my morning at the Morning Seaside Cat Café, where every sip comes with a side of snuggles and sea breeze.' and make it positive, and I'm Oscar.

👀 Ham iletişimi incelemek için mesaj balonunuzu, ardından sohbet penceresinde temsilcinin yanıt balonunu tıklayın. Her birini tıkladığınızda, gönderilen veya alınan tam JSON-RPC 2.0 mesajı gösterilir. Bu, hata ayıklama için çok değerlidir.

👉💻 Terminalde, dağıtılan Sosyal Aracınızın URL'sini alın:

export SOCIAL_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep social-agent)
echo $SOCIAL_AGENT_URL

👉💻 Çıkış URL'sini kopyalayın.

👉 A2A Inspector kullanıcı arayüzünde URL'yi Agent URL (Aracı URL'si) alanına yapıştırıp Connect (Bağlan) seçeneğini tıklayın.

👀 Temsilcinin kart bilgileri ve JSON'u, başarılı bir bağlantıyı onaylayarak Temsilci Kartı sekmesinde görünmelidir.

10-04-social-a2a.png

👉 A2A Inspector'da Chat sekmesini tıklayın. Burada, dağıtılan aracınızla doğrudan etkileşim kurabilir ve veritabanınızdaki kullanıcı profillerini analiz etmesi için araca mesaj gönderebilirsiniz:

Can you tell me about both Ian and Kevin's profile, what are their common interests?

👀 Ham iletişimi incelemek için mesaj balonunuzu, ardından sohbet penceresinde temsilcinin yanıt balonunu tıklayın. Her birini tıkladığınızda, gönderilen veya alınan tam JSON-RPC 2.0 mesajı gösterilir. Bu, hata ayıklama için çok değerlidir.

👉 Tüm temsilcilerimizin denetimini tamamladık. Artık A2A Inspector sekmesini kapatabilirsiniz.

11. Orchestrator Aracısı (A2A İstemcisi)

Artık Cloud Run'da bağımsız, A2A özellikli hizmetler olarak çalışan üç uzman aracımız (Planlayıcı, Platform, Sosyal) var. Son parça, Orchestrator Agent'tır. Bu aracı, merkezi koordinatör veya A2A istemcisi olarak görev yapar. Kullanıcı isteklerini alır, isteği karşılamak için hangi uzak aracıların gerektiğini (potansiyel olarak sırayla) belirler ve ardından görevleri bu uzak aracılara devretmek için A2A protokolünü kullanır. Bu atölye çalışmasında, Orchestrator aracısını ADK Dev UI'yi kullanarak yerel olarak çalıştıracağız.

all-agent-orchestrator

Öncelikle, Orchestrator'ın keşfettiği uzak aracıların kaydını işlemek için mantığını geliştirelim. Başlatma sırasında getirilen Temsilci Kartları'ndaki bağlantı ayrıntılarını depolar.

👉📝 ~/instavibe-bootstrap/agents/orchestrate/agent.py içinde #REPLACE ME REG AGENT CARD yerine şunu yazın:

async with httpx.AsyncClient(timeout=30) as client:
            for i, address in enumerate(REMOTE_AGENT_ADDRESSES):
                log.info(f"--- STEP 3.{i}: Attempting connection to: {address} ---")
                try:
                    card_resolver = A2ACardResolver(client, address)
                    card = await card_resolver.get_agent_card()
                    
                    remote_connection = RemoteAgentConnections(agent_card=card, agent_url=address)
                    self.remote_agent_connections[card.name] = remote_connection
                    self.cards[card.name] = card
                    log.info(f"--- STEP 5.{i}: Successfully stored connection for {card.name} ---")

                except Exception as e:
                    log.error(f"--- CRITICAL FAILURE at STEP 4.{i} for address: {address} ---")
                    log.error(f"--- The hidden exception type is: {type(e).__name__} ---")
                    log.error(f"--- Full exception details and traceback: ---", exc_info=True)

Ardından, ADK'da Orchestrator aracısı için aracı tanımlayın.

  • send_message (işi devretmek için kullanılan A2A işlevi).

👉📝 ~/instavibe-bootstrap/agents/orchestrate/agent.py içindeki #REPLACE ME CREATE AGENT yerine aşağıdakileri girin:

def create_agent(self) -> Agent:
        """Synchronously creates the ADK Agent object."""
        return Agent(
            model="gemini-2.5-flash",
            name="orchestrate_agent",
            instruction=self.root_instruction,
            before_agent_callback=self.before_agent_callback,
            description=("Orchestrates tasks for child agents."),
            tools=[self.send_message], 
        )

Orchestrator'ın temel mantığı, A2A'yı nasıl kullanacağını belirten talimatlarında yatar.

👉📝 #REPLACE ME INSTRUCTIONS içindeki ~/instavibe-bootstrap/agents/orchestrate/agent.py kısmını talimat oluşturma yöntemiyle değiştirin:

def root_instruction(self, context: ReadonlyContext) -> str:
        current_agent = self.check_active_agent(context)
        return f"""
                You are an expert AI Orchestrator. Your primary responsibility is to intelligently interpret user requests, break them down into a logical plan of discrete actions, and delegate each action to the most appropriate specialized remote agent using the send_message function. You do not perform the tasks yourself but manage their assignment, sequence, and critically, their outcomes.
                    **Core Directives & Decision Making:**

                    *   **Understand User Intent & Complexity:**
                        *   Carefully analyze the user's request to determine the core task(s) they want to achieve. Pay close attention to keywords and the overall goal.
                        *   Identify if the request requires a single agent or a sequence of actions from multiple agents. For example, "Analyze John Doe's profile and then create a positive post about his recent event attendance" would require two agents in sequence.

                    *   **Task Planning & Sequencing (for Multi-Step Requests):**
                        *   Before delegating, outline the clear sequence of agent tasks.
                        *   Identify dependencies. If Task B requires output from Task A, execute them sequentially. If tasks are independent (like creating a post and then creating an event), execute them one after the other as separate delegations.
                        *   Agent Reusability: An agent's completion of one task does not make it unavailable. If a user's plan involves multiple, distinct actions that fall under the same agent's expertise (e.g., create a post, then create an event), you must call that same agent again for the subsequent task.

                    *   **Task Delegation & Management (using `send_message`):**
                        *   **Delegation:** Use `send_message` to assign actionable tasks to the selected remote agent. Your `send_message` call MUST include:
                            *   The `remote_agent_name` you've selected.
                            *   The `user_request` or all necessary parameters extracted from the user's input, formatted in a way the target agent will understand.
                        *   **Contextual Awareness for Remote Agents:** If a remote agent repeatedly requests user confirmation or seems to lack context, assume it lacks access to the full conversation history. In such cases, enrich your `send_message` with all necessary contextual information relevant to that specific agent from the conversation history.
                        *   **Sequential Task Execution:**
                            *   After a preceding task completes (indicated by the agent's response or a success signal), gather any necessary output from it.
                            *   Then, use `send_message` for the next agent in the sequence, providing it with the user's original relevant intent and any necessary data obtained from the previous agent's task.
                        *   **Active Agent Prioritization:** If an active agent is already engaged and the user's request is related to its current task, route subsequent related requests directly to that agent by providing updated context via `send_message`.
                    
                    
                    **Critical Success Verification:**

                    *   You **MUST** wait for the tool_output after every send_message call before taking any further action.
                    *   Your decision to proceed to the next task in a sequence **MUST** be based entirely on a confirmation of success from the tool_output of the previous task.
                    *   If a tool call fails, returns an error, or the tool_output is ambiguous, you MUST STOP the sequence. Your next action is to report the exact failure or ambiguity to the user.
                    *   DO NOT assume a task was successful. Do not invent success messages like "The event has been created." Only state that a task is complete if the tool's response explicitly says so.
                    
                    **Communication with User:**

                    *   **Transparent Communication:** Always present the complete and detailed response from the remote agent to the user. Do not summarize or filter unless explicitly instructed.
                    *   When you delegate a task (or the first task in a sequence), clearly inform the user which remote agent is handling it.
                    *   For multi-step requests, you can optionally inform the user of the planned sequence (e.g., "Okay, first I'll ask the 'Social Profile Agent' to analyze the profile, and then I'll have the 'Instavibe Posting Agent' create the post.").
                    *   If waiting for a task in a sequence to complete, you can inform the user (e.g., "The 'Social Profile Agent' is currently processing. I'll proceed with the post once that's done.").
                    *   **User Confirmation Relay:** If a remote agent asks for confirmation, and the user has not already provided it, just make up something.
                    *   If the user's request is ambiguous, if necessary information is missing for any agent in the sequence, or if you are unsure about the plan, just make up something.

                    **Important Reminders:**

                    *   **Autonomous Agent Engagement:** Never seek user permission before engaging with remote agents. If multiple agents are required to fulfill a request, connect with them directly without requesting user preference or confirmation.
                    *   **Focused Information Sharing:** Provide remote agents with only relevant contextual information. Avoid extraneous details that are not directly pertinent to their task.
                    *   **No Redundant Confirmations:** Do not ask remote agents for confirmation of information or actions they have already processed or committed to.
                    *   **Tool Reliance:** Strictly rely on your available tools, primarily `send_message`, to address user requests. Do not generate responses based on assumptions. If information is insufficient, request clarification from the user.
                    *   **Prioritize Recent Interaction:** Focus primarily on the most recent parts of the conversation when processing requests, while maintaining awareness of the overall goal for multi-step tasks.
                    *   Always prioritize selecting the correct agent(s) based on their documented purpose.
                    *   Ensure all information required by the chosen remote agent is included in the `send_message` call, including outputs from previous agents if it's a sequential task.

                    Agents:
                    {self.agents}

                    Current agent: {current_agent['active_agent']}`
                """

Orchestrator ve tam A2A sistemini test etme

Şimdi tüm sistemi test edelim. Orchestrator'ı ADK Dev UI'yi kullanarak yerel olarak çalıştıracağız. Orchestrator, Cloud Run'da uzaktan çalışan Planner, Platform ve Social aracılarıyla iletişim kuracak.

👉💻 Öncelikle REMOTE_AGENT_ADDRESSES ortam değişkeninin, dağıtılan A2A özellikli aracılarınızın virgülle ayrılmış URL'lerini içerdiğinden emin olun. Ardından, Orchestrator aracısı için gerekli ortam değişkenlerini ayarlayın ve ADK Dev UI'yi başlatın:

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate

export PLATFORM_MPC_CLIENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep platform-mcp-client)
export PLANNER_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner-agent)
export SOCIAL_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep social-agent)

export REMOTE_AGENT_ADDRESSES=${PLANNER_AGENT_URL},${PLATFORM_MPC_CLIENT_URL},${SOCIAL_AGENT_URL}

cd  ~/instavibe-bootstrap/agents
sed -i "s|^\(O\?REMOTE_AGENT_ADDRESSES\)=.*|REMOTE_AGENT_ADDRESSES=${REMOTE_AGENT_ADDRESSES}|" ~/instavibe-bootstrap/agents/orchestrate/.env
adk web

👉 ADK Dev kullanıcı arayüzünü açın (Web önizlemesi aracılığıyla bağlantı noktasını tekrar 8000 olarak değiştirin).

10-08-web-preview.png

👉 Aracı açılır listesinde orchestrate aracısını seçin.

👉 Şimdi, birden fazla uzak aracının koordine edilmesini gerektiren karmaşık bir görev verin. Önce Sosyal Ajan'ın, ardından Planlayıcı Ajan'ın dahil olması gereken şu ilk örneği deneyin:

You are an expert event planner for a user named  Diana.
    Your task is to design a fun and personalized event.

    Here are the details for the plan:
    - Friends to invite: Ian, Nora
    - Desired date: "2025-10-15"
    - Location idea or general preference: "Chicago"

    Your process should be:
    1. Analyze the provided friend names. If you have access to a tool to get their InstaVibe profiles or summarized interests, please use it.
    2. Based on their potential interests (or general good taste if profiles are unavailable), create a tailored plan for the outing, check if you have access to any event planner tools.
    3. Ensure the plan includes the original `planned_date`.

    The user wants a comprehensive plan that includes:
    - The list of invited friends.
    - A catchy and descriptive name for the event.
    - The exact planned date for the event.
    - A summary of what the group will do.
    - Specific recommended spots (e.g., restaurants, bars, activity venues) with their names, (if possible, approximate latitude/longitude for mapping, and address), and a brief description of why it fits the plan.
    - A short, exciting message that {Diana} can send to {Ian, Nora} to get them excited about the event.

Düzenleme

ADK Dev UI sohbet penceresindeki etkileşimi gözlemleyin. Orchestrator'ın yanıtlarına dikkat edin.Orchestrator, görevleri hangi uzak aracıya devrettiğini belirtmelidir (ör. "Tamam, önce Ian ve Nora hakkında Sosyal Profil Aracısı'na soracağım...").

Ayrıca, uzak aracıların URL'lerine yapılan temel araç çağrılarını (send_message) görmek için kullanıcı arayüzündeki Etkinlikler sekmesini kontrol edin.

Görevi Gönderme

👉 Şimdi, Platform Entegrasyon Aracısı'nın doğrudan dahil olması gereken ikinci bir örneği deneyin:

Hey, can you register an event on Instavibe for Laura and Charlie? Let's call it 'Vienna Concert & Castles Day'.
here are more info
"event_name": "Vienna Concert & Castles Day",
  "description": "A refined and unforgettable day in Vienna with Laura and Charlie. The day begins with a guided tour of the magnificent Schönbrunn Palace, showcasing imperial architecture and history. In the evening, enjoy a classical music concert in one of Vienna's most iconic concert halls.",
  "event_date": "2025-10-14T10:00:00+02:00",
  "locations": [
    {
      "name": "Schönbrunn Palace",
      "description": "A UNESCO World Heritage Site and former imperial summer residence, Schönbrunn Palace offers opulent rooms, beautiful baroque gardens, and a glimpse into the life of the Habsburg monarchy. Visitors can stroll the grounds or take a guided historical tour.",
      "latitude": 48.184516,
      "longitude": 16.312222,
      "address": "Schönbrunner Schloßstraße 47, 1130 Wien, Austria"
    },
    {
      "name": "Musikverein Vienna",
      "description": "Home to the world-renowned Vienna Philharmonic, the Musikverein is one of the finest concert halls in the world. Its 'Golden Hall' is famous for its acoustics and ornate design. Attendees can enjoy a powerful classical concert in an unforgettable setting.",
      "latitude": 48.200132,
      "longitude": 16.373777,
      "address": "Musikvereinsplatz 1, 1010 Wien, Austria"
    }
  ],
  "attendee_names": ["Laura", "Charlie", "Oscar"] And I am Oscar

Sohbeti ve Etkinlikler sekmesini tekrar izleyin. Orkestratör, etkinlik oluşturma ihtiyacını belirlemeli ve görevi (sağlanan tüm ayrıntılarla birlikte) "Platform Entegrasyon Aracısı"na devretmelidir. Sorgu yanıt sürelerini ve yürütülen işlemleri analiz etmek için izlemeleri görüntülemek üzere İzleme düğmesini de tıklayabilirsiniz. Etkinlik Gönder

Ardından, etkinliğin InstaVibe web uygulamasında göründüğünü doğrulayabilirsiniz. InstaVibe etkinliği

Bu, ADK ve A2A protokolü kullanılarak çoklu aracı sisteminin başarılı bir şekilde uygulandığını gösterir. Merkezi bir düzenleyici, görevleri uzmanlaşmış uzak aracılara devreder.

Testi tamamladığınızda ADK Dev UI'yi (terminalde Ctrl+C) durdurmayı unutmayın.

12. InstaVibe'dan Agent Engine ve Uzak Arama

Şimdiye kadar uzmanlaşmış temsilcilerimizi Cloud Run'da çalıştırdık ve ADK Geliştirici Kullanıcı Arayüzü'nü kullanarak Orchestrator'ı yerel olarak test ettik. Üretim senaryosunda, aracıları barındırmak için sağlam, ölçeklenebilir ve yönetilen bir ortama ihtiyacımız var. Google Vertex AI Agent Engine bu noktada devreye girer.

Agent Engine, Vertex AI'da özel olarak yapay zeka aracılarını dağıtmak ve ölçeklendirmek için tasarlanmış, tümüyle yönetilen bir hizmettir. Altyapı yönetimi, güvenlik ve operasyonel yükü soyutlayarak geliştiricilerin (özellikle karmaşık bulut ortamlarına daha az aşina olanlar) sunucuları yönetmek yerine aracının mantığına ve özelliklerine odaklanmasına olanak tanır. Bu hizmet, yapay zeka aracılı iş yükleri için optimize edilmiş özel bir çalışma zamanı sağlar.

Şimdi Orchestrator aracımızı Agent Engine'e dağıtacağız. (Not: Aşağıda gösterilen dağıtım mekanizmasında, atölye materyallerinde sağlanan özel bir komut dosyası (agent_engine_app.py) kullanılmaktadır. Bunun nedeni, resmi doğrudan ADK-to-Agent-Engine dağıtım araçlarının hâlâ geliştirilme aşamasında olabilmesidir. Bu komut dosyası, gerekli uzak aracı adresleriyle yapılandırılmış Orchestrator aracısının paketlenmesini ve dağıtılmasını sağlar.

Orchestrator aracısını Agent Engine'e dağıtmak için aşağıdaki komutu yürütün. Cloud Run'daki Planner, Platform ve Social aracılarının URL'lerini içeren REMOTE_AGENT_ADDRESSES ortam değişkeninin önceki bölümde doğru şekilde ayarlandığından emin olun.

👉💻 Orchestrate aracısını Agent Engine'e dağıtacağız (Not: Bu, dağıtımın kendi uygulamamdır. ADK'da dağıtıma yardımcı olacak bir CLI vardır. BYO-SA uygulandıktan sonra bunu güncelleyeceğim).

cd ~/instavibe-bootstrap/agents/
. ~/instavibe-bootstrap/set_env.sh

gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member="serviceAccount:service-$PROJECT_NUMBER@gcp-sa-aiplatform-re.iam.gserviceaccount.com" \
    --role="roles/viewer"


source ~/instavibe-bootstrap/env/bin/activate
export PLATFORM_MPC_CLIENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep platform-mcp-client)
export PLANNER_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner-agent)
export SOCIAL_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep social-agent)

export REMOTE_AGENT_ADDRESSES=${PLANNER_AGENT_URL},${PLATFORM_MPC_CLIENT_URL},${SOCIAL_AGENT_URL}
sed -i "s|^\(O\?REMOTE_AGENT_ADDRESSES\)=.*|REMOTE_AGENT_ADDRESSES=${REMOTE_AGENT_ADDRESSES}|" ~/instavibe-bootstrap/agents/orchestrate/.env

adk deploy agent_engine \
--display_name "orchestrate-agent" \
--project $GOOGLE_CLOUD_PROJECT \
--region $GOOGLE_CLOUD_LOCATION \
--staging_bucket gs://$GOOGLE_CLOUD_PROJECT-agent-engine \
--trace_to_cloud \
--requirements_file orchestrate/requirements.txt \
orchestrate

Orkestratör, yönetilen Agent Engine platformunda barındırıldığı için InstaVibe web uygulamamızın orkestratörle iletişim kurması gerekiyor. Web uygulaması, ADK Dev UI üzerinden etkileşim kurmak yerine Agent Engine uç noktasına uzaktan çağrı yapar.

10-agent-remote.png

Öncelikle, dağıtılan Orchestrator aracımızın benzersiz kimliğini kullanarak Agent Engine istemcisini başlatmak için InstaVibe uygulama kodunu değiştirmemiz gerekir. Bu kimlik, platformda doğru aracı örneğini hedeflemek için gereklidir.

👉📝 ~/instavibe-bootstrap/instavibe/introvertally.py bağlantısını açın ve #REPLACE ME initiate agent_engine yerine aşağıdaki kodu girin. Bu kod, bir ortam değişkeninden (kısa süre içinde ayarlayacağız) Agent Engine kimliğini alır ve bir istemci nesnesi edinir:

ORCHESTRATE_AGENT_ID = os.environ.get('ORCHESTRATE_AGENT_ID')
agent_engine = agent_engines.get(ORCHESTRATE_AGENT_ID)

InstaVibe'daki planlanan kullanıcı akışımız, temsilciyle iki etkileşim içerir: Birincisi, önerilen planı oluşturma; ikincisi ise temsilci etkinliği platformda yayınlamadan önce kullanıcıdan onay isteme.

InstaVibe web uygulaması (Cloud Run'da çalışır) ve Orchestrator aracısı (Agent Engine'de çalışır) artık ayrı hizmetler olduğundan web uygulamasının, aracıyla etkileşim kurmak için Agent Engine uç noktasına uzaktan çağrı yapması gerekir.

👉📝 Plan önerisi oluşturmak için ilk çağrıyı yapan kodu güncelleyelim. Aynı introvertally.py dosyasında, #REPLACE ME Query remote agent get plan öğesini, kullanıcının isteğini göndermek için agent_engine istemcisini kullanan aşağıdaki snippet ile değiştirin:

agent_engine.stream_query(
                user_id=user_id,
                message=prompt_message,
            )

👉📝 Ardından, kullanıcının onayını işleyen kodu güncelleyin (ör. kullanıcı "Planı Onayla"yı tıkladığında). Bu işlem, Agent Engine'deki aynı görüşmeye bir takip mesajı göndererek Orchestrator'a etkinliği yayınlamaya devam etmesini (Platform Integration temsilcisine devredeceği) bildirir. introvertally.py içindeki onay için #REPLACE ME Query remote agent for confirmation yerine aşağıdakileri girin:

agent_engine.stream_query(
            user_id=agent_session_user_id,
            message=prompt_message,
        )

Web uygulamasının rotalarının bu işlevlere erişmesi gerekir. introvertally.py dosyasındaki gerekli işlevlerin Flask rotaları dosyasına aktarıldığından emin olun.

👉📝 cd ~/instavibe-bootstrap/instavibe/ally_routes.py bölümünde, önce # REPLACE ME TO ADD IMPORT yerine aşağıdaki örnekleri kullanacağız:

from introvertally import call_agent_for_plan, post_plan_event

👉📝 InstaVibe'a prototip özelliğini ekleyin. ~/instavibe-bootstrap/instavibe/templates/base.html bölümünde <!–REPLACE_ME_LINK_TO_INTROVERT_ALLY–> yerine aşağıdakileri ekleyin:

            <li class="nav-item">
              <a class="nav-link" href="{{ url_for('ally.introvert_ally_page') }}">Introvert Ally</a>
            </li>

InstaVibe uygulamasını yeniden dağıtabilmemiz için Agent Engine'e dağıttığımız Orchestrator aracısının Resource ID gerekir.

Şu anda bu bilgiyi gcloud üzerinden programatik olarak almak sınırlı olabilir. Bu nedenle, kimliği getirmek ve bir ortam değişkeninde saklamak için yardımcı bir Python komut dosyası (temp-endpoint.py atölye çalışmasında sağlanır) kullanacağız.

👉💻 Komut dosyasını yürütmek için aşağıdaki komutları çalıştırın. Komut dosyası, Agent Engine Endpoint ID'yi yakalar ve aracı motorunun varsayılan hizmet hesabına gerekli izinleri verir (Not: Komut dosyası, şu anda kullanıcı tarafından değiştirilemediği için varsayılan hizmet hesabını kullanacak şekilde yapılandırılmıştır).

. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap/instavibe/
source ~/instavibe-bootstrap/env/bin/activate
python temp-endpoint.py
export ORCHESTRATE_AGENT_ID=$(cat temp_endpoint.txt)
echo "ORCHESTRATE_AGENT_ID set to: ${ORCHESTRATE_AGENT_ID}"

Agent Engine Endpoint ID

Son olarak, InstaVibe web uygulamasını güncellenmiş kod ve yeni ORCHESTRATE_AGENT_ID ortam değişkeniyle yeniden dağıtmamız gerekiyor. Böylece uygulama, Agent Engine'de çalışan aracımıza nasıl bağlanacağını bilecek.

👉💻 Aşağıdaki komutlar, InstaVibe uygulama görüntüsünü yeniden oluşturur ve yeni sürümü Cloud Run'a dağıtır:

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/instavibe/

export IMAGE_TAG="latest"
export APP_FOLDER_NAME="instavibe"
export IMAGE_NAME="instavibe-webapp"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="instavibe"

echo "Building ${APP_FOLDER_NAME} webapp image..."
gcloud builds submit . \
  --tag=${IMAGE_PATH} \
  --project=${PROJECT_ID}

echo "Deploying ${SERVICE_NAME} to Cloud Run..."

gcloud run deploy ${SERVICE_NAME} \
  --image=${IMAGE_PATH} \
  --platform=managed \
  --region=${REGION} \
  --allow-unauthenticated \
  --set-env-vars="SPANNER_INSTANCE_ID=${SPANNER_INSTANCE_ID}" \
  --set-env-vars="SPANNER_DATABASE_ID=${SPANNER_DATABASE_ID}" \
  --set-env-vars="APP_HOST=0.0.0.0" \
  --set-env-vars="APP_PORT=8080" \
  --set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
  --set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
  --set-env-vars="GOOGLE_MAPS_API_KEY=${GOOGLE_MAPS_API_KEY}" \
  --set-env-vars="ORCHESTRATE_AGENT_ID=${ORCHESTRATE_AGENT_ID}" \
  --project=${PROJECT_ID} \
  --min-instances=1 \
  --cpu=2 \
  --memory=2Gi

Son dağıtım tamamlandıktan sonra farklı bir tarayıcı sekmesinde InstaVibe uygulama URL'nize gidin.

Tam Yapay Zeka Destekli InstaVibe Deneyimini Test Etme

Vertex AI Agent Engine aracılığıyla düzenlenmiş ve A2A üzerinden iletişim kuran çoklu aracı sistemimiz tarafından desteklenen "InstaVibe Ally" özelliği kullanıma sunuldu.

12-02-new.png

"InstaVibe Ally"yi tıklayın ve etkinlik planlamasını isteyin.

12-03-introvertally.png

Temsilciler çalışırken sağdaki etkinlik günlüğünü inceleyin (90-120 saniye sürebilir). Plan göründüğünde inceleyin ve yayınlama işlemine devam etmek için "Bu Planı Onayla"yı tıklayın.

12-04-confirm.png

Düzenleyici artık Platform aracısına InstaVibe'da gönderi ve etkinlik oluşturmasını söyleyecek. 12-05-posting.png

Yeni gönderi ve etkinlik için InstaVibe ana sayfasını kontrol edin. 12-06-instavibe.png

Etkinlik sayfasında, temsilci tarafından oluşturulan ayrıntılar gösterilir.

12-07-event.png

Cloud Trace ile Performansı Analiz Etme

Bu işlemin biraz zaman aldığını fark edebilirsiniz. Vertex AI Agent Engine, Cloud Trace ile entegre olarak çoklu aracı sistemimizin gecikmesini analiz etmemize olanak tanır.

Google Cloud Console'da İzler'e gidin, Kapsam'da agent_run[orchestrate_agent] simgesini seçin. Birkaç Kapsam görmeniz gerekir. Kapsam'ı tıklayın.

12-08-trace.png

İzleme ayrıntılarında hangi bölümlerin daha uzun sürdüğünü belirleyebilirsiniz. Örneğin, Planlayıcı aracısına yapılan çağrılarda, arama temellendirmesi ve karmaşık oluşturma nedeniyle daha yüksek gecikme süresi görülebilir. 12-09-plan.png

Benzer şekilde, gönderi ve etkinlik oluştururken Orchestrator'ın verileri işlemek ve Platform temsilcisi için araç çağrıları hazırlamak üzere harcadığı süreyi görebilirsiniz. 12-10-post.png

Bu izleri incelemek, aracı sisteminizin performansını anlamanıza ve optimize etmenize yardımcı olur.

celebrate.png

Tebrikler! Google'ın ADK, A2A, MCP ve Google Cloud hizmetlerini kullanarak gelişmiş bir çoklu aracı yapay zeka sistemi oluşturmayı, dağıtmayı ve test etmeyi başardınız. Aracı düzenleme, araç kullanımı, durum yönetimi ve bulut dağıtımı konularında başarılı bir şekilde ilerleyerek InstaVibe için işlevsel bir yapay zeka destekli özellik oluşturdunuz. Atölyeyi tamamladığınız için tebrik ederiz.

13. Temizleme

Google Cloud hesabınızın sürekli olarak ücretlendirilmesini önlemek için bu atölye çalışması sırasında oluşturduğumuz kaynakları silmeniz önemlidir. Aşağıdaki komutlar, Spanner örneğini, Cloud Run hizmetlerini, Artifact Registry deposunu, API anahtarını, Vertex AI Agent Engine'i ve ilişkili IAM izinlerini kaldırmanıza yardımcı olur.

Önemli:

  • Bu komutları, çalıştay için kullanılan Google Cloud projesinde çalıştırdığınızdan emin olun.
  • Cloud Shell terminalinizi kapattıysanız $PROJECT_ID, $SPANNER_INSTANCE_ID gibi bazı ortam değişkenleri ayarlanmamış olabilir. Bu öğeleri, atölye kurulumu sırasında yaptığınız gibi yeniden dışa aktarmanız veya aşağıdaki komutlardaki değişkenleri gerçek değerleriyle değiştirmeniz gerekir.
  • Bu komutlar, kaynaklarınızı kalıcı olarak siler. Bu projede başka önemli verileriniz varsa çalıştırmadan önce tekrar kontrol edin.

👉💻 Temizleme işlemi için aşağıdaki komut dosyalarını çalıştırın.

Ortam değişkenlerini sıfırlama

. ~/instavibe-bootstrap/set_env.sh

Temsilci Motorunu Silme:

cd ~/instavibe-bootstrap/utils
source ~/instavibe-bootstrap/env/bin/activate
export ORCHESTRATE_AGENT_ID=$(cat ~/instavibe-bootstrap/instavibe/temp_endpoint.txt)
echo "ORCHESTRATE_AGENT_ID set to: ${ORCHESTRATE_AGENT_ID}"
python remote_delete.py
deactivate
echo "Vertex AI Agent Engine deletion initiated."

Cloud Run hizmetlerini silme:

# InstaVibe Web Application
gcloud run services delete instavibe --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet

# MCP Tool Server
gcloud run services delete mcp-tool-server --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet

# Planner Agent (A2A Server)
gcloud run services delete planner-agent --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet

# Platform MCP Client Agent (A2A Server)
gcloud run services delete platform-mcp-client --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet

# Social Agent (A2A Server)
gcloud run services delete social-agent --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet

echo "Cloud Run services deletion initiated."

A2A Inspector Docker Kapsayıcısını Durdurma ve Kaldırma

docker rm --force a2a-inspector

Spanner örneğini silme:

echo "Deleting Spanner instance: ${SPANNER_INSTANCE_ID}..."
gcloud spanner instances delete ${SPANNER_INSTANCE_ID} --project=${PROJECT_ID} --quiet
echo "Spanner instance deletion initiated."

Artifact Registry deposunu silme:

echo "Deleting Artifact Registry repository: ${REPO_NAME}..."
gcloud artifacts repositories delete ${REPO_NAME} --location=${REGION} --project=${PROJECT_ID} --quiet
echo "Artifact Registry repository deletion initiated."

Hizmet hesabından rolleri kaldırma:

echo "Removing roles from service account: $SERVICE_ACCOUNT_NAME in project $PROJECT_ID"

# Remove Project-level roles for default service account
gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/spanner.admin"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/spanner.databaseUser"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/artifactregistry.admin"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/cloudbuild.builds.editor"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/run.admin"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/iam.serviceAccountUser"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/aiplatform.user"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/logging.logWriter"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/logging.viewer"


echo "All specified roles have been removed."

Yerel Atölye Dosyalarını Silme:

echo "Removing local workshop directory ~/instavibe-bootstrap..."
rm -rf ~/instavibe-bootstrap
rm -rf ~/a2a-inspector
rm -f ~/mapkey.txt
rm -f ~/project_id.txt
echo "Local directory removed."

Temizleme