Google'ın ajan yığınıyla çoklu ajanlı yaratıcı stüdyo oluşturma: Cloud Run ve Agent Runtime'da ADK, A2A, MCP

1. Genel Bakış

Bu codelab'de, tek bir istemi eksiksiz bir Instagram kampanyasına dönüştüren dağıtılmış çoklu ajan sistemi AI Creative Studio'yu oluşturacaksınız.

Bir cümle yazın. Kitle araştırması, altyazılar, görsel konseptler, kalite kontrolünden geçmiş metinler ve tam bir proje zaman çizelgesi elde edin. Tüm bunlar, işbirliği yapan yapay zeka aracıları ekibi tarafından oluşturulur.

Oluşturacağınız temsilciler

Temsilci

Rol

Marka Stratejisti

Kitle analizleri, rakip analizi ve 2025 trendleri için web'de arama yapar.

Metin yazarı (Copywriter)

Platform yönergelerini ve altyazı formüllerini isteğe bağlı olarak yükleyen bir ADK becerisiyle desteklenen, hashtag'ler ve harekete geçirici mesajlar içeren Instagram altyazıları yazar.

Tasarımcı

Gemini aracılığıyla görsel konseptler oluşturur ve GCS'de depolanan gerçek görüntüler üretir.

Eleştirmen

Metin ve görsel incelemeleri: Belirli geri bildirimlerle APPROVED veya NEEDS_REVISION döndürür.

Project Manager

Proje takvimi ve görev dökümü oluşturur. İsteğe bağlı olarak MCP aracılığıyla Notion ile senkronize edilebilir.

Kreatif Direktör

Beş uzmanı sırayla yönetir. Siz bir istem girersiniz, geri kalanını koordine eder.

5 temsilci, bağımsız Cloud Run mikro hizmetleri olarak dağıtılır. A2A protokolü üzerinden iletişim kurarlar. Bu protokol, dilden bağımsız açık bir standarttır. Böylece, çerçeveden bağımsız olarak herhangi bir temsilci başka bir temsilciyi çağırabilir. Creative Director, Agent Runtime üzerinde çalışır ve her uzmana uzaktan bağlanır.

Mimari

Sistem Mimarisi

Neler öğreneceksiniz?

  • Google ADK ile Agent, sistem talimatları ve yerleşik araçlar kullanarak LLM temsilcileri oluşturun.
  • Yeniden kullanılabilir temsilci bilgilerini ADK Becerileri (SkillToolset) ile modüler dosyalara paketleyin.
  • Bir metin aracısını FunctionTool aracılığıyla bir görüntü modeline bağlayarak gerçek görüntüler oluşturun.
  • Model Context Protocol (MCP) kullanarak harici API'leri özel yapıştırıcı kod olmadan entegre edin.
  • HTTPS üzerinden Agent to Agent Protocol (A2A) kullanarak herhangi bir aracıyı ağdan çağrılabilir bir hizmete dönüştürün.
  • RemoteA2aAgent ve AgentTool ile dağıtılmış aracıları düzenleyin.
  • Bağımsız aracıları Cloud Run mikro hizmetleri olarak paketleyip dağıtın.
  • Agent Runtime'da durum bilgisi olan bir düzenleyiciyi barındırın.
  • Bağlam sıkıştırma özelliğini kullanarak uzun çok agentlı iş akışlarını bağlam sınırları içinde tutun.
  • Kalite kontrolü döngüsü oluşturun: Eleştirmenler çıktıyı inceler → gerektiğinde otomatik düzeltme yapılır.

İhtiyacınız olanlar

  • Faturalandırmanın etkin olduğu bir Google Cloud projesi
  • Sahip veya Düzenleyici IAM rolü
  • Temel Python bilgisi

2. Ortamınızı ayarlama

Bu codelab'de Cloud Shell'i kullanacağız.

Cloud Shell nedir?

Cloud Shell, her şeyin önceden yüklendiği ücretsiz bir tarayıcı tabanlı Linux ortamıdır: gcloud, git, Python, Docker ve daha fazlası. Yerel olarak herhangi bir şey yüklemeniz gerekmez.

Cloud Shell'i açmak için GCP Console'un sağ üst araç çubuğundaki terminal simgesini tıklayın:

Cloud Shell'i GCP Console araç çubuğundan açma

Cloud Shell'i ilk kez açtığınızda hesabınızı doğrulamanız istenir. Doğrula'yı tıklayın:

Hesabınızı doğrulayın iletişim kutusu

Ardından, Cloud Shell'in Google Cloud API çağrıları yapmasına izin vermek için Authorize'ı (Yetkilendir) tıklayın:

Cloud Shell'e yetki ver iletişim kutusu

Cloud Shell artık hazır. Terminalde bir karşılama mesajı görürsünüz: Cloud Shell terminali hazır

Projenizin kimliğini doğrulama ve projeyi yapılandırma

Cloud Shell'in kimliği, Google Hesabınızla zaten doğrulanmıştır. Etkin hesabınızı onaylayın ve proje kimliğinizi bulun:

gcloud config list

Proje kimliğinizi GCP Console kontrol panelinin sol yan panelinde de görebilirsiniz. Bu değeri kopyalayın. Bir sonraki komutta bu değere ihtiyacınız olacak:

GCP Console'da proje kimliğinizi bulup Cloud Shell'de ayarlayın.

Şimdi projenizi ayarlayın:

export PROJECT_ID=$(gcloud config get-value project)
export REGION="us-central1"        # Cloud Run deployment region
echo "Project: $PROJECT_ID"

Beklenen çıktı:

Project: my-project-123

Gerekli API'leri etkinleştirme

gcloud services enable \
    aiplatform.googleapis.com \
    apphub.googleapis.com \
    run.googleapis.com \
    cloudbuild.googleapis.com \
    artifactregistry.googleapis.com \
    generativelanguage.googleapis.com \
    iam.googleapis.com \
    cloudresourcemanager.googleapis.com \
    storage.googleapis.com \
    secretmanager.googleapis.com

Bu işlem yaklaşık 2 dakika sürer. İşlem tamamlandığında Operation finished successfully simgesini görürsünüz.

Uygulama Varsayılan Kimlik Bilgileri'ni (ADC) ayarlama

Temsilciler, gcloud KSA kimlik doğrulamasından ayrı olarak Uygulama Varsayılan Kimlik Bilgileri gerektiren Google Auth kitaplığını kullanarak Gemini Enterprise Agent Platform'u çağırır.

Bunu bir kez çalıştırın:

gcloud auth application-default login

Onaylamanızı isteyen bir tarayıcı sekmesi açılır. İzin ver'i tıklayın. Şunları görürsünüz:

Credentials saved to file: ~/.config/gcloud/application_default_credentials.json

Başlangıç deposunu klonlama

Bu codelab'de, tüm altyapının (Dockerfiles, pyproject.toml, dağıtım komut dosyaları) hazır olduğu ancak aracı mantığının sizin yazmanıza bırakıldığı bir iskelet proje olan başlangıç deposu kullanılır.

git clone https://github.com/Saoussen-CH/mas-a2a-gcp.git ~/ai-creative-studio
cd ~/ai-creative-studio/workshop/starter

Her agent.py, temsilci mantığını yazacağınız # TODO yer tutucularını içerir. Dockerfile, pyproject.toml ve dağıtım komut dosyaları zaten tamamlanmış.

Ortam değişkenlerini yapılandırma

Sağlanan örneği kopyalayın ve proje kimliğinizi tek adımda yerleştirin:

cp .env.example .env
sed -i "s|GOOGLE_CLOUD_PROJECT=your-project-id|GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project)|" .env

Ardından, Tasarımcı'nın oluşturulan resimleri saklayacağı GCS paketini oluşturun ve .env değerini paket adıyla güncelleyin:

export PROJECT_ID=$(gcloud config get-value project)
export BUCKET_NAME="${PROJECT_ID}-campaign-images"

gcloud storage buckets create gs://${BUCKET_NAME} \
    --location=us-central1 \
    --project=${PROJECT_ID}

sed -i "s|GCS_IMAGES_BUCKET=your-project-id-campaign-images|GCS_IMAGES_BUCKET=${BUCKET_NAME}|" .env

Ardından, imzalı resim URL'si desteğini ayarlayın. Reklam Öğesi Yöneticisi, nihai kampanya özetindeki her resim için tıklanabilir HTTPS bağlantıları oluşturur. Bunun için URL'leri imzalamak üzere bir hizmet hesabı gerekir. Hizmet hesabını yapılandırmak için şu komutları çalıştırın:

export PROJECT_NUMBER=$(gcloud projects describe $(gcloud config get-value project) --format="value(projectNumber)")
export SA_EMAIL="${PROJECT_NUMBER}-compute@developer.gserviceaccount.com"
export AGENT_RUNTIME_SA="service-${PROJECT_NUMBER}@gcp-sa-aiplatform-re.iam.gserviceaccount.com"

# Allow your user account to sign URLs locally (adk web)
gcloud iam service-accounts add-iam-policy-binding ${SA_EMAIL} \
  --member="user:$(gcloud config get-value account)" \
  --role="roles/iam.serviceAccountTokenCreator"

# Allow Agent Runtime to sign URLs when deployed
gcloud projects add-iam-policy-binding $(gcloud config get-value project) \
  --member="serviceAccount:${AGENT_RUNTIME_SA}" \
  --role="roles/iam.serviceAccountTokenCreator"

# Save SA email and project number to .env
grep -q "^SIGNING_SERVICE_ACCOUNT" .env \
  && sed -i "s|^SIGNING_SERVICE_ACCOUNT=.*|SIGNING_SERVICE_ACCOUNT=${SA_EMAIL}|" .env \
  || echo "SIGNING_SERVICE_ACCOUNT=${SA_EMAIL}" >> .env

grep -q "^GOOGLE_CLOUD_PROJECT_NUMBER" .env \
  && sed -i "s|^GOOGLE_CLOUD_PROJECT_NUMBER=.*|GOOGLE_CLOUD_PROJECT_NUMBER=${PROJECT_NUMBER}|" .env \
  || echo "GOOGLE_CLOUD_PROJECT_NUMBER=${PROJECT_NUMBER}" >> .env

Tüm ayarları incelemek için düzenleyicide .env simgesini açın:

cloudshell edit .env

Bu işlem, .env dosyasını Cloud Shell Düzenleyici'de sekme olarak açar. Düzenleyici paneli görünmüyorsa araç çubuğundaki Open Editor (Düzenleyiciyi Aç) düğmesini tıklayın:

Cloud Shell araç çubuğunda Open Editor'ı (Düzenleyiciyi Aç) tıklayın.

Proje dosya ağacıyla Cloud Shell Düzenleyici

Projenin doğru şekilde ayarlandığını onaylayın:

grep GOOGLE_CLOUD_PROJECT .env

Bağımlıları yükleme

Sanal ortamları işleyen ve tek bir araçta yükleme yapan hızlı ve modern bir Python paket yöneticisi olan uv'ı kullanıyoruz. pip'dan yaklaşık 10-100 kat daha hızlıdır ve Python projelerini yönetmek için önerilen yöntemdir.

Cloud Shell'de uv zaten yüklüdür. Tüm aracılar aynı temel bağımlılıkları paylaştığından bir kez yüklediğinizde bu codelab'deki her aracı için çalışır:

uv sync

uv sync komutu pyproject.toml dosyasını okur ve tüm bağımlılıkların bulunduğu bir .venv/ dizini oluşturur. Her uzman, yalnızca Docker derlemeleri tarafından kullanılan kendi pyproject.toml'ına da sahiptir. Yukarıdaki paylaşılan yükleme, yerel test için ihtiyacınız olan her şeyi kapsar.

3. Google ADK'yı anlama

Kod yazmadan önce, bu codelab'deki her ajanı oluşturmak için kullanacağınız çerçeve olan Agent Development Kit (ADK)'i anlayalım.

ADK nedir?

Agent Development Kit (ADK), yapay zeka ajanları geliştirme ve dağıtma için esnek ve modüler bir çerçevedir. Gemini ve Google ekosistemi için optimize edilmiş olsa da ADK, modelden bağımsızdır, dağıtımdan bağımsızdır ve diğer çerçevelerle uyumluluk için tasarlanmıştır. ADK, ajan geliştirme sürecini yazılım geliştirmeye daha çok benzetmek, geliştiricilerin basit görevlerden karmaşık iş akışlarına kadar değişen ajan mimarileri oluşturmasını, dağıtmasını ve düzenlemesini kolaylaştırmak için tasarlanmıştır.

