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 mikroservice Cloud Run independen. Mereka berkomunikasi melalui A2A protocol - 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

Arsitektur Sistem

Yang akan Anda pelajari

  • Bangun agen LLM dengan Google ADK - Agent, petunjuk sistem, dan alat bawaan.
  • Kemasi pengetahuan agen yang dapat digunakan kembali ke dalam file modular dengan Keterampilan ADK (SkillToolset).
  • Membuat gambar asli dengan menghubungkan agen teks ke model gambar melalui FunctionTool.
  • Integrasikan 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.
  • Mengorkestrasi agen terdistribusi dengan RemoteA2aAgent dan AgentTool.
  • Kemasan dan deploy agen independen sebagai microservice Cloud Run.
  • Menghosting 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:

Membuka Cloud Shell dari toolbar GCP Console

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

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 samping kiri. Salin - Anda akan memerlukannya 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 library Google Auth, 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 sendiri.

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 kampanye akhir. Tindakan 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 terinstal 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 bersifat tidak bergantung model, agnostik deployment, dan dibangun 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 komponen 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 pengelola 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, dan dengan argumen apa. Anda menulis petunjuknya - 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 instruksi 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 ini

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 Copywriter - Keterampilan 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 berfungsi, tetapi membuat setiap permintaan menjadi terlalu besar dengan konten yang hanya diperlukan 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 jendela konteks saat agen benar-benar membutuhkannya.

Kemampuan 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

Temukan 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 - Daftarkan 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

Designer 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). Model gambar tersebut tidak mendukung pemanggilan fungsi, sehingga tidak dapat digunakan secara langsung sebagai agen ADK. Sebagai gantinya, model tersebut 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
          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 tampilkan 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. Buat Kritik - 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 harus menggunakan model Pydantic untuk output Gemini

Aturan ini berkaitan dengan 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 secara 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 dan perintah Pydantic disediakan. Selesaikan tiga 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

Mengirim kueri ke halaman yang ada

API-post-page

Membuat halaman baru

API-patch-page

Memperbarui halaman yang ada

LLM memanggil fungsi ini seperti fungsi lainnya - LLM tidak tahu bahwa fungsi ini 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 integrasi MCP beraksi, selesaikan penyiapan di bawah sekarang. TODO yang mengikuti merujuk ke 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 "Project & Tugas" 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 tokennya

  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

Dapatkan ID database:

  1. Klik link database Projects untuk membukanya - database 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 database-nya
  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 sudah 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 Agent-to-Agent Protocol (A2A) untuk menghubungkan berbagai agen dalam sistem kita. Mari pahami cara kerjanya.

Masalah yang dipecahkan A2A

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

A2A menyelesaikannya 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 POST JSON-RPC. Isi berisi pesan (perintah untuk spesialis).

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

Kartu agen

Setiap agen memublikasikan deskripsi dirinya di /.well-known/agent.json. Hal ini seperti kartu nama - memberi tahu dunia tentang kemampuan agen dan cara 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 otomatis mengaktifkan .venv - 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

Tetapkan URL localhost di .env

Di Terminal 6, perbarui .env dengan URL agen lokal agar Direktur Kreatif 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 A2A protocol 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.

Informasi yang ditampilkan:

  • Kartu agen - metadata terstruktur yang diiklankan agen Anda: nama, deskripsi, mode input/output yang didukung, dan URL endpoint. Inilah yang dibaca Creative Director 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 akan memeriksa apakah kartu agen sesuai dengan spesifikasi A2A, menampilkan kolom yang tidak ada atau respons yang salah format sejak 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 diberikan kartu agen

Kartu agen lebih dari sekadar metadata - kartu ini adalah kontrak kemampuan penuh 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 menonjol:

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 dalam kartu agen. Agen lain di jaringan dapat menemukan dengan tepat 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 demikian, 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.

Itulah sebabnya pemeriksa menjadi alat pen-debug yang canggih: sekilas melihat kartu agen, Anda dapat mengetahui 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 - dengan cara yang sama seperti 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 kegagalan, dan konteks yang dihilangkan secara diam-diam antar-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 akan diberi petunjuk 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 tidak dapat melacak posisinya dalam alur kerja - terutama setelah menerima respons yang panjang dari spesialis. Membuat garis besar rencana terlebih dahulu akan memandu orkestrator: orkestrator tahu 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 spesialis dan membuat konten secara langsung - hal ini lebih cepat dan LLM "tahu" cara melakukannya. Instruksi harus membuat hal ini 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, laporkan, jangan lanjutkan

Pada versi awal, orkestrator akan menerima error dari satu spesialis, membuat output yang masuk akal untuknya, dan melanjutkan ke agen berikutnya. Pengguna mendapatkan kampanye yang tampak lengkap yang dibangun di atas fondasi halusinasi. Perbaikannya jelas: SEGERA HENTIKAN. Laporkan error yang tepat. 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 ini, setiap agen bekerja tanpa mengetahui apa pun.

Elemen 6 - Klasifikasi permintaan: sederhana vs. kompleks

Tidak setiap permintaan memerlukan kelima agen. Perintah menginstruksikan pengelola 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

Perintahnya secara eksplisit menyatakan bahwa pengelola 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 hal ini, orkestrator akan menulis ulang output spesialis dengan kata-katanya sendiri, sehingga kehilangan detail, menimbulkan kesalahan, dan menggagalkan tujuan memiliki spesialis.

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

Mode kegagalan yang halus tetapi penting: pengelola 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.

Hal ini akan menghentikan orkestrator memperlakukan proses yang berjalan sebagian sebagai selesai.

Loop Kendali Mutu

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 ke Manajer Proyek. Direktur Kreatif 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 yang disebutkan sebelumnya menyatakan bahwa pengelola "mengurai" respons Kritikus. Tidak ada kode Python yang melakukan penguraian ini - tidak ada regex, tidak ada pencocokan string. Creative Director adalah LLM yang membaca instruksinya sendiri. Instruksi 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 Kritikus dan mengikuti cabang. Inilah alasan 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 tanpa pemberitahuan.

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

Batas 1 revisi dan alasannya

Setelah satu putaran revisi, orkestrator akan melanjutkan ke Project Manager, terlepas dari skornya. Instruksi ini melacaknya 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.

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 - Daftarkan setiap spesialis sebagai 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 Agen 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 menghilangkan 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 terjadi setelah setiap 3 penyelesaian agen. Untuk pipeline 5 agen, ini berarti pemadatan terjadi sekali (setelah agen 1–3), lalu Critic dan PM melihat ringkasan 1–3 ditambah output agen sebelumnya yang 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 menentukan kapan harus memanggil setiap alat berdasarkan petunjuk sistem dan permintaan pengguna. Pengelola 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 hanya akan meneruskan permintaan ini ke 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 Pakar

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 setiap URL Cloud Run 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 akun tersebut dapat mengupload gambar yang dihasilkan.

Jika Anda mengonfigurasi kredensial Notion di .env, skrip juga akan menyimpannya secara aman di Secret Manager (sebagai notion-token, notion-project-db-id, notion-tasks-db-id) dan memasukkannya 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"

Direktur Kreatif 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 Web Preview → Change 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 dijangkau 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, tanpa status, masing-masing menangani satu tugas. Creative Director memiliki persyaratan yang berbeda:

Persyaratan

Mengapa hal ini penting

Status sesi

Alur kerja multi-langkah memerlukan 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. 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 yang dikelola 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 berkualitas dengan skor DISETUJUI / PERLU_REVISI
  5. (Revisi jika diperlukan) → Copywriter atau Desainer dipanggil lagi untuk memberikan 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 permintaan 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. Perluas 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 apa 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

Yang akan dihapus

Layanan Cloud Run

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

Runtime Agent

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 yang sudah ada milik Anda.

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 Agent

Pola utama yang Anda pelajari

  1. ADK Agent - menentukan agen LLM dengan petunjuk + alat opsional
  2. adk web - menjalankan dan menguji agen ADK secara lokal dengan UI chat bawaan
  3. SkillToolset - mengemas pengetahuan yang dapat digunakan kembali ke dalam file modular yang dimuat sesuai permintaan
  4. FunctionTool - menggabungkan 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 dalam skala besar
  11. Agent Runtime - meng-hosting orkestrator dengan sesi dan pelacakan terkelola

Langkah berikutnya

  • Menambahkan pengeditan gambar multi-turn ke Designer menggunakan kemampuan pengeditan gemini-3.1-flash-image
  • 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