Membangun Studio Kreatif Multi-Agen dengan Agent Stack Google: ADK, A2A, MCP di Cloud Run & Agent Runtime

1. Ringkasan

Dalam codelab ini, Anda akan membangun AI Creative Studio - sistem multi-agen terdistribusi yang mengubah satu perintah menjadi kampanye Instagram yang lengkap.

Ketik satu kalimat. Dapatkan kembali riset audiens, teks, konsep visual, teks yang telah ditinjau kualitasnya, dan linimasa project lengkap - semuanya dihasilkan oleh tim agen AI yang berkolaborasi.

Agen yang akan Anda buat

Agen

Peran

Brand Strategist

Menelusuri web untuk mendapatkan analisis audiens, analisis kompetitor, dan tren 2025

Copywriter

Menulis teks Instagram dengan hashtag dan CTA - didukung oleh Skill ADK yang memuat pedoman platform dan formula teks sesuai permintaan

Desainer

Membuat konsep visual dan menghasilkan gambar asli melalui Gemini, yang disimpan di GCS

Kritik

Salinan dan visual ulasan - menampilkan APPROVED atau NEEDS_REVISION dengan masukan spesifik

Project Manager

Membuat linimasa project dan perincian tugas, yang secara opsional disinkronkan ke Notion melalui MCP

Creative Director

Mengatur semua lima spesialis secara berurutan - Anda memberikan satu perintah, lalu model akan mengoordinasikan sisanya

Kelima agen di-deploy sebagai microservice Cloud Run yang independen. Agen berkomunikasi melalui protokol A2A - standar terbuka yang tidak bergantung pada bahasa sehingga agen mana pun dapat memanggil agen lain terlepas dari framework-nya. Creative Director berjalan di Agent Runtime dan terhubung ke setiap spesialis dari jarak jauh.

Arsitektur

Ringkasan Sistem

Yang akan Anda pelajari

  • Bangun agen LLM dengan Google ADK - Agent, petunjuk sistem, dan alat bawaan.
  • Paketkan pengetahuan agen yang dapat digunakan kembali ke dalam file modular dengan Keterampilan ADK (SkillToolset).
  • Membuat gambar nyata dengan menghubungkan agen teks ke model gambar melalui FunctionTool.
  • Mengintegrasikan API eksternal tanpa kode lem kustom menggunakan Model Context Protocol (MCP).
  • Ubah agen apa pun menjadi layanan yang dapat dipanggil melalui jaringan menggunakan Protokol Agent to Agent (A2A) melalui HTTPS.
  • Mengatur agen terdistribusi dengan RemoteA2aAgent dan AgentTool.
  • Kemasan dan deploy agen independen sebagai microservice Cloud Run.
  • Menampung orkestrator stateful di Agent Runtime.
  • Menjaga alur kerja multi-agen yang panjang dalam batas konteks menggunakan pemadatan konteks.
  • Membangun loop kontrol kualitas: Output ulasan kritikus → revisi otomatis jika diperlukan.

Yang Anda butuhkan

  • Project Google Cloud dengan penagihan diaktifkan
  • Peran IAM Pemilik atau Editor
  • Pengetahuan Python dasar

2. Menyiapkan Lingkungan Anda

Untuk codelab ini, kita akan menggunakan Cloud Shell.

Apa itu Cloud Shell?

Cloud Shell adalah lingkungan Linux berbasis browser gratis dengan semuanya telah diinstal sebelumnya: gcloud, git, Python, Docker, dan lainnya. Anda tidak perlu menginstal apa pun secara lokal.

Untuk membuka Cloud Shell, klik ikon terminal di toolbar kanan atas Konsol GCP:

Buka Cloud Shell dari toolbar GCP Console

Saat membuka Cloud Shell untuk pertama kalinya, Anda akan diminta untuk memverifikasi akun Anda - klik Verify:

Dialog verifikasi akun Anda

Kemudian, klik Authorize untuk mengizinkan Cloud Shell melakukan panggilan Google Cloud API:

Dialog Otorisasi Cloud Shell

Cloud Shell kini sudah siap. Anda akan melihat pesan selamat datang di terminal: Terminal Cloud Shell siap

Lakukan autentikasi dan konfigurasi project Anda

Cloud Shell sudah diautentikasi dengan Akun Google Anda. Konfirmasi akun aktif Anda dan temukan Project ID Anda:

gcloud config list

Anda juga dapat melihat Project ID di dasbor Konsol GCP di panel sisi kiri. Salin - Anda akan membutuhkannya di perintah berikutnya:

Temukan Project ID Anda di Konsol GCP dan tetapkan di Cloud Shell

Sekarang tetapkan project Anda:

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

Output yang diharapkan:

Project: my-project-123

Aktifkan API yang diperlukan

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

Proses ini memerlukan waktu sekitar 2 menit. Anda akan melihat Operation finished successfully setelah selesai.

Menyiapkan Kredensial Default Aplikasi (ADC)

Agen akan memanggil Gemini Enterprise Agent Platform menggunakan Google Auth library, yang memerlukan Kredensial Default Aplikasi - terpisah dari autentikasi CLI gcloud.

Jalankan ini sekali:

gcloud auth application-default login

Tab browser akan terbuka dan meminta Anda untuk mengonfirmasi. Klik Izinkan. Anda akan melihat:

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

Meng-clone repositori starter

Codelab ini menggunakan repositori awal - project kerangka dengan semua infrastruktur yang sudah ada (Dockerfiles, pyproject.toml, skrip deployment), tetapi dengan logika agen yang harus Anda tulis.

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

Setiap agent.py berisi placeholder # TODO tempat Anda akan menulis logika agen. Skrip Dockerfile, pyproject.toml, dan deployment sudah selesai.

Mengonfigurasi variabel lingkungan

Salin contoh yang diberikan dan masukkan project ID Anda dalam satu langkah:

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

Kemudian, buat bucket GCS tempat Designer akan menyimpan gambar yang dihasilkan dan perbarui .env dengan namanya:

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

Kemudian, siapkan dukungan URL gambar bertanda tangan. Direktur Kreatif membuat link HTTPS yang dapat diklik untuk setiap gambar dalam ringkasan akhir kampanye. Hal ini memerlukan akun layanan untuk menandatangani URL. Jalankan perintah berikut untuk mengonfigurasinya:

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

Buka .env di editor untuk meninjau semua setelan:

cloudshell edit .env

Tindakan ini akan membuka .env sebagai tab di Cloud Shell Editor - klik tombol Open Editor di toolbar jika panel editor tidak terlihat:

Klik Open Editor di toolbar Cloud Shell

Cloud Shell Editor dengan struktur file project

Pastikan project telah disetel dengan benar:

grep GOOGLE_CLOUD_PROJECT .env

Menginstal dependensi

Kita menggunakan uv - pengelola paket Python modern yang cepat yang menangani lingkungan virtual dan menginstal dalam satu alat. Cara ini ~10-100x lebih cepat daripada pip dan merupakan cara yang direkomendasikan untuk mengelola project Python.

uv sudah diinstal di Cloud Shell. Semua agen memiliki dependensi inti yang sama, jadi instal sekali dan akan berfungsi untuk setiap agen dalam codelab ini:

uv sync

Perintah uv sync membaca pyproject.toml dan membuat direktori .venv/ dengan semua dependensi. Setiap spesialis juga memiliki pyproject.toml sendiri yang digunakan secara eksklusif oleh build Docker - penginstalan bersama di atas mencakup semua yang Anda butuhkan untuk pengujian lokal.

3. Memahami Google ADK

Sebelum menulis kode, mari kita pahami Agent Development Kit (ADK) - framework yang akan Anda gunakan untuk membangun setiap agen dalam codelab ini.

Apa itu ADK?

Agent Development Kit (ADK) adalah framework fleksibel dan modular untuk mengembangkan serta men-deploy agen AI. Meskipun dioptimalkan untuk Gemini dan ekosistem Google, ADK tidak bergantung pada model, tidak bergantung pada deployment, dan dibuat agar kompatibel dengan framework lain. ADK dirancang agar pengembangan agen terasa lebih seperti pengembangan software, sehingga memudahkan developer membuat, men-deploy, dan mengorkestrasi arsitektur agentic yang berkisar dari tugas sederhana hingga alur kerja yang kompleks.

ADK menangani bagian yang kompleks - panggilan alat, percakapan multi-giliran, pengelolaan konteks, streaming - sehingga Anda dapat berfokus pada logika agen.

Elemen penyusun agen ADK

Setiap agen terdiri dari empat elemen penyusun:

Blokir

Peran

Model

LLM yang menalar sasaran, menentukan rencana, dan menghasilkan respons

Alat

Fungsi yang mengambil data atau melakukan tindakan dengan memanggil API atau layanan

Orkestrasi

Mempertahankan memori dan status di seluruh giliran, merutekan panggilan alat, meneruskan hasil kembali ke model

Runtime

Mengeksekusi sistem saat dipanggil - secara lokal melalui adk web, atau sebagai layanan yang di-deploy

Definisi agen

Setiap dari 5 agen dalam codelab ini ditentukan dengan cara yang sama:

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
)

Kolom

Tujuan

name

ID unik - digunakan oleh orkestrator untuk merutekan panggilan

model

Model Gemini yang mendukung agen ini

instruction

Perintah sistem - menentukan peran, batasan, dan format output agen

description

Ringkasan satu baris - orchestrator membaca ini untuk memutuskan spesialis mana yang akan dipanggil

tools

Fungsi yang dapat dipanggil LLM (bawaan seperti google_search, atau fungsi Python kustom)

Cara ADK menjalankan agen

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 secara mandiri memutuskan apakah akan memanggil alat, alat mana yang akan dipanggil, dan dengan argumen apa. Anda menulis petunjuk - ADK akan menangani sisanya.

4. Membangun dan Menguji agen Brand Strategist

Mari kita mulai dengan agen pertama: Ahli Strategi Merek. Agen ini hanya untuk riset guna menelusuri insight audiens target, analisis kompetitor, dan topik trending menggunakan Google Penelusuran.

Buka file agen kerangka di Cloud Shell Editor:

cloudshell edit agents/brand_strategist/agent.py

Anda akan melihat dua bagian # TODO yang harus Anda isi.

TODO 1 - Tulis petunjuk sistem

Pertama, Anda akan menulis petunjuk sistem untuk agen. Petunjuk sistem adalah string yang menentukan peran, batasan, dan format output agen.

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 - Buat root_agent

Selanjutnya, ganti root_agent yang belum selesai dengan:

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

Menguji secara lokal dengan UI web ADK

Sekarang mari kita uji agen menggunakan UI web ADK - antarmuka chat bawaan untuk menguji agen sebelum men-deploy ke cloud.

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

Anda akan melihat:

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

Server kini berjalan di dalam Cloud Shell:

Untuk membukanya di browser, gunakan Pratinjau Web:

  1. Lihat toolbar Cloud Shell di bagian atas halaman
  2. Klik ikon Web Preview (terlihat seperti kotak dengan panah ke atas, di kanan atas toolbar Cloud Shell)
  3. Klik "Ubah port" dan masukkan 8000, lalu klik "Ubah dan Pratinjau"

Tab browser baru akan terbuka dengan UI web ADK. Klik dropdown "Select an agent" di kiri atas - Anda akan melihat semua agen Anda tercantum:

Pilih brand_strategist untuk mulai menguji:

Coba perintah pengujian berikut

Di kotak chat UI web ADK, coba:

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

Anda akan melihat agen memanggil Google Penelusuran dan menampilkan riset terstruktur dengan bagian Audience Insights, Competitive Analysis, dan Trending Topics.

5. Membangun Keterampilan Copywriter - ADK

Peran: Mengubah riset merek menjadi teks Instagram. Copywriter membuat 3 variasi teks yang mencakup berbagai gaya bahasa (inspiratif, edukatif, komunitas), masing-masing dengan hashtag dan CTA.

Konsep: Keterampilan ADK

Pendekatan sederhana akan menyematkan semua pengetahuan platform - batas karakter, tingkat hashtag, formula teks, contoh gaya bahasa merek - langsung dalam perintah sistem. Cara ini memang berfungsi, tetapi membuat setiap permintaan menjadi besar dengan konten yang hanya dibutuhkan agen sesekali.

Keterampilan ADK (SkillToolset, diperkenalkan di ADK 1.25.0) memungkinkan Anda mengemas pengetahuan tersebut ke dalam file modular dengan tiga tingkat pemuatan:

  • L1 - frontmatter (name + description di SKILL.md): selalu tersedia, digunakan untuk penemuan skill
  • L2 - instructions (isi SKILL.md): dimuat saat agen memicu skill
  • L3 - resource (file references/ dan assets/): dimuat hanya saat agen membacanya secara eksplisit

Petunjuk sistem menyusut menjadi pernyataan peran singkat ditambah "muat skill sebelum menulis". Detail platform hanya masuk ke periode konteks saat agen benar-benar membutuhkannya.

Keahlian Copywriter ada di agents/copywriter/skills/instagram-copywriting/:

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

Buka file langsung di editor Cloud Shell:

cloudshell edit agents/copywriter/agent.py

TODO 1 - Impor load_skill_from_dir dan skill_toolset

Cari komentar # TODO 1: Import load_skill_from_dir and skill_toolset dan tambahkan dua impor:

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

TODO 2 - Muat keahlian dan buat SkillToolset

Temukan dua komentar di bawah impor:

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

Ganti dengan:

_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 membaca SKILL.md serta file apa pun di references/ dan assets/. SkillToolset membungkusnya ke dalam format yang diterima agen ADK - toolset, bukan kemampuan mentah.

TODO 3 - Mendaftarkan toolset ke agen

Temukan tools=[], # TODO 3: Add the SkillToolset here dan ganti dengan:

tools=[_copywriting_skills],

Buka file skill untuk melihat strukturnya:

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

Biarkan UI web ADK tetap berjalan. Gunakan dropdown agen untuk beralih ke copywriter tanpa memulai ulang server.

Jika tidak berjalan, mulai lagi:

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

Coba: Alihkan dropdown ke copywriter dan kirim:

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. Membangun Designer - Pembuatan Gambar Multimodal

Biarkan UI web ADK tetap berjalan. Gunakan dropdown agen untuk mengganti agen tanpa memulai ulang server.

Peran: Membuat konsep visual untuk setiap teks dan membuat gambar sebenarnya menggunakan pembuatan gambar native Gemini. Designer menghasilkan tepat 1 konsep visual per teks - dengan perintah, gaya, palet warna, suasana, dan format Instagram yang mendetail - lalu segera memanggil alat generate_image untuk menghasilkan gambar sebenarnya dan menguploadnya ke GCS.

Konsep: Menghubungkan agen teks dengan model gambar melalui alat

Desainer berjalan di gemini-3-flash-preview (model teks yang ditetapkan melalui GEMINI_MODEL di .env), tetapi pembuatan gambar memerlukan model khusus (gemini-3.1-flash-image-preview). Model gambar tersebut tidak mendukung panggilan fungsi, sehingga tidak dapat digunakan secara langsung sebagai agen ADK. Sebagai gantinya, fungsi ini di-wrap dalam fungsi Python biasa dan didaftarkan sebagai FunctionTool.

Pola ini berlaku untuk model atau API apa pun yang tidak dapat dipanggil LLM secara langsung: bungkus dalam alat, biarkan agen mengatur kapan harus memanggilnya, dan dapatkan hasil terstruktur.

Designer agent (text model)
        
          decides visual concept, writes image prompt
        
  generate_image tool
        
          calls gemini-3.1-flash-image-preview
          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)

Buka file langsung di editor Cloud Shell:

cloudshell edit agents/designer/image_gen_tool.py

Tanda tangan fungsi, penyiapan lingkungan, dan penyisipan rasio aspek disediakan. Selesaikan ketiga TODO secara berurutan:

TODO 1 - Panggil model gambar Gemini

Temukan komentar # TODO 1 dan ganti dengan:

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

TODO 2 - Ekstrak byte gambar dari respons

Temukan komentar # TODO 2 dan ganti dengan:

        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"}

TODO 3 - Upload ke GCS dan menampilkan URI

Temukan komentar # TODO 3 dan ganti dengan:

        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}"

Coba: Alihkan dropdown ke designer dan kirim:

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. Membangun Kritikus - Output Terstruktur

Peran: Memastikan kualitas teks dan visual sebelum diserahkan kepada Project Manager. Kritikus menilai kedua hasil kerja dan menampilkan APPROVED atau NEEDS_REVISION dengan saran spesifik. Jika nilai gcs_uri ada dalam input, alat review_image akan dipanggil untuk memeriksa setiap gambar yang dihasilkan secara visual sebelum diberi skor.

Konsep: Kapan menggunakan model Pydantic untuk output Gemini

Aturan ini terkait siapa yang menggunakan output:

  • Kode Python menggunakannya → gunakan response_schema + Pydantic. Kode tidak dapat menangani ambiguitas, jadi Anda memerlukan struktur yang terjamin untuk mengekstrak kolom secara andal.
  • LLM menggunakannya → format teks + petunjuk sistem sudah cukup. LLM memahami aturan pemformatan dan mentoleransi variasi.

Di review_image, kode Python memerlukan score, approval_status, what_works, issues, dan suggestions sebagai nilai yang diketik. Penerusan response_schema=_GeminiReview membatasi Gemini di tingkat API untuk menampilkan JSON yang valid; model_validate_json() menguraikannya menjadi objek yang diketik yang dapat digunakan kode Anda dengan andal.

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

Buka file langsung di editor Cloud Shell:

cloudshell edit agents/critic/image_review_tool.py

Model Pydantic dan perintah disediakan. Selesaikan ketiga TODO secara berurutan:

TODO 1 - Buat bagian gambar dari URI GCS

Temukan komentar # TODO 1 dan ganti dengan:

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

TODO 2 - Panggil Gemini dengan skema respons terstruktur

Temukan komentar # TODO 2 dan ganti dengan:

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

TODO 3 - Parse respons dan tampilkan hasilnya

Temukan komentar # TODO 3 dan ganti dengan:

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

Coba: Alihkan dropdown ke critic dan kirim:

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.

Verifikasi bahwa respons berisi **POSTS REVIEW:**, Status: APPROVED (atau NEEDS_REVISION), dan **OVERALL ASSESSMENT:**. Jika bagian tersebut ada, Critic siap dihubungkan ke orkestrator.

Setelah Anda selesai menguji ketiga agen, tekan Ctrl+C untuk menghentikan server.

8. Membangun Agen Project Manager dengan MCP

Project Manager memperkenalkan konsep baru: MCP (Model Context Protocol).

Buka file:

cloudshell edit agents/project_manager/agent.py

File ini lebih kompleks - memiliki fungsi create_project_manager_agent() dengan dua cabang: satu tanpa Notion (linimasa hanya teks) dan satu dengan toolset MCP Notion. Anda akan mengisi keduanya.

Masalah yang diatasi MCP

Agen Anda perlu memanggil layanan eksternal - misalnya, membuat halaman di Notion. Anda dapat menulis kode Python yang memanggil Notion REST API secara langsung. Namun, kemudian:

  • Setiap developer menulis wrapper yang berbeda
  • Anda perlu mempertahankan kode integrasi kustom
  • LLM tidak mengetahui keberadaan API kecuali jika Anda menjelaskan setiap endpoint secara manual

MCP memecahkan masalah ini dengan menentukan cara standar bagi layanan eksternal untuk mengekspos kemampuannya sebagai alat yang dapat ditemukan dan dipanggil secara otomatis oleh LLM.

Apa itu MCP?

MCP (Model Context Protocol) adalah standar terbuka (dipublikasikan oleh Anthropic) untuk menghubungkan agen AI ke alat dan sumber data eksternal. Cara kerjanya seperti adaptor universal.

Server MCP adalah program kecil yang:

  1. Membungkus API eksternal (Notion, GitHub, database, filesystem...)
  2. Mengekspos API tersebut sebagai daftar alat yang diketik dan didokumentasikan
  3. Berkomunikasi dengan agen melalui protokol sederhana (stdio atau HTTP)

Agen terhubung ke server MCP, otomatis menemukan alat yang tersedia, dan dapat memanggilnya seperti alat lainnya - LLM melihat API-post-page(...) sebagai fungsi yang dapat dipanggil.

A2A vs MCP - apa perbedaannya?

Hal ini sering kali membingungkan. Berikut perbedaan utamanya:

A2A

MCP

Yang terhubung

Agen ↔ Agen

Agen ↔ Alat/layanan eksternal

Sisi lainnya adalah

Agen LLM lainnya

Wrapper API (tanpa LLM)

Contoh

Direktur Kreatif menelepon Pakar Strategi Merek

Project Manager memanggil Notion API

Protokol

JSON-RPC melalui HTTPS

Aliran stdio atau HTTP

Ditentukan oleh

Google

Anthropic

Pikirkan seperti ini:

  • A2A = cara agen berkomunikasi dengan agen lain
  • MCP = cara agen berkomunikasi dengan alat dan layanan

Dalam project ini, keduanya digunakan bersama:

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

Cara kerja MCP dalam project ini

Saat agen berjalan, ADK meluncurkan notion-mcp-server sebagai proses turunan. Proses tersebut mengekspos alat ini langsung ke LLM:

Alat

Fungsinya

API-retrieve-a-database

Mengambil skema (nama properti, jenis, nilai yang valid)

API-post-database-query

Mengkueri halaman yang ada

API-post-page

Membuat halaman baru

API-patch-page

Memperbarui halaman yang ada

LLM memanggilnya seperti fungsi lainnya - LLM tidak tahu bahwa fungsi tersebut melewati MCP ke Notion REST API di balik layar.

Mengapa stdio? Mengapa tidak menggunakan HTTP saja?

Server MCP berjalan sebagai proses turunan agen, yang berkomunikasi melalui stdin/stdout. Artinya:

  • Tidak memerlukan port jaringan tambahan
  • Siklus proses dikelola oleh agen (dimulai sesuai permintaan, dihentikan saat keluar)
  • Semuanya dikirim dalam satu image Docker - tidak ada layanan terpisah untuk di-deploy

(Opsional) Mengaktifkan Integrasi Notion

Anda dapat melewati seluruh bagian ini. Agen Project Manager selalu menghasilkan linimasa kampanye berbasis teks yang lengkap, dengan atau tanpa Notion. Jika Anda melewati penyiapan ini, agen akan kembali ke mode dalam memori dan menampilkan linimasa sebagai teks biasa dalam chat. Tidak ada yang rusak - Anda hanya tidak akan melihat tugas muncul di database Notion. Langsung buka TODO 1 jika Anda ingin melewati.

Jika Anda memiliki akun Notion dan ingin melihat cara kerja integrasi MCP, selesaikan penyiapan di bawah sekarang. TODO yang mengikuti merujuk pada ID database Notion - di sinilah Anda mendapatkannya.

Langkah 1 - Buat database Notion dari template

Kami menggunakan template Notion Projects & Tasks resmi sebagai database kami. Kami sengaja memilih template ini untuk mendemonstrasikan setelan dunia nyata yang kompleks - template ini memiliki beberapa jenis properti (status, rentang tanggal, relasi, pilihan) dengan nama yang tidak jelas. Ini adalah pengujian yang bagus untuk penemuan skema dinamis MCP: agen harus mengetahui nama properti yang tepat saat runtime, bukan memiliki nama properti yang di-hardcode.

Klik link di bawah untuk menambahkan template ke ruang kerja Notion Anda:

→ Menambahkan template "Projects & Tasks" ke Notion

Template Notion Projects & Tasks di Marketplace

Setelah ditambahkan, Anda akan memiliki dua database tertaut: Projects dan Tasks. Template dilengkapi dengan contoh entri - hapus semuanya sebelum melanjutkan agar agen memulai dengan ruang kerja yang bersih (pilih semua → Hapus).

Langkah 2 - Buat integrasi Notion

Buat integrasi:

  1. Buka notion.so/my-integrations
  2. Klik New Integration → beri nama AI Creative Studio
  3. Mengaitkannya dengan ruang kerja Anda
  4. Klik Konfigurasi setelan → pastikan kemampuan Baca konten, Perbarui konten, dan Sisipkan konten semuanya dicentang

Setelan integrasi Notion - beri nama “AI Creative Studio” dan salin token

  1. Salin Internal Integration Token (ntn_...) dan tempelkan ke file .env Anda:
NOTION_TOKEN=ntn_your-token-here

Hubungkan integrasi ke database Anda:

  1. Buka halaman template yang baru saja Anda duplikasikan, lalu klik database Projects
  2. Klik menu ... (kanan atas) → KoneksiTambahkan koneksi → pilih AI Creative Studio

Klik Koneksi di menu database untuk berbagi dengan integrasi Anda

AI Creative Studio muncul sebagai koneksi aktif

  1. Lakukan hal yang sama untuk database Tasks

Mendapatkan ID database:

  1. Klik link database Projects untuk membukanya - link ini akan terbuka di halamannya sendiri dengan URL seperti:
https://www.notion.so/9887b6a94f7f83f68f8581e038d1aaa4?v=2c37b6a94f7f838685f1086e312c7278

Membuka database Project dari halaman template

ID database adalah UUID pertama dalam URL - semua yang ada sebelum ?v=:

https://www.notion.so/{DATABASE_ID}?v=...
                       ^^^^^^^^^^^^^^^^
                       9887b6a94f7f83f68f8581e038d1aaa4  ← this is your DATABASE_ID
  1. Lakukan hal yang sama untuk link database Tasks guna mendapatkan ID databasenya
  2. Tambahkan ketiga nilai ke .env Anda:
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

Langkah 3 - Instal server MCP Notion

Project Manager terhubung ke Notion melalui paket Node.js @notionhq/notion-mcp-server resmi. Instal secara global:

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

Verifikasi penginstalan:

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

Output yang diharapkan:

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

notion-mcp-server: command not found

? Pastikan Node.js telah diinstal (node --version) dan bin global npm Anda ada di PATH Anda (export PATH=$PATH:$(npm bin -g)).

Langkah 4 - Verifikasi .env Anda

Buka .env dan konfirmasi bahwa ketiga nilai Notion telah ditetapkan (Anda menambahkannya di Langkah 2):

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

Agen Project Manager otomatis mendeteksi variabel ini saat startup dan mengaktifkan set alat MCP Notion.

Cara kerja penemuan skema

Project Manager menggunakan penemuan skema dinamis - tidak pernah meng-hardcode nama properti Notion:

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

Artinya, agen akan otomatis menyesuaikan diri dengan struktur database Notion apa pun - ganti nama properti Anda ke bahasa Prancis, Arab, atau bahasa lainnya, dan agen akan tetap berfungsi.

TODO 1 - Tulis petunjuk sistem

Starter sudah menghitung notion_section - string kosong saat Notion tidak dikonfigurasi, atau blok yang berisi ID database ditambah panduan alat lengkap saat dikonfigurasi. Hal ini membuat petunjuk Notion sepenuhnya tidak ada dalam perintah agen tanpa Notion; LLM tidak pernah melihat aturan untuk alat yang tidak dimilikinya.

Tugas Anda adalah mengganti placeholder return dengan petunjuk sistem sebenarnya yang menggunakan {notion_section}:

    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 - Agen tanpa Notion

Di dalam create_project_manager_agent(), di cabang if not notion_token, ganti agen yang belum lengkap dengan:

        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",
        )

TODO 3 - Agen dengan Notion MCP

Catatan: File awal sudah berisi callback handle_notion_error yang telah ditulis sebelumnya di atas create_project_manager_agent(). Alat ini mencegat error Notion API (400/404) dan mengganti payload error mentah dengan pesan yang jelas dan dapat ditindaklanjuti sehingga LLM dapat mengoreksi diri sendiri. Anda hanya perlu menghubungkannya melalui after_tool_callback.

Pertama, baca kedua ID database di bagian atas create_project_manager_agent():

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

Kemudian di cabang else, buat toolset MCP dan agen:

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

Praktik terbaik: Jangan pernah gagal total pada integrasi opsional. Linimasa teks selalu menjadi hasil utama; Notion bersifat tambahan.

Menguji Project Manager Secara Lokal dengan ADK Web

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

Buka Pratinjau Web di port 8000. Gunakan dropdown agen untuk memilih project_manager, lalu coba:

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.

Anda akan melihat linimasa teks terstruktur dengan fase, daftar tugas, dan tonggak pencapaian. Jika kredensial Notion ditetapkan di .env, agen juga akan membuat entri di ruang kerja Notion Anda.

9. Memahami A2A Protocol

Kita akan menggunakan Protokol Agent-to-Agent (A2A) untuk menghubungkan berbagai agen dalam sistem kita. Mari kita pahami cara kerjanya.

Masalah yang dipecahkan A2A

Bayangkan Anda memiliki agen Brand Strategist yang dibangun dengan ADK dan agen Copywriter yang dibangun dengan LangGraph. Bagaimana cara memanggil orang lain? Mereka menggunakan bahasa internal yang berbeda. Anda harus menulis kode penggabungan kustom setiap saat.

A2A mengatasi hal ini dengan menentukan bahasa universal yang dapat digunakan oleh agen mana pun, terlepas dari framework-nya. Ini adalah HTTP dunia agen: standar yang disepakati semua orang sehingga siapa pun dapat berbicara dengan siapa pun.

Apa itu A2A?

Agent-to-Agent (A2A) adalah standar terbuka untuk komunikasi agen yang dipublikasikan oleh Google. Parameter ini menentukan:

  1. Cara agen mendeskripsikan dirinya sendiri - kartu agen di /.well-known/agent.json
  2. Cara agen lain memanggilnya - JSON-RPC melalui HTTPS
  3. Cara hasil ditampilkan - streaming atau respons tunggal

Yang membuat A2A fleksibel:

  • Agnostik bahasa - Agen Python dapat berkomunikasi dengan agen TypeScript
  • Agnostik framework - Agen ADK dapat berkomunikasi dengan agen LangGraph atau CrewAI
  • Agnostik terhadap infrastruktur - agen lokal dapat berkomunikasi dengan agen cloud

Cara kerjanya - langkah demi langkah

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    │
      │  ◄───────────────────────────────│
      │  ◄───────────────────────────────│
      │  ◄───────────────────────────────│

Langkah 1 - Penemuan: Pengelola mengambil kartu agen satu kali untuk mempelajari nama, URL, dan kemampuan agen.

Langkah 2 - Pemanggilan: Pengelola mengirimkan tugas melalui JSON-RPC POST. Isi berisi pesan (perintah untuk spesialis).

Langkah 3 - Respons: Pakar akan mengalirkan kembali responsnya dalam bagian-bagian, seperti panggilan LLM biasa.

Kartu agen

Setiap agen memublikasikan deskripsi dirinya di /.well-known/agent.json. Ini seperti kartu nama - memberi tahu dunia tentang kemampuan agen dan tempat untuk menghubunginya:

{
  "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"
    }
  ]
}

Orchestrator membaca kartu ini untuk membuat objek RemoteA2aAgent-nya - tidak diperlukan pengetahuan hardcode tentang internal spesialis.

Mengekspos agen melalui A2A di ADK

to_a2a() membungkus agen ADK apa pun dalam aplikasi FastAPI yang kompatibel dengan A2A. Satu baris:

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)

Tindakan ini akan otomatis membuat:

  • /.well-known/agent.json - kartu agen
  • / - endpoint JSON-RPC (semua permintaan tugas A2A masuk ke jalur root)

10. Mengekspos agen sebagai layanan A2A

Untuk mengekspos agen sebagai layanan A2A, Anda dapat menggunakan fungsi utilitas to_a2a() dari ADK.

Cara kerja to_a2a()

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() membungkus agen ADK Anda dalam aplikasi FastAPI yang otomatis mengekspos:

  • /.well-known/agent.json - kartu agen (nama, deskripsi, kemampuan)
  • /a2a/{agent_name} - endpoint JSON-RPC untuk menerima tugas

Kode kerangka setiap agen sudah menyertakan blok __main__ yang membungkus agen di server A2A menggunakan to_a2a(). Anda tidak perlu menulis kode ini - kode ini sudah disediakan.

Memahami konfigurasi URL ganda

Saat Anda menjalankan python agent.py, blok __main__ menggunakan dua konfigurasi URL terpisah:

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

Lingkungan

HOST:PORT (mendengarkan)

PUBLIC_HOST:PUBLIC_PORT (diiklankan di kartu agen)

Lokal

0.0.0.0:8082

http://localhost:8082

Cloud Run

0.0.0.0:8080

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

Secara lokal, keduanya mengarah ke mesin yang sama. Di Cloud Run, penampung memproses secara internal di 8080, tetapi kartu agen harus mengiklankan URL HTTPS publik. Jika tidak, Creative Director tidak dapat menghubungi spesialis dari luar penampung.

Mulai semua 5 server A2A spesialis

Mari jalankan kelima spesialis sebagai server A2A secara bersamaan, lalu uji Creative Director secara lokal yang mengarah ke mereka.

Buka 5 terminal Cloud Shell terpisah (klik ikon + di panel tab terminal) dan jalankan satu agen per terminal.

uv run mengaktifkan .venv secara otomatis - tidak perlu source manual di setiap terminal.

Terminal 1 - Brand Strategist (port 8082):

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

Terminal 2 - Copywriter (port 8083):

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

Terminal 3 - Desainer (port 8084):

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

Terminal 4 - Kritikus (port 8085):

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

Terminal 5 - Project Manager (port 8086):

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

Menetapkan URL localhost di .env

Di Terminal 6, perbarui .env dengan URL agen lokal agar Creative Director dapat menemukannya:

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

Memeriksa agen dengan A2A Inspector

A2A Inspector adalah alat developer open source yang menggunakan protokol A2A secara native. Dengan alat ini, Anda dapat terhubung langsung ke agen A2A yang sedang berjalan, membaca kartu agennya, dan mengirim tugas - semuanya tanpa menulis kode klien apa pun.

Informasi yang ditampilkan:

  • Kartu agen - metadata terstruktur yang diiklankan agen Anda: nama, deskripsi, mode input/output yang didukung, dan URL endpoint-nya. Inilah yang dibaca Direktur Kreatif saat menemukan spesialis.
  • Antarmuka chat - kirim pesan apa pun ke agen melalui A2A dan lihat respons mentahnya. Anda dapat menguji perintah secara terpisah sebelum menghubungkan agen.
  • Validasi protokol - pemeriksa memastikan bahwa kartu agen sesuai dengan spesifikasi A2A, menampilkan kolom yang tidak ada atau respons yang salah format lebih awal.

Mengapa hal ini penting: Saat Anda men-deploy ke Cloud Run nanti, Creative Director akan menemukan setiap spesialis dengan mengambil kartu agennya dari /.well-known/agent.json. Jika kartu tersebut salah - URL buruk, kemampuan tidak ada - orkestrator akan gagal secara diam-diam. Pemeriksa memungkinkan Anda mendeteksi masalah ini secara lokal sebelum deployment cloud.

Kartu agen Brand Strategist

Kartu agen menampilkan identitas dan kemampuan spesialis persis seperti yang dilihat agen lain.

Detail kartu agen

Menginstal dan memulai pemeriksa

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

Update .env adalah perintah satu kali. Gunakan Terminal 6 untuk memulai pemeriksa berikutnya:

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

Untuk membuka UI pemeriksa, gunakan Web PreviewChange port → ketik 5001.

Terhubung dengan Brand Strategist

Masukkan http://localhost:8082 di kolom URL pemeriksa, lalu klik Hubungkan. Pemeriksa mengambil kartu agen dan menampilkan metadata spesialis.

A2A Inspector terhubung ke Brand Strategist

Informasi yang ditampilkan kartu agen

Kartu agen lebih dari sekadar metadata - kartu ini adalah kontrak kapabilitas lengkap yang diiklankan agen ke jaringan. Hubungkan ke Project Manager (http://localhost:8086) untuk melihat contoh yang paling lengkap:

{
  "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"
    }
  ]
}

Ada tiga hal yang terlihat jelas:

1. Alat MCP menjadi kemampuan A2A - Setiap alat Notion yang dapat diakses oleh Project Manager (API-post-page, API-retrieve-a-database, dll.) dicantumkan sebagai kemampuan terpisah di kartu agen. Agen lain di jaringan dapat menemukan secara persis alat apa yang dapat digunakan agen ini - tanpa membaca kode apa pun.

2. Petunjuk sistem disematkan - description keterampilan pertama berisi petunjuk sistem lengkap, termasuk tanggal hari ini dan ID database Notion. Dengan cara ini, Direktur Kreatif mengetahui apa yang harus diteruskan saat memanggil Manajer Proyek.

3. URL adalah endpoint aktif - Bidang url adalah apa yang digunakan RemoteA2aAgent saat Creative Director memanggil spesialis ini. Jika URL di kartu salah, orkestrator tidak dapat menjangkau agen.

Inilah alasan mengapa pemeriksa adalah alat pen-debug yang canggih: sekilas melihat kartu agen akan memberi tahu Anda apakah agen sedang berjalan, alat apa yang dimilikinya, dan apakah endpoint sudah benar.

Mengirim pesan uji coba

Setelah terhubung, ketik perintah di panel chat, lalu kirimkan. Pemeriksa mengirimkannya sebagai tugas A2A dan mengalirkan respons kembali - sama seperti cara Direktur Kreatif akan memanggil agen ini dalam produksi.

Melakukan percakapan dengan Ahli Strategi Merek melalui A2A Inspector

Arahkan pemeriksa ke port lokal (8082–8086) untuk menguji setiap spesialis satu per satu.

11. Membangun Pengelola Direktur Kreatif

Direktur Kreatif adalah pengatur utama. Plugin ini membaca URL spesialis dari variabel lingkungan, membungkus setiap URL sebagai RemoteA2aAgent, dan mengeksposnya sebagai AgentTool yang dapat dipanggil oleh LLM.

Pastikan 5 agen spesialis masih berjalan (Terminal 1–5 dari Langkah 10).

Di Terminal 6 (terminal A2A Inspector), hentikan pemeriksa dengan Ctrl+C.

Buka file:

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

File ini memiliki tiga TODO. Selesaikan secara berurutan.

TODO 1 - Tinjau instruksi sistem yang sudah ditulis

Petunjuk sistem ada di prompt.py di direktori yang sama - petunjuk ini diimpor secara otomatis:

from .prompt import SYSTEM_INSTRUCTION_TEMPLATE

Buka prompt.py untuk membacanya sebelum melanjutkan:

cloudshell edit agents/creative_director/prompt.py

Memahaminya penting karena mengontrol seluruh perilaku orkestrasi.

Mengapa perintah orchestrator mengontrol segalanya

Buka prompt.py bersama bagian ini - contoh di bawah merujuk pada bagian tertentu di dalamnya.

Perintah di prompt.py bukan hanya dokumentasi, tetapi juga bidang kontrol seluruh sistem. Perintah orkestrator yang disusun dengan buruk akan menghasilkan: agen yang dipanggil secara tidak berurutan, konten yang dibuat oleh orkestrator, bukan spesialis, alur kerja yang berlanjut setelah terjadi kegagalan, dan konteks yang dihilangkan secara diam-diam di antara agen. Sembilan elemen ini mencegah kegagalan yang paling umum:

Elemen 0 - Rencanakan terlebih dahulu, lalu jalankan

Ini adalah elemen yang paling penting. Sebelum memanggil spesialis, orkestrator diinstruksikan untuk menampilkan rencana bernomor:

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

Tanpa langkah ini, LLM langsung melakukan panggilan alat dan kehilangan jejak posisinya dalam alur kerja - terutama setelah menerima respons panjang dari spesialis. Menguraikan rencana terlebih dahulu akan memandu orkestrator: orkestrator mengetahui langkah yang sedang dilakukan, langkah selanjutnya, dan seperti apa proses yang lengkap. Melewati langkah ini akan menyebabkan orchestrator terhenti di tengah alur kerja atau mengulangi langkah-langkah.

Elemen 1 - Penentuan peran eksplisit

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

Tanpa larangan eksplisit, LLM terkadang akan melewati panggilan ke spesialis dan membuat konten secara langsung - hal ini lebih cepat dan LLM "tahu" cara melakukannya. Petunjuknya pasti salah.

Elemen 2 - Sintaksis panggilan alat dengan pola yang salah tercantum

Hanya menampilkan sintaksis yang benar saja tidak cukup. LLM dapat menghasilkan panggilan yang terlihat masuk akal, tetapi gagal tanpa pemberitahuan. Perintah secara eksplisit mencantumkan pola yang benar dan pola yang tidak boleh digunakan:

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

Mencantumkan pola yang salah secara eksplisit mengurangi panggilan alat yang salah bentuk hingga ~95% dalam produksi.

Elemen 3 - Eksekusi berurutan yang dijelaskan langkah demi langkah

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

Tanpa langkah (b) dan (c), LLM terkadang akan memanggil dua agen secara bersamaan, atau menganggap berhasil dan melanjutkan sebelum menerima respons.

Elemen 4 - Petunjuk error: STOP, report, do not proceed

Pada versi awal, orkestrator akan menerima error dari salah satu spesialis, membuat output yang masuk akal untuk error tersebut, dan melanjutkan ke agen berikutnya. Pengguna mendapatkan kampanye yang tampak lengkap yang dibangun di atas fondasi halusinasi. Perbaikannya jelas: STOP segera. Laporkan error yang muncul. Jangan pernah melanjutkan.

Elemen 5 - Aturan penerusan konteks

Agen jarak jauh tidak memiliki histori percakapan. Saat orkestrator memanggil Copywriter melalui A2A, Copywriter hanya melihat pesan dalam satu permintaan tersebut - dia tidak tahu apa yang dikatakan Brand Strategist. Orchestrator harus secara eksplisit menggabungkan output sebelumnya ke dalam setiap panggilan berikutnya:

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.")

Petunjuk ini menyatakan secara eksplisit: "Agen jarak jauh TIDAK memiliki memori bersama - Anda harus meneruskan output sebelumnya secara eksplisit." Tanpa hal ini, setiap agen bekerja tanpa mengetahui konteks.

Elemen 6 - Klasifikasi permintaan: sederhana vs. kompleks

Tidak setiap permintaan memerlukan kelima agen. Perintah menginstruksikan orkestrator untuk mengklasifikasikan permintaan sebelum membuat rencana:

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

Tanpa klasifikasi ini, orkestrator akan menjalankan kelima agen untuk setiap permintaan - termasuk "beri saya 3 ide postingan" - sehingga menambah latensi dan biaya yang tidak perlu.

Elemen 7 - Aturan komunikasi: tampilkan output lengkap, tanpa pemfilteran

Perintah secara eksplisit menyatakan bahwa orkestrator tidak boleh meringkas atau mengedit apa yang ditampilkan oleh spesialis:

- 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

Tanpa ini, orkestrator akan menulis ulang output spesialis dengan kata-katanya sendiri, sehingga kehilangan detail, menimbulkan error, dan menggagalkan tujuan memiliki spesialis.

Elemen 8 - Penyelesaian alur kerja: jangan pernah berhenti lebih awal

Mode kegagalan yang halus tetapi penting: orchestrator mengumumkan rencana 5 langkah, menyelesaikan 3 langkah, lalu menyajikan hasil seolah-olah sudah selesai. Perintah mencegah hal ini dengan checklist eksplisit yang harus lulus sebelum orkestrator dapat menyelesaikan:

✓ 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.

Tindakan ini akan menghentikan orkestrator memperlakukan eksekusi sebagian sebagai selesai.

Loop Kontrol Kualitas

Alur kerja revisi adalah bagian paling rumit dari prompt.py. Buka bagian ## REVISION WORKFLOW dan ikuti.

Cara kerjanya

Setelah Kritikus merespons, Direktur Kreatif tidak melanjutkan begitu saja kepada Manajer Proyek. Node ini membaca output Kritikus dan membuat cabang:

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)

Ini didukung LLM, bukan kode

Codelab sebelumnya menyebutkan bahwa orkestrator "mengurai" respons Kritikus. Tidak ada kode Python yang melakukan parsing ini - tidak ada ekspresi reguler, tidak ada pencocokan string. Creative Director adalah LLM yang membaca instruksinya sendiri. Petunjuk tersebut menyatakan:

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

LLM membaca string persis tersebut dalam output Critic dan mengikuti cabang. Itulah sebabnya format Kritikus tidak dapat dinegosiasikan: jika Kritikus menulis "perlu sedikit perbaikan" alih-alih NEEDS_REVISION, LLM tidak akan menemukan kecocokan dalam instruksinya dan akan melewati langkah revisi secara diam-diam.

Cara konteks diteruskan dalam panggilan revisi

Panggilan revisi mengikuti aturan penerusan konteks yang sama dari Elemen 5 - orkestrator harus menyertakan semuanya secara eksplisit karena Copywriter tidak memiliki memori versi pertamanya:

"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."

Tanpa bagian "VERSI PERTAMA ANDA", Copywriter akan menulis dari awal, bukan meningkatkan kualitas hasil yang sudah dibuatnya.

Batas 1 revisi dan alasannya

Setelah satu putaran revisi, orchestrator akan melanjutkan ke Project Manager, terlepas dari skornya. Instruksi melacak hal ini secara mental:

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

Tanpa batas ini, loop dapat berjalan tanpa batas: Kritikus menandai masalah → Copywriter merevisi → Kritikus menandai lagi → Copywriter merevisi lagi. Setiap putaran membutuhkan token dan waktu. Satu revisi sudah cukup untuk meningkatkan kualitas tanpa risiko siklus yang tidak terkendali.

Informasi yang diteruskan ke Manajer Proyek

Project Manager selalu menerima versi akhir yang disetujui, bukan versi aslinya. Jika revisi terjadi, pengelola akan meneruskan salinan dan visual yang direvisi. Jika semuanya disetujui pada tahap pertama, maka semuanya akan langsung disetujui. PM tidak pernah melihat draf yang ditolak.

TODO 2 - Register each specialist as a RemoteA2aAgent + AgentTool

Temukan komentar # TODO 2: For each specialist URL... dan ganti dengan:

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

TODO 3 - Bungkus dalam Aplikasi dengan pemadatan konteks

Alasan pemadatan diperlukan

Setiap pesan dalam percakapan - perintah pengguna, setiap panggilan alat, setiap respons alat - ditambahkan ke jendela konteks yang dibaca LLM pada giliran berikutnya. Dalam alur kerja 5 agen, hal ini akan terakumulasi dengan cepat:

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
...

Pada Agen 4 (Kritikus), jendela konteks berisi output lengkap dari ketiga agen sebelumnya - sering kali 8.000–12.000 token hanya dalam respons alat. Meskipun dengan jendela konteks besar Gemini 2.5 Pro, kualitas penalaran pengelola menurun karena harus memperhatikan histori yang terus bertambah. Tanpa pemadatan, alur kerja yang panjang akan mencapai batas praktis di sekitar Agent 4.

Fungsi pemadatan

Daripada menyimpan setiap peristiwa secara penuh, ADK secara berkala memanggil LLM untuk meringkas peristiwa lama menjadi representasi yang ringkas. Hanya ringkasan peristiwa sebelumnya + output lengkap agen terbaru yang disimpan dalam konteks.

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

Ringkasan ini mempertahankan fakta penting (insight utama, teks yang disetujui, konsep visual) sekaligus menghapus format yang bertele-tele, konteks berulang yang diteruskan ke setiap agen, dan penalaran perantara. Pengkritik masih memiliki semua yang diperlukan untuk melakukan evaluasi - hanya saja ia membaca ringkasan, bukan tiga laporan lengkap.

Kode

Temukan komentar # TODO 3: Wrap the agent in an App... dan ganti placeholder App(...) dengan:

    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 - pemadatan diaktifkan setelah setiap 3 penyelesaian agen. Untuk pipeline 5 agen, ini berarti pipeline dijalankan sekali (setelah agen 1–3), lalu Critic dan PM melihat ringkasan 1–3 ditambah output agen sebelumnya secara lengkap.

overlap_size=1 - output lengkap agen terbaru selalu dipertahankan kata demi kata, tidak pernah diringkas. Hal ini penting karena Kritikus memerlukan output lengkap Desainer - termasuk nilai gcs_uri - untuk memuat dan meninjau gambar sebenarnya. Ringkasan akan kehilangan URI tersebut.

Cara penerapannya dalam menjalankan kampanye lengkap:

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]

Memahami RemoteA2aAgent dan AgentTool

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 memutuskan kapan harus memanggil setiap alat berdasarkan petunjuk sistem dan permintaan pengguna. Orchestrator tidak pernah memanggil agen secara langsung dalam kode - semuanya didorong oleh penalaran LLM.

Menguji Creative Director secara lokal

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

Buka Pratinjau Web di port 8000. Gunakan dropdown agen untuk memilih creative_director, lalu coba:

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

Anda akan melihat bahwa Creative Director akan mengarahkan permintaan ini hanya kepada Brand Strategist dan Anda akan mendapatkan respons dari Brand Strategist.

Untuk kampanye lengkap, coba hal berikut:

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.

Anda akan melihat Creative Director mengoordinasikan kelima spesialis secara berurutan, dengan output setiap agen mengalir ke agen berikutnya.

Demo: Penayangan Kampanye secara Menyeluruh

Hentikan Creative Director (Ctrl+C) sebelum melanjutkan - pemeriksa A2A juga menggunakan port 8000.

Hentikan 5 server spesialis (Ctrl+C di setiap terminal) setelah pengujian lokal selesai.

12. Men-deploy dan Menguji Agen Spesialis

Sekarang kita siap men-deploy agen ke Google Cloud. Cloud Run adalah layanan yang tepat untuk men-deploy agen. Layanan ini serverless, skalabel, dan mudah digunakan. Setiap agen spesialis di-deploy sebagai layanan Cloud Run independen.

Konfigurasi deployment

Dockerfile untuk setiap spesialis mengikuti pola ini:

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

Men-deploy semua 5 spesialis secara berurutan

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

uv run deploy/deploy_all_specialists.py

Skrip ini men-deploy semua 5 agen satu per satu (total ~10-12 menit). Deployment berurutan menghindari kuota polling Cloud Build (60 permintaan/menit). Setelah selesai, skrip ini akan menulis kembali URL Cloud Run setiap agen ke .env.

Setelah Designer di-deploy, skrip akan otomatis memberikan izin roles/storage.objectCreator pada akun layanan Cloud Run-nya di bucket GCS Anda sehingga dapat mengupload gambar yang dihasilkan.

Jika Anda mengonfigurasi kredensial Notion di .env, skrip juga akan menyimpannya dengan aman di Secret Manager (sebagai notion-token, notion-project-db-id, notion-tasks-db-id) dan menyuntikkannya ke layanan Project Manager melalui --set-secrets, bukan variabel lingkungan biasa. Artinya, token tidak pernah muncul di tab lingkungan Cloud Run atau di histori perintah gcloud.

Memverifikasi deployment

Setelah deployment selesai, skrip akan otomatis menulis kembali URL Cloud Run ke .env, menggantikan URL localhost dari langkah sebelumnya:

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"

Creative Director akan otomatis menggunakan URL Cloud Run ini saat di-deploy ke Agent Runtime pada langkah berikutnya.

Memverifikasi kartu agen

Setiap agen yang di-deploy menampilkan kartu agen di /.well-known/agent.json. Ambil untuk mengonfirmasi bahwa semuanya sudah aktif:

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

Output yang diharapkan untuk setiap agen:

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

Menguji dengan A2A Inspector (Cloud Run)

A2A Inspector sudah diinstal dari Langkah 10. Mulai:

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

Buka Pratinjau Web → Ubah port5001. Masukkan URL Cloud Run Anda di kolom koneksi:

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

Klik Connect - tidak memerlukan token autentikasi karena layanan di-deploy dengan --allow-unauthenticated.

Pemeriksa terhubung, memvalidasi kartu agen, dan memungkinkan Anda melakukan chat secara interaktif melalui A2A.

Memeriksa agen yang di-deploy ke Cloud Run

Setelah men-deploy ke Cloud Run, arahkan pemeriksa ke URL HTTPS publik untuk memverifikasi bahwa deployment cloud berfungsi:

A2A Inspector terhubung ke agen Cloud Run

Alur kerjanya sama - tempelkan URL Cloud Run, hubungkan, dan kirim pesan pengujian. Jika kartu agen dimuat dan chat merespons, spesialis telah di-deploy dan dapat dihubungi dengan benar.

13. Men-deploy Creative Director ke Agent Runtime

Orchestrator di-deploy ke Agent Runtime, yang menyediakan status sesi terkelola, penskalaan otomatis, dan pelacakan bawaan.

Mengapa Agent Runtime untuk orkestrator?

Lima spesialis di-deploy ke Cloud Run - ringan, stateless, masing-masing menangani satu tugas. Direktur Kreatif memiliki persyaratan yang berbeda:

Persyaratan

Mengapa hal ini penting

Status sesi

Alur kerja multi-langkah membutuhkan waktu 45+ detik. Runtime Agen mempertahankan status percakapan di antara panggilan alat orkestrator sehingga tidak ada yang hilang di tengah pipeline.

Beban variabel

Terkadang satu kampanye per jam, terkadang banyak secara paralel. Agent Runtime diskalakan hingga nol saat tidak ada aktivitas dan diskalakan secara otomatis - Anda tidak membayar kapasitas yang tidak digunakan.

Kemampuan observasi

Cloud Logging, Cloud Monitoring, dan Cloud Trace sudah tersedia secara bawaan. Anda dapat melihat setiap panggilan A2A, setiap token yang digunakan, setiap lonjakan latensi - tanpa menambahkan instrumentasi apa pun.

Alur kerja yang berjalan lama

Cloud Run memiliki waktu tunggu permintaan 3.600 detik. Agent Runtime dirancang untuk alur kerja yang dapat memakan waktu beberapa menit, dengan percobaan ulang terkelola dan persistensi status.

Cloud Run adalah platform yang tepat untuk spesialis stateless. Agent Runtime adalah platform yang tepat untuk orkestrator stateful.

Men-deploy orchestrator

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

uv run deploy/deploy_orchestrator.py --action deploy

Proses ini memerlukan waktu sekitar 5–10 menit. Setelah selesai, AGENT_ENGINE_ID dan AGENT_ENGINE_RESOURCE_NAME akan disimpan ke .env.

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

Cara kerja deployment

client.agent_engines.create() mengemas objek App Anda, menguploadnya dengan dependensinya, dan men-deploy-nya ke infrastruktur terkelola. Berikut fungsi setiap parameter:

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]

Yang terjadi di balik layar:

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

Setelah deployment, orkestrator terhubung ke lima spesialis Cloud Run melalui URL di variabel lingkungannya

  • Variabel ini diteruskan melalui .env sebelum skrip deployment dijalankan.

14. Menjalankan kampanye secara menyeluruh

Seluruh sistem di-deploy. Jalankan kampanye lengkap dari playground Agent Runtime.

Buka playground Agent Runtime

  1. Buka https://console.cloud.google.com/agent-platform/runtimes. Anda juga dapat membuka Agent Runtime dari Agent Platform > Agents > Deployments.
  2. Pilih Agent Runtime yang di-deploy (creative-director)
  3. Klik Playground di sidebar kiri
  4. Klik Sesi baru untuk membuka percakapan baru

Menjalankan kampanye lengkap

Tempelkan ringkasan ini ke dalam chat dan kirim:

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

Direktur Kreatif akan menjalankan semua 5 agen secara berurutan:

  1. Ahli Strategi Merek → riset pasar, analisis pesaing, insight audiens
  2. Copywriter → 3 postingan Instagram dengan teks, hashtag, CTA
  3. Desainer → konsep visual + gambar asli yang dibuat melalui Gemini (URI GCS) untuk setiap postingan
  4. Kritik → ulasan kualitas dengan skor DISETUJUI / PERLU_REVISI
  5. (Revisi jika diperlukan) → Copywriter atau Desainer dipanggil lagi untuk mendapatkan masukan
  6. Project Manager → Rentang waktu 2 minggu, perincian tugas, alokasi anggaran

Demo: Kampanye yang dijalankan dengan integrasi Notion

Menguji pemilihan rute agen tunggal

Kirim permintaan yang lebih singkat ini dalam sesi baru:

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

Perhatikan bahwa Creative Director merutekan ini hanya ke Brand Strategist - tidak ada agen lain yang dipanggil. Ini adalah logika klasifikasi permintaan dari petunjuk sistem yang berfungsi dengan benar.

Memeriksa rekaman aktivitas eksekusi

Saat masih berada di konsol:

  1. Klik Traces di sidebar kiri (di samping Playground)
  2. Di bagian Trace View, pilih rekaman aktivitas untuk sesi yang baru saja Anda jalankan
  3. Luaskan pohon rekaman aktivitas untuk melihat setiap panggilan agen, input/output, latensi, dan penggunaan tokennya

Setiap panggilan A2A ke spesialis muncul sebagai rentang terpisah. Anda dapat melihat dengan tepat konteks yang diteruskan Direktur Kreatif ke setiap agen dan apa yang diterima kembali.

Opsional: Menjalankan dari terminal

Anda juga dapat menjalankan kampanye secara terprogram menggunakan skrip run_campaign.py yang sudah disertakan dalam starter.

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

15. Pembersihan

Bersihkan resource Google Cloud untuk menghindari biaya berkelanjutan.

Jalankan skrip pembongkaran - skrip ini akan membaca .env Anda dan menghapus semua yang dibuat selama codelab ini:

bash deploy/teardown_gcp.sh

Skrip akan menunjukkan dengan tepat apa yang akan dihapus dan meminta konfirmasi sebelum melakukan apa pun:

Resource

Data yang dihapus

Layanan Cloud Run

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

Runtime Agen

Mesin penalaran Creative Director + semua sesi

Artifact Registry

Repositori cloud-run-source-deploy + semua image Docker

Bucket GCS

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

Secret Manager

notion-token, notion-project-db-id, notion-tasks-db-id (dilewati jika tidak dibuat)

Pastikan semuanya telah dihapus

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

Output yang diharapkan: daftar kosong atau hanya resource Anda sendiri yang sudah ada.

16. Ringkasan

Selamat! Anda telah membangun dan men-deploy sistem AI multi-agen tingkat produksi di Google Cloud.

Yang Anda bangun

Agen

Kemampuan

Deployment

Brand Strategist

Riset pasar melalui Google Penelusuran

Cloud Run

{i>Copywriter<i}

Pembuatan teks Instagram

Cloud Run

Desainer

Pembuatan gambar melalui Gemini + upload GCS

Cloud Run

Kritik

Ulasan berkualitas dengan pemberian skor

Cloud Run

Manajer Project

Linimasa + MCP Notion

Cloud Run

Creative Director

Orkestrasi penuh melalui A2A

Runtime Agen

Pola utama yang Anda pelajari

  1. ADK Agent - menentukan agen LLM dengan petunjuk + alat opsional
  2. adk web - menjalankan dan menguji agen ADK apa pun secara lokal dengan UI chat bawaan
  3. SkillToolset - mengemas pengetahuan yang dapat digunakan kembali ke dalam file modular yang dimuat sesuai permintaan
  4. FunctionTool - gabungkan fungsi Python (atau model eksternal) sebagai alat agen yang dapat dipanggil
  5. to_a2a() - mengekspos agen ADK sebagai layanan HTTPS yang kompatibel dengan A2A
  6. RemoteA2aAgent + AgentTool - mengatur agen jarak jauh sebagai alat yang dapat dipanggil
  7. McpToolset - terhubung ke layanan eksternal melalui server stdio MCP
  8. EventsCompactionConfig - menangani batas token dalam alur kerja multi-agen yang panjang
  9. Output kritikus terstruktur - kontrol kualitas yang dapat dibaca mesin dengan revisi otomatis
  10. Cloud Run - men-deploy agen dalam container dengan skala besar
  11. Agent Runtime - meng-hosting orkestrator dengan sesi dan pelacakan terkelola

Langkah berikutnya

  • Menambahkan pengeditan gambar multi-turn ke Desainer menggunakan kemampuan pengeditan gemini-3.1-flash-image-preview
  • Menambahkan autentikasi IAM ke layanan Cloud Run (menghapus --allow-unauthenticated)
  • Mengganti satu pakar dengan agen LangGraph atau CrewAI - A2A tidak bergantung pada framework
  • Menambahkan masukan pengguna sebagai alat agar peserta dapat memberi rating dan melakukan iterasi pada output
  • Menjelajahi pelacakan Runtime Agen di Konsol Cloud

Resource