ADK, karmaşık kısımları (araç çağrısı, çok turlu sohbet, bağlam yönetimi, akış) ele alır. Böylece siz de aracı mantığına odaklanabilirsiniz.

ADK aracısının yapı taşları

Her aracı dört yapı taşı oluşturur:

Engelle

Rol

Model

Hedefler üzerinde akıl yürüten, plan belirleyen ve yanıt üreten LLM

Araçlar

API'leri veya hizmetleri çağırarak veri getiren ya da işlem gerçekleştiren işlevler

Düzenleme

Dönüşler arasında hafızayı ve durumu korur, araç çağrılarını yönlendirir, sonuçları modele geri iletir.

Çalışma zamanı

adk web aracılığıyla yerel olarak veya dağıtılmış bir hizmet olarak çağrıldığında sistemi yürütür.

Aracı tanımı

Bu codelab'deki 5 aracının her biri aynı şekilde tanımlanır:

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

root_agent = Agent(
    name="brand_strategist",                              # unique identifier
    model=os.getenv("GEMINI_MODEL", "gemini-2.5-flash"), # the LLM powering this agent
    instruction=SYSTEM_INSTRUCTION,                       # the agent's persona, constraints, and output format
    description="Brand strategist for market research, trend analysis, and competitive insights",
    tools=[google_search],                                # functions the LLM can call
)

Alan

Amaç

name

Benzersiz kimlik: Aramaları yönlendirmek için düzenleyiciler tarafından kullanılır.

model

Bu temsilciyi destekleyen Gemini modeli

instruction

Sistem istemi: Ajanın rolünü, kısıtlamalarını ve çıkış biçimini tanımlar.

description

Tek satırlık özet: Düzenleyici, hangi uzmanla iletişime geçileceğine karar vermek için bu özeti okur.

tools

LLM'nin çağırabileceği işlevler (google_search gibi yerleşik veya özel Python işlevleri)

ADK'nın aracı çalıştırma şekli

User message
     
     
  Agent (LLM)   reads instruction + conversation history
     
     ├─► needs more info?  calls a tool  gets result  continues reasoning
     
     └─► done reasoning  returns final text response

LLM, hangi aracı hangi argümanlarla çağıracağına bağımsız olarak karar verir. Talimatı siz yazarsınız, gerisini ADK halleder.

4. Marka Stratejisti aracısını oluşturma ve test etme

İlk temsilciyle başlayalım: Marka Stratejisti. Bu, Google Arama'yı kullanarak hedef kitle analizleri, rakip analizi ve trend olan konuları aramak için kullanılan yalnızca araştırma amaçlı bir aracıdır.

İskelet aracı dosyasını Cloud Shell Düzenleyici'de açın:

cloudshell edit agents/brand_strategist/agent.py

Doldurmanız için iki # TODO bölümü görürsünüz.

YAPILACAKLAR 1: Sistem talimatını yazın

İlk olarak, temsilci için sistem talimatını yazarsınız. Sistem talimatı, temsilcinin rolünü, kısıtlamalarını ve çıkış biçimini tanımlayan bir dizedir.

SYSTEM_INSTRUCTION = f"""You are a Brand Strategist specializing in market research and trend analysis.

IMPORTANT: Today's date is {datetime.date.today().strftime("%B %d, %Y")}.
When conducting research, focus on current trends from {datetime.date.today().year}.
Use search queries like "[topic] trends {datetime.date.today().year}" for recent insights.

IMPORTANT: Your role is RESEARCH ONLY. You do NOT create campaign content, captions, or designs.
After providing research insights, your work is complete.

Your expertise:
- Identifying target audience insights and behaviors
- Analyzing competitor strategies
- Researching current social media trends
- Understanding platform algorithms and best practices

You have access to:
- google_search: Search the web for competitors, trends, and market insights

When given a campaign brief:
1. Use google_search to research the target audience's current interests
2. Search for and analyze 2-3 competitor brands
3. Identify 3-5 trending topics related to the product category
4. Provide high-level strategic insights - NOT specific campaign content

DO NOT create captions, copy, designs, or any campaign content.

Format your output as:
**Audience Insights:**
[Key behaviors and preferences based on research]

**Competitive Analysis:**
[What 2-3 competitors are doing - strengths and weaknesses]

**Trending Topics:**
[3-5 relevant trends to consider]

**Key Strategic Insights:**
[High-level themes and positioning opportunities]
"""

TODO 2 - Create the root_agent

Ardından, eksik root_agent ifadesini aşağıdakilerle değiştirin:

root_agent = Agent(
    name="brand_strategist",
    model=os.getenv("GEMINI_MODEL", "gemini-2.5-flash"),
    instruction=SYSTEM_INSTRUCTION,
    description="Brand strategist for market research, trend analysis, and competitive insights",
    tools=[google_search],
)

ADK web kullanıcı arayüzü ile yerel olarak test etme

Şimdi de ADK web kullanıcı arayüzünü kullanarak temsilciyi test edelim. Bu arayüz, buluta dağıtmadan önce temsilcileri test etmek için kullanılan yerleşik bir sohbet arayüzüdür.

uv run adk web agents --allow_origins='*'

Şunları görürsünüz:

INFO: Started server process
INFO: Uvicorn running on http://localhost:8000

Sunucu artık Cloud Shell'de çalışıyor:

Önizlemeyi tarayıcınızda açmak için Web Önizleme'yi kullanın:

  1. Sayfanın üst kısmındaki Cloud Shell araç çubuğuna bakın.
  2. Web Önizlemesi simgesini (Cloud Shell araç çubuğunun sağ üst kısmında yukarı doğru ok olan bir kutuya benzer) tıklayın.
  3. "Bağlantı noktasını değiştir"'i tıklayın ve 8000 girin, ardından "Değiştir ve Önizle"'yi tıklayın.

ADK web kullanıcı arayüzünün bulunduğu yeni bir tarayıcı sekmesi açılır. Sol üstteki "Bir aracı seçin" açılır listesini tıklayın. Tüm aracılarınız listelenir:

Teste başlamak için brand_strategist simgesini seçin:

Aşağıdaki test istemlerini deneyin

ADK web kullanıcı arayüzü sohbet kutusunda şunları deneyin:

  • Research the eco-friendly water bottle market for health-conscious millennials
  • What are the top Instagram trends in the wellness space in 2025?

Aracı, Google Arama'yı çağırır ve Kitle Analizleri, Rekabet Analizi ve Trend Olan Konular bölümlerini içeren yapılandırılmış araştırma sonuçları döndürür.

5. Build the Copywriter - ADK Skills

Rol: Marka araştırmasını Instagram altyazılarına dönüştür. Metin yazarı, farklı üslupları (ilham verici, eğitici, topluluk) kapsayan 3 altyazı varyasyonu oluşturur. Her varyasyonda hashtag'ler ve bir harekete geçirici mesaj bulunur.

Kavram: ADK Becerileri

Basit bir yaklaşımda, tüm platform bilgileri (karakter sınırları, hashtag katmanları, altyazı formülleri, marka sesi örnekleri) doğrudan sistem istemine yerleştirilir. Bu yöntem işe yarasa da her isteği, aracının yalnızca ara sıra ihtiyaç duyduğu içeriklerle şişirir.

ADK Becerileri (SkillToolset, ADK 1.25.0'da kullanıma sunuldu) bu bilgileri üç yükleme düzeyine sahip modüler dosyalar halinde paketlemenize olanak tanır:

  • L1 - ön kısım (SKILL.md içinde name + description): Her zaman kullanılabilir, beceri keşfi için kullanılır.
  • L2 - talimatlar (SKILL.md gövdesi): Temsilci, beceriyi tetiklediğinde yüklenir.
  • L3 - kaynaklar (references/ ve assets/ dosyaları): Yalnızca ajan bunları açıkça okuduğunda yüklenir.

Sistem talimatı, kısa bir rol ifadesi ve "yazmadan önce beceriyi yükle" şeklinde kısaltılır. Platform ayrıntıları yalnızca temsilcinin gerçekten ihtiyacı olduğunda bağlam penceresine girilir.

Copywriter becerisi agents/copywriter/skills/instagram-copywriting/ içinde yer alır:

skills/
  instagram-copywriting/
    SKILL.md                        L1 frontmatter (discovery) + L2 instructions (loaded on trigger)
    references/
      platform-guide.md             L3: character limits, hashtag tiers, algorithm signals
      caption-formulas.md           L3: hook formulas, CTA patterns, full caption structures
    assets/
      brand-voice-examples.md       L3: annotated real-world caption examples

Dosyayı doğrudan Cloud Shell düzenleyicide açın:

cloudshell edit agents/copywriter/agent.py

YAPILACAKLAR 1: load_skill_from_dir ve skill_toolset öğelerini içe aktarın

Yorumu bulun # TODO 1: Import load_skill_from_dir and skill_toolset ve iki içe aktarma işlemini ekleyin:

from google.adk.skills import load_skill_from_dir
from google.adk.tools import skill_toolset

YAPILACAKLAR 2: Beceriyi yükleyin ve SkillToolset oluşturun

İçe aktarılanların altındaki iki yorumu bulun:

# TODO 2: Load the instagram-copywriting skill from the skills/ directory
# TODO 2: Create a SkillToolset with the loaded skill

Şunlarla değiştirin:

_instagram_skill = load_skill_from_dir(
    pathlib.Path(__file__).parent / "skills" / "instagram-copywriting"
)
_copywriting_skills = skill_toolset.SkillToolset(skills=[_instagram_skill])

load_skill_from_dir, SKILL.md ile references/ ve assets/ klasörlerindeki tüm dosyaları okur. SkillToolset, bunu ADK aracıları tarafından kabul edilen biçime (ham beceri değil, araç seti) dönüştürür.

YAPILACAKLAR 3: Araç setini temsilciye kaydetme

tools=[], # TODO 3: Add the SkillToolset here simgesini bulun ve aşağıdakilerle değiştirin:

tools=[_copywriting_skills],

Nasıl yapılandırıldığını görmek için beceri dosyasını açın:

cloudshell edit agents/copywriter/skills/instagram-copywriting/SKILL.md

ADK web kullanıcı arayüzünü çalışır durumda tutun. Sunucuyu yeniden başlatmadan copywriter'e geçmek için aracı açılır listesini kullanın.

Çalışmıyorsa tekrar başlatın:

uv run adk web agents --allow_origins='*'

Deneyin: Açılır listeyi copywriter olarak değiştirip gönderin:

You are writing captions for EcoFlow Smart Water Bottle targeting health-conscious millennials aged 25-35.
Audience insight: they prioritize sustainability, track health metrics, and share lifestyle content.
Competitor insight: Hydro Flask dominates with lifestyle branding; S'well leads on premium aesthetics.
Write 3 Instagram captions - one inspirational, one educational, one community-focused. Include 5 hashtags each and a CTA.

6. Tasarımcıyı geliştirme - Çok formatlı görüntü üretme

ADK web kullanıcı arayüzünü çalışır durumda tutun. Sunucuyu yeniden başlatmadan aracı değiştirmek için aracı açılır listesini kullanın.

Rol: Her altyazı için görsel konseptler oluşturun ve Gemini'ın yerel görüntü üretme özelliğini kullanarak gerçek görüntüleri oluşturun. Tasarımcı, her altyazı için tam olarak 1 görsel konsept (ayrıntılı istem, stil, renk paleti, ruh hali ve Instagram biçimiyle) oluşturur, ardından generate_image aracını hemen çağırarak gerçek görüntüyü üretir ve GCS'ye yükler.

Kavram: Bir metin aracısını bir araç üzerinden görüntü modeliyle bağlama

Tasarımcı, gemini-3-flash-preview (.env içinde GEMINI_MODEL aracılığıyla ayarlanan metin modeli) üzerinde çalışır ancak görüntü oluşturma için özel bir model (gemini-3.1-flash-image) gerekir. Bu görüntü modeli, işlev çağrısını desteklemediğinden doğrudan ADK aracısı olarak kullanılamaz. Bunun yerine, düz bir Python işlevine sarmalanır ve FunctionTool olarak kaydedilir.

Bu, LLM'nin doğrudan çağıramadığı tüm modeller veya API'ler için geçerli olan bir kalıptır: Modeli veya API'yi bir araçla sarmalayın, ne zaman çağrılacağını ajanın düzenlemesine izin verin ve yapılandırılmış bir sonuç alın.

Designer agent (text model)
        
          decides visual concept, writes image prompt
        
  generate_image tool
        
          calls gemini-3.1-flash-image
          uploads result to GCS
        
  {"status": "success", "gcs_uri": "gs://..."}
        
          returned to agent, included in response
        
  Critic (receives gcs_uri, passes to Vertex AI for multimodal review)

Dosyayı doğrudan Cloud Shell düzenleyicide açın:

cloudshell edit agents/designer/image_gen_tool.py

İşlev imzası, ortam kurulumu ve en boy oranı yerleştirme sağlanır. Üç yapılacak işi sırayla tamamlayın:

YAPILACAKLAR 1: Gemini görüntü modelini çağırma

# TODO 1 yorumunu bulun ve aşağıdakiyle değiştirin:

        client = genai.Client(vertexai=True, project=project_id, location=location)

        response = client.models.generate_content(
            model=image_model,
            contents=prompt_with_aspect,
            config=types.GenerateContentConfig(
                response_modalities=["IMAGE", "TEXT"],
                http_options=types.HttpOptions(
                    retry_options=types.HttpRetryOptions(
                        attempts=5, exp_base=2, initial_delay=30,
                        http_status_codes=[429, 500, 503, 504],
                    ),
                    timeout=180_000,
                ),
            ),
        )

YAPILACAKLAR 2: Yanıttan görüntü baytlarını ayıklayın

# TODO 2 yorumunu bulun ve aşağıdakiyle değiştirin:

        image_bytes = None
        mime_type = "image/png"
        for part in response.candidates[0].content.parts:
            if part.inline_data is not None:
                image_bytes = part.inline_data.data
                mime_type = part.inline_data.mime_type or "image/png"
                break

        if not image_bytes:
            return {"status": "error", "error": "Gemini returned no image data"}

YAPILACAKLAR 3: GCS'ye yükleyin ve URI'yi döndürün

# TODO 3 yorumunu bulun ve aşağıdakiyle değiştirin:

        ext = "jpg" if "jpeg" in mime_type else "png"
        from google.cloud import storage
        gcs_client = storage.Client(project=project_id)
        bucket = gcs_client.bucket(bucket_name)
        blob_name = f"campaign-images/{concept_name}-{uuid.uuid4().hex[:8]}.{ext}"
        blob = bucket.blob(blob_name)
        blob.upload_from_file(io.BytesIO(image_bytes), content_type=mime_type)
        gcs_uri = f"gs://{bucket_name}/{blob_name}"

Deneyin: Açılır listeyi designer olarak değiştirip gönderin:

Create a visual concept and generate the image for an EcoFlow Smart Water Bottle Instagram post targeting health-conscious millennials.
Style: clean, modern, lifestyle-focused. Include a detailed prompt with color palette, mood, and format (1080x1080 or 1080x1350).

7. Build the Critic - Structured Output

Rol: Metin ve görselleri proje yöneticisine teslim etmeden önce kalite kontrolünden geçirir. Eleştirmen, her iki teslimatı da puanlar ve belirli önerilerle birlikte APPROVED veya NEEDS_REVISION döndürür. Girişte gcs_uri değerleri varsa puanlama yapmadan önce oluşturulan her resmi görsel olarak incelemek için review_image aracını çağırır.

Kavram: Gemini çıktısı için ne zaman Pydantic modeli kullanılır?

Kural, çıktıyı kimin tükettiğiyle ilgilidir:

  • Python kodu bunu kullanırresponse_schema + Pydantic kullanın. Kod, belirsizliği işleyemez. Bu nedenle, alanları güvenilir bir şekilde ayıklamak için garantili bir yapıya ihtiyacınız vardır.
  • LLM tarafından kullanılıyorsa → metin biçimi + sistem talimatı yeterlidir. LLM'ler biçimlendirme kurallarını anlar ve varyasyonlara tolerans gösterir.

review_image içinde Python kodu, yazılan değerler olarak score, approval_status, what_works, issues ve suggestions değerlerini gerektirir. response_schema=_GeminiReview, Gemini'ı API düzeyinde geçerli JSON döndürecek şekilde kısıtlar. model_validate_json() ise bunu, kodunuzun güvenilir bir şekilde kullanabileceği türü belirlenmiş bir nesneye ayrıştırır.

class _GeminiReview(BaseModel):
    score: int = Field(ge=1, le=10)
    approval_status: Literal["APPROVED", "NEEDS_REVISION"]
    what_works: str
    issues: str
    suggestions: str

Dosyayı doğrudan Cloud Shell düzenleyicide açın:

cloudshell edit agents/critic/image_review_tool.py

Pydantic modelleri ve istem sağlanır. Üç yapılacak işi sırayla tamamlayın:

YAPILACAKLAR 1: GCS URI'sinden bir resim bölümü oluşturun.

# TODO 1 yorumunu bulun ve aşağıdakiyle değiştirin:

        image_part = types.Part.from_uri(file_uri=gcs_uri, mime_type=mime_type)

YAPILACAKLAR 2 - Yapılandırılmış bir yanıt şemasıyla Gemini'ı çağırma

# TODO 2 yorumunu bulun ve aşağıdakiyle değiştirin:

        response = client.models.generate_content(
            model=model,
            contents=[image_part, prompt],
            config=types.GenerateContentConfig(
                response_schema=_GeminiReview,
                response_mime_type="application/json",
            ),
        )

YAPILACAKLAR 3: Yanıtı ayrıştırın ve sonucu döndürün

# TODO 3 yorumunu bulun ve aşağıdakiyle değiştirin:

        review = _GeminiReview.model_validate_json(response.text)
        return ImageReviewResult(status="success", concept_name=concept_name, **review.model_dump())

Deneyin: Açılır listeyi critic olarak değiştirip gönderin:

Review this Instagram caption for an eco-friendly water bottle brand targeting millennials:
"Hydrate smarter, live greener. 💧 Our EcoFlow bottle tracks your intake, keeps your drink cold for 24h, and never touches single-use plastic. Because what you drink from matters as much as what you drink. #EcoFlow #HydrationGoals #SustainableLiving #ZeroWaste #HealthyHabits - Shop link in bio."
Score it and indicate APPROVED or NEEDS_REVISION with specific feedback.

Yanıtın **POSTS REVIEW:**, Status: APPROVED (veya NEEDS_REVISION) ve **OVERALL ASSESSMENT:** içerdiğini doğrulayın. Bu bölümler varsa Critic, düzenleyiciye bağlanmaya hazırdır.

Üç aracıyı da test etmeyi tamamladığınızda sunucuyu durdurmak için Ctrl+C tuşuna basın.

8. MCP ile proje yöneticisi temsilcisi oluşturma

Proje yöneticisi yeni bir kavram tanıtıyor: MCP (Model Context Protocol).

Dosyayı açın:

cloudshell edit agents/project_manager/agent.py

Bu dosya daha karmaşıktır. İki dallı bir create_project_manager_agent() işlevi vardır: biri Notion'ın olmadığı (yalnızca metin içeren zaman çizelgeleri), diğeri ise Notion MCP araç setinin olduğu. Her ikisini de dolduracaksınız.

MCP'nin çözdüğü sorun

Aracınızın harici bir hizmeti (ör. Notion'da sayfa oluşturma) çağırması gerekiyor. Doğrudan Notion REST API'yi çağıran Python kodu yazabilirsiniz. Ancak:

  • Her geliştirici farklı bir sarmalayıcı yazar
  • Özel entegrasyon kodunu korumanız gerekir.
  • Her uç noktayı manuel olarak açıklamadığınız sürece LLM, API'nin var olduğunu bilmez.

MCP, bu sorunu harici hizmetlerin özelliklerini, LLM'nin otomatik olarak keşfedip çağırabileceği araçlar olarak kullanıma sunması için standart bir yöntem tanımlayarak çözer.

MCP nedir?

MCP (Model Context Protocol), yapay zeka ajanlarını harici araçlara ve veri kaynaklarına bağlamak için kullanılan bir açık standarttır (Anthropic tarafından yayınlanmıştır). Evrensel adaptör gibi çalışır.

MCP sunucusu, aşağıdaki işlevleri yerine getiren küçük bir programdır:

  1. Harici bir API'yi (Notion, GitHub, veritabanları, dosya sistemleri vb.) sarmalar.
  2. Bu API'yi, türü belirlenmiş ve belgelenmiş araçlar listesi olarak kullanıma sunar.
  3. Basit bir protokol (stdio veya HTTP) aracılığıyla aracıyla iletişim kurar.

Aracı, MCP sunucusuna bağlanır, kullanılabilir araçları otomatik olarak keşfeder ve bunları diğer araçlar gibi çağırabilir. LLM, API-post-page(...) öğesini çağrılabilir bir işlev olarak görür.

A2A ve MCP arasındaki fark nedir?

Bu, sıkça karıştırılan bir noktadır. Aradaki temel fark şudur:

A2A

MCP

Bağlantı kurulanlar

Temsilci ↔ Temsilci

Aracı ↔ Harici araç/hizmet

Diğer tarafı ise

Başka bir LLM aracısı

API sarmalayıcı (LLM yok)

Örnek

Kreatif direktör, marka stratejistini arıyor

Proje yöneticisi, Notion API'yi çağırır.

Protokol

HTTPS üzerinden JSON-RPC

stdio veya HTTP akışı

Tanımlayan

Google

Anthropic

Şöyle düşünün:

  • A2A = Temsilcilerin diğer temsilcilerle konuşma şekli
  • MCP = Ajanların araçlar ve hizmetlerle nasıl iletişim kurduğu

Bu projede her ikisi de birlikte kullanılır:

Creative Director
    
      (A2A)  Brand Strategist ─── (google_search tool built into ADK)
      (A2A)  Copywriter
      (A2A)  Designer
      (A2A)  Critic
      (A2A)  Project Manager
                   
                     (MCP)  notion-mcp-server ──► Notion REST API

ÇGP'nin bu projede işleyiş şekli

Aracı çalıştırıldığında ADK, notion-mcp-server öğesini alt işlem olarak başlatır. Bu işlem, araçları doğrudan LLM'ye sunar:

Araç

Ne işe yarar?

API-retrieve-a-database

Şemayı (özellik adları, türler, geçerli değerler) getirir.

API-post-database-query

Mevcut sayfaları sorgulama

API-post-page

Yeni bir sayfa oluşturur.

API-patch-page

Mevcut bir sayfayı günceller.

LLM, bunları diğer işlevler gibi çağırır. Bu işlevlerin arka planda Notion REST API'ye gitmek için MCP'den geçtiğini bilmez.

Neden stdio? Neden sadece HTTP değil?

MCP sunucusu, stdin/stdout üzerinden iletişim kurarak aracının alt süreci olarak çalışır. Bunun anlamı şudur:

  • Ek ağ bağlantı noktası gerekmez
  • Yaşam döngüsü, temsilci tarafından yönetilir (isteğe bağlı olarak başlatılır, çıkışta durdurulur)
  • Her şey tek bir Docker görüntüsünde gönderilir. Dağıtılacak ayrı bir hizmet yoktur.

(İsteğe bağlı) Notion entegrasyonunu etkinleştirme

Bu bölümün tamamını atlayabilirsiniz. Proje yöneticisi aracısı, Notion ile veya Notion olmadan her zaman metin tabanlı eksiksiz bir kampanya takvimi oluşturur. Bu kurulumu atlarsanız aracı, bellek içi moda geri döner ve zaman çizelgesini sohbette düz metin olarak çıkarır. Hiçbir şey bozulmaz. Yalnızca görevler Notion veritabanında görünmez. Atlamak istiyorsanız doğrudan YAPILACAKLAR 1'e gidin.

Notion hesabınız varsa ve MCP entegrasyonunu kullanmak istiyorsanız aşağıdaki kurulumu hemen tamamlayın. Aşağıdaki YAPILACAKLAR, Notion veritabanı kimliklerine referans verir. Bu kimlikleri buradan alabilirsiniz.

1. adım: Şablondan Notion veritabanı oluşturun

Veritabanımız olarak resmi Notion Projects & Tasks şablonunu kullanıyoruz. Bu şablonu, karmaşık bir gerçek dünya ortamını göstermek için özellikle seçtik. Şablonda, açık olmayan adlara sahip birden fazla özellik türü (durum, tarih aralıkları, ilişkiler, seçimler) bulunuyor. Bu, MCP'nin dinamik şema keşfi için harika bir testtir: Temsilci, özellik adlarını sabit kodlanmış olarak değil, çalışma zamanında tam olarak belirlemelidir.

Şablonu Notion çalışma alanınıza eklemek için aşağıdaki bağlantıyı tıklayın:

→ "Projeler ve Görevler " şablonunu Notion'a ekleme

Pazar yerindeki Notion Projects & Tasks şablonu

Eklendikten sonra Projeler ve Görevler olmak üzere iki bağlı veritabanınız olur. Şablonda örnek girişler bulunur. Devam etmeden önce tümünü silin. Böylece, aracı temiz bir çalışma alanıyla başlar (Tümünü seç → Sil).

2. adım: Notion entegrasyonu oluşturun

Entegrasyonu oluşturun:

  1. notion.so/my-integrations adresine gidin.
  2. New Integration'ı (Yeni Entegrasyon) tıklayın → AI Creative Studio olarak adlandırın.
  3. Çalışma alanınızla ilişkilendirin
  4. Ayarları yapılandır'ı tıklayın → İçeriği okuma, İçeriği güncelleme ve İçerik ekleme özelliklerinin tümünün işaretli olduğundan emin olun.

Notion entegrasyon ayarları - "AI Creative Studio" olarak adlandırın ve jetonu kopyalayın.

  1. Dahili Entegrasyon Jetonu'nu (ntn_...) kopyalayıp .env dosyanıza yapıştırın:
NOTION_TOKEN=ntn_your-token-here

Entegrasyonu veritabanlarınıza bağlayın:

  1. Yeni kopyaladığınız şablon sayfasını açın ve Projeler veritabanını tıklayın.
  2. ... menüsü (sağ üst) → BağlantılarBağlantı ekle'yi tıklayın → AI Creative Studio simgesini seçin.

Entegrasyonunuzla paylaşmak için veritabanı menüsünde Bağlantılar'ı tıklayın.

Yapay Zeka Creative Studio, etkin bir bağlantı olarak görünür.

  1. Aynı işlemi Görevler veritabanı için de yapın.

Veritabanı kimliklerini alma:

  1. Projeler veritabanı bağlantısını tıklayarak açın. Veritabanı, şu gibi bir URL ile kendi sayfasında açılır:
https://www.notion.so/9887b6a94f7f83f68f8581e038d1aaa4?v=2c37b6a94f7f838685f1086e312c7278

Şablon sayfasından Projeler veritabanını açma

Veritabanı kimliği, URL'deki ilk UUID'dir (?v= öncesindeki her şey):

https://www.notion.so/{DATABASE_ID}?v=...
                       ^^^^^^^^^^^^^^^^
                       9887b6a94f7f83f68f8581e038d1aaa4  ← this is your DATABASE_ID
  1. Veritabanı kimliğini almak için Görevler veritabanı bağlantısı için de aynı işlemi yapın.
  2. Üç değeri de .env öğenize ekleyin:
NOTION_TOKEN=ntn_your-token-here
NOTION_PROJECT_DATABASE_ID=9887b6a94f7f83f68f8581e038d1aaa4   # <-- your Projects DB ID
NOTION_TASKS_DATABASE_ID=your-tasks-db-id                      # <-- your Tasks DB ID

3. adım: Notion MCP sunucusunu yükleyin

Proje yöneticisi, resmi @notionhq/notion-mcp-server Node.js paketi aracılığıyla Notion'a bağlanır. Global olarak yükleyin:

npm install -g @notionhq/notion-mcp-server@1.9.1

Yüklemeyi doğrulayın:

npm list -g @notionhq/notion-mcp-server

Beklenen çıktı:

└── @notionhq/notion-mcp-server@1.9.1

notion-mcp-server: command not found

? Node.js'nin yüklendiğinden (node --version) ve npm global bin'inizin PATH'inizde olduğundan (export PATH=$PATH:$(npm bin -g)) emin olun.

4. adım: .env dosyanızı doğrulayın

.env simgesini açın ve üç Notion değerinin de ayarlandığını onaylayın (2. adımda eklemiştiniz):

cloudshell edit .env
NOTION_TOKEN=ntn_...                           # integration token
NOTION_PROJECT_DATABASE_ID=...                 # Projects database ID
NOTION_TASKS_DATABASE_ID=...                   # Tasks database ID

Proje Yöneticisi aracısı, bu değişkenleri başlangıçta otomatik olarak algılar ve Notion MCP araç setini etkinleştirir.

Şema keşfinin işleyiş şekli

Proje yöneticisi, dinamik şema keşfini kullanır. Notion özellik adlarını asla sabit kodlamaz:

Step 1: Call API-retrieve-a-database to discover exact property names
Step 2: Read the "properties" object in the response
Step 3: Use ONLY discovered property names (case-sensitive) in API calls
Step 4: For select/status fields, use only values from the options array

Bu sayede, ajan herhangi bir Notion veritabanı yapısına otomatik olarak uyum sağlar. Özelliklerinizi Fransızca, Arapça veya başka bir dilde yeniden adlandırdığınızda ajan çalışmaya devam eder.

YAPILACAKLAR 1: Sistem talimatını yazın

Başlatıcı, Notion yapılandırılmadığında boş bir dize olan notion_section değerini, yapılandırıldığında ise veritabanı kimliklerini ve tam araç kılavuzunu içeren bir bloğu önceden hesaplar. Bu sayede Notion talimatları, Notion'ın olmadığı aracı isteminden tamamen çıkarılır. LLM, sahip olmadığı araçlarla ilgili kuralları asla görmez.

Göreviniz, return yer tutucusunu {notion_section} kullanan gerçek bir sistem talimatıyla değiştirmek:

    return f"""You are a Project Manager specializing in creative campaign execution.

Today's date is {datetime.date.today().strftime("%B %d, %Y")}.
Use this as the starting point for all timelines.

Your goal: create a complete project plan for the campaign.
{notion_section}
**Project Timeline:**
Phase 1: Strategy & Research | [date]  [date] | [key activities]
Phase 2: Content Creation    | [date]  [date] | [key activities]
Phase 3: Review & Revision   | [date]  [date] | [key activities]
Phase 4: Launch & Monitoring | [date]  [date] | [key activities]

**Task List:**
| Task | Owner | Deadline | Status |
[list each task with realistic deadlines from today; set Owner to TBD]

**Budget Breakdown:**
[by category with approximate allocations]

**Milestones:**
[3-5 key checkpoints with dates]

**Notion Status:**
[What happened - e.g. "Project created (ID: xxx), 8 tasks linked" or "Notion not configured - text timeline only"]
"""

TODO 2 - Agent without Notion

create_project_manager_agent() içinde, if not notion_token dalında, tamamlanmamış temsilciyi aşağıdakilerle değiştirin:

        return Agent(
            name="project_manager",
            model=os.getenv("GEMINI_MODEL", "gemini-2.5-flash"),
            generate_content_config=GENERATE_CONTENT_CONFIG,
            instruction=get_system_instruction(),
            description="Project manager that creates campaign timelines and task breakdowns",
        )

YAPILACAKLAR 3 - Notion MCP'li Ajan

Not: Başlangıç dosyasında, create_project_manager_agent() üzerinde önceden yazılmış bir handle_notion_error geri çağırma işlevi bulunur. Bu işlev, Notion API hatalarını (400/404) yakalar ve ham hata yüklerini temiz, uygulanabilir mesajlarla değiştirir. Böylece LLM kendi kendini düzeltebilir. Bu işlevi after_tool_callback üzerinden bağlamanız yeterlidir.

Öncelikle, create_project_manager_agent() bölümünün en üstündeki her iki veritabanı kimliğini okuyun:

    notion_token           = os.getenv("NOTION_TOKEN")
    notion_project_db_id   = os.getenv("NOTION_PROJECT_DATABASE_ID")
    notion_tasks_db_id     = os.getenv("NOTION_TASKS_DATABASE_ID")

Ardından, else dalında MCP araç setini ve temsilciyi oluşturun:

        from google.adk.tools.mcp_tool import McpToolset, StdioConnectionParams
        from mcp import StdioServerParameters

        server_params = StdioServerParameters(
            command="notion-mcp-server",
            env={
                "NOTION_TOKEN": notion_token,
                "PATH": os.environ.get("PATH", ""),
            }
        )
        notion_toolset = McpToolset(
            connection_params=StdioConnectionParams(
                server_params=server_params,
                timeout=30.0
            )
        )

        return Agent(
            name="project_manager",
            model=os.getenv("GEMINI_MODEL", "gemini-2.5-flash"),
            generate_content_config=GENERATE_CONTENT_CONFIG,
            after_tool_callback=handle_notion_error,
            instruction=get_system_instruction(
                project_database_id=notion_project_db_id,
                tasks_database_id=notion_tasks_db_id,
            ),
            description="Project manager with Notion integration for task tracking",
            tools=[notion_toolset],
        )

En iyi uygulama: İsteğe bağlı entegrasyonlarda hiçbir zaman kesin olarak başarısız olmayın. Metin zaman çizelgesi her zaman birincil çıktı, Notion ise ek çıktıdır.

ADK Web ile Proje Yöneticisi'ni yerel olarak test etme

uv run adk web agents --allow_origins='*'

8000 numaralı bağlantı noktasında web önizlemesini açın. Aracı açılır listesini kullanarak project_manager seçin ve şunları deneyin:

Create a project plan for a GreenBrew organic coffee brand Instagram campaign.
Budget: $2,500. Launch in 3 weeks. Target audience: eco-conscious millennials aged 22-30.
Include phases, tasks with deadlines from today, and milestones.

Aşamalar, görev listesi ve kilometre taşları içeren yapılandırılmış bir metin zaman çizelgesi görürsünüz. .env içinde Notion kimlik bilgileri ayarlanmışsa aracı, Notion çalışma alanınızda da girişler oluşturur.

9. A2A protokolünü anlama

Sistemimizdeki farklı temsilcileri bağlamak için Temsilciden Temsilciye Protokolü'nü (A2A) kullanırız. Bu özelliğin işleyiş şeklini açıklayalım.

A2A'nın çözdüğü sorun

ADK ile oluşturulmuş bir Marka Stratejisti temsilciniz ve LangGraph ile oluşturulmuş bir Metin Yazarı temsilciniz olduğunu düşünün. Bir kullanıcı diğerini nasıl arar? Farklı iç diller konuşuyorlar. Her seferinde özel yapıştırıcı kodu yazmanız gerekir.

A2A, herhangi bir temsilcinin (çerçeveden bağımsız olarak) konuşabileceği evrensel bir dil tanımlayarak bu sorunu çözer. Temsilci dünyasının HTTP'si gibidir: Herkesin kabul ettiği bir standarttır, böylece herkes herkesle konuşabilir.

A2A nedir?

Agent-to-Agent (A2A), Google tarafından yayınlanan aracı iletişimi için açık bir standarttır. Bu politika şunları tanımlar:

  1. Bir temsilcinin kendini nasıl tanımladığı - /.well-known/agent.json adresindeki temsilci kartı
  2. Başka bir aracının buna verdiği ad: HTTPS üzerinden JSON-RPC
  3. Sonuçlar nasıl döndürülür? (Yayın veya tek yanıt)

A2A'yı esnek kılan özellikler:

  • Dilden bağımsız: Python aracıları, TypeScript aracılarıyla konuşabilir.
  • Çerçeveden bağımsız: ADK temsilcileri, LangGraph veya CrewAI temsilcileriyle konuşabilir.
  • Altyapıdan bağımsız: Yerel aracıların bulut aracılarıyla konuşmasına olanak tanır.

İşleyiş şekli - adım adım

Creative Director                  Brand Strategist
      │                                  │
      │  1. GET /.well-known/agent.json  │
      │ ────────────────────────────────►│
      │  ◄──── agent card (name, url,    │
      │         skills, capabilities) ───│
      │                                  │
      │  2. POST /                       │
      │     {"method": "tasks/send",     │
      │      "params": {"message": ...}} │
      │ ────────────────────────────────►│
      │                                  │  LLM does
      │                                  │  the work...
      │  3. streaming response chunks    │
      │  ◄───────────────────────────────│
      │  ◄───────────────────────────────│
      │  ◄───────────────────────────────│

1. adım: Keşif: Düzenleyici, aracının adını, URL'sini ve özelliklerini öğrenmek için aracı kartını bir kez getirir.

2. adım: Çağırma: Düzenleyici, JSON-RPC POST üzerinden bir görev gönderir. Gövde, mesajı (uzman için istem) içerir.

3. adım: Yanıt: Uzman, yanıtını normal bir LLM çağrısında olduğu gibi parçalar halinde geri aktarır.

Temsilci kartı

Her temsilci, /.well-known/agent.json adresinde kendi açıklamasını yayınlar. Bu, bir kartvizit gibidir. Temsilcinin neler yapabileceğini ve kendisine nasıl ulaşılabileceğini tüm dünyaya bildirir:

{
  "name": "brand_strategist",
  "description": "Market research and competitive analysis",
  "url": "https://brand-strategist-xyz.run.app",
  "capabilities": { "streaming": true },
  "skills": [
    {
      "id": "market_research",
      "description": "Research target audiences, competitors, and trends"
    }
  ]
}

Düzenleyici, RemoteA2aAgent nesnesini oluşturmak için bu kartı okur. Uzmanın iç işleyişiyle ilgili sabit kodlanmış bilgiye gerek yoktur.

ADK'da A2A aracılığıyla aracı kullanıma sunma

to_a2a(), tüm ADK aracısını A2A uyumlu bir FastAPI uygulamasına sarar. Tek satır:

from google.adk.a2a.utils.agent_to_a2a import to_a2a

# root_agent = your normal ADK Agent(...)
a2a_app = to_a2a(root_agent, host=PUBLIC_HOST, port=PUBLIC_PORT, protocol=PROTOCOL)
uvicorn.run(a2a_app, host=HOST, port=PORT)

Bu işlem otomatik olarak şunları oluşturur:

  • /.well-known/agent.json - temsilci kartı
  • / - JSON-RPC uç noktası (tüm A2A görev istekleri kök yola gider)

10. Aracıları A2A hizmetleri olarak kullanıma sunma

Aracıları A2A hizmetleri olarak kullanıma sunmak için ADK'daki to_a2a() yardımcı işlevini kullanabilirsiniz.

to_a2a() nasıl çalışır?

from google.adk.a2a.utils.agent_to_a2a import to_a2a

a2a_app = to_a2a(root_agent, host=PUBLIC_HOST, port=PUBLIC_PORT, protocol=PROTOCOL)
uvicorn.run(a2a_app, host=HOST, port=PORT)

to_a2a(), ADK aracınızı otomatik olarak aşağıdaki öğeleri kullanıma sunan bir FastAPI uygulamasına sarmalar:

  • /.well-known/agent.json - the agent card (name, description, capabilities)
  • /a2a/{agent_name}: Görevleri almak için kullanılan JSON-RPC uç noktası

Her aracının iskelet kodu, to_a2a() kullanılarak aracı bir A2A sunucusuna sarmalayan bir __main__ bloğu içerir. Bu kodu yazmanız gerekmez. Kod sağlanır.

Çift URL yapılandırmasını anlama

python agent.py komutunu çalıştırdığınızda __main__ bloğu iki ayrı URL yapılandırması kullanır:

# Where the server actually listens (network interface):
HOST = "0.0.0.0"
PORT = 8082  # Brand Strategist (others use 80838086 locally)

# What gets advertised in the agent card (the address other agents use to reach it):
PUBLIC_HOST = os.getenv("PUBLIC_HOST", "localhost")
PUBLIC_PORT = int(os.getenv("PUBLIC_PORT", str(PORT)))
PROTOCOL    = os.getenv("PROTOCOL", "http")

a2a_app = to_a2a(root_agent, host=PUBLIC_HOST, port=PUBLIC_PORT, protocol=PROTOCOL)
uvicorn.run(a2a_app, host=HOST, port=PORT)

Ortam

HOST:PORT (dinleme sayısı)

PUBLIC_HOST:PUBLIC_PORT (ajan kartında reklamı yapılıyor)

Yerel

0.0.0.0:8082

http://localhost:8082

Cloud Run

0.0.0.0:8080

https://brand-strategist-xyz.run.app:443

Her ikisi de yerel olarak aynı makineye işaret eder. Cloud Run'da kapsayıcı, dahili olarak 8080 üzerinde dinler ancak aracı kartı, herkese açık HTTPS URL'sini yayınlamalıdır. Aksi takdirde, Creative Director kapsayıcının dışından uzmana ulaşamaz.

5 uzman A2A sunucusunu da başlatın.

5 uzmanı da aynı anda A2A sunucuları olarak çalıştıralım, ardından reklam öğesi yöneticisini yerel olarak test edelim.

5 ayrı Cloud Shell terminali açın (terminal sekme çubuğundaki + simgesini tıklayın) ve her terminalde bir aracı çalıştırın.

uv run, .venv'ı otomatik olarak etkinleştirir. Her terminalde manuel source gerekmez.

Terminal 1 - Marka Stratejisti (bağlantı noktası 8082):

cd ~/ai-creative-studio/workshop/starter
PORT=8082 uv run agents/brand_strategist/agent.py

Terminal 2 - Copywriter (bağlantı noktası 8083):

cd ~/ai-creative-studio/workshop/starter
PORT=8083 uv run agents/copywriter/agent.py

Terminal 3 - Designer (bağlantı noktası 8084):

cd ~/ai-creative-studio/workshop/starter
PORT=8084 uv run agents/designer/agent.py

Terminal 4 - Critic (bağlantı noktası 8085):

cd ~/ai-creative-studio/workshop/starter
PORT=8085 uv run agents/critic/agent.py

Terminal 5 - Project Manager (bağlantı noktası 8086):

cd ~/ai-creative-studio/workshop/starter
PORT=8086 uv run agents/project_manager/agent.py

.env dosyasında localhost URL'lerini ayarlayın.

Terminal 6'da, reklam öğesi yöneticisinin bulabilmesi için .env bölümünü yerel ajans URL'leriyle güncelleyin:

cd ~/ai-creative-studio/workshop/starter

sed -i \
  -e 's|STRATEGIST_AGENT_URL=.*|STRATEGIST_AGENT_URL=http://localhost:8082|' \
  -e 's|COPYWRITER_AGENT_URL=.*|COPYWRITER_AGENT_URL=http://localhost:8083|' \
  -e 's|DESIGNER_AGENT_URL=.*|DESIGNER_AGENT_URL=http://localhost:8084|' \
  -e 's|CRITIC_AGENT_URL=.*|CRITIC_AGENT_URL=http://localhost:8085|' \
  -e 's|PM_AGENT_URL=.*|PM_AGENT_URL=http://localhost:8086|' \
  .env

A2A Inspector ile aracıları inceleme

A2A Inspector, A2A protokolünü yerel olarak kullanan açık kaynaklı bir geliştirici aracıdır. Bu araç, çalışan herhangi bir A2A aracısına doğrudan bağlanmanıza, aracı kartını okumanıza ve görev göndermenize olanak tanır. Tüm bu işlemleri herhangi bir istemci kodu yazmadan yapabilirsiniz.

Gösterdiği bilgiler:

  • Aracı kartı: Aracınızın reklamını yaptığı yapılandırılmış meta veriler (adı, açıklaması, desteklenen giriş/çıkış modları ve uç nokta URL'si). Bu, uzman keşfedildiğinde Kreatif Direktör'ün okuduğu metindir.
  • Sohbet arayüzü: A2A üzerinden temsilciye herhangi bir mesajı gönderin ve ham yanıtı görün. İstemleri, aracıları birbirine bağlamadan önce ayrı ayrı test edebilirsiniz.
  • Protokol doğrulama: Denetleyici, aracı kartının A2A spesifikasyonuna uygun olup olmadığını kontrol eder ve eksik alanları veya hatalı biçimlendirilmiş yanıtları erken aşamada gösterir.

Önemi: Daha sonra Cloud Run'a dağıtım yaptığınızda, Creative Director /.well-known/agent.json üzerinden aracı kartını getirerek her uzmanı keşfeder. Bu kart yanlışsa (URL hatalı, özellikler eksik) düzenleyici sessizce başarısız olur. Inspector, bu sorunları herhangi bir bulut dağıtımından önce yerel olarak yakalamanıza olanak tanır.

Marka Stratejisti temsilci kartı

Temsilci kartında, uzmanın kimliği ve yetenekleri diğer temsilcilerin gördüğü şekilde gösterilir.

Ajan kartı ayrıntıları

İnceleyiciyi yükleme ve başlatma

cd ~/ai-creative-studio/workshop
./setup_inspector.sh

.env güncellemesi tek seferlik bir komuttur. İnceleyiciyi başlatmak için Terminal 6'yı kullanın:

cd ~/a2a-inspector
bash scripts/run.sh

Denetleyici kullanıcı arayüzünü açmak için Web önizlemesiBağlantı noktasını değiştir'i kullanın → 5001 yazın.

Marka stratejisi uzmanına bağlanma

Denetçinin URL alanına http://localhost:8082 girin ve Bağlan'ı tıklayın. Denetleyici, temsilci kartını getirir ve uzmanın meta verilerini gösterir.

A2A Inspector, Marka Stratejisi Uzmanı&#39;na bağlandı

Temsilci kartında yer alan bilgiler

Aracı kartı, meta verilerden daha fazlasıdır. Aracı, ağa reklamını yaptığı tam özellik sözleşmesidir. En kapsamlı örneği görmek için Proje Yöneticisi'ne (http://localhost:8086) bağlanın:

{
  "name": "project_manager",
  "description": "Project manager with Notion integration for task tracking",
  "protocolVersion": "0.3.0",
  "defaultInputModes": ["text/plain"],
  "defaultOutputModes": ["text/plain"],
  "skills": [
    {
      "id": "project_manager",
      "name": "model",
      "tags": ["llm"],
      "description": "... full system instruction including today's date and Notion database IDs ..."
    },
    {
      "id": "project_manager-API-post-page",
      "name": "API-post-page",
      "tags": ["llm", "tools"],
      "description": "Notion | Create a page"
    },
    {
      "id": "project_manager-API-retrieve-a-database",
      "name": "API-retrieve-a-database",
      "tags": ["llm", "tools"],
      "description": "Notion | Retrieve a database"
    }
  ]
}

Üç nokta öne çıkıyor:

1. MCP araçları A2A becerileri haline gelir: Proje yöneticisinin erişebildiği her Notion aracı (API-post-page, API-retrieve-a-database vb.) aracı kartında ayrı bir beceri olarak listelenir. Ağdaki diğer tüm aracılar, bu aracının hangi araçları kullanabileceğini herhangi bir kodu okumadan tam olarak keşfedebilir.

2. Sistem talimatı yerleştirilmiş: İlk becerinin description bölümünde, bugünün tarihi ve Notion veritabanı kimlikleri de dahil olmak üzere sistem talimatının tamamı yer alıyor. Böylece, Kreatif Direktör, Proje Yöneticisi'ni aradığında neyi ileteceğini bilir.

3. URL, canlı uç noktadır: url alanı, Kreatif Direktör bu uzmanı aradığında RemoteA2aAgent tarafından kullanılan alandır. Karttaki URL yanlışsa düzenleyici, temsilciye ulaşamaz.

Bu nedenle, inceleyici güçlü bir hata ayıklama aracıdır: Aracı kartına bir göz atarak aracının çalışıp çalışmadığını, hangi araçlara sahip olduğunu ve uç noktanın doğru olup olmadığını öğrenebilirsiniz.

Test mesajı gönderme

Bağlantı kurulduktan sonra sohbet paneline bir istem yazıp gönderin. İnceleme uzmanı, bunu A2A görevi olarak gönderir ve yanıtı geri aktarır. Bu işlem, Creative Director'ın bu aracı üretimde çağırmasıyla aynı şekilde yapılır.

A2A inceleyici aracılığıyla Marka Stratejisti ile sohbet etme

Her uzmanı ayrı ayrı test etmek için inceleyiciyi yerel bir bağlantı noktasına (8082-8086) yönlendirin.

11. Kreatif Direktör Orkestratörü'nü oluşturma

Kreatif direktör, ana düzenleyicidir. Ortam değişkenlerindeki uzman URL'lerini okur, her birini RemoteA2aAgent olarak sarmalar ve LLM'nin çağırabileceği AgentTool olarak kullanıma sunar.

5 uzman aracının hâlâ çalıştığından emin olun (10. adımdaki 1-5 numaralı terminaller).

Terminal 6'da (A2A Inspector terminali) Ctrl+C ile inceleyiciyi durdurun.

Dosyayı açın:

cd ~/ai-creative-studio/workshop/starter
cloudshell edit agents/creative_director/agent.py

Bu dosyada üç yapılacak iş var. Bunları sırayla uygulayın.

YAPILACAKLAR 1: Önceden yazılmış sistem talimatını inceleyin

Sistem talimatı, aynı dizindeki prompt.py içinde yer alır ve otomatik olarak içe aktarılır:

from .prompt import SYSTEM_INSTRUCTION_TEMPLATE

Devam etmeden önce okumak için prompt.py açın:

cloudshell edit agents/creative_director/prompt.py

Orkestrasyon davranışının tamamını kontrol ettiğinden bu parametreyi anlamak önemlidir.

Orkestratör istemi neden her şeyi kontrol eder?

Bu bölümün yanında prompt.py bölümünü açın. Aşağıdaki örneklerde bu bölümün belirli kısımlarına referans verilmektedir.

prompt.py içindeki istem yalnızca dokümantasyon değildir. Aynı zamanda tüm sistemin kontrol düzlemidir. Kötü yapılandırılmış bir düzenleyici istemi şunlara yol açar: sırası gelmeden çağrılan aracılar, uzmanlar yerine düzenleyici tarafından oluşturulan içerikler, hatalardan sonra devam eden iş akışları ve aracılar arasında bağlamın sessizce bırakılması. Bu dokuz öğe, en sık karşılaşılan hataları önler:

0. öğe: Önce planlayın, sonra uygulayın

Bu en önemli unsurdur. Düzenleyiciye, herhangi bir uzmanı aramadan önce numaralandırılmış bir plan oluşturması talimatı verilir:

I'll create your campaign by coordinating the specialist agents in sequence:
1. Brand Strategist - develop positioning and audience insights
2. Copywriter - write captions using those insights
3. Visual Designer - create image prompts aligned with the copy
4. Critic - review and score the full package
5. Project Manager - build the timeline and task breakdown

Bu adım olmadan LLM doğrudan araç çağrılarına geçer ve iş akışında nerede olduğunu takip edemez. Özellikle bir uzmandan uzun bir yanıt aldıktan sonra bu durum daha da belirginleşir. Önce planı özetlemek, düzenleyiciyi sabitler. Düzenleyici, hangi adımda olduğunu, bir sonraki adımın ne olduğunu ve tam bir çalıştırmanın nasıl göründüğünü bilir. Bu adımı atlamak, düzenleyicinin iş akışının ortasında durmasına veya adımları tekrarlamasına neden olur.

1. öğe: Açık rol tanımı

❌ "You are a helpful creative assistant."
✅ "You orchestrate specialists. You do NOT write captions, designs, or timelines yourself."

Açıkça yasaklanmadığı takdirde LLM, bazen uzmanlara danışmayı atlayıp doğrudan içerik oluşturur. Bu yöntem daha hızlıdır ve LLM, bunu nasıl yapacağını "bilir". Talimat bu durumu yanlış yapmalıdır.

2. öğe: Yanlış kalıpların listelendiği araç çağrısı söz dizimi

Yalnızca doğru söz dizimini göstermek yeterli değildir. LLM, makul görünen ancak sessizce başarısız olan aramalar oluşturabilir. İstemde hem doğru kalıp hem de asla kullanılmaması gereken kalıplar açıkça listeleniyor:

✅ copywriter(request="...")          ← correct
❌ print(copywriter(...))             ← breaks silently
❌ default_api.copywriter(...)        ← breaks silently
❌ copywriter.run(...)                ← breaks silently
❌ agents.copywriter(...)             ← breaks silently

Yanlış kalıpların açıkça listelenmesi, üretimde hatalı biçimlendirilmiş araç çağrılarını yaklaşık% 95 oranında azalttı.

3. öğe: Adım adım açıklanan sıralı yürütme

a) Call the tool
b) Wait for tool_output
c) Verify the output is not an error
d) Confirm to the user: "✓ Brand Strategist complete"
e) Then move to the next agent

(b) ve (c) adımları olmadan LLM bazen iki aracıya aynı anda çağrı gönderir veya yanıtı almadan önce başarılı olduğunu varsayarak devam eder.

4. öğe: Hata yönergeleri: STOP, report, do not proceed

İlk sürümlerde, düzenleyici bir uzmandan hata alıyor, bu hata için makul bir çıkış uyduruyor ve bir sonraki aracıya geçiyordu. Kullanıcı, halüsinasyon temelli, eksiksiz görünen bir kampanya elde ediyordu. Düzeltme açıkça belirtilmiştir: Hemen DUR. Hatayı tam olarak bildir. Asla devam etme.

5. öğe: Bağlam geçirme kuralları

Uzaktan çalışan temsilcilerin konuşma geçmişi yoktur. Düzenleyici, A2A aracılığıyla metin yazarına çağrı yaptığında metin yazarı yalnızca tek bir isteğin mesajını görür. Marka stratejistinin ne söylediği hakkında bilgisi yoktur. Düzenleyici, önceki çıktıları açıkça her sonraki çağrıya dahil etmelidir:

copywriter(request="Create 3 posts for EcoFlow water bottle targeting millennials.
Use these insights from the Brand Strategist: [paste full strategist output here].
Create engaging captions with hashtags.")

Talimatta bu açıkça belirtilmiştir: "Uzak aracılar, paylaşılan belleğe sahip DEĞİLDİR. Önceki çıkışları açıkça iletmeniz gerekir." Bu olmadan her temsilci tahminlerle hareket eder.

6. öğe: İstek sınıflandırması: basit ve karmaşık

Her istek için beş aracının tamamı gerekmez. İstem, düzenleyiciye planlamadan önce isteği sınıflandırması talimatını veriyor:

SIMPLE  → one agent needed
  "Research the eco-friendly water bottle market" → brand_strategist only
  "Write 3 Instagram captions"                    → copywriter only

COMPLEX → all agents sequentially
  "Create a complete campaign with timeline"      → all 5 agents

Bu sınıflandırma olmadan düzenleyici, "bana 3 gönderi fikri ver" gibi her istek için beş aracının tamamını çalıştırarak gereksiz gecikmeye ve maliyete neden olur.

7. öğe: İletişim kuralları: Tam çıktıları göster, filtreleme yok

İstemde, düzenleyicinin uzmanların döndürdüğü yanıtları özetlememesi veya düzenlememesi gerektiği açıkça belirtiliyor:

- DO NOT summarize unless the output exceeds 2000 words
- DO NOT filter or edit agent responses
- Show the user exactly what each specialist produced
- NEVER say results are ready unless you received them in tool_output

Bu olmadan, düzenleyici uzman çıktılarının kendi kelimeleriyle yeniden yazarak ayrıntıları kaybeder, hatalar ekler ve uzmanların var olma amacını boşa çıkarır.

8. öğe: İş akışını tamamlama: asla erken durmayın

İnce ancak kritik bir hata modu: Düzenleyici, 5 adımlık bir plan duyurur, 3 adımı tamamlar ve ardından sonuçları tamamlanmış gibi sunar. İstem, düzenleyicinin tamamlanabilmesi için geçilmesi gereken açık bir yapılacaklar listesiyle bunu önler:

✓ Did I announce a plan with N agents?
✓ Have I called ALL N agents from my plan?
✓ Did each agent respond successfully?
✓ Am I presenting complete results from ALL agents?

If any answer is NO → continue executing the remaining agents.

Bu, düzenleyicinin kısmi bir çalıştırmayı tamamlanmış olarak değerlendirmesini engeller.

Kalite Kontrol Döngüsü

Düzeltme iş akışı, prompt.py'nın en karmaşık kısmıdır. ## REVISION WORKFLOW bölümünü açın ve adımları uygulayın.

İşleyiş şekli

Eleştirmen yanıt verdikten sonra Yaratıcı Yönetmen, Proje Yöneticisi'ne körü körüne devam etmez. Eleştirmen'in çıktısını okur ve dallanır:

Critic output
      │
      ├── "All Approved: YES"
      │         └──► proceed to Project Manager
      │
      └── "Status: NEEDS_REVISION"
                │
                ├── posts fail   → call copywriter again with feedback
                ├── visuals fail → call designer again with feedback
                └── both fail    → call copywriter, then designer
                          │
                          └──► revised output → Project Manager
                               (1 revision max per deliverable)

Bu özellik, kodla değil LLM ile desteklenir.

Daha önce belirtildiği gibi, codelab'de düzenleyicinin, eleştirmenin yanıtını"ayrıştırdığı" ifade ediliyor. Bu ayrıştırmayı yapan bir Python kodu yoktur (normal ifade veya dize eşleştirme yok). Kreatif Direktör, kendi talimatını okuyan bir LLM'dir. Bu talimatta şunlar belirtilir:

Look for "Status: NEEDS_REVISION" in the critic's response.
Posts need revision  → call copywriter
Visuals need revision → call designer

LLM, eleştirmenin çıktısındaki bu tam dizeleri okur ve dala göre hareket eder. Bu nedenle, eleştirmen biçimi üzerinde değişiklik yapılamaz: Eleştirmen, NEEDS_REVISION yerine "biraz çalışılması gerekiyor" yazarsa LLM, talimatında eşleşme bulamaz ve düzeltme adımını sessizce atlar.

Bağlam, düzeltme çağrısında nasıl yönlendirilir?

Düzeltme çağrısı, 5. öğedeki bağlam geçirme kuralıyla aynıdır. Kopyayı yazan kişi ilk sürümü hatırlamadığı için düzenleyici her şeyi açıkça eklemelidir:

"I need you to revise the Instagram posts based on critic feedback.

ORIGINAL BRIEF:
[the original user request]

YOUR FIRST VERSION:
[the posts the copywriter created]

CRITIC FEEDBACK (Score: 6/10 - NEEDS_REVISION):
[the critic's specific suggestions]

Please revise the posts addressing this feedback while maintaining
the strengths the critic identified."

"İLK VERSİYONUNUZ" bölümü olmadan, Copywriter daha önce ürettiği içeriği iyileştirmek yerine sıfırdan yazardı.

1 revizyon sınırı ve önemi

Bir düzeltme turundan sonra düzenleyici, puandan bağımsız olarak proje yöneticisine geçer. Talimat bunu zihinsel olarak takip eder:

After calling copywriter for revision once:
→ mark "copywriter_revised = true" in context
→ even if the critic still suggests changes, proceed to PM

Bu sınır olmadan döngü süresiz olarak devam edebilir: Eleştirmen bir sorunu işaretler → Metin yazarı düzeltir → Eleştirmen tekrar işaretler → Metin yazarı tekrar düzeltir. Her tur için jeton ve zaman gerekir. Kontrolsüz bir döngü riski olmadan kaliteyi artırmak için tek bir revizyon yeterlidir.

Proje yöneticisine hangi bilgiler iletilir?

Proje yöneticisi her zaman orijinal sürümleri değil, onaylanmış son sürümleri alır. Revizyonlar yapıldıysa düzenleyici, revize edilmiş kopyayı ve görselleri iletir. İlk geçişte her şey onaylandıysa doğrudan geçer. PM, reddedilen taslakları asla görmez.

YAPILACAKLAR 2: Her uzmanı RemoteA2aAgent + AgentTool olarak kaydetme

# TODO 2: For each specialist URL... yorumunu bulun ve aşağıdakiyle değiştirin:

    if strategist_url:
        available_agents_list.append(
            "- **brand_strategist**: Market research, competitor analysis, trend identification"
        )
        strategist_agent = RemoteA2aAgent(
            name="brand_strategist",
            description="Researches markets, competitors, and trends using Google Search",
            agent_card=f"{strategist_url}/.well-known/agent.json",
        )
        agent_tools.append(AgentTool(agent=strategist_agent))

    if copywriter_url:
        available_agents_list.append(
            "- **copywriter**: Instagram captions, hashtags, and CTAs"
        )
        copywriter_agent = RemoteA2aAgent(
            name="copywriter",
            description="Creates Instagram captions with hashtags and CTAs",
            agent_card=f"{copywriter_url}/.well-known/agent.json",
        )
        agent_tools.append(AgentTool(agent=copywriter_agent))

    if designer_url:
        available_agents_list.append(
            "- **designer**: Visual concepts and real images generated via Gemini (GCS URIs returned)"
        )
        designer_agent = RemoteA2aAgent(
            name="designer",
            description="Creates visual concepts and generates real images via Gemini, stored in GCS",
            agent_card=f"{designer_url}/.well-known/agent.json",
        )
        agent_tools.append(AgentTool(agent=designer_agent))

    if critic_url:
        available_agents_list.append(
            "- **critic**: Quality review with APPROVED/NEEDS_REVISION scoring"
        )
        critic_agent = RemoteA2aAgent(
            name="critic",
            description="Reviews campaign materials and returns structured quality feedback",
            agent_card=f"{critic_url}/.well-known/agent.json",
        )
        agent_tools.append(AgentTool(agent=critic_agent))

    if pm_url:
        available_agents_list.append(
            "- **project_manager**: Project timelines, task breakdowns, Notion integration"
        )
        pm_agent = RemoteA2aAgent(
            name="project_manager",
            description="Creates project timelines and task breakdowns, optionally in Notion",
            agent_card=f"{pm_url}/.well-known/agent.json",
        )
        agent_tools.append(AgentTool(agent=pm_agent))

YAPILACAKLAR 3: Bağlam sıkıştırmasıyla bir uygulamaya sarmalama

Sıkıştırma neden gereklidir?

Bir sohbetteki her mesaj (kullanıcının istemi, her araç çağrısı, her araç yanıtı), LLM'nin bir sonraki dönüşte okuduğu bağlam penceresine eklenir. 5 temsilcili bir iş akışında bu süre hızla artar:

Turn 1:  user prompt                           ~200 tokens
Turn 2:  orchestrator plan                     ~300 tokens
Turn 3:  brand_strategist tool_call            ~150 tokens
Turn 4:  brand_strategist tool_output          ~1,500 tokens   full research report
Turn 5:  copywriter tool_call                  ~300 tokens     must include strategist output
Turn 6:  copywriter tool_output                ~2,000 tokens   3 captions
Turn 7:  designer tool_call                    ~500 tokens
Turn 8:  designer tool_output                  ~1,500 tokens
...

4. Ajan (Eleştirmen) tarafından bağlam penceresi, önceki üç ajanın tüm çıktısını içerir. Bu,genellikle yalnızca araç yanıtlarında 8.000-12.000 parçaya karşılık gelir. Gemini 2.5 Pro'nun büyük bağlam penceresiyle bile,düzenleyicinin akıl yürütme kalitesi, sürekli büyüyen bir geçmişe dikkat etmesi gerektiğinden düşer. Sıkıştırma olmadan, uzun iş akışları 4. Ajan civarında pratik sınırlara ulaşır.

Sıkıştırma ne işe yarar?

ADK, her etkinliği tam olarak tutmak yerine eski etkinlikleri kompakt bir şekilde özetlemek için düzenli olarak bir LLM çağırır. Yalnızca geçmiş etkinliklerin özeti ve en son aracının tam çıktısı bağlamda tutulur.

Without compaction:
  [full strategist output] + [full copywriter output] + [full designer output] + → Critic

With compaction (interval=3, overlap=1):
  [summary of strategist + copywriter] + [full designer output] + → Critic

Özet, ayrıntılı biçimlendirmeyi, her aracıya iletilen tekrarlanan bağlamı ve ara muhakemeyi atarken temel bilgileri (önemli analizler, onaylanmış altyazılar, görsel konseptler) korur. Eleştirmen, değerlendirme için gereken her şeye sahiptir. Yalnızca üç tam rapor yerine bir özet okur.

Kod

# TODO 3: Wrap the agent in an App... yorumunu bulun ve yer tutucu App(...) ile değiştirin:

    from google.adk.apps import App
    from google.adk.apps.app import EventsCompactionConfig
    from google.adk.apps.llm_event_summarizer import LlmEventSummarizer
    from google.adk.models import Gemini

    compaction_config = EventsCompactionConfig(
        summarizer=LlmEventSummarizer(llm=Gemini(model_id=os.getenv("GEMINI_MODEL", "gemini-2.5-flash"))),
        compaction_interval=3,   # Summarize after every 3 agent completions
        overlap_size=1,          # Keep the most recent agent's output in full
    )

    app = App(
        name="creative_director",
        root_agent=agent,
        events_compaction_config=compaction_config,
        plugins=[LoggingPlugin()],
    )
    return agent, app

compaction_interval=3: Yoğunlaştırma, her 3 aracı tamamlandıktan sonra tetiklenir. 5 aracılı bir işlem hattında bu, bir kez tetikleneceği (1-3 numaralı aracılardan sonra) ve ardından Eleştirmen ile Proje Yöneticisi'nin 1-3 numaralı aracılarla ilgili bir özetin yanı sıra önceki aracının tam çıktısını göreceği anlamına gelir.

overlap_size=1: En son temsilcinin tam çıktısı her zaman kelimesi kelimesine korunur, asla özetlenmez. Eleştirmen, gerçek görüntüleri yükleyip incelemek için tasarımcının gcs_uri değerleri de dahil olmak üzere tam çıktısına ihtiyaç duyduğundan bu önemlidir. Özet bu URI'leri kaybeder.

Kampanya tam olarak yayınlandığında nasıl bir sonuç elde edilir?

Agent 1 (Strategist)  → full context
Agent 2 (Copywriter)  → full context
Agent 3 (Designer)    → full context
                        ↓ compaction fires: summarizes agents 1-2, keeps 3 in full
Agent 4 (Critic)      → sees [summary of 1-2] + [full output of 3]
Agent 5 (PM)          → sees [summary of 1-3] + [full output of 4]

RemoteA2aAgent ve AgentTool'ı anlama

RemoteA2aAgent("brand_strategist", agent_card=url)
     
       wraps the remote service so ADK can call it
     
AgentTool(agent=strategist_agent)
     
       exposes it as a callable tool to the LLM
     
Agent(tools=[...])
     
       LLM calls tool("brand_strategist", message=...) when needed
     
brand-strategist-xxxx.run.app   actual HTTP A2A call happens here

LLM, sistem talimatına ve kullanıcının isteğine göre her aracın ne zaman çağrılacağına karar verir. Düzenleyici, kodda hiçbir zaman doğrudan aracıları çağırmaz. Her şey LLM'nin muhakemesiyle yönlendirilir.

Kreatif Direktör'ü yerel olarak test etme

uv run adk web agents --allow_origins='*'

8000 numaralı bağlantı noktasında web önizlemesini açın. Aracı açılır listesini kullanarak creative_director seçin ve şunları deneyin:

Research the eco-friendly water bottle market for health-conscious millennials

Reklam öğesi yöneticisinin bunu yalnızca marka stratejistine yönlendirdiğini ve marka stratejistinden yanıt aldığınızı görürsünüz.

Kampanyanın tamamı için şunları deneyin:

Create a complete Instagram campaign for SolarPack portable solar charger targeting
outdoor enthusiasts and digital nomads aged 22-35.
Budget $2,000, launch in 2 weeks.

Reklam öğesi yöneticisinin, 5 uzmanı sırayla koordine ettiğini ve her bir uzmanın çıktısının bir sonraki uzmana aktarıldığını göreceksiniz.

Demo: Uçtan uca kampanya yayınlama

Devam etmeden önce Creative Director'ı (Ctrl+C) durdurun. A2A inceleyici de 8000 numaralı bağlantı noktasını kullanır.

Yerel test tamamlandığında 5 uzman sunucuyu durdurun (her terminalde Ctrl+C).

12. Uzman Ajanları Dağıtma ve Test Etme

Artık aracılarınızı Google Cloud'a dağıtmaya hazırız. Cloud Run, aracıları dağıtmak için harika bir hizmettir. Sunucusuz, ölçeklenebilir ve kullanımı kolaydır. Her uzman aracısı bağımsız bir Cloud Run hizmeti olarak dağıtılır.

Dağıtım yapılandırması

Her uzman için Dockerfile şu deseni izler:

FROM python:3.12-slim
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends gcc curl

# Fast dependency install with uv
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
COPY pyproject.toml .
RUN uv sync --no-install-project --no-dev

COPY . .
RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
USER appuser

ENV PYTHONUNBUFFERED=1 PORT=8080 HOST=0.0.0.0
EXPOSE 8080
CMD ["uv", "run", "python", "agent.py"]

5 uzmanı sırayla dağıtın

cd ~/ai-creative-studio/workshop/starter
source .env

uv run deploy/deploy_all_specialists.py

Bu komut dosyası, 5 aracının tamamını tek tek dağıtır (toplamda yaklaşık 10-12 dakika sürer). Sıralı dağıtım, Cloud Build yoklama kotasını (60 istek/dakika) aşmayı önler. İşlem tamamlandığında her aracının Cloud Run URL'sini .env'ya geri yazar.

Tasarımcı dağıtıldıktan sonra, oluşturulan resimleri yükleyebilmesi için komut dosyası, GCS paketinize roles/storage.objectCreator Cloud Run hizmet hesabına otomatik olarak izin verir.

.env içinde Notion kimlik bilgilerini yapılandırdıysanız komut dosyası bunları Secret Manager'da (notion-token, notion-project-db-id, notion-tasks-db-id olarak) güvenli bir şekilde depolar ve düz ortam değişkenleri yerine --set-secrets aracılığıyla Project Manager hizmetine yerleştirir. Bu, jetonun Cloud Run'ın ortam sekmesinde veya gcloud komut geçmişinde hiçbir zaman görünmediği anlamına gelir.

Dağıtımları doğrulama

Dağıtım tamamlandığında komut dosyası, Cloud Run URL'lerini otomatik olarak .env'ya geri yazar ve önceki adımdaki localhost URL'lerinin yerini alır:

source .env

echo "Deployed URLs:"
echo "  Brand Strategist: $STRATEGIST_AGENT_URL"
echo "  Copywriter:       $COPYWRITER_AGENT_URL"
echo "  Designer:         $DESIGNER_AGENT_URL"
echo "  Critic:           $CRITIC_AGENT_URL"
echo "  Project Manager:  $PM_AGENT_URL"

Reklam Öğesi Yönetmeni, sonraki adımda Agent Runtime'a dağıtıldığında bu Cloud Run URL'lerini otomatik olarak kullanır.

Temsilci kartlarını doğrulama

Dağıtılan her aracı, /.well-known/agent.json adresinde bir aracı kartı gösterir. Her şeyin yayında olduğunu doğrulamak için bunları getirin:

source .env

for agent_url in $STRATEGIST_AGENT_URL $COPYWRITER_AGENT_URL $DESIGNER_AGENT_URL $CRITIC_AGENT_URL $PM_AGENT_URL; do
    echo "=== Agent Card: $agent_url ==="
    curl -s "${agent_url}/.well-known/agent.json" | python3 -m json.tool | grep -E '"name"|"url"|"description"'
    echo ""
done

Her temsilci için beklenen çıktı:

"name": "brand_strategist",
"url": "https://brand-strategist-xxxx.run.app",
"description": "Brand strategist for market research and competitive insights"

A2A inceleyici ile test etme (Cloud Run)

A2A Inspector, 10. adımda zaten yüklenmiştir. Başlatın:

cd ~/a2a-inspector
bash scripts/run.sh

Web önizlemesini açın → Bağlantı noktasını değiştir'i → 5001 tıklayın. Bağlantı alanına Cloud Run URL'nizi girin:

https://brand-strategist-xxxx.us-central1.run.app

Bağlan'ı tıklayın. Hizmetler --allow-unauthenticated ile dağıtıldığından kimlik doğrulama jetonu gerekmez.

İnceleme aracı bağlanır, temsilci kartını doğrular ve A2A üzerinden etkileşimli olarak sohbet etmenize olanak tanır.

Cloud Run'a dağıtılan aracıları inceleme

Cloud Run'a dağıttıktan sonra, bulut dağıtımının çalıştığını doğrulamak için denetleyiciyi herkese açık HTTPS URL'sine yönlendirin:

Cloud Run aracısına bağlı A2A Inspector

İş akışı aynıdır: Cloud Run URL'sini yapıştırın, bağlanın ve bir test mesajı gönderin. Ajan kartı yüklenir ve sohbet yanıt verirse uzman doğru şekilde dağıtılmış ve ulaşılabilir demektir.

13. Creative Director'ı Agent Runtime'a dağıtma

Orkestratör, yönetilen oturum durumu, otomatik ölçeklendirme ve yerleşik izleme sağlayan Agent Runtime'a dağıtılır.

Düzenleyici için neden Agent Runtime?

Beş uzman, Cloud Run'a dağıtılır. Bunlar, her biri bir görevi yerine getiren, hafif ve durum bilgisiz uzmanlardır. Yaratıcı yönetmenin farklı gereksinimleri vardır:

Gereksinim

Neden önemli?

Oturum durumu

Çok adımlı bir iş akışı 45 saniyeden uzun sürüyor. Agent Runtime, orkestratörün araç çağrıları arasındaki görüşme durumunu korur. Böylece, işlem hattının ortasında hiçbir şey kaybolmaz.

Değişken yük

Bazen saatte bir kampanya, bazen de paralel olarak birçok kampanya. Aracı çalışma zamanı, boşta kaldığında sıfıra ölçeklenir ve otomatik olarak ölçeklendirilir. Boşta kalan kapasite için ödeme yapmazsınız.

Gözlemlenebilirlik

Cloud Logging, Cloud Monitoring ve Cloud Trace yerleşik olarak sunulur. Her A2A çağrısını, kullanılan her jetonu ve her gecikme artışını herhangi bir enstrüman eklemeden görebilirsiniz.

Uzun süren iş akışları

Cloud Run'da 3.600 saniyelik istek zaman aşımı vardır. Aracı çalışma zamanı, yönetilen yeniden denemeler ve durum kalıcılığı ile dakikalar sürebilen iş akışları için tasarlanmıştır.

Cloud Run, durum bilgisiz uzmanlar için doğru platformdur. Agent Runtime, durum bilgisi olan düzenleyici için doğru platformdur.

Orchestrator'ı dağıtma

cd ~/ai-creative-studio/workshop/starter
source .env

uv run deploy/deploy_orchestrator.py --action deploy

Bu işlem yaklaşık 5-10 dakika sürer. İşlem tamamlandığında AGENT_ENGINE_ID ve AGENT_ENGINE_RESOURCE_NAME, .env hesabına kaydedilir.

source .env
echo "Agent Engine ID: $AGENT_ENGINE_ID"
echo "Resource: $AGENT_ENGINE_RESOURCE_NAME"

Dağıtımın işleyiş şekli

client.agent_engines.create(), App nesnenizi paketler, bağımlılıklarıyla birlikte yükler ve yönetilen altyapıya dağıtır. Her parametrenin işlevi şöyledir:

import vertexai
from vertexai import Client, agent_engines

vertexai.init(project=PROJECT_ID, location=LOCATION, staging_bucket=STAGING_BUCKET)

# Wrap the App in an AdkApp adapter - enables tracing in Cloud Trace
adk_app = agent_engines.AdkApp(app=root_app, enable_tracing=True)

# Initialize client and deploy
client = Client(project=PROJECT_ID, location=LOCATION)

agent_engine_resource = client.agent_engines.create(
    agent=adk_app,
    config={
        "staging_bucket": STAGING_BUCKET,   # GCS bucket for packaging artifacts
        "display_name": "Creative Director",
        # Python packages installed in the managed runtime - pin for reproducibility
        "requirements": [
            "google-cloud-aiplatform[agent_engines]>=1.132.0,<2.0.0",
            "google-adk[a2a]==1.31.1",
            "google-genai>=1.70.0",
            "google-cloud-storage>=2.10.0",
            "python-dotenv>=1.0.0",
            "pydantic>=2.0.0",
            "cloudpickle>=3.0.0",
        ],
        # Specialist URLs passed as env vars - the orchestrator reads these at runtime
        "env_vars": {
            "COPYWRITER_AGENT_URL": COPYWRITER_URL,
            "DESIGNER_AGENT_URL":   DESIGNER_URL,
            "STRATEGIST_AGENT_URL": STRATEGIST_URL,
            "CRITIC_AGENT_URL":     CRITIC_URL,
            "PM_AGENT_URL":         PM_URL,
        },
    },
)

resource_name = agent_engine_resource.api_resource.name
agent_engine_id = resource_name.split("/")[-1]

Arka planda neler olur?

1. Agent Engine packages your App + requirements into a container
2. Uploads it to the staging bucket in your project
3. Deploys to managed compute (you never see or manage the VM)
4. Returns a resource name: projects/.../locations/.../reasoningEngines/<id>
5. That ID is saved to .env as AGENT_ENGINE_ID

Dağıtım sonrasında düzenleyici, ortam değişkenlerindeki URL'ler aracılığıyla beş Cloud Run uzmanına bağlanır.

  • Bunlar, dağıtım komut dosyası çalıştırılmadan önce .env üzerinden iletilir.

14. Uçtan uca kampanya yayınlama

Sistemin tamamı dağıtılır. Agent Runtime playground'da eksiksiz bir kampanya yayınlayın.

Agent Runtime playground'unu açma

  1. https://console.cloud.google.com/agent-platform/runtimes adresine gidin. Ayrıca Agent Platform > Agents > Deployments'tan (Aracı Platformu > Aracıları > Dağıtımlar) Agent Runtime'a (Aracı Çalışma Zamanı) da gidebilirsiniz.
  2. Dağıtılan Agent Runtime'ınızı seçin (creative-director)
  3. Sol kenar çubuğunda Playground'u (Deneme Alanı) tıklayın.
  4. Yeni bir görüşme açmak için Yeni oturum'u tıklayın.

Tam bir kampanya yayınlama

Bu özeti sohbete yapıştırıp gönderin:

Create a complete Instagram campaign for:
- Product: EcoFlow Smart Water Bottle (tracks hydration, keeps drinks cold 24h)
- Target Audience: Health-conscious millennials, 25-35 years old
- Platform: Instagram
- Goal: Brand awareness + drive website traffic
- Brand Voice: Motivational, clean, science-backed
- Budget: $3,000
- Timeline: Launch in 2 weeks

Yaratıcı yönetmen, 5 aracının tamamını sırayla çalıştırır:

  1. Marka stratejisti → pazar araştırması, rakip analizi, kitle analizleri
  2. Metin yazarı → Altyazı, hashtag ve harekete geçirici mesaj içeren 3 Instagram gönderisi
  3. Tasarımcı: Her yayın için Gemini (GCS URI'leri) aracılığıyla oluşturulan görsel konseptler ve gerçek görüntüler
  4. Eleştirmen → ONAYLANDI / DÜZENLENMESİ_GEREKİYOR puanlarıyla birlikte kalite incelemesi
  5. (Gerekirse düzeltme) → Metin yazarı veya tasarımcı, geri bildirimle tekrar arandı
  6. Proje yöneticisi → 2 haftalık zaman çizelgesi, görev dökümü, bütçe ayırma

Demo: Notion entegrasyonuyla kampanya yayınlama

Tek temsilci yönlendirmesini test etme

Bu kısa isteği yeni bir oturumda gönderin:

Research the luxury skincare market - top brands and trends in 2025

Reklam Öğesi Yöneticisi'nin bu isteği yalnızca Marka Stratejisti'ne yönlendirdiğini, başka hiçbir temsilcinin aranmadığını fark edin. Bu, sistem talimatındaki istek sınıflandırma mantığının doğru şekilde çalıştığını gösterir.

Yürütme izlerini inceleme

Hâlâ konsoldayken:

  1. Sol kenar çubuğunda Traces'i (Playground'un yanında) tıklayın.
  2. İzleme Görünümü bölümünde, az önce çalıştırdığınız oturumun izlemesini seçin.
  3. Her aracı çağrısını, giriş/çıkışlarını, gecikmesini ve jeton kullanımını görmek için izleme ağacını genişletin.

Uzmanla yapılan her A2A görüşmesi ayrı bir aralık olarak görünür. Yaratıcı yönetmenin her bir aracıya hangi bağlamı ilettiğini ve karşılığında ne aldığını tam olarak görebilirsiniz.

İsteğe bağlı: Terminalden çalıştırma

Ayrıca, kampanyayı, başlangıç paketinde yer alan run_campaign.py komut dosyasını kullanarak programatik olarak da çalıştırabilirsiniz.

cd ~/ai-creative-studio/workshop/starter
uv run run_campaign.py

15. Temizleme

Devam eden ücretlerden kaçınmak için Google Cloud kaynaklarını temizleyin.

Sökme komut dosyasını çalıştırın. Bu komut dosyası, .env dosyanızı okur ve bu codelab sırasında oluşturulan her şeyi siler:

bash deploy/teardown_gcp.sh

Komut dosyası, tam olarak neyi sileceğini gösterir ve herhangi bir işlem yapmadan önce onay ister:

Kaynak

Neler silinir?

Cloud Run hizmetleri

brand-strategist, copywriter, designer, critic, project-manager

Agent Runtime

Kreatif direktör muhakeme motoru + tüm oturumlar

Artifact Registry

cloud-run-source-deploy depo + tüm Docker görüntüleri

GCS paketleri

{PROJECT_ID}-campaign-images, {PROJECT_ID}-agent-staging ve run-sources-{PROJECT_ID}-{REGION}

Secret Manager

notion-token, notion-project-db-id, notion-tasks-db-id (oluşturulmamışsa atlanır)

Her şeyin kaldırıldığını doğrulayın

gcloud run services list --region=us-central1
gcloud storage buckets list --project=$GCP_PROJECT_ID

Beklenen çıktı: Boş listeler veya yalnızca önceden var olan kendi kaynaklarınız.

16. Özet

Tebrikler! Google Cloud'da üretim düzeyinde çoklu ajanlı bir yapay zeka sistemi oluşturup dağıttınız.

Oluşturduğunuz öğeler

Temsilci

Kapasite

Dağıtım

Marka Stratejisti

Google Arama aracılığıyla pazar araştırması

Cloud Run

Metin Yazarı

Instagram'da başlık oluşturma

Cloud Run

Tasarımcı

Gemini + GCS yükleme ile görüntü üretme

Cloud Run

Eleştirmen

Puanlama içeren kalite incelemesi

Cloud Run

Proje Yöneticisi

Timeline + Notion MCP

Cloud Run

Kreatif Direktör

A2A aracılığıyla tam düzenleme

Agent Runtime

Öğrendiğiniz önemli kalıplar

  1. ADK Agent: Talimat ve isteğe bağlı araçlarla bir LLM aracısı tanımlayın
  2. adk web: Yerleşik bir sohbet kullanıcı arayüzüyle herhangi bir ADK aracısını yerel olarak çalıştırma ve test etme
  3. SkillToolset: Yeniden kullanılabilir bilgileri, isteğe bağlı olarak yüklenen modüler dosyalara paketleyin.
  4. FunctionTool: Herhangi bir Python işlevini (veya harici modeli) çağrılabilir bir aracı olarak sarmalama
  5. to_a2a(): Herhangi bir ADK aracısını A2A uyumlu bir HTTPS hizmeti olarak kullanıma sunma
  6. RemoteA2aAgent + AgentTool: Uzak aracıları çağrılabilir araçlar olarak düzenleyin
  7. McpToolset: MCP stdio sunucuları üzerinden harici hizmetlere bağlanma
  8. EventsCompactionConfig - Uzun çok aracılı iş akışlarında jeton sınırlarını yönetme
  9. Yapılandırılmış eleştirmen çıktısı: Otomatik düzeltme ile makine tarafından okunabilir kalite kontrolü
  10. Cloud Run: Container mimarisine alınmış aracıları geniş ölçekte dağıtma
  11. Agent Runtime: Yönetilen oturumlar ve izleme ile düzenleyicilere ev sahipliği yapın

Sonraki adımlar

  • gemini-3.1-flash-image'ın düzenleme özelliğini kullanarak Tasarımcı'ya çok turlu görüntü düzenleme ekleme
  • Cloud Run hizmetlerine IAM kimlik doğrulaması ekleme (--allow-unauthenticated simgesini kaldırma)
  • Bir uzmanı LangGraph veya CrewAI aracısıyla değiştirin. A2A, çerçeveye özel değildir.
  • Katılımcıların çıkışları değerlendirip yineleyebilmesi için kullanıcı geri bildirimini araç olarak ekleyin.
  • Cloud Console'da Agent Runtime izlemeyi keşfedin

Kaynaklar