Membangun Agen Lembaga Amal yang Tepercaya dengan ADK dan AP2 Google

1. Membangun Kepercayaan untuk Mendorong Kedermawanan

banner

Momen Inspirasi

Ponsel Anda bergetar. Anda melihat berita tentang program literasi yang berhasil membantu anak-anak di komunitas yang kurang terlayani belajar membaca. Anda merasakan keinginan kuat untuk berkontribusi. Anda membuka browser dan menelusuri "donasi program literasi anak".

google penelusuran

Ratusan hasil muncul.

Anda mengklik link pertama. Situs terlihat profesional. Anda men-scroll ke bawah untuk melihat laporan keuangan mereka. "Biaya administratif: 28%." Anda menjeda. Hanya 72 sen dari setiap dolar yang Anda donasikan yang akan mendanai program ini. Apakah itu bagus? Anda tidak yakin.

Anda mencoba organisasi lain. Anda belum pernah mendengarnya. Apakah mereka sah? Penelusuran cepat akan membuat Anda tersesat. Anda menemukan rangkaian pesan Reddit dari dua tahun lalu yang berisi klaim seorang pengguna, "Ini penipuan, donasi saya tidak pernah sampai ke mana pun." Orang lain dengan penuh semangat membela mereka, "Mereka di lapangan melakukan pekerjaan nyata!" Ambiguitas ini melumpuhkan.

Tiga puluh menit kemudian, Anda terjebak dalam labirin ulasan yang saling bertentangan, rating efisiensi, dan catatan IRS, dan Anda masih belum berdonasi. Dorongan awal untuk berbuat baik telah digantikan oleh kesulitan dalam melakukan riset. Tab tetap terbuka selama beberapa hari, sebagai pengingat kecil tentang niat baik, hingga akhirnya Anda menutupnya.

Ini Bukan Kegagalan Pribadi; Ini adalah Kegagalan Sistem

Pengalaman ini bersifat universal. Keinginan untuk memberi sangat besar, tetapi prosesnya penuh dengan rintangan yang menyebabkan keraguan dan kebimbangan:

  • Hambatan Riset: Setiap lembaga amal memerlukan penyelidikannya sendiri.
  • Verifikasi Kepercayaan: Sulit untuk membedakan organisasi yang sangat efektif dari organisasi yang tidak efisien atau bahkan penipuan.
  • Kelumpuhan Analisis (Analysis Paralysis): Terlalu banyak pilihan menyebabkan kelelahan dalam mengambil keputusan.
  • Kehilangan Momentum: Dorongan emosional untuk memberi memudar seiring bertambahnya beban logistik.

Hambatan ini memiliki biaya yang sangat besar di dunia nyata. Pemberian perorangan di Amerika Serikat sangat besar—donatur perorangan memberikan sekitar $374 miliar pada tahun 2023 saja, menurut Giving USA 2024. Namun, penelitian menunjukkan bahwa hambatan untuk memberikan donasi—termasuk biaya penelusuran, gesekan psikologis, dan batasan waktu—secara signifikan mengurangi jumlah yang mencapai tujuan amal. Studi yang melibatkan jutaan donatur telah menemukan bahwa bahkan sedikit hambatan dalam proses pemberian donasi online mencegah orang memenuhi niat amal mereka.

Hal ini mewakili miliaran dolar dalam bentuk donasi yang dimaksudkan, tetapi tidak pernah sampai ke lembaga yang membutuhkannya.

Visi

Bayangkan pengalaman yang berbeda. Alih-alih sesi riset selama 30 menit, Anda cukup mengatakan:

"Saya ingin menyumbang Rp500.000 untuk program literasi anak-anak. Cari lembaga amal yang terverifikasi, efisien, dan memiliki rating tinggi."

Dalam hitungan detik, Anda akan mendapatkan respons yang meningkatkan keyakinan:

kartu hasil badan amal

Inilah janji Agen Pemberi AI. Namun, untuk mewujudkan visi ini, kita harus memecahkan tantangan mendasar: ketika agen AI otonom menangani uang, kepercayaan bukanlah sesuatu yang opsional; kepercayaan adalah seluruh fondasinya.

  • Bagaimana cara membuktikan apa yang diizinkan pengguna?
  • Siapa yang bertanggung jawab jika terjadi kesalahan?
  • Bagaimana cara memberikan kepercayaan kepada donatur, lembaga amal, dan jaringan pembayaran untuk berpartisipasi?

Misi Anda Hari Ini

Dalam workshop ini, Anda akan membangun agen tepercaya tersebut dengan menggabungkan dua teknologi canggih:

Google Agent Development Kit (ADK)

Agent Payments Protocol (AP2)

Jabatan

Factory untuk membangun agen AI kelas produksi

Blueprint arsitektur untuk kepercayaan dalam transaksi AI

Yang disediakan

• Framework untuk orkestrasi multi-agen
• Fitur keamanan bawaan seperti konfirmasi alat
• Kemampuan observasi dan pelacakan yang siap digunakan dalam produksi
• Antarmuka Python sederhana untuk perilaku agen yang kompleks

• Batas keamanan berbasis peran
• Kredensial digital yang dapat diverifikasi (mandat)
• Bukti kriptografi izin
• Jejak audit lengkap untuk akuntabilitas

Pelajari lebih lanjut

Dokumentasi ADK

Protokol AP2

Yang Akan Anda Buat

arsitektur

Pada akhir workshop ini, Anda akan membuat:

Sistem Multi-Agen dengan peran khusus:

  • Agen Shopping yang menemukan lembaga amal terverifikasi
  • Agen Penjual yang membuat penawaran donasi yang mengikat
  • Penyedia Kredensial yang memproses pembayaran dengan aman
  • Orchestrator yang mengoordinasikan seluruh alur

Tiga Jenis Kredensial yang Dapat Diverifikasi:

  • IntentMandate: "Find me an education charity" (Carikan lembaga amal pendidikan untuk saya)
  • CartMandate: "$50 untuk Room to Read, ditandatangani oleh penjual"
  • PaymentMandate: "Process via simulated payment" (Proses melalui pembayaran simulasi)

Keamanan di Setiap Lapisan:

  • Batas kepercayaan berbasis peran
  • Izin pengguna eksplisit

Jejak Audit Lengkap:

  • Setiap keputusan dapat dilacak
  • Setiap izin yang direkam
  • Setiap pengalihan terlihat

🔒 Penting: Ini adalah Lingkungan Pembelajaran yang Aman

Siap Membangun Kepercayaan?

Dalam modul berikutnya, kita akan menyiapkan lingkungan pengembangan dan membangun agen AI pertama Anda. Anda akan segera mengetahui mengapa agen sederhana tidak dapat dipercaya—dan kemudian menghabiskan sisa workshop untuk mempelajari cara memperbaikinya.

Mari kita mulai dengan memahami masalahnya secara langsung.

2. Mempersiapkan Ruang Kerja Anda

Foundation for Trustworthy Agents

Sebelum dapat membangun Agen Pemberian AI, kita perlu menyiapkan lingkungan pengembangan yang bersih, konsisten, dan dikonfigurasi dengan benar. Modul ini adalah langkah terfokus untuk memastikan semua alat dan layanan yang diperlukan sudah tersedia.

Jika penyiapan ini berhasil diselesaikan, Anda dapat sepenuhnya berfokus pada pekerjaan menarik dalam membangun logika agen di modul mendatang, tanpa mengkhawatirkan masalah konfigurasi.

Mengakses Cloud Shell

Pertama, kita akan membuka Cloud Shell, yang merupakan terminal berbasis browser dengan Google Cloud SDK dan alat penting lainnya yang sudah diinstal sebelumnya.

Perlu Kredit Google Cloud?

Klik Activate Cloud Shell di bagian atas Konsol Google Cloud (ikon terminal di menu navigasi kanan atas).

cloud shell

Temukan ID Project Google Cloud Anda:

  • Buka Konsol Google Cloud: https://console.cloud.google.com
  • Pilih project yang ingin Anda gunakan untuk workshop ini dari dropdown project di bagian atas halaman.
  • Project ID Anda ditampilkan di kartu Info project di Dasbor id project

Setelah Cloud Shell terbuka, verifikasi bahwa Anda telah diautentikasi:

# Check that you are logged in
gcloud auth list

Anda akan melihat akun Anda tercantum sebagai (ACTIVE).

Mengonfigurasi Project Anda

Sekarang, mari siapkan project Google Cloud Anda dan aktifkan API yang diperlukan.

Menetapkan Project ID Anda

# Set your project using the auto-detected environment variable in Cloud Shell
gcloud config set project $GOOGLE_CLOUD_PROJECT

# Verify the project has been set
echo "Your active Google Cloud project is: $(gcloud config get-value project)"

Mengaktifkan API yang Diperlukan

Agen Anda memerlukan akses ke beberapa layanan Google Cloud:

gcloud services enable \
    aiplatform.googleapis.com \
    secretmanager.googleapis.com \
    cloudtrace.googleapis.com

Proses ini mungkin perlu waktu 1-2 menit. Anda akan melihat:

Operation "operations/..." finished successfully.

Yang disediakan oleh API ini:

  • aiplatform.googleapis.com: Akses ke model Gemini untuk penalaran agen
  • secretmanager.googleapis.com: Penyimpanan yang aman untuk kunci API (praktik terbaik produksi)
  • cloudtrace.googleapis.com: Observability untuk jejak audit kami

Meng-clone Kode Awal

Dapatkan repositori workshop dengan semua kode dan resource template:

git clone https://github.com/ayoisio/adk-ap2-charity-agents
cd adk-ap2-charity-agents
git checkout codelab

Mari kita verifikasi apa yang kita miliki:

ls -la

Anda akan melihat:

  • charity_advisor/ - Tempat kita akan membangun agen dan alat
  • scripts/ - Skrip helper untuk pengujian dan verifikasi
  • deploy.sh - Skrip pembantu untuk deployment
  • setup.py - Skrip pembantu untuk penginstalan modul
  • .env.template - File variabel lingkungan

Menyiapkan Lingkungan Python

Sekarang kita akan membuat lingkungan Python yang terisolasi untuk project kita.

Membuat dan Mengaktifkan Lingkungan Virtual

# Create the virtual environment
python3 -m venv venv

# Activate it
source venv/bin/activate

Verifikasi: Perintah Anda sekarang akan menampilkan awalan (venv).

Instal Dependensi

pip install -r charity_advisor/requirements.txt
pip install -e .

Tindakan ini akan menginstal:

  • google-adk: Framework Agent Development Kit
  • google-cloud-aiplatform: Integrasi Vertex AI dan Gemini
  • ap2: Agent Payments Protocol SDK (dari GitHub)
  • python-dotenv: Pengelolaan variabel lingkungan

Dengan tanda -e, Anda dapat mengimpor modul adk_ap2_charity_agents dari mana saja.

Mengonfigurasi File Lingkungan

Buat konfigurasi dari template:

# Copy the template
cp .env.template .env

# Get your current Project ID
PROJECT_ID=$(gcloud config get-value project)

# Replace the placeholder with your actual project ID
sed -i "s/your-project-id/$PROJECT_ID/g" .env

# Verify the replacement worked
grep GOOGLE_CLOUD_PROJECT .env

Anda akan melihat:

GOOGLE_CLOUD_PROJECT=your-actual-project-id

Verifikasi

Jalankan skrip verifikasi untuk memastikan semuanya dikonfigurasi dengan benar:

python scripts/verify_setup.py

Anda akan melihat semua tanda centang hijau:

======================================================================
SETUP VERIFICATION
======================================================================

✓ Python version: 3.11.x
✓ google-adk: 1.17.0
✓ google-cloud-aiplatform: 1.111.0+
✓ ap2: 0.1.0
✓ python-dotenv: 1.0.0+
✓ .env file found and contains project ID
✓ Google Cloud project configured: your-project-id

✓ Mock charity database found
✓ Agent templates ready
✓ All directories present

======================================================================
✓ Setup complete! You are ready to build trustworthy agents.
======================================================================

Pemecahan masalah

Apa Langkah Selanjutnya?

Lingkungan Anda kini sudah siap sepenuhnya. Anda telah:

  • ✅ Project Google Cloud dikonfigurasi
  • ✅ API yang diperlukan diaktifkan
  • ✅ Library ADK dan AP2 telah diinstal
  • ✅ Kode template siap diubah

Dalam modul berikutnya, Anda akan membuat agen AI pertama dengan beberapa baris kode dan mengetahui alasan agen sederhana tidak dapat dipercaya saat menangani transaksi keuangan.

3. Agen Pertama Anda & Menemukan Kesenjangan Kepercayaan

banner

Dari Ide menjadi Interaksi

Pada modul sebelumnya, kita telah menyiapkan lingkungan pengembangan. Sekarang, pekerjaan yang menarik dimulai. Kita akan membuat dan menjalankan agen pertama, memberinya kemampuan pertama, dan dengan demikian, menemukan tantangan mendasar yang harus kita selesaikan agar agen tersebut benar-benar tepercaya.

Modul ini adalah gambar "sebelum" Anda - momen yang mengungkapkan mengapa membangun agen tepercaya memerlukan lebih dari sekadar memberi LLM akses ke alat.

Langkah 1: Periksa Agen Pemula

Pertama, mari kita lihat template untuk agen pertama kita. File ini berisi struktur dasar dengan placeholder yang akan kita selesaikan di langkah berikutnya.

👉 Buka file

charity_advisor/simple_agent/agent.py

di editor Anda.

Anda akan melihat:

"""
A simple agent that can research charities using Google Search.
"""

# MODULE_3_STEP_2_IMPORT_COMPONENTS


simple_agent = Agent(
    name="SimpleAgent",
    model="gemini-2.5-flash",
    
    # MODULE_3_STEP_3_WRITE_INSTRUCTION
    instruction="""""",
    
    # MODULE_3_STEP_4_ADD_TOOLS
    tools=[]
)

Perhatikan bahwa komentar placeholder mengikuti pola: MODULE_3_STEP_X_DESCRIPTION. Kita akan mengganti penanda ini untuk membangun agen kita secara progresif.

Langkah 2: Impor Komponen yang Diperlukan

Sebelum dapat membuat instance class Agent atau menggunakan alat google_search, kita perlu mengimpornya ke dalam file.

👉 Temukan:

# MODULE_3_STEP_2_IMPORT_COMPONENTS

👉 Ganti satu baris tersebut dengan:

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

Sekarang class Agent dan alat google_search tersedia di file kita.

Langkah 3: Tulis Petunjuk Agen

Instruksi adalah "deskripsi tugas" agen—instruksi memberi tahu LLM kapan dan cara menggunakan alatnya. Mari kita tulis satu yang memandu agen kita untuk menelusuri informasi lembaga amal.

👉 Temukan:

# MODULE_3_STEP_3_WRITE_INSTRUCTION
instruction="""""",

👉 Ganti kedua baris tersebut dengan:

instruction="""You are a helpful research assistant. When a user asks you to find information about charities,
use the google_search tool to find the most relevant and up-to-date results from the web.
Synthesize the search results into a helpful summary.""",

Langkah 4: Tambahkan Alat Penelusuran

Agen tanpa alat hanyalah seorang komunikator. Mari kita beri agen kemampuan pertamanya: kemampuan untuk menelusuri web.

👉 Temukan:

# MODULE_3_STEP_4_ADD_TOOLS
tools=[]

👉 Ganti kedua baris tersebut dengan:

tools=[google_search]

Langkah 5: Verifikasi Agen Lengkap Anda

Mari kita pastikan semua bagian sudah terpasang sebelum kita menguji.

👉 Langkah-langkah lengkap

charity_advisor/simple_agent/agent.py

file sekarang akan terlihat persis seperti ini:

"""
A simple agent that can research charities using Google Search.
"""

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


simple_agent = Agent(
    name="SimpleAgent",
    model="gemini-2.5-flash",
    instruction="""You are a helpful research assistant. When a user asks you to find information about charities,
use the google_search tool to find the most relevant and up-to-date results from the web.
Synthesize the search results into a helpful summary.""",
    tools=[google_search]
)

Langkah 6: Menguji Agen - Memaparkan Kesenjangan Kepercayaan

Setelah agen dikonfigurasi sepenuhnya, mari kita uji dan analisis perilakunya. Di sinilah kita menemukan alasan mengapa agen sederhana tidak dapat dipercaya saat menangani keputusan keuangan.

Tes 1: Masalah Penemuan

👉 Di terminal Cloud Shell, jalankan perintah berikut:

adk run charity_advisor/simple_agent

Anda akan melihat output seperti:

INFO:google.adk.agents:Loading agent from charity_advisor/simple_agent
INFO:google.adk.agents:Agent 'SimpleAgent' ready

[user]:

Perintah [user]: kini menunggu input Anda.

👉 Di perintah [pengguna], ketik:

Can you find me a verified, highly-rated charity for children's literacy?

👉 Tekan Enter dan amati responsnya.

Setelah beberapa saat, agen akan menyintesis hasil penelusuran menjadi respons seperti ini:

Berdasarkan penelusuran web, beberapa lembaga amal yang sangat dihormati untuk literasi anak-anak adalah Reading Is Fundamental dan Room to Read. Sumber seperti Charity Navigator dan GuideStar sering direkomendasikan untuk memverifikasi status dan rating mereka. Saya juga menemukan beberapa diskusi online, termasuk di forum seperti Reddit, tempat pengguna membagikan pengalaman pribadi mereka dengan berbagai program literasi lokal yang lebih kecil.

Mari kita analisis. Apakah agen telah menyelesaikan masalah kita?

Tidak. Model ini telah mereplikasi dengan sempurna pengalaman manusia yang kami jelaskan dalam Modul 1. AI ini telah berhasil mengotomatiskan proses "mencari di Google" dan mengembalikan masalah "kelumpuhan analisis" kepada kita.

Hal ini memunculkan kesenjangan kepercayaan pertama: kurangnya data yang kredibel.

Agen menelusuri web terbuka, yang berarti:

  • ✅ Menemukan hasil dengan cepat (pengalaman pengguna yang lebih baik)
  • ❌ Mencampuradukkan organisasi dengan rating tinggi dan diskusi Reddit (sumber tidak tepercaya)
  • ❌ Tidak dapat membedakan antara lembaga amal yang terpercaya dan potensi penipuan (tidak ada verifikasi)
  • ❌ Meminta kami untuk memverifikasi informasi yang baru saja diberikannya (mengalihkan beban)

Uji 2: Masalah Eksekusi

Sekarang untuk pengujian kedua yang krusial. Pada prompt [user]:, coba selesaikan donasi:

Okay, please donate $50 to Room to Read for me.

Agen akan merespons dengan permintaan maaf, mengakui keterbatasannya:

Kami memahami bahwa Anda ingin berdonasi, yang sangat bagus. Namun, kami adalah asisten riset dan tidak memiliki kemampuan untuk menangani transaksi keuangan atau memproses pembayaran. Untuk memberikan donasi, Anda harus membuka situs resmi Room to Read secara langsung.

Ini adalah momen "Aha!" kedua yang sama pentingnya.

Agen tidak hanya tidak dapat dipercaya untuk menemukan lembaga amal yang tepat, tetapi juga belum dapat dipercaya untuk melakukan tindakan menyumbang.

👉 Tekan

Ctrl+C

untuk keluar setelah selesai menguji.

Visualisasi Dua Kesenjangan

masalah kepercayaan

Yang Baru Saja Anda Pelajari

Dalam modul ini, Anda berhasil membangun dan melengkapi agen AI pertama Anda. Dengan demikian, Anda telah menemukan dua tantangan mendasar dalam membangun sistem yang tepercaya.

Konsep Utama yang Dikuasai

Class Agen:

  • Elemen penyusun inti ADK
  • Menggabungkan penalaran LLM (otak) dengan alat (tangan)
  • Dikonfigurasi dengan model, petunjuk, dan alat

Struktur Berbasis Folder:

  • Setiap agen berada di foldernya sendiri
  • ADK mencari agent_folder/agent.py
  • Jalankan dengan adk run agent_folder

Daftar Alat:

  • Menentukan kemampuan agen
  • LLM memutuskan kapan dan cara menggunakan alat
  • Dapat berisi beberapa alat untuk berbagai tindakan

Perintah Instruksi:

  • Memandu perilaku agen seperti deskripsi pekerjaan
  • Menentukan peran, pemicu, tindakan, dan format output
  • Penting untuk penggunaan alat yang andal

Masalah Kepercayaan:

  • Kesenjangan penemuan: Sumber yang tidak diseleksi, kualitas campuran
  • Kesenjangan eksekusi: Tidak ada kemampuan yang aman, tidak ada izin, tidak ada jejak audit

Langkah Berikutnya

Dalam modul berikutnya, kita akan mulai membangun solusi dengan menerapkan arsitektur berbasis peran AP2.

Mari kita buat agen pertama dan lihat pemisahan peran dalam tindakan.

4. Membangun Agen Shopping - Penemuan Berbasis Peran

banner

Fondasi Kepercayaan: Pemisahan Peran

Dalam modul terakhir, Anda menemukan bahwa agen sederhana dan serbaguna gagal dalam dua hal: agen tersebut tidak dapat memberikan penemuan yang tepercaya, dan tidak dapat mengeksekusi transaksi yang aman. Sekarang kita akan mulai menyelesaikan masalah ini dengan menerapkan prinsip pertama dari Protokol Pembayaran Agen: arsitektur berbasis peran.

Sebelum menulis kode apa pun, mari kita pahami mengapa prinsip ini penting.

Prinsip AP2: Pemisahan Peran

Masalah dengan Agen "Lakukan Semuanya"

Bayangkan Anda mempekerjakan satu orang untuk menjadi penasihat keuangan, akuntan, dan broker investasi Anda. Praktis? Ya. Aman? Pastinya tidak. Mereka akan memiliki:

  • Sasaran investasi Anda (peran penasihat)
  • Akses ke akun Anda (peran akuntan)
  • Otoritas untuk memindahkan uang Anda (peran broker)

Jika orang ini disusupi—atau melakukan kesalahan—semuanya berisiko.

Solusi AP2: Satu Agen, Satu Tugas

AP2 menerapkan prinsip pemisahan masalah untuk membuat batas kepercayaan:

arsitektur

Mengapa hal ini penting:

  • Radius dampak terbatas: Jika Agen Shopping disusupi, penyerang tidak dapat mengakses kredensial pembayaran
  • Privasi: Penyedia Kredensial tidak pernah melihat percakapan belanja Anda
  • Kepatuhan: Lebih mudah memenuhi persyaratan PCI-DSS saat data pembayaran diisolasi
  • Akuntabilitas: Tanggung jawab yang jelas untuk setiap langkah

Cara Agen Berkomunikasi: State sebagai Notepad Bersama

Karena agen tidak dapat mengakses data satu sama lain secara langsung, mereka berkomunikasi melalui status bersama. Anggap saja ini sebagai papan tulis yang dapat ditulis dan dibaca oleh semua agen:

# Shopping Agent writes:
state["intent_mandate"] = {
    "natural_language_description": "Donate $50 to Room to Read",
    "merchants": ["Room to Read"],
    "intent_expiry": "2024-11-07T15:32:16Z",
    "amount": 50.0
}

# Merchant Agent reads:
intent = state["intent_mandate"]
charity_name = intent["merchants"][0]
amount = intent["amount"]
# Creates CartMandate based on IntentMandate...

# Credentials Provider reads:
cart_mandate = state["cart_mandate"]
# Processes payment...

Dengan cara ini, kami mempertahankan batas kepercayaan sekaligus memungkinkan kolaborasi.

Agen Pertama Kami: Agen Belanja

Tanggung jawab Agen Shopping sederhana dan terfokus:

  1. Gunakan alat find_charities untuk mengkueri database tepercaya kami
  2. Menampilkan opsi kepada pengguna
  3. Gunakan alat save_user_choice untuk membuat IntentMandate dan menyimpannya ke status
  4. Menyerahkan kepada agen berikutnya (Penjual)

Selesai. Tidak ada penanganan pembayaran, tidak ada pembuatan keranjang—hanya penemuan dan pengalihan.

Mari kita buat langkah demi langkah.

Langkah 1: Tambahkan Input Validation Helper

Saat membuat alat produksi, validasi input sangat penting. Mari buat fungsi helper yang memvalidasi data lembaga amal sebelum menyimpannya ke status.

👉 Buka

charity_advisor/tools/charity_tools.py

Anda akan melihat fungsi find_charities (sudah selesai) di bagian atas. Scroll ke bawah untuk menemukan:

# MODULE_4_STEP_1_ADD_VALIDATION_HELPER

👉 Ganti satu baris tersebut dengan:

def _validate_charity_data(charity_name: str, charity_ein: str, amount: float) -> tuple[bool, str]:
    """
    Validates charity selection data before saving to state.
    
    This helper function performs basic validation to ensure data quality
    before it gets passed to other agents in the pipeline.
    
    Args:
        charity_name: Name of the selected charity
        charity_ein: Employer Identification Number (should be format: XX-XXXXXXX)
        amount: Donation amount in USD
        
    Returns:
        (is_valid, error_message): Tuple where is_valid is True if all checks pass,
                                    and error_message contains details if validation fails
    """
    # Validate charity name
    if not charity_name or not charity_name.strip():
        return False, "Charity name cannot be empty"
    
    # Validate EIN format (should be XX-XXXXXXX)
    if not charity_ein or len(charity_ein) != 10 or charity_ein[2] != '-':
        return False, f"Invalid EIN format: {charity_ein}. Expected format: XX-XXXXXXX"
    
    # Validate amount
    if amount <= 0:
        return False, f"Donation amount must be positive, got: ${amount}"
    
    if amount > 1_000_000:
        return False, f"Donation amount exceeds maximum of $1,000,000: ${amount}"
    
    # All checks passed
    return True, ""

Langkah 2: Tambahkan IntentMandate Creation Helper

Sekarang, mari kita buat helper yang membangun struktur IntentMandate AP2. Ini adalah salah satu dari tiga kredensial yang dapat diverifikasi di AP2.

👉 Dalam file yang sama, temukan:

# MODULE_4_STEP_2_ADD_INTENTMANDATE_CREATION_HELPER

👉 Ganti satu baris tersebut dengan:

def _create_intent_mandate(charity_name: str, charity_ein: str, amount: float) -> dict:
    """
    Creates an IntentMandate - AP2's verifiable credential for user intent.
    
    This function uses the official Pydantic model from the `ap2` package
    to create a validated IntentMandate object before converting it to a dictionary.
    
    Args:
        charity_name: Name of the selected charity
        charity_ein: Employer Identification Number
        amount: Donation amount in USD
        
    Returns:
        Dictionary containing the IntentMandate structure per AP2 specification
    """
    from datetime import datetime, timedelta, timezone
    from ap2.types.mandate import IntentMandate
    
    # Set the expiry for the intent
    expiry = datetime.now(timezone.utc) + timedelta(hours=1)
    
    # Step 1: Instantiate the Pydantic model with official AP2 fields
    intent_mandate_model = IntentMandate(
        user_cart_confirmation_required=True,
        natural_language_description=f"Donate ${amount:.2f} to {charity_name}",
        merchants=[charity_name],
        skus=None,
        requires_refundability=False,
        intent_expiry=expiry.isoformat()
    )
    
    # Step 2: Convert the validated model to a dictionary for state storage
    intent_mandate_dict = intent_mandate_model.model_dump()
    
    # Step 3: Add the codelab's custom fields to the dictionary
    timestamp = datetime.now(timezone.utc)
    intent_mandate_dict.update({
        "timestamp": timestamp.isoformat(),
        "intent_id": f"intent_{charity_ein.replace('-', '')}_{int(timestamp.timestamp())}",
        "charity_ein": charity_ein,
        "amount": amount,
        "currency": "USD"
    })
    
    return intent_mandate_dict

Langkah 3: Bangun Alat Pengalihan Status dengan IntentMandate

Sekarang, mari kita buat alat yang membuat IntentMandate dan menyimpannya ke status.

👉 Dalam file yang sama, scroll ke bawah ke

save_user_choice

fungsi . Cari:

# MODULE_4_STEP_3_COMPLETE_SAVE_TOOL

👉 Ganti satu baris tersebut dengan:

    # Validate inputs before creating IntentMandate
    is_valid, error_message = _validate_charity_data(charity_name, charity_ein, amount)
    if not is_valid:
        logger.error(f"Validation failed: {error_message}")
        return {"status": "error", "message": error_message}
    
    # Create AP2 IntentMandate using our updated helper function
    intent_mandate = _create_intent_mandate(charity_name, charity_ein, amount)
    
    # Write the IntentMandate to shared state for the next agent
    tool_context.state["intent_mandate"] = intent_mandate
    
    logger.info(f"Successfully created IntentMandate and saved to state")
    logger.info(f"Intent ID: {intent_mandate['intent_id']}")
    logger.info(f"Intent expires: {intent_mandate['intent_expiry']}")
    
    # Return success confirmation
    return {
        "status": "success",
        "message": f"Created IntentMandate: ${amount:.2f} donation to {charity_name} (EIN: {charity_ein})",
        "intent_id": intent_mandate["intent_id"],
        "expiry": intent_mandate["intent_expiry"]
    }

Langkah 4: Tambahkan Pembantu Pemformatan Tampilan

Sebelum membangun agen, mari tambahkan satu lagi helper yang memformat data lembaga amal untuk ditampilkan dengan mudah oleh pengguna.

👉 Scroll untuk menemukan:

# MODULE_4_STEP_4_ADD_FORMATTING_HELPER

👉 Ganti satu baris tersebut dengan:

def _format_charity_display(charity: dict) -> str:
    """
    Formats a charity dictionary into a user-friendly display string.
    
    This helper function demonstrates how to transform structured data
    into readable text for the user.
    
    Args:
        charity: Dictionary containing charity data (name, ein, mission, rating, efficiency)
        
    Returns:
        Formatted string suitable for display to the user
    """
    name = charity.get('name', 'Unknown')
    ein = charity.get('ein', 'N/A')
    mission = charity.get('mission', 'No mission statement available')
    rating = charity.get('rating', 0.0)
    efficiency = charity.get('efficiency', 0.0)
    
    # Format efficiency as percentage
    efficiency_pct = int(efficiency * 100)
    
    # Build formatted string
    display = f"""
**{name}** (EIN: {ein})
⭐ Rating: {rating}/5.0
💰 Efficiency: {efficiency_pct}% of funds go to programs
📋 Mission: {mission}
    """.strip()
    
    return display

Langkah 5: Bangun Agen Shopping - Impor Komponen

Setelah alat kita selesai dan kuat, mari kita buat agen yang akan menggunakannya.

👉 Buka

charity_advisor/shopping_agent/agent.py

Anda akan melihat template dengan komentar placeholder. Mari kita buat langkah demi langkah.

👉 Temukan:

# MODULE_4_STEP_5_IMPORT_COMPONENTS

👉 Ganti satu baris tersebut dengan:

from google.adk.agents import Agent
from google.adk.tools import FunctionTool
from charity_advisor.tools.charity_tools import find_charities, save_user_choice

Langkah 6: Tulis Petunjuk Agen

Di bagian petunjuk, kita menentukan deskripsi pekerjaan dan alur kerja agen. Hal ini sangat penting—petunjuk yang ditulis dengan buruk akan menghasilkan perilaku yang tidak dapat diandalkan.

👉 Temukan:

# MODULE_4_STEP_6_WRITE_INSTRUCTION
instruction="""""",

👉 Ganti kedua baris tersebut dengan:

    instruction="""You are a research specialist helping users find verified charities.

Your workflow:

1. When the user describes what cause they want to support (e.g., "education", "health", "environment"),
   use the find_charities tool to search our vetted database.

2. Present the results clearly. The tool returns formatted charity information that you should
   show to the user.

3. When the user selects a charity and specifies an amount, use the save_user_choice tool
   to create an IntentMandate and record their decision. You MUST call save_user_choice with:
   - charity_name: The exact name of the chosen charity
   - charity_ein: The EIN of the chosen charity  
   - amount: The donation amount in dollars (as a number, not a string)

4. After successfully saving, inform the user:
   - That you've created an IntentMandate (mention the intent ID if provided)
   - When the intent expires
   - That you're passing their request to the secure payment processor

IMPORTANT BOUNDARIES:
- Your ONLY job is discovery and creating the IntentMandate
- You do NOT process payments
- You do NOT see the user's payment methods
- You do NOT create cart offers (that's the Merchant Agent's job)
- After calling save_user_choice, your work is done

WHAT IS AN INTENTMANDATE:
An IntentMandate is a structured record of what the user wants to do. It includes:
- Natural language description ("Donate $50 to Room to Read")
- Which merchants can fulfill it
- When the intent expires
- Whether user confirmation is required

This is the first of three verifiable credentials in our secure payment system.

If the user asks you to do anything related to payment processing, politely explain that
you don't have that capability and that their request will be handled by the appropriate
specialist agent.""",

Langkah 7: Tambahkan Alat ke Agen

Sekarang, mari beri agen akses ke kedua alat.

👉 Temukan:

# MODULE_4_STEP_7_ADD_TOOLS

👉 Ganti kedua baris tersebut dengan:

    tools=[
        FunctionTool(func=find_charities),
        FunctionTool(func=save_user_choice)
    ]

Langkah 8: Verifikasi Agen Lengkap Anda

Mari kita periksa apakah semuanya sudah terhubung dengan benar.

👉 Langkah-langkah lengkap

charity_advisor/shopping_agent/agent.py

kini akan terlihat seperti ini:

"""
Shopping Agent - Finds charities from a trusted database and saves the user's choice.
This agent acts as our specialized "Research Analyst."
"""

from google.adk.agents import Agent
from google.adk.tools import FunctionTool
from charity_advisor.tools.charity_tools import find_charities, save_user_choice


shopping_agent = Agent(
    name="ShoppingAgent",
    model="gemini-2.5-pro",
    description="Finds and recommends vetted charities from a trusted database, then creates an IntentMandate capturing the user's donation intent.",
    instruction="""You are a research specialist helping users find verified charities.

Your workflow:

1. When the user describes what cause they want to support (e.g., "education", "health", "environment"),
   use the find_charities tool to search our vetted database.

2. Present the results clearly. The tool returns formatted charity information that you should
   show to the user.

3. When the user selects a charity and specifies an amount, use the save_user_choice tool
   to create an IntentMandate and record their decision. You MUST call save_user_choice with:
   - charity_name: The exact name of the chosen charity
   - charity_ein: The EIN of the chosen charity  
   - amount: The donation amount in dollars (as a number, not a string)

4. After successfully saving, inform the user:
   - That you've created an IntentMandate (mention the intent ID if provided)
   - When the intent expires
   - That you're passing their request to the secure payment processor

IMPORTANT BOUNDARIES:
- Your ONLY job is discovery and creating the IntentMandate
- You do NOT process payments
- You do NOT see the user's payment methods
- You do NOT create cart offers (that's the Merchant Agent's job)
- After calling save_user_choice, your work is done

WHAT IS AN INTENTMANDATE:
An IntentMandate is a structured record of what the user wants to do. It includes:
- Natural language description ("Donate $50 to Room to Read")
- Which merchants can fulfill it
- When the intent expires
- Whether user confirmation is required

This is the first of three verifiable credentials in our secure payment system.

If the user asks you to do anything related to payment processing, politely explain that
you don't have that capability and that their request will be handled by the appropriate
specialist agent.""",
    tools=[
        FunctionTool(func=find_charities),
        FunctionTool(func=save_user_choice)
    ]
)

Sempurna! Anda telah membangun agen yang sesuai dengan AP2 dan berkualitas produksi dengan:

  • Validasi masukan
  • Pembuatan IntentMandate yang tepat menggunakan model Pydantic AP2
  • Output yang diformat
  • Batasan peran yang jelas
  • Petunjuk mendetail
  • Penanganan error

Langkah 9: Uji Agen Shopping

Mari kita verifikasi bahwa agen kita berfungsi dengan benar, membuat IntentMandate yang tepat, dan mematuhi batasnya.

👉 Di terminal Cloud Shell, jalankan:

adk run charity_advisor/shopping_agent

Perintah [user]: akan muncul.

Pengujian 1: Penemuan dengan Trusted Database

👉 Jenis:

I want to donate to an education charity. What are my options?

Setelah beberapa saat, agen akan memberikan respons. Berkat helper _format_charity_display, hasilnya diformat dengan indah:

Kami menemukan 3 lembaga amal pendidikan terverifikasi dalam database kami:

Room to Read (EIN: 77-0479905)
⭐ Rating: 4,9/5,0
💰 Efisiensi: 88% dana digunakan untuk program
📋 Misi: Mengubah kehidupan jutaan anak di komunitas berpenghasilan rendah dengan berfokus pada literasi dan kesetaraan gender dalam pendidikan.

Teach For America (EIN: 13-3541913)
⭐ Rating: 4,7/5,0
💰 Efisiensi: 81% dana digunakan untuk program
📋 Misi: Berupaya memperluas peluang pendidikan bagi anak-anak yang menghadapi kesulitan.

Tech Education Alliance (EIN: 45-2345678)
⭐ Rating: 4,8/5,0
💰 Efisiensi: 92% dana digunakan untuk program
📋 Misi: Membawa pendidikan ilmu komputer ke sekolah-sekolah yang kurang terlayani.

Lembaga amal mana yang ingin Anda dukung, dan berapa jumlah donasi yang ingin Anda berikan?

Bandingkan dengan agen sederhana Modul 3, yang memberi Anda hasil Google yang belum diseleksi. Inilah perbedaan antara penemuan tidak tepercaya dan tepercaya.

Uji 2: Merekam Pilihan Pengguna dan Membuat IntentMandate

👉 Jenis:

I'll donate $50 to Room to Read.

Anda akan melihat agen memanggil alat save_user_choice dengan parameter yang benar. Berkat pembuatan IntentMandate kami menggunakan model AP2, Anda akan melihat respons yang lebih terstruktur:

Sempurna! Kami telah membuat IntentMandate untuk donasi Anda:

Detail Maksud:

  • ID Maksud: intent_774795905_1730927536
  • Jumlah: $50,00 untuk Room to Read (EIN: 77-0479905)
  • Berakhir: 2024-11-07T15:32:16Z (dalam 1 jam)

IntentMandate ini mencatat niat donasi Anda dan menyertakan batasan untuk memastikan transaksi diproses dengan aman. Sekarang kami meneruskan permintaan ini ke pemroses pembayaran aman kami untuk membuat penawaran resmi dan menyelesaikan donasi Anda.

Di balik layar, beberapa hal baru saja terjadi:

  1. _validate_charity_data() memverifikasi input
  2. _create_intent_mandate() menggunakan model Pydantic AP2 untuk membangun struktur:
   # Created and validated via Pydantic
   intent_mandate_model = IntentMandate(
       user_cart_confirmation_required=True,
       natural_language_description="Donate $50.00 to Room to Read",
       merchants=["Room to Read"],
       skus=None,
       requires_refundability=False,
       intent_expiry="2024-11-07T15:32:16Z"
   )
   
   # Converted to dict and extended with custom fields
   intent_mandate_dict = intent_mandate_model.model_dump()
   intent_mandate_dict.update({
       "charity_ein": "77-0479905",
       "amount": 50.0,
       "currency": "USD"
   })
  1. Alat ini HANYA menulis IntentMandate ke status bersama:
   state["intent_mandate"] = intent_mandate_dict
  1. Pesan keberhasilan dengan detail maksud dikembalikan ke LLM
  2. LLM menyusun pesan akhir pengguna

IntentMandate ini kini siap diambil oleh Agen Penjual (Modul 5). Penjual akan mengekstrak nama dan jumlah lembaga amal dari IntentMandate dan memvalidasi bahwa masa berlakunya belum berakhir.

Tes 3: Validasi dalam Tindakan

Mari kita verifikasi apakah helper validasi kita menangkap input yang buruk.

👉 Jenis:

I'll donate -$25 to Room to Read.

Agen harus menangkap jumlah yang tidak valid:

Saya mengalami masalah: Jumlah donasi harus positif, yang didapatkan: $-25.0

Harap tentukan jumlah donasi positif dan saya akan membuat IntentMandate untuk Anda.

Ini adalah contoh defensive programming. Helper _validate_charity_data kami mencegah data yang tidak valid masuk ke sistem dan membuat IntentMandate yang salah format.

Pengujian 4: Memverifikasi Batas Kepercayaan

👉 Coba minta agen untuk memproses pembayaran:

Now process my credit card payment.

Agen harus menolak, dengan menghormati batas perannya:

Saya tidak memiliki kemampuan untuk memproses pembayaran—hal itu bukan bagian dari tugas saya. Tugas saya adalah membantu Anda menemukan lembaga amal terverifikasi dan membuat IntentMandate yang mencatat niat donasi Anda.

IntentMandate Anda telah dibuat dan diteruskan ke pemroses pembayaran aman kami. Agen Penjual akan membuat penawaran formal (CartMandate), lalu Penyedia Kredensial akan menangani pembayaran sebenarnya dengan izin eksplisit Anda.

Ini adalah batas kepercayaan yang sedang beroperasi. Agen mengetahui bahwa agen tidak diberi otorisasi untuk menangani data pembayaran, dan instruksinya secara eksplisit memandunya untuk menjelaskan hal ini kepada pengguna sekaligus mengajari mereka tentang konsep IntentMandate.

👉 Tekan

Ctrl+C

untuk keluar setelah selesai menguji.

Yang Baru Saja Anda Buat

Anda telah berhasil menerapkan bagian pertama arsitektur AP2 dengan pembuatan IntentMandate yang tepat menggunakan model Pydantic AP2.

Konsep Utama yang Dikuasai

Arsitektur Berbasis Peran:

  • Setiap agen memiliki satu tugas yang ditentukan dengan jelas
  • Agen berkomunikasi melalui status bersama, bukan akses langsung
  • Batas kepercayaan membatasi dampak kompromi

IntentMandate (AP2 Credential #1):

  • Dibuat menggunakan model Pydantic AP2 resmi untuk validasi
  • Pengambilan terstruktur niat pengguna
  • Mencakup masa berlaku untuk keamanan (mencegah serangan replay)
  • Menentukan batasan (penjual, pengembalian dana, konfirmasi)
  • Deskripsi natural language untuk manusia
  • Dapat dibaca mesin untuk agen
  • Model divalidasi sebelum dikonversi menjadi kamus

Status sebagai Memori Bersama:

  • tool_context.state adalah "notepad" yang dapat diakses semua agen
  • Menulis ke status = membuat kredensial yang dapat diverifikasi tersedia
  • Membaca dari status = menggunakan dan memvalidasi kredensial
  • Agen hilir mengekstrak apa yang mereka butuhkan dari kredensial

FunctionTool:

  • Mengonversi fungsi Python menjadi alat yang dapat dipanggil LLM
  • Mengandalkan docstring dan petunjuk jenis untuk pemahaman LLM
  • Menangani pemanggilan secara otomatis
  • Komposisi alat: alat kecil yang fokus > alat monolitik

Petunjuk Agen:

  • Panduan alur kerja langkah demi langkah
  • Batasan eksplisit ("JANGAN...")
  • Spesifikasi parameter untuk mencegah error
  • Definisi teknis (apa itu IntentMandate)
  • Penanganan kasus ekstrem (apa yang harus dikatakan saat...)

Langkah Berikutnya

Dalam modul berikutnya, kita akan membuat Agen Penjual untuk menerima IntentMandate dan membuat kredensial tepercaya kedua: CartMandate.

Agen Shopping telah membuat IntentMandate yang merekam maksud pengguna dengan masa berlaku. Sekarang kita memerlukan agen untuk membaca kredensial tersebut, memvalidasi bahwa masa berlakunya belum habis, dan membuat penawaran resmi yang ditandatangani yang menyatakan: "Saya, penjual, akan mematuhi harga ini dan mengirimkan barang ini".

Mari kita bangun Agen Penjual dan lihat cara kerja kredensial AP2 kedua.

5. Membangun Agen Penjual - Mengikat Penawaran & CartMandate

banner

Dari Penemuan hingga Komitmen

Dalam modul sebelumnya, Anda membuat Agen Belanja—spesialis yang menemukan lembaga amal terverifikasi dan membuat IntentMandate yang merekam maksud pengguna. Sekarang kita memerlukan agen untuk menerima IntentMandate tersebut dan membuat penawaran formal yang mengikat.

Di sinilah prinsip utama kedua AP2 berperan: kredensial yang dapat diverifikasi melalui CartMandate.

Prinsip AP2: CartMandate & Penawaran Mengikat

Alasan Kami Memerlukan Peran Penjual

Di Modul 4, Agen Shopping membuat IntentMandate dan menyimpannya ke status:

state["intent_mandate"] = {
    "natural_language_description": "Donate $50 to Room to Read",
    "merchants": ["Room to Read"],
    "amount": 50.0,
    "intent_expiry": "2024-11-07T15:32:16Z"
}

Namun, ini hanyalah maksud pengguna. Sebelum pembayaran dapat diproses, kami memerlukan:

  • Struktur penawaran formal yang dipahami oleh sistem pembayaran
  • Bukti bahwa penjual akan mematuhi harga ini
  • Komitmen mengikat yang tidak dapat diubah di tengah transaksi
  • Validasi bahwa masa berlaku intent belum berakhir

Ini adalah tugas Agen Penjual.

Apa itu CartMandate?

CartMandate adalah istilah AP2 untuk "keranjang belanja digital" yang berfungsi sebagai penawaran yang mengikat. Objek ini disusun sesuai dengan standar PaymentRequest W3C, yang berarti:

  • Pemroses pembayaran di seluruh dunia mengenali format ini
  • Berisi semua detail transaksi dengan cara yang standar
  • Dapat ditandatangani secara kriptografis untuk membuktikan keaslian

Anggap saja seperti kutipan tertulis dari kontraktor:

  • ❌ Verbal: "Ya, saya bisa melakukan pekerjaan itu dengan harga sekitar lima puluh ribu"
  • ✅ Kutipan tertulis: Biaya per item, total, tanda tangan, tanggal

Penawaran tertulis bersifat mengikat. CartMandate adalah versi digitalnya.

niat ke keranjang

Struktur CartMandate

CartMandate di AP2 memiliki struktur bertingkat tertentu:

cart_mandate = {
    "contents": {  # ← AP2 wrapper
        "id": "cart_xyz123",
        "cart_expiry": "2024-11-07T15:47:16Z",
        "merchant_name": "Room to Read",
        "user_cart_confirmation_required": False,
        
        "payment_request": {  # ← W3C PaymentRequest nested inside
            "method_data": [...],
            "details": {...},
            "options": {...}
        }
    },
    "merchant_authorization": "SIG_a3f7b2c8"  # ← Merchant signature
}

Tiga komponen utama:

1. contents - Pembungkus keranjang yang berisi:

  • ID dan masa berlaku keranjang
  • Nama penjual
  • W3C PaymentRequest

2. payment_request (di dalam konten) - Apa yang dibeli:

  • method_data: Jenis pembayaran yang diterima
  • Detail: Item dan total
  • opsi: Pengiriman, persyaratan info pembayar

3. merchant_authorization - Tanda tangan kriptografi

Tanda Tangan Penjual: Bukti Komitmen

Tanda tangan penjual sangat penting. Hal ini membuktikan:

  • Penawaran ini berasal dari penjual resmi
  • Penjual berkomitmen untuk memberikan harga yang sama persis dengan harga ini
  • Penawaran tidak diubah sejak dibuat

Dalam produksi, ini akan menjadi tanda tangan kriptografi menggunakan PKI (Public Key Infrastructure) atau JWT (JSON Web Tokens). Untuk workshop edukasi ini, kita akan menyimulasikannya dengan hash SHA-256.

# Production (real signature):
signature = sign_with_private_key(cart_data, merchant_private_key)

# Workshop (simulated signature):
cart_hash = hashlib.sha256(cart_json.encode()).hexdigest()
signature = f"SIG_{cart_hash[:16]}"

Misi Kami: Membangun Agen Penjual

Agen Penjual akan:

  1. Membaca IntentMandate dari status (apa yang ditulis Agen Shopping)
  2. Memvalidasi bahwa masa berlaku intent belum berakhir
  3. Mengekstrak nama lembaga amal, jumlah, dan detail lainnya
  4. Buat struktur PaymentRequest yang sesuai dengan W3C menggunakan model Pydantic AP2
  5. Gabungkan dalam CartMandate AP2 dengan masa berlaku
  6. Menambahkan tanda tangan penjual simulasi
  7. Tulis CartMandate untuk menyatakan Penyedia Kredensial (modul berikutnya)

Mari kita buat langkah demi langkah.

Langkah 1: Tambahkan Pembantu Validasi Masa Aktif

Pertama, siapkan file alat terkait penjual dan tambahkan helper untuk memvalidasi masa berlaku IntentMandate.

👉 Buka

charity_advisor/tools/merchant_tools.py

Mari tambahkan validasi masa berlaku:

👉 Temukan:

# MODULE_5_STEP_1_ADD_EXPIRY_VALIDATION_HELPER

👉 Ganti satu baris tersebut dengan:

def _validate_intent_expiry(intent_expiry_str: str) -> tuple[bool, str]:
    """
    Validates that the IntentMandate hasn't expired.
    
    This is a critical security check - expired intents should not be processed.
    
    Args:
        intent_expiry_str: The ISO 8601 timestamp string from the IntentMandate.
        
    Returns:
        (is_valid, error_message): Tuple indicating if intent is still valid.
    """
    try:
        # The .replace('Z', '+00:00') is for compatibility with older Python versions
        expiry_time = datetime.fromisoformat(intent_expiry_str.replace('Z', '+00:00'))
        now = datetime.now(timezone.utc)
        
        if expiry_time < now:
            return False, f"IntentMandate expired at {intent_expiry_str}"
        
        time_remaining = expiry_time - now
        logger.info(f"IntentMandate valid. Expires in {time_remaining.total_seconds():.0f} seconds")
        
        return True, ""
        
    except (ValueError, TypeError) as e:
        return False, f"Invalid intent_expiry format: {e}"

Langkah 2: Tambahkan Pembantu Pembuatan Tanda Tangan

Sekarang, mari kita buat helper yang menghasilkan tanda tangan penjual simulasi.

👉 Temukan:

# MODULE_5_STEP_2_ADD_SIGNATURE_HELPER

👉 Ganti satu baris tersebut dengan:

def _generate_merchant_signature(cart_contents: CartContents) -> str:
    """
    Generates a simulated merchant signature for the CartMandate contents.
    
    In production, this would use PKI or JWT with the merchant's private key.
    For this codelab, we use a SHA-256 hash of the sorted JSON representation.
    
    Args:
        cart_contents: The Pydantic model of the cart contents to sign.
        
    Returns:
        Simulated signature string (format: "SIG_" + first 16 chars of hash).
    """
    # Step 1: Dump the Pydantic model to a dictionary. The `mode='json'` argument
    # ensures that complex types like datetimes are serialized correctly.
    cart_contents_dict = cart_contents.model_dump(mode='json')
    
    # Step 2: Use the standard json library to create a stable, sorted JSON string.
    # separators=(',', ':') removes whitespace for a compact and canonical representation.
    cart_json = json.dumps(cart_contents_dict, sort_keys=True, separators=(',', ':'))
    
    # Step 3: Generate SHA-256 hash.
    cart_hash = hashlib.sha256(cart_json.encode('utf-8')).hexdigest()
    
    # Step 4: Create signature in a recognizable format.
    signature = f"SIG_{cart_hash[:16]}"
    
    logger.info(f"Generated merchant signature: {signature}")
    return signature

Langkah 3A: Buat Tanda Tangan Alat dan Penyiapan

Sekarang, mari kita mulai membangun alat utama. Kita akan membuatnya secara bertahap dalam empat sub-langkah. Pertama, tanda tangan fungsi dan penyiapan awal.

👉 Temukan:

# MODULE_5_STEP_3A_CREATE_TOOL_SIGNATURE

👉 Ganti satu baris tersebut dengan:

async def create_cart_mandate(tool_context: Any) -> Dict[str, Any]:
    """
    Creates a W3C PaymentRequest-compliant CartMandate from the IntentMandate.
    
    This tool reads the IntentMandate from shared state, validates it, and
    creates a formal, signed offer using the official AP2 Pydantic models.
    
    Returns:
        Dictionary containing status and the created CartMandate.
    """
    logger.info("Tool called: Creating CartMandate from IntentMandate")
    
    # MODULE_5_STEP_3B_ADD_VALIDATION_LOGIC

Langkah 3B: Tambahkan Logika Validasi

Sekarang, mari kita tambahkan logika untuk membaca dan memvalidasi IntentMandate menggunakan model Pydantic AP2, serta mengekstrak data yang kita butuhkan.

👉 Temukan:

# MODULE_5_STEP_3B_ADD_VALIDATION_LOGIC

👉 Ganti satu baris tersebut dengan:

    # 1. Read IntentMandate dictionary from state
    intent_mandate_dict = tool_context.state.get("intent_mandate")
    if not intent_mandate_dict:
        logger.error("No IntentMandate found in state")
        return {
            "status": "error",
            "message": "No IntentMandate found. Shopping Agent must create intent first."
        }
    
    # 2. Parse dictionary into a validated Pydantic model
    try:
        intent_mandate_model = IntentMandate.model_validate(intent_mandate_dict)
    except Exception as e:
        logger.error(f"Could not validate IntentMandate structure: {e}")
        return {"status": "error", "message": f"Invalid IntentMandate structure: {e}"}
    
    # 3. Validate that the intent hasn't expired (CRITICAL security check)
    is_valid, error_message = _validate_intent_expiry(intent_mandate_model.intent_expiry)
    if not is_valid:
        logger.error(f"IntentMandate validation failed: {error_message}")
        return {"status": "error", "message": error_message}
    
    # 4. Extract data. Safely access standard fields from the model, and
    # custom fields (like 'amount') from the original dictionary.
    charity_name = intent_mandate_model.merchants[0] if intent_mandate_model.merchants else "Unknown Charity"
    amount = intent_mandate_dict.get("amount", 0.0)
    
    # MODULE_5_STEP_3C_CREATE_CARTMANDATE_STRUCTURE

Langkah 3C: Buat Struktur CartMandate

Sekarang, mari kita buat struktur PaymentRequest yang sesuai dengan W3C dan gabungkan dalam CartMandate AP2 menggunakan model Pydantic.

👉 Temukan:

# MODULE_5_STEP_3C_CREATE_CARTMANDATE_STRUCTURE

👉 Ganti satu baris tersebut dengan:

    # 5. Build the nested Pydantic models for the CartMandate
    timestamp = datetime.now(timezone.utc)
    cart_id = f"cart_{hashlib.sha256(f'{charity_name}{timestamp.isoformat()}'.encode()).hexdigest()[:12]}"
    cart_expiry = timestamp + timedelta(minutes=15)
    
    payment_request_model = PaymentRequest(
        method_data=[PaymentMethodData(
            supported_methods="CARD",
            data={"supported_networks": ["visa", "mastercard", "amex"], "supported_types": ["debit", "credit"]}
        )],
        details=PaymentDetailsInit(
            id=f"order_{cart_id}",
            display_items=[PaymentItem(
                label=f"Donation to {charity_name}",
                amount=PaymentCurrencyAmount(currency="USD", value=amount)  # Pydantic v2 handles float -> str conversion
            )],
            total=PaymentItem(
                label="Total Donation",
                amount=PaymentCurrencyAmount(currency="USD", value=amount)
            )
        ),
        options=PaymentOptions(request_shipping=False)
    )
    
    cart_contents_model = CartContents(
        id=cart_id,
        cart_expiry=cart_expiry.isoformat(),
        merchant_name=charity_name,
        user_cart_confirmation_required=False,
        payment_request=payment_request_model
    )
    
    # MODULE_5_STEP_3D_ADD_SIGNATURE_AND_SAVE

Langkah 3D: Tambahkan Tanda Tangan dan Simpan ke Status

Terakhir, mari tandatangani CartMandate menggunakan model Pydantic kita dan simpan ke status untuk agen berikutnya.

👉 Temukan:

# MODULE_5_STEP_3D_ADD_SIGNATURE_AND_SAVE

👉 Ganti satu baris tersebut dengan:

    # 6. Generate signature from the validated Pydantic model
    signature = _generate_merchant_signature(cart_contents_model)
    
    # 7. Create the final CartMandate model, now including the signature
    cart_mandate_model = CartMandate(
        contents=cart_contents_model,
        merchant_authorization=signature
    )
    
    # 8. Convert the final model to a dictionary for state storage and add the custom timestamp
    cart_mandate_dict = cart_mandate_model.model_dump(mode='json')
    cart_mandate_dict["timestamp"] = timestamp.isoformat()
    
    # 9. Write the final dictionary to state
    tool_context.state["cart_mandate"] = cart_mandate_dict
    
    logger.info(f"CartMandate created successfully: {cart_id}")
    
    return {
        "status": "success",
        "message": f"Created signed CartMandate {cart_id} for ${amount:.2f} donation to {charity_name}",
        "cart_id": cart_id,
        "cart_expiry": cart_expiry.isoformat(),
        "signature": signature
    }

Langkah 4: Bangun Agen Penjual - Impor Komponen

Sekarang, mari buat agen yang akan menggunakan alat ini.

👉 Buka

charity_advisor/merchant_agent/agent.py

Anda akan melihat template dengan penanda placeholder. Mari kita mulai dengan mengimpor apa yang kita butuhkan.

👉 Temukan:

# MODULE_5_STEP_4_IMPORT_COMPONENTS

👉 Ganti satu baris tersebut dengan:

from google.adk.agents import Agent
from google.adk.tools import FunctionTool
from charity_advisor.tools.merchant_tools import create_cart_mandate

Langkah 5: Tulis Instruksi Agen Penjual

Sekarang, mari kita tulis instruksi yang memberi tahu agen kapan dan bagaimana cara menggunakan alatnya.

👉 Temukan:

# MODULE_5_STEP_5_WRITE_INSTRUCTION
instruction="""""",

👉 Ganti kedua baris tersebut dengan:

    instruction="""You are a merchant specialist responsible for creating formal, signed offers (CartMandates).

Your workflow:

1. Read the IntentMandate from shared state.
   The IntentMandate was created by the Shopping Agent and contains:
   - merchants: List of merchant names
   - amount: Donation amount
   - charity_ein: Tax ID
   - intent_expiry: When the intent expires

2. Use the create_cart_mandate tool to create a W3C PaymentRequest-compliant CartMandate.
   This tool will:
   - Validate the IntentMandate hasn't expired (CRITICAL security check)
   - Extract the charity name and amount from the IntentMandate
   - Create a structured offer with payment methods, transaction details, and merchant info
   - Generate a merchant signature to prove authenticity
   - Save the CartMandate to state for the payment processor

3. After creating the CartMandate, inform the user:
   - That you've created a formal, signed offer
   - The cart ID
   - When the cart expires (15 minutes)
   - That you're passing it to the secure payment processor

IMPORTANT BOUNDARIES:
- Your ONLY job is creating signed CartMandates from valid IntentMandates
- You do NOT process payments
- You do NOT see the user's payment methods or credentials
- You do NOT interact with payment networks
- You MUST validate that the IntentMandate hasn't expired before creating a cart
- After calling create_cart_mandate, your work is done

WHAT IS A CARTMANDATE:
A CartMandate is a binding commitment that says:
"I, the merchant, commit to accepting $X for this charity donation, and I prove it with my signature."

This commitment is structured using the W3C PaymentRequest standard and includes:
- Payment methods accepted (card, bank transfer)
- Transaction details (amount, charity name)
- Cart expiry (15 minutes from creation)
- Merchant signature (proof of commitment)

This is the second of three verifiable credentials in our secure payment system.""",

Langkah 6: Tambahkan Alat ke Agen Penjual

👉 Temukan:

# MODULE_5_STEP_6_ADD_TOOLS
tools=[],

👉 Ganti kedua baris tersebut dengan:

    tools=[
        FunctionTool(func=create_cart_mandate)
    ],

Langkah 7: Verifikasi Agen Penjual Lengkap

Pastikan semuanya terhubung dengan benar.

👉 Langkah-langkah lengkap

charity_advisor/merchant_agent/agent.py

kini akan terlihat seperti ini:

"""
Merchant Agent - Creates W3C-compliant CartMandates with merchant signatures.
This agent acts as our "Contract Creator."
"""

from google.adk.agents import Agent
from google.adk.tools import FunctionTool
from charity_advisor.tools.merchant_tools import create_cart_mandate


merchant_agent = Agent(
    name="MerchantAgent",
    model="gemini-2.5-flash",
    description="Creates formal, signed CartMandates for charity donations following W3C PaymentRequest standards.",
    tools=[
        FunctionTool(func=create_cart_mandate)
    ],
    instruction="""You are a merchant specialist responsible for creating formal, signed offers (CartMandates).

Your workflow:

1. Read the IntentMandate from shared state.
   The IntentMandate was created by the Shopping Agent and contains:
   - merchants: List of merchant names
   - amount: Donation amount
   - charity_ein: Tax ID
   - intent_expiry: When the intent expires

2. Use the create_cart_mandate tool to create a W3C PaymentRequest-compliant CartMandate.
   This tool will:
   - Validate the IntentMandate hasn't expired (CRITICAL security check)
   - Extract the charity name and amount from the IntentMandate
   - Create a structured offer with payment methods, transaction details, and merchant info
   - Generate a merchant signature to prove authenticity
   - Save the CartMandate to state for the payment processor

3. After creating the CartMandate, inform the user:
   - That you've created a formal, signed offer
   - The cart ID
   - When the cart expires (15 minutes)
   - That you're passing it to the secure payment processor

IMPORTANT BOUNDARIES:
- Your ONLY job is creating signed CartMandates from valid IntentMandates
- You do NOT process payments
- You do NOT see the user's payment methods or credentials
- You do NOT interact with payment networks
- You MUST validate that the IntentMandate hasn't expired before creating a cart
- After calling create_cart_mandate, your work is done

WHAT IS A CARTMANDATE:
A CartMandate is a binding commitment that says:
"I, the merchant, commit to accepting $X for this charity donation, and I prove it with my signature."

This commitment is structured using the W3C PaymentRequest standard and includes:
- Payment methods accepted (card, bank transfer)
- Transaction details (amount, charity name)
- Cart expiry (15 minutes from creation)
- Merchant signature (proof of commitment)

This is the second of three verifiable credentials in our secure payment system."""
)

Checkpoint: Sekarang Anda memiliki Agen Penjual yang lengkap dengan pembuatan CartMandate AP2 yang tepat menggunakan model Pydantic.

Langkah 8: Uji Agen Penjual

Sekarang, mari kita verifikasi bahwa agen kita membuat CartMandates dengan tanda tangan dan memvalidasi masa berlaku dengan benar.

Penyiapan Pengujian: Jalankan Skrip Pengujian

👉 Di terminal Cloud Shell, jalankan:

python scripts/test_merchant.py

Output yang diharapkan:

======================================================================
MERCHANT AGENT TEST
======================================================================

Simulated IntentMandate from Shopping Agent:
  charity: Room to Read
  amount: $50.00
  expiry: 2024-11-07T16:32:16Z

----------------------------------------------------------------------
Merchant Agent Response:
----------------------------------------------------------------------
Perfect! I've received your IntentMandate and created a formal, signed offer (CartMandate) for your donation.

**CartMandate Details:**
- **Cart ID**: cart_3b4c5d6e7f8a
- **Donation Amount**: $50.00 to Room to Read
- **Payment Methods Accepted**: Credit/debit cards (Visa, Mastercard, Amex) or bank transfer
- **Cart Expires**: 2024-11-07T15:47:16Z (in 15 minutes)
- **Merchant Signature**: SIG_a3f7b2c8d9e1f4a2

This signed CartMandate proves my commitment to accept this donation amount. I'm now passing this to the secure payment processor to complete your transaction.

======================================================================
CARTMANDATE CREATED:
======================================================================
  ID: cart_3b4c5d6e7f8a
  Amount: 50.00
  Merchant: Room to Read
  Expires: 2024-11-07T15:47:16Z
  Signature: SIG_a3f7b2c8d9e1f4a2
======================================================================

Pengujian 2: Memverifikasi Kepatuhan W3C

Mari kita validasi bahwa struktur CartMandate kita sepenuhnya mematuhi standar AP2 dan W3C PaymentRequest.

👉 Jalankan skrip validasi:

python scripts/validate_cartmandate.py

Output yang diharapkan:

======================================================================
AP2 & W3C PAYMENTREQUEST VALIDATION
======================================================================
✅ CartMandate is AP2 and W3C PaymentRequest compliant

Structure validation passed:
  ✓ AP2 'contents' wrapper present
  ✓ AP2 'merchant_authorization' signature present
  ✓ cart_expiry present
  ✓ payment_request nested inside contents
  ✓ method_data present and valid
  ✓ details.total.amount present with currency and value
  ✓ All required W3C PaymentRequest fields present
======================================================================

Yang Baru Saja Anda Buat

Anda telah berhasil menerapkan CartMandate AP2 menggunakan model Pydantic untuk struktur yang tepat, validasi masa berlaku, dan tanda tangan penjual.

Konsep Utama yang Dikuasai

CartMandate (AP2 Credential #2):

  • Dibuat menggunakan model Pydantic AP2 resmi
  • Struktur AP2 dengan wrapper konten
  • W3C PaymentRequest bertingkat di dalamnya
  • Masa berlaku keranjang (lebih pendek dari niat)
  • Tanda tangan penjual untuk komitmen yang mengikat
  • Validasi model memastikan kepatuhan terhadap spesifikasi

Validasi Tanggal Habis Masa Berlaku:

  • Membaca IntentMandate dari status
  • Memvalidasi struktur dengan IntentMandate.model_validate()
  • Mem-parsing stempel waktu ISO 8601
  • Membandingkan dengan waktu saat ini
  • Fitur keamanan yang mencegah pemrosesan yang tidak valid

Tanda Tangan Penjual:

  • Membuktikan keaslian dan komitmen
  • Dibuat dari model Pydantic yang divalidasi
  • Menggunakan model_dump(mode='json') untuk representasi kanonis
  • Disimulasikan dengan SHA-256 untuk pendidikan
  • Produksi menggunakan PKI/JWT
  • Menandatangani model konten, bukan kamus

W3C PaymentRequest:

  • Dibuat menggunakan model Pydantic PaymentRequest AP2
  • Standar industri untuk data pembayaran
  • Bertingkat di dalam struktur AP2
  • Berisi method_data, details, options
  • Memungkinkan interoperabilitas

Rantai Kredensial dengan Model:

  • Shopping → IntentMandate (telah divalidasi)
  • Penjual membaca IntentMandate → CartMandate (kedua model divalidasi)
  • Penyedia Kredensial akan membaca CartMandate → PaymentMandate
  • Setiap langkah memvalidasi kredensial sebelumnya menggunakan Pydantic

Pengembangan Berbasis Model:

  • Validasi input melalui model_validate()
  • Konstruksi yang aman untuk jenis
  • Serialisasi otomatis melalui model_dump()
  • Pola siap produksi

Langkah Berikutnya

Pada modul berikutnya, kita akan membuat Credentials Provider untuk memproses pembayaran dengan aman.

Agen Penjual telah membuat penawaran yang mengikat dengan masa berlaku menggunakan model AP2. Sekarang kita memerlukan agen untuk membaca CartMandate tersebut, mendapatkan izin pengguna, dan mengeksekusi pembayaran.

Bangun Penyedia Kredensial dan selesaikan rantai kredensial AP2.

6. Membangun Penyedia Kredensial - Eksekusi Pembayaran Aman

banner

Dari Penawaran Mengikat hingga Eksekusi Pembayaran

Dalam Modul 5, Anda membangun Agen Penjual—spesialis yang membaca IntentMandate, memvalidasi bahwa IntentMandate belum habis masa berlakunya, dan membuat CartMandate yang mengikat dengan tanda tangan penjual. Sekarang kita memerlukan agen untuk menerima CartMandate tersebut dan mengeksekusi pembayaran sebenarnya.

Di sinilah prinsip ketiga dan terakhir AP2 berperan: eksekusi pembayaran yang aman melalui PaymentMandate.

Prinsip AP2: PaymentMandate & Payment Execution

Alasan Kami Membutuhkan Peran Penyedia Kredensial

Dalam Modul 5, Agen Penjual membuat CartMandate dan menyimpannya ke status:

state["cart_mandate"] = {
    "contents": {
        "id": "cart_abc123",
        "cart_expiry": "2025-11-07:15:47:16Z",
        "payment_request": {
            "details": {
                "total": {
                    "amount": {"currency": "USD", "value": "50.00"}
                }
            }
        }
    },
    "merchant_authorization": "SIG_a3f7b2c8"
}

Namun, ini hanyalah penawaran yang mengikat. Sebelum pembayaran dapat dilakukan, kami memerlukan:

  • Validasi bahwa masa berlaku keranjang belum habis
  • Izin pengguna untuk melanjutkan pembayaran
  • Kredensial yang mengizinkan eksekusi pembayaran
  • Pemrosesan pembayaran sebenarnya (atau simulasi untuk workshop kami)

Ini adalah tugas Penyedia Kredensial.

Apa yang dimaksud dengan PaymentMandate?

PaymentMandate adalah istilah AP2 untuk otorisasi akhir yang memungkinkan pembayaran dilakukan. Ini adalah kredensial yang dapat diverifikasi ketiga dan terakhir dalam rantai AP2.

Anggap ketiga kredensial tersebut seperti proses penandatanganan kontrak:

  • IntentMandate: "Saya tertarik untuk membeli ini" (Surat pernyataan minat)
  • CartMandate: "Saya, penjual, menawarkan untuk menjual dengan harga ini" (Kutipan tertulis)
  • PaymentMandate: "Saya mengizinkan Anda untuk menagih metode pembayaran saya" (Kontrak yang ditandatangani)

Pembayaran hanya dapat dilakukan setelah ketiga kredensial ada.

rantai kredensial lengkap

Struktur PaymentMandate

PaymentMandate di AP2 memiliki struktur tertentu:

payment_mandate = {
    "payment_mandate_contents": {  # ← AP2 wrapper
        "payment_mandate_id": "payment_xyz123",
        "payment_details_id": "cart_abc123",  # Links to CartMandate
        "user_consent": True,
        "consent_timestamp": "2025-11-07T15:48:00Z",
        "amount": {
            "currency": "USD",
            "value": "50.00"
        },
        "merchant_name": "Room to Read"
    },
    "agent_present": True,  # Human-in-the-loop flow
    "timestamp": "2025-11-07T15:48:00Z"
}

Komponen utama:

1. payment_mandate_contents - Wrapper otorisasi yang berisi:

  • payment_mandate_id: ID unik
  • payment_details_id: Ditautkan kembali ke CartMandate
  • user_consent: Apakah pengguna menyetujui
  • jumlah: Jumlah pembayaran (diekstrak dari CartMandate)

2. agent_present - Apakah ini alur yang memerlukan interaksi manusia

3. timestamp - Waktu saat otorisasi dibuat

Misi Kami: Membangun Penyedia Kredensial

Penyedia Kredensial akan:

  1. Membaca CartMandate dari status (yang ditulis Agen Penjual)
  2. Validasi bahwa masa berlaku keranjang belum berakhir menggunakan model Pydantic AP2
  3. Mengekstrak detail pembayaran dari struktur bertingkat
  4. Membuat PaymentMandate dengan izin pengguna menggunakan model AP2
  5. Simulasikan pemrosesan pembayaran (dalam produksi, akan memanggil API pembayaran sebenarnya)
  6. Menulis PaymentMandate dan hasil pembayaran ke status

Mari kita buat langkah demi langkah.

Langkah 1: Tambahkan Pembantu Validasi Masa Berlaku Keranjang

Pertama, mari kita buat helper yang memvalidasi bahwa CartMandate belum habis masa berlakunya—sama seperti kita memvalidasi habis masa berlaku IntentMandate di Modul 5.

👉 Buka

charity_advisor/tools/payment_tools.py

Mari tambahkan validasi masa berlaku:

👉 Temukan:

# MODULE_6_STEP_1_ADD_CART_EXPIRY_VALIDATION_HELPER

👉 Ganti satu baris tersebut dengan:

def _validate_cart_expiry(cart: CartMandate) -> tuple[bool, str]:
    """
    Validates that the CartMandate hasn't expired.
    
    This is a critical security check - expired carts should not be processed.
    
    Args:
        cart: The Pydantic CartMandate model to validate.
        
    Returns:
        (is_valid, error_message): Tuple indicating if cart is still valid.
    """
    try:
        expiry_str = cart.contents.cart_expiry
        expiry_time = datetime.fromisoformat(expiry_str.replace('Z', '+00:00'))
        now = datetime.now(timezone.utc)
        
        if expiry_time < now:
            return False, f"CartMandate expired at {expiry_str}"
        
        time_remaining = expiry_time - now
        logger.info(f"CartMandate valid. Expires in {time_remaining.total_seconds():.0f} seconds")
        
        return True, ""
        
    except (ValueError, TypeError, AttributeError) as e:
        return False, f"Invalid cart_expiry format or structure: {e}"

Langkah 2: Tambahkan Pembantu Pembuatan Otorisasi Pembayaran

Sekarang, mari buat helper yang membangun struktur PaymentMandate menggunakan model Pydantic AP2 resmi.

👉 Temukan:

# MODULE_6_STEP_2_ADD_PAYMENT_MANDATE_CREATION_HELPER

👉 Ganti satu baris tersebut dengan:

def _create_payment_mandate(cart: CartMandate, consent_granted: bool) -> dict:
    """
    Creates a PaymentMandate using the official AP2 Pydantic models.
    
    It links to the CartMandate and includes user consent status.
    
    Args:
        cart: The validated Pydantic CartMandate model being processed.
        consent_granted: Whether the user has consented to the payment.
        
    Returns:
        A dictionary representation of the final, validated PaymentMandate.
    """
    timestamp = datetime.now(timezone.utc)
    
    # Safely extract details from the validated CartMandate model
    cart_id = cart.contents.id
    merchant_name = cart.contents.merchant_name
    total_item = cart.contents.payment_request.details.total
    
    # Create the nested PaymentResponse model for the mandate
    payment_response_model = PaymentResponse(
        request_id=cart_id,
        method_name="CARD",  # As per the simulated flow
        details={"token": "simulated_payment_token_12345"}
    )
    
    # Create the PaymentMandateContents model
    payment_mandate_contents_model = PaymentMandateContents(
        payment_mandate_id=f"payment_{hashlib.sha256(f'{cart_id}{timestamp.isoformat()}'.encode()).hexdigest()[:12]}",
        payment_details_id=cart_id,
        payment_details_total=total_item,
        payment_response=payment_response_model,
        merchant_agent=merchant_name,
        timestamp=timestamp.isoformat()
    )
    
    # Create the top-level PaymentMandate model
    # In a real system, a user signature would be added to this model
    payment_mandate_model = PaymentMandate(
        payment_mandate_contents=payment_mandate_contents_model
    )
    
    # Convert the final Pydantic model to a dictionary for state storage
    final_dict = payment_mandate_model.model_dump(mode='json')
    
    # Add any custom/non-standard fields required by the codelab's logic to the dictionary
    # The spec does not have these fields, but your original code did. We add them
    # back to ensure compatibility with later steps.
    final_dict['payment_mandate_contents']['user_consent'] = consent_granted
    final_dict['payment_mandate_contents']['consent_timestamp'] = timestamp.isoformat() if consent_granted else None
    final_dict['agent_present'] = True
    
    return final_dict

Langkah 3A: Buat Tanda Tangan Alat dan Penyiapan

Sekarang, mari kita mulai membangun alat utama secara bertahap. Pertama, tanda tangan fungsi dan penyiapan awal.

👉 Temukan:

# MODULE_6_STEP_3A_CREATE_TOOL_SIGNATURE

👉 Ganti satu baris tersebut dengan:

async def create_payment_mandate(tool_context: Any) -> Dict[str, Any]:
    """
    Creates a PaymentMandate and simulates payment processing using Pydantic models.
    
    This tool now reads the CartMandate from state, parses it into a validated model,
    and creates a spec-compliant PaymentMandate.
    """
    logger.info("Tool called: Creating PaymentMandate and processing payment")
    
    # MODULE_6_STEP_3B_VALIDATE_CARTMANDATE

Langkah 3B: Validasi CartMandate

Sekarang, mari tambahkan logika untuk membaca, memvalidasi CartMandate menggunakan model Pydantic AP2, dan memeriksa masa berlaku.

👉 Temukan:

# MODULE_6_STEP_3B_VALIDATE_CARTMANDATE

👉 Ganti satu baris tersebut dengan:

    # 1. Read CartMandate dictionary from state
    cart_mandate_dict = tool_context.state.get("cart_mandate")
    if not cart_mandate_dict:
        logger.error("No CartMandate found in state")
        return { "status": "error", "message": "No CartMandate found. Merchant Agent must create cart first." }
    
    # 2. Parse dictionary into a validated Pydantic model
    try:
        cart_model = CartMandate.model_validate(cart_mandate_dict)
    except Exception as e:
        logger.error(f"Could not validate CartMandate structure: {e}")
        return {"status": "error", "message": f"Invalid CartMandate structure: {e}"}
    
    # 3. Validate that the cart hasn't expired using the Pydantic model
    is_valid, error_message = _validate_cart_expiry(cart_model)
    if not is_valid:
        logger.error(f"CartMandate validation failed: {error_message}")
        return {"status": "error", "message": error_message}
    
    # MODULE_6_STEP_3C_EXTRACT_PAYMENT_DETAILS

Langkah 3C: Mengekstrak Detail Pembayaran dari Struktur Bertingkat

Sekarang, mari kita navigasikan model CartMandate yang divalidasi untuk mengekstrak detail pembayaran yang kita butuhkan.

👉 Temukan:

# MODULE_6_STEP_3C_EXTRACT_PAYMENT_DETAILS

👉 Ganti satu baris tersebut dengan:

    # 4. Safely extract data from the validated model
    cart_id = cart_model.contents.id
    merchant_name = cart_model.contents.merchant_name
    amount_value = cart_model.contents.payment_request.details.total.amount.value
    currency = cart_model.contents.payment_request.details.total.amount.currency
    consent_granted = True  # Assume consent for this codelab flow
    
    # MODULE_6_STEP_3D_CREATE_PAYMENTMANDATE_AND_SIMULATE

Langkah 3D: Buat PaymentMandate dan Simulasikan Pembayaran

Terakhir, mari buat PaymentMandate menggunakan helper berbasis Pydantic, simulasikan pemrosesan pembayaran, dan simpan semuanya ke status.

👉 Temukan:

# MODULE_6_STEP_3D_CREATE_PAYMENTMANDATE_AND_SIMULATE

👉 Ganti satu baris tersebut dengan:

    # 5. Create the spec-compliant PaymentMandate using the validated CartMandate model
    payment_mandate_dict = _create_payment_mandate(cart_model, consent_granted)
    
    # 6. Simulate payment processing
    transaction_id = f"txn_{hashlib.sha256(f'{cart_id}{datetime.now(timezone.utc).isoformat()}'.encode()).hexdigest()[:16]}"
    payment_result = {
        "transaction_id": transaction_id,
        "status": "completed",
        "amount": amount_value,
        "currency": currency,
        "merchant": merchant_name,
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "simulation": True
    }
    
    # 7. Write the compliant PaymentMandate dictionary and result to state
    tool_context.state["payment_mandate"] = payment_mandate_dict
    tool_context.state["payment_result"] = payment_result
    
    logger.info(f"Payment processed successfully: {transaction_id}")
    
    return {
        "status": "success",
        "message": f"Payment of {currency} {amount_value:.2f} to {merchant_name} processed successfully",
        "transaction_id": transaction_id,
        "payment_mandate_id": payment_mandate_dict["payment_mandate_contents"]["payment_mandate_id"]
    }

Langkah 4: Bangun Agen Penyedia Kredensial - Impor Komponen

Sekarang, mari kita buat agen yang menggunakan alat ini.

👉 Buka

charity_advisor/credentials_provider/agent.py

Anda akan melihat template dengan penanda placeholder. Mari kita mulai dengan mengimpor apa yang kita butuhkan.

👉 Temukan:

# MODULE_6_STEP_4_IMPORT_COMPONENTS

👉 Ganti satu baris tersebut dengan:

from google.adk.agents import Agent
from google.adk.tools import FunctionTool
from charity_advisor.tools.payment_tools import create_payment_mandate

Langkah 5: Tulis Instruksi Penyedia Kredensial

Sekarang, mari kita tulis petunjuk yang memandu agen.

👉 Temukan:

# MODULE_6_STEP_5_WRITE_INSTRUCTION
instruction="""""",

👉 Ganti kedua baris tersebut dengan:

    instruction="""You are a payment specialist responsible for securely processing payments with user consent.

Your workflow:

1. Read the CartMandate from shared state.
   The CartMandate was created by the Merchant Agent and has this structure:
   - contents: AP2 wrapper containing:
     - id: Cart identifier
     - cart_expiry: When the cart expires
     - merchant_name: Who is receiving payment
     - payment_request: W3C PaymentRequest with transaction details
   - merchant_authorization: Merchant's signature

2. Extract payment details from the nested structure:
   - Navigate: cart_mandate["contents"]["payment_request"]["details"]["total"]["amount"]
   - This gives you the currency and value

3. **IMPORTANT - Two-Turn Conversational Confirmation Pattern:**
   Before calling create_payment_mandate, you MUST:
   - Present the payment details clearly to the user
   - Ask explicitly: "I'm ready to process a payment of $X to [Charity Name]. Do you want to proceed with this donation?"
   - WAIT for the user's explicit confirmation (e.g., "yes", "proceed", "confirm")
   - ONLY call create_payment_mandate AFTER receiving explicit confirmation
   - If user says "no" or "cancel", DO NOT call the tool

4. After user confirms, use the create_payment_mandate tool to:
   - Validate the CartMandate hasn't expired (CRITICAL security check)
   - Create a PaymentMandate (the third AP2 credential)
   - Simulate payment processing
   - Record the transaction result

5. After processing, inform the user:
   - That payment was processed successfully (this is a simulation)
   - The transaction ID
   - The amount and merchant
   - That this completes the three-agent AP2 credential chain

IMPORTANT BOUNDARIES:
- Your ONLY job is creating PaymentMandates and processing payments
- You do NOT discover charities (that's Shopping Agent's job)
- You do NOT create offers (that's Merchant Agent's job)
- You MUST validate that the CartMandate hasn't expired before processing
- You MUST get explicit user confirmation before calling create_payment_mandate
- In production, this consent mechanism would be even more robust

WHAT IS A PAYMENTMANDATE:
A PaymentMandate is the final credential that authorizes payment execution. It:
- Links to the CartMandate (proving the merchant's offer)
- Records user consent
- Contains payment details extracted from the CartMandate
- Enables the actual payment transaction

This is the third and final verifiable credential in our secure payment system.

THE COMPLETE AP2 CREDENTIAL CHAIN:
1. Shopping Agent creates IntentMandate (user's intent)
2. Merchant Agent reads IntentMandate, creates CartMandate (merchant's binding offer)
3. You read CartMandate, get user confirmation, create PaymentMandate (authorized payment execution)

Each credential:
- Has an expiry time (security feature)
- Links to the previous credential
- Is validated before the next step
- Creates an auditable chain of trust""",

Langkah 6: Tambahkan Alat ke Penyedia Kredensial

👉 Temukan:

# MODULE_6_STEP_6_ADD_TOOLS
tools=[],

👉 Ganti kedua baris tersebut dengan:

    tools=[
        FunctionTool(func=create_payment_mandate)
    ],

Langkah 7: Verifikasi Penyedia Kredensial Lengkap

Pastikan semuanya terhubung dengan benar.

👉 Langkah-langkah lengkap

charity_advisor/credentials_provider/agent.py

kini akan terlihat seperti ini:

"""
Credentials Provider Agent - Handles payment processing with user consent.
This agent acts as our "Payment Processor."
"""

from google.adk.agents import Agent
from google.adk.tools import FunctionTool
from charity_advisor.tools.payment_tools import create_payment_mandate


credentials_provider = Agent(
    name="CredentialsProvider",
    model="gemini-2.5-flash",
    description="Securely processes payments by creating PaymentMandates and executing transactions with user consent.",
    tools=[
        FunctionTool(func=create_payment_mandate)
    ],
    instruction="""You are a payment specialist responsible for securely processing payments with user consent.

Your workflow:

1. Read the CartMandate from shared state.
   The CartMandate was created by the Merchant Agent and has this structure:
   - contents: AP2 wrapper containing:
     - id: Cart identifier
     - cart_expiry: When the cart expires
     - merchant_name: Who is receiving payment
     - payment_request: W3C PaymentRequest with transaction details
   - merchant_authorization: Merchant's signature

2. Extract payment details from the nested structure:
   - Navigate: cart_mandate["contents"]["payment_request"]["details"]["total"]["amount"]
   - This gives you the currency and value

3. **IMPORTANT - Two-Turn Conversational Confirmation Pattern:**
   Before calling create_payment_mandate, you MUST:
   - Present the payment details clearly to the user
   - Ask explicitly: "I'm ready to process a payment of $X to [Charity Name]. Do you want to proceed with this donation?"
   - WAIT for the user's explicit confirmation (e.g., "yes", "proceed", "confirm")
   - ONLY call create_payment_mandate AFTER receiving explicit confirmation
   - If user says "no" or "cancel", DO NOT call the tool

4. After user confirms, use the create_payment_mandate tool to:
   - Validate the CartMandate hasn't expired (CRITICAL security check)
   - Create a PaymentMandate (the third AP2 credential)
   - Simulate payment processing
   - Record the transaction result

5. After processing, inform the user:
   - That payment was processed successfully (this is a simulation)
   - The transaction ID
   - The amount and merchant
   - That this completes the three-agent AP2 credential chain

IMPORTANT BOUNDARIES:
- Your ONLY job is creating PaymentMandates and processing payments
- You do NOT discover charities (that's Shopping Agent's job)
- You do NOT create offers (that's Merchant Agent's job)
- You MUST validate that the CartMandate hasn't expired before processing
- You MUST get explicit user confirmation before calling create_payment_mandate
- In production, this consent mechanism would be even more robust

WHAT IS A PAYMENTMANDATE:
A PaymentMandate is the final credential that authorizes payment execution. It:
- Links to the CartMandate (proving the merchant's offer)
- Records user consent
- Contains payment details extracted from the CartMandate
- Enables the actual payment transaction

This is the third and final verifiable credential in our secure payment system.

THE COMPLETE AP2 CREDENTIAL CHAIN:
1. Shopping Agent creates IntentMandate (user's intent)
2. Merchant Agent reads IntentMandate, creates CartMandate (merchant's binding offer)
3. You read CartMandate, get user confirmation, create PaymentMandate (authorized payment execution)

Each credential:
- Has an expiry time (security feature)
- Links to the previous credential
- Is validated before the next step
- Creates an auditable chain of trust"""
)

Checkpoint: Sekarang Anda memiliki Penyedia Kredensial lengkap dengan pembacaan CartMandate dan pembuatan PaymentMandate yang tepat menggunakan model Pydantic AP2.

Langkah 8: Uji Penyedia Kredensial

Sekarang, mari kita verifikasi bahwa agen kita memproses pembayaran dengan benar dan menyelesaikan rantai kredensial.

👉 Di terminal Cloud Shell, jalankan:

python scripts/test_credentials_provider.py

Output yang diharapkan:

======================================================================
CREDENTIALS PROVIDER TEST (MOCK - NO CONFIRMATION)
======================================================================

Simulated CartMandate from Merchant Agent:
  - Cart ID: cart_test123
  - Merchant: Room to Read
  - Amount: $50.00
  - Expires: 2025-11-07T15:47:16Z
  - Signature: SIG_test_signature

Calling Credentials Provider to process payment...
======================================================================
INFO:charity_advisor.tools.payment_tools:Tool called: Creating PaymentMandate and processing payment
INFO:charity_advisor.tools.payment_tools:CartMandate valid. Expires in 900 seconds
INFO:charity_advisor.tools.payment_tools:Payment processed successfully: txn_a3f7b2c8d9e1f4a2

======================================================================
CREDENTIALS PROVIDER RESPONSE:
======================================================================
I've successfully processed your payment. Here are the details:

**Payment Completed** (Simulated)
- Transaction ID: txn_a3f7b2c8d9e1f4a2
- Amount: USD 50.00
- Merchant: Room to Read
- Status: Completed

This completes the three-agent AP2 credential chain:
1.  Shopping Agent created IntentMandate (your intent)
2.  Merchant Agent created CartMandate (binding offer)
3.  Credentials Provider created PaymentMandate (payment authorization)

Your donation has been processed securely through our verifiable credential system.

======================================================================
PAYMENTMANDATE CREATED:
======================================================================
  Payment Mandate ID: payment_3b4c5d6e7f8a
  Linked to Cart: cart_test123
  User Consent: True
  Amount: USD 50.00
  Merchant: Room to Read
  Agent Present: True
======================================================================

======================================================================
PAYMENT RESULT:
======================================================================
  Transaction ID: txn_a3f7b2c8d9e1f4a2
  Status: completed
  Amount: USD 50.00
  Merchant: Room to Read
  Simulation: True
======================================================================

Langkah 9: Uji Pipeline Tiga Agen Lengkap

Sekarang mari kita uji ketiga agen yang bekerja sama.

👉 Jalankan pengujian pipeline lengkap:

python scripts/test_full_pipeline.py

Output yang diharapkan:

======================================================================
THREE-AGENT PIPELINE TEST (AP2 CREDENTIAL CHAIN)
======================================================================

[1/3] SHOPPING AGENT - Finding charity and creating IntentMandate...
----------------------------------------------------------------------
✓ IntentMandate created
  - Intent ID: intent_774799058_1730927536
  - Description: Donate $75.00 to Room to Read
  - Merchant: Room to Read
  - Amount: $75.0
  - Expires: 2025-11-07T16:32:16Z

[2/3] MERCHANT AGENT - Reading IntentMandate and creating CartMandate...
----------------------------------------------------------------------
✓ CartMandate created
  - ID: cart_3b4c5d6e7f8a
  - Expires: 2025-11-07T15:47:16Z
  - Signature: SIG_a3f7b2c8d9e1f4a2

[3/3] CREDENTIALS PROVIDER - Creating PaymentMandate and processing...
----------------------------------------------------------------------
NOTE: In the web UI, this would show a confirmation dialog
      For this test, consent is automatically granted
✓ Payment processed (SIMULATED)
  - Transaction ID: txn_a3f7b2c8d9e1f4a2
  - Amount: $75.0
  - Status: completed

======================================================================
COMPLETE AP2 CREDENTIAL CHAIN
======================================================================

✓ Credential 1: IntentMandate (User's Intent)
  - Intent ID: intent_774799058_1730927536
  - Description: Donate $75.00 to Room to Read
  - Expiry: 2025-11-07T16:32:16Z

✓ Credential 2: CartMandate (Merchant's Offer)
  - Cart ID: cart_3b4c5d6e7f8a
  - Cart Expiry: 2025-11-07T15:47:16Z
  - Merchant Signature: SIG_a3f7b2c8d9e1f4a2

✓ Credential 3: PaymentMandate (Payment Execution)
  - Payment Mandate ID: payment_3b4c5d6e7f8a
  - Linked to Cart: cart_3b4c5d6e7f8a
  - Agent Present: True

✓ Transaction Result:
  - Transaction ID: txn_a3f7b2c8d9e1f4a2
  - Simulation: True

======================================================================
✅ COMPLETE PIPELINE TEST PASSED
======================================================================

Berikut adalah rantai kredensial AP2 lengkap yang sedang beraksi.

Setiap agen:

  1. Membaca kredensial dari status
  2. Memvalidasinya menggunakan model Pydantic (struktur + pemeriksaan masa berlaku)
  3. Membuat kredensial berikutnya menggunakan model AP2
  4. Menulis ke state untuk agen berikutnya

Yang Baru Saja Anda Buat

Anda telah berhasil menyelesaikan rantai kredensial tiga agen AP2 dengan validasi struktur yang tepat menggunakan model Pydantic dan simulasi pembayaran.

Konsep Utama yang Dikuasai

PaymentMandate (AP2 Credential #3):

  • Dibuat menggunakan model Pydantic AP2 resmi
  • Kredensial akhir yang mengizinkan eksekusi pembayaran
  • Menautkan ke CartMandate melalui payment_details_id
  • Mencatat izin pengguna dan stempel waktu
  • Berisi jumlah pembayaran yang diekstrak dari CartMandate
  • Mencakup tanda agent_present untuk interaksi manusia
  • Validasi model memastikan kepatuhan terhadap spesifikasi

Membaca dari CartMandate:

  • Memvalidasi struktur dengan CartMandate.model_validate()
  • Akses atribut yang aman untuk jenis: cart_model.contents.payment_request.details.total.amount
  • Memahami pemisahan wrapper AP2 vs. standar W3C
  • Mengekstrak merchant_name, amount, currency dengan aman dari model
  • Pydantic menangkap error struktur secara otomatis

Validasi Masa Berlaku Keranjang:

  • Menerima model CartMandatePydantic yang divalidasi
  • Membaca dari cart.contents.cart_expiry (akses atribut)
  • Fitur keamanan yang mencegah pemrosesan keranjang yang tidak aktif
  • Durasi lebih pendek (15 menit) daripada niat (1 jam)

Simulasi Pembayaran:

  • Simulasi edukasi pemroses pembayaran sungguhan
  • Membuat ID transaksi
  • Mencatat payment_result dalam status
  • Ditandai dengan jelas sebagai simulasi (tanda: Benar)

Menyelesaikan Rantai AP2 dengan Model:

  • Tiga agen, tiga kredensial, tiga validasi Pydantic
  • Setiap agen memvalidasi struktur kredensial sebelumnya menggunakan model
  • Setiap kredensial ditautkan ke kredensial sebelumnya untuk jalur audit
  • Pengalihan berbasis status mempertahankan pemisahan peran
  • Keamanan jenis di seluruh rantai

Pengembangan Berbasis Model:

  • Validasi input melalui model_validate()
  • Konstruksi yang aman untuk jenis dengan model bertingkat
  • Serialisasi otomatis melalui model_dump(mode='json')
  • Pola siap produksi sejak awal

Langkah Berikutnya

Dalam modul berikutnya, kita akan membuat Orchestrator Agent yang mengoordinasikan ketiga agen spesialis.

Anda telah membangun tiga agen spesialis yang canggih menggunakan model Pydantic AP2. Sekarang, mari kita buat konduktor yang mengatur semuanya menjadi pengalaman donasi yang lancar.

Mari kita buat Orchestrator dan lihat cara kerja sistem lengkapnya.

7. Orkestrasi - Menyatukan Semuanya

pipeline berurutan

Dari Spesialis hingga Pengalaman yang Lancar

Pada modul sebelumnya, Anda telah membuat tiga agen khusus:

  • Agen Shopping: Menemukan lembaga amal, membuat IntentMandate
  • Agen Penjual: Membuat CartMandate dari IntentMandate
  • Penyedia Kredensial: Membuat PaymentMandate, memproses pembayaran

Agen ini secara alami dibagi menjadi dua tahap:

  • Tahap 1 (Shopping): Percakapan multi-turn untuk menemukan dan memilih lembaga amal
  • Tahap 2 (Pemrosesan): Eksekusi atomik pembuatan penawaran dan pembayaran

Namun, saat ini, Anda harus mengatur fase ini secara manual.

Di sinilah pola orkestrasi ADK berperan.

Prinsip AP2: Orkestrasi Menerapkan Batas Kepercayaan

Mengapa Orkestrasi Penting untuk Keamanan

Orkestrasi bukan hanya tentang kenyamanan, tetapi juga tentang penerapan batas kepercayaan melalui arsitektur.

Tanpa orkestrasi:

# User could accidentally skip steps or reorder them
shopping_agent.run("Find charity")
# Oops, forgot to create CartMandate!
credentials_provider.run("Process payment")  # No offer to validate!

Dengan orkestrasi:

# Pipeline enforces correct order
donation_processing_pipeline = SequentialAgent(
    sub_agents=[
        merchant_agent,      # Must run first
        credentials_provider # Must run second
    ]
)
# Steps ALWAYS run in order, no skipping allowed

Pipeline berurutan menjamin:

  • ✅ IntentMandate dibuat sebelum CartMandate
  • ✅ CartMandate dibuat sebelum pemrosesan pembayaran
  • ✅ Setiap agen berjalan dalam konteks terisolasi
  • ✅ Status mengalir maju melalui rantai kredensial

Misi Kami: Membangun Sistem yang Lengkap

Kita akan membuat dua lapisan:

Lapisan 1: Pipeline Pemrosesan (SequentialAgent)

  • Menghubungkan Merchant → Kredensial
  • Berjalan otomatis secara berurutan setelah lembaga amal dipilih
  • Eksekusi penawaran dan pembayaran secara atomik

Lapisan 2: Pengelola Root (Agen yang menghadap pengguna)

  • Kepribadian yang ramah
  • Mendelegasikan ke shopping_agent untuk pemilihan lembaga amal
  • Mendelegasikan ke pipeline pemrosesan setelah IntentMandate dibuat
  • Menangani transisi percakapan dan fase

Pendekatan dua lapisan ini sesuai dengan alur alami:

  • Fase Belanja: Percakapan multi-putaran (pengguna menjelajah, mengajukan pertanyaan, memutuskan)
  • Fase Pemrosesan: Eksekusi atomik (penawaran → pembayaran)

Mari kita buat keduanya.

Langkah 1: Impor Komponen Orchestration

Pertama, siapkan file orkestrasi dengan impor yang diperlukan.

👉 Buka

charity_advisor/agent.py

Mari kita mulai dengan impor:

👉 Temukan:

# MODULE_7_STEP_1_IMPORT_COMPONENTS

👉 Ganti satu baris tersebut dengan:

from google.adk.agents import Agent, SequentialAgent
from charity_advisor.shopping_agent.agent import shopping_agent
from charity_advisor.merchant_agent.agent import merchant_agent
from charity_advisor.credentials_provider.agent import credentials_provider

Langkah 2: Buat Pipeline Pemrosesan

Sekarang, mari kita buat pipeline yang menjalankan pembuatan penawaran dan pemrosesan pembayaran secara atomik.

👉 Temukan:

# MODULE_7_STEP_2_CREATE_SEQUENTIAL_PIPELINE

👉 Ganti kedua baris tersebut dengan:

# Create the donation processing pipeline
# This runs Merchant → Credentials in sequence AFTER charity is selected
donation_processing_pipeline = SequentialAgent(
    name="DonationProcessingPipeline",
    description="Creates signed offer and processes payment after charity is selected",
    sub_agents=[
        merchant_agent,
        credentials_provider
    ]
)

Langkah 3A: Buat Penyiapan Agen Root

Sekarang, mari kita buat agen yang berinteraksi dengan pengguna yang mengatur kedua fase. Kita akan membangunnya dalam tiga bagian: penyiapan (3A), petunjuk (3B), dan sub-agen (3C).

👉 Temukan:

# MODULE_7_STEP_3A_CREATE_ROOT_AGENT_SETUP

👉 Ganti satu baris tersebut dengan:

# Create the root orchestrator agent
# This is what users interact with directly
root_agent = Agent(
    name="CharityAdvisor",
    model="gemini-2.5-pro",
    description="A friendly charity giving assistant that helps users donate to verified organizations.",
    # MODULE_7_STEP_3B_WRITE_ROOT_AGENT_INSTRUCTION

Langkah 3B: Tulis Instruksi Agen Root

Sekarang, mari kita tambahkan petunjuk yang memandu perilaku penasihat lembaga amal di kedua fase.

👉 Temukan:

# MODULE_7_STEP_3B_WRITE_ROOT_AGENT_INSTRUCTION

👉 Ganti satu baris tersebut dengan:

    instruction="""You are a helpful and friendly charity giving advisor.

Your workflow has TWO distinct phases:

PHASE 1: CHARITY SELECTION (delegate to shopping_agent)
When a user expresses interest in donating:
1. Delegate to shopping_agent immediately
2. The shopping_agent will:
   - Search for charities matching their cause
   - Present verified options with ratings
   - Engage in conversation (user may ask questions, change their mind)
   - Wait for user to select a specific charity and amount
   - Create an IntentMandate when user decides
3. Wait for shopping_agent to complete

You'll know Phase 1 is complete when shopping_agent's response includes:
- "IntentMandate created" or "Intent ID: intent_xxx" 
- Charity name and donation amount

PHASE 2: PAYMENT PROCESSING (delegate to DonationProcessingPipeline)
After shopping_agent completes:
1. Acknowledge the user's selection naturally:
   "Perfect! Let me process your $X donation to [Charity]..."
2. Delegate to DonationProcessingPipeline
3. The pipeline will automatically:
   - Create signed cart offer (MerchantAgent)
   - Get consent and process payment (CredentialsProvider)
4. After pipeline completes, summarize the transaction

CRITICAL RULES:
- Phase 1 may take multiple conversation turns (this is normal)
- Only proceed to Phase 2 after IntentMandate exists
- Don't rush the user during charity selection
- Don't ask user to "proceed" between phases - transition automatically

EXAMPLE FLOW:
User: "I want to donate to education"
You: [delegate to shopping_agent]
Shopping: "Here are 3 education charities..." [waits]
User: "Tell me more about the first one"
Shopping: "Room to Read focuses on..." [waits]
User: "Great, I'll donate $50 to Room to Read"
Shopping: "IntentMandate created (ID: intent_123)..."
You: "Perfect! Processing your $50 donation to Room to Read..." [delegate to DonationProcessingPipeline]
Pipeline: [creates offer, gets consent, processes payment]
You: "Done! Your donation was processed successfully. Transaction ID: txn_456"

Your personality:
- Warm and encouraging
- Patient during charity selection
- Clear about educational nature
- Smooth transitions between phases""",
# MODULE_7_STEP_3C_ADD_ROOT_AGENT_SUBAGENTS

Langkah 3C: Tambahkan Sub-Agen

Terakhir, beri penasihat lembaga amal akses ke agen belanja dan pipeline pemrosesan, lalu tutup definisi Agen.

👉 Temukan:

# MODULE_7_STEP_3C_ADD_ROOT_AGENT_SUBAGENTS

👉 Ganti satu baris tersebut dengan:

    sub_agents=[
        shopping_agent,
        donation_processing_pipeline
    ]
)

Langkah 4: Verifikasi Sistem Lengkap

Mari kita pastikan orkestrasi sudah terhubung dengan benar.

👉 Langkah-langkah lengkap

charity_advisor/agent.py

kini akan terlihat seperti ini:

"""
Main orchestration: The donation processing pipeline and root orchestrator agent.
"""

from google.adk.agents import Agent, SequentialAgent
from charity_advisor.shopping_agent.agent import shopping_agent
from charity_advisor.merchant_agent.agent import merchant_agent
from charity_advisor.credentials_provider.agent import credentials_provider

# Create the donation processing pipeline
# This runs Merchant → Credentials in sequence AFTER charity is selected
donation_processing_pipeline = SequentialAgent(
    name="DonationProcessingPipeline",
    description="Creates signed offer and processes payment after charity is selected",
    sub_agents=[
        merchant_agent,
        credentials_provider
    ]
)

# Create the root orchestrator agent
# This is what users interact with directly
root_agent = Agent(
    name="CharityAdvisor",
    model="gemini-2.5-flash",
    description="A friendly charity giving assistant that helps users donate to verified organizations.",
    instruction="""You are a helpful and friendly charity giving advisor.

Your workflow has TWO distinct phases:

PHASE 1: CHARITY SELECTION (delegate to shopping_agent)
When a user expresses interest in donating:
1. Delegate to shopping_agent immediately
2. The shopping_agent will:
   - Search for charities matching their cause
   - Present verified options with ratings
   - Engage in conversation (user may ask questions, change their mind)
   - Wait for user to select a specific charity and amount
   - Create an IntentMandate when user decides
3. Wait for shopping_agent to complete

You'll know Phase 1 is complete when shopping_agent's response includes:
- "IntentMandate created" or "Intent ID: intent_xxx" 
- Charity name and donation amount

PHASE 2: PAYMENT PROCESSING (delegate to DonationProcessingPipeline)
After shopping_agent completes:
1. Acknowledge the user's selection naturally:
   "Perfect! Let me process your $X donation to [Charity]..."
2. Delegate to DonationProcessingPipeline
3. The pipeline will automatically:
   - Create signed cart offer (MerchantAgent)
   - Get consent and process payment (CredentialsProvider)
4. After pipeline completes, summarize the transaction

CRITICAL RULES:
- Phase 1 may take multiple conversation turns (this is normal)
- Only proceed to Phase 2 after IntentMandate exists
- Don't rush the user during charity selection
- Don't ask user to "proceed" between phases - transition automatically

EXAMPLE FLOW:
User: "I want to donate to education"
You: [delegate to shopping_agent]
Shopping: "Here are 3 education charities..." [waits]
User: "Tell me more about the first one"
Shopping: "Room to Read focuses on..." [waits]
User: "Great, I'll donate $50 to Room to Read"
Shopping: "IntentMandate created (ID: intent_123)..."
You: "Perfect! Processing your $50 donation to Room to Read..." [delegate to DonationProcessingPipeline]
Pipeline: [creates offer, gets consent, processes payment]
You: "Done! Your donation was processed successfully. Transaction ID: txn_456"

Your personality:
- Warm and encouraging
- Patient during charity selection
- Clear about educational nature
- Smooth transitions between phases""",
    sub_agents=[
        shopping_agent,
        donation_processing_pipeline
    ]
)

Langkah 5: Perkuat dengan Panggilan Balik Validasi (Opsional, Lanjutkan ke Langkah 7)

callback

SequentialAgent menjamin urutan eksekusi, tetapi bagaimana jika:

  • Agen Shopping gagal tanpa pemberitahuan (IntentMandate tidak pernah dibuat)
  • Satu jam berlalu antara Shopping dan Merchant (niat habis)
  • Status rusak atau dihapus
  • Seseorang mencoba menelepon Merchant secara langsung, melewati Shopping

Callback menambahkan penerapan arsitektur - callback memvalidasi prasyarat bahkan sebelum agen memulai panggilan LLM-nya. Ini adalah pertahanan secara mendalam: alat memvalidasi selama eksekusi, callback memvalidasi sebelum eksekusi.

Mari tambahkan callback validasi ke agen Penyedia Kredensial dan Penjual.

Langkah 5A: Tambahkan Validasi Penjual - Impor Jenis Panggilan Balik

Pertama, mari kita tambahkan impor yang diperlukan untuk callback.

👉 Buka

charity_advisor/merchant_agent/agent.py

Di bagian atas file, setelah impor yang ada, tambahkan:

from typing import Optional
from datetime import datetime, timezone
from google.adk.agents.callback_context import CallbackContext
from google.genai.types import Content, Part
import logging

logger = logging.getLogger(__name__)

Langkah 5B: Buat Fungsi Validasi Maksud

Sekarang, buat fungsi callback yang memvalidasi IntentMandate sebelum Agen Penjual berjalan.

👉 Di

charity_advisor/merchant_agent/agent.py

, tambahkan fungsi ini SEBELUM

merchant_agent = Agent(...)

definisi:

def validate_intent_before_merchant(
    callback_context: CallbackContext,
) -> Optional[Content]:
    """
    Validates IntentMandate exists and hasn't expired before Merchant runs.
    
    This callback enforces that the Shopping Agent completed successfully
    before the Merchant Agent attempts to create a CartMandate.
    
    Returns:
        None: Allow Merchant Agent to proceed normally
        Content: Skip Merchant Agent and return error to user
    """
    state = callback_context.state
    
    # Check credential exists
    if "intent_mandate" not in state:
        logger.error("❌ IntentMandate missing - Shopping Agent may have failed")
        return Content(parts=[Part(text=(
            "Error: Cannot create cart. User intent was not properly recorded. "
            "Please restart the donation process."
        ))])
    
    intent_mandate = state["intent_mandate"]
    
    # Validate expiry (critical security check)
    try:
        expiry_time = datetime.fromisoformat(
            intent_mandate["intent_expiry"].replace('Z', '+00:00')
        )
        now = datetime.now(timezone.utc)
        
        if expiry_time < now:
            logger.error(f"❌ IntentMandate expired at {intent_mandate['intent_expiry']}")
            return Content(parts=[Part(text=(
                "Error: Your donation intent has expired. "
                "Please select a charity again to restart."
            ))])
        
        time_remaining = expiry_time - now
        logger.info(f"✓ IntentMandate validated. Expires in {time_remaining.total_seconds():.0f}s")
        
    except (KeyError, ValueError) as e:
        logger.error(f"❌ Invalid IntentMandate structure: {e}")
        return Content(parts=[Part(text=(
            "Error: Invalid intent data. Please restart the donation."
        ))])
    
    # All checks passed - allow Merchant Agent to proceed
    logger.info(f"✓ Prerequisites met for Merchant Agent: {intent_mandate['intent_id']}")
    return None

Langkah 5C: Lampirkan Panggilan Balik ke Agen Penjual

Sekarang, hubungkan callback ke agen.

👉 Di

charity_advisor/merchant_agent/agent.py

, ubah

merchant_agent = Agent(...)

definisi:

Temukan baris ini dalam definisi Agen:

merchant_agent = Agent(
    name="MerchantAgent",
    model="gemini-2.5-flash",
    description="Creates formal, signed CartMandates for charity donations following W3C PaymentRequest standards.",

Tambahkan baris ini tepat setelah

description

line:

    before_agent_callback=validate_intent_before_merchant,

Definisi agen Anda sekarang akan terlihat seperti:

merchant_agent = Agent(
    name="MerchantAgent",
    model="gemini-2.5-flash",
    description="Creates formal, signed CartMandates for charity donations following W3C PaymentRequest standards.",
    before_agent_callback=validate_intent_before_merchant,
    tools=[
        FunctionTool(func=create_cart_mandate)
    ],
    instruction="""..."""
)

Langkah 6: Tambahkan Validasi Penyedia Kredensial (Opsional, Lanjutkan ke Langkah 7)

Pola yang sama - mari tambahkan validasi untuk langkah pembayaran.

Langkah 6A: Impor Jenis Callback

👉 Buka

charity_advisor/credentials_provider/agent.py

Di bagian atas file, setelah impor yang ada, tambahkan:

from typing import Optional
from datetime import datetime, timezone
from google.adk.agents.callback_context import CallbackContext
from google.genai.types import Content, Part
import logging

logger = logging.getLogger(__name__)

Langkah 6B: Buat Fungsi Validasi Keranjang

👉 Di

charity_advisor/credentials_provider/agent.py

, tambahkan fungsi ini SEBELUM

credentials_provider = Agent(...)

definisi:

def validate_cart_before_payment(
    callback_context: CallbackContext,
) -> Optional[Content]:
    """
    Validates CartMandate exists and hasn't expired before payment processing.
    
    This callback enforces that the Merchant Agent completed successfully
    before the Credentials Provider attempts to process payment.
    
    Returns:
        None: Allow Credentials Provider to proceed
        Content: Skip payment processing and return error
    """
    state = callback_context.state
    
    # Check credential exists
    if "cart_mandate" not in state:
        logger.error("❌ CartMandate missing - Merchant Agent may have failed")
        return Content(parts=[Part(text=(
            "Error: Cannot process payment. Cart was not properly created. "
            "Please restart the donation process."
        ))])
    
    cart_mandate = state["cart_mandate"]
    
    # Validate AP2 structure
    if "contents" not in cart_mandate:
        logger.error("❌ CartMandate missing AP2 contents wrapper")
        return Content(parts=[Part(text=(
            "Error: Invalid cart structure. Please restart."
        ))])
    
    # Validate expiry
    try:
        contents = cart_mandate["contents"]
        expiry_time = datetime.fromisoformat(
            contents["cart_expiry"].replace('Z', '+00:00')
        )
        now = datetime.now(timezone.utc)
        
        if expiry_time < now:
            logger.error(f"❌ CartMandate expired at {contents['cart_expiry']}")
            return Content(parts=[Part(text=(
                "Error: Your cart has expired (15 minute limit). "
                "Please restart the donation to get a fresh offer."
            ))])
        
        time_remaining = expiry_time - now
        logger.info(f"✓ CartMandate validated. Expires in {time_remaining.total_seconds():.0f}s")
        
    except (KeyError, ValueError) as e:
        logger.error(f"❌ Invalid CartMandate structure: {e}")
        return Content(parts=[Part(text=(
            "Error: Invalid cart data. Please restart the donation."
        ))])
    
    # All checks passed - allow payment processing
    logger.info(f"✓ Prerequisites met for Credentials Provider: {contents['id']}")
    return None

Langkah 6C: Lampirkan Callback ke Penyedia Kredensial

👉 Di

charity_advisor/credentials_provider/agent.py

, ubah

credentials_provider = Agent(...)

definisi:

Temukan baris ini dalam definisi Agen:

credentials_provider = Agent(
    name="CredentialsProvider",
    model="gemini-2.5-flash",
    description="Securely processes payments by creating PaymentMandates and executing transactions with user consent.",

Tambahkan baris ini tepat setelah

description

line:

    before_agent_callback=validate_cart_before_payment,

Definisi agen Anda sekarang akan terlihat seperti:

credentials_provider = Agent(
    name="CredentialsProvider",
    model="gemini-2.5-flash",
    description="Securely processes payments by creating PaymentMandates and executing transactions with user consent.",
    before_agent_callback=validate_cart_before_payment,
    tools=[
        FunctionTool(func=create_payment_mandate)
    ],
    instruction="""..."""
)

Langkah 7: Uji dengan UI Web ADK

Sekarang, mari kita uji sistem yang telah diperkuat sepenuhnya dengan callback validasi yang aktif.

👉 Di terminal Cloud Shell, jalankan:

adk web

Anda akan melihat output seperti:

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

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

👉 Selanjutnya, untuk mengakses UI Web ADK dari browser Anda:

Dari ikon Pratinjau web (terlihat seperti mata atau persegi dengan panah) di toolbar Cloud Shell (biasanya di kanan atas), pilih Ubah port. Di jendela pop-up, tetapkan port ke 8000, lalu klik "Ubah dan Pratinjau". Kemudian, Cloud Shell akan membuka tab browser baru yang menampilkan UI Web ADK.

webpreview

👉 Pilih agen Anda dari menu dropdown:

Di UI Web ADK, Anda akan melihat menu dropdown di bagian atas. Pilih charity_advisor dari daftar.

agent-select

Anda akan melihat antarmuka web ADK dengan:

  • Panel chat: Sisi kiri, untuk percakapan
  • Panel rekaman aktivitas: Sisi kanan, untuk kemampuan pengamatan (kita akan menggunakannya di Modul 9)

Pengujian 1: Selesaikan Alur Donasi (Kasus Normal)

👉 Di antarmuka chat, ketik:

I want to donate to an education charity

Tonton alur lengkapnya:

agen belanja web adk

pipeline pemrosesan donasi web adk

Apa yang terjadi (terlihat di panel rekaman aktivitas di sebelah kanan):

1. Delegasi penasihat ke ShoppingAgent:

  • ShoppingAgent menelusuri lembaga amal pendidikan
  • Menampilkan 3 opsi terverifikasi dengan detail

2. Anda berinteraksi dengan ShoppingAgent (mungkin memerlukan beberapa giliran):

User: "Tell me more about Room to Read"
Shopping: [explains mission and impact]
User: "I'll donate $50 to Room to Read"

3. ShoppingAgent membuat IntentMandate:

  • Membuat dan menandatangani intent
  • Menampilkan konfirmasi dengan ID Maksud

4. Penasihat beralih ke tahap pemrosesan:

Sempurna! Memproses donasi $50 Anda untuk Room to Read...

5. DonationProcessingPipeline diaktifkan:

  • Callback penjual memvalidasi IntentMandate (✓ lulus) ← BARU!
  • Agen Penjual membuat CartMandate dengan tanda tangan
  • Panggilan balik kredensial memvalidasi CartMandate (✓ lulus) ← BARU!
  • Penyedia Kredensial menyiapkan pembayaran

6. Proses pembayaran:

  • Penyedia Kredensial membuat PaymentMandate
  • Mensimulasikan pemrosesan pembayaran
  • ID transaksi pengembalian

7. Penasihat merangkum:

Sempurna! Donasi Anda telah berhasil diproses. 🎉

Detail:

  • Jumlah: $50,00
  • Lembaga amal: Room to Read (EIN: 77-0479905)
  • ID Transaksi: txn_a3f7b2c8d9e1f4a2

Uji 2: Verifikasi Callback Mendeteksi Kegagalan (Uji Lanjutan Opsional)

Ingin melihat cara kerja callback dalam menangkap error? Anda harus merusak status secara manual (debug lanjutan), tetapi dalam produksi, callback akan menangkap:

  • Alat Agen Shopping gagal → Pemblokiran callback penjual: "Error: Cannot create cart..." (Error: Tidak dapat membuat keranjang...)
  • 2 jam berlalu → Panggilan balik penjual diblokir: "Error: Intent expired..." (Error: Maksud berakhir...)
  • Masa berlaku keranjang habis → Pemblokiran callback kredensial: "Error: Cart expired (15 min limit)..."

Kasus ekstrem ini kini diterapkan secara arsitektur oleh callback validasi Anda.

Yang Baru Saja Anda Buat

Anda telah berhasil mengatur tiga agen khusus menjadi sistem yang lancar dan tepercaya dengan validasi arsitektur.

Langkah Berikutnya

Anda telah menyelesaikan inti teknis untuk membangun agen yang tepercaya:

Anda telah membangun sistem tepercaya yang lengkap secara lokal yang menerapkan rantai kredensial. Sekarang, mari kita buat dapat diakses oleh pengguna sebenarnya melalui deployment produksi—dan aktifkan jejak audit yang memungkinkan Modul 9.

Mari kita men-deploy agen yang telah di-hardening ke Google Cloud.

8. Deployment

banner

Sistem donasi tepercaya Anda kini dilengkapi dengan tiga agen khusus yang bekerja secara lokal:

Namun, hanya berjalan di mesin pengembangan Anda. Agar sistem ini berguna bagi pengguna nyata—dan untuk merekam jejak akuntabilitas yang membuktikan kredibilitas—Anda harus men-deploy-nya ke produksi.

Modul ini memandu Anda men-deploy agen ke Google Cloud dengan kemampuan pengamatan yang diaktifkan sejak hari pertama. Flag --trace_to_cloud yang akan Anda gunakan selama deployment memungkinkan adanya jejak akuntabilitas di Modul 9.

Memahami Opsi Deployment

ADK mendukung beberapa target deployment. Setiap opsi memiliki karakteristik yang berbeda untuk kompleksitas, pengelolaan sesi, penskalaan, dan biaya:

Faktor

Lokal (adk web)

Agent Engine

Cloud Run

Kompleksitas

Minimal

Rendah

Sedang

Persistensi Sesi

Khusus dalam memori (hilang saat dimulai ulang)

Dikelola Vertex AI (otomatis)

Cloud SQL (PostgreSQL) atau dalam memori

Infrastruktur

Tidak ada (khusus mesin dev)

Terkelola sepenuhnya

Penampung + database opsional

Start Dingin

T/A

100-500 md

100-2000 md

Penskalaan

Instance tunggal

Otomatis

Otomatis (hingga nol)

Model Biaya

Gratis (komputasi lokal)

Berbasis komputasi

Berbasis permintaan + paket gratis

Dukungan UI

Ya (bawaan)

Tidak (khusus API)

Ya (melalui flag --with_ui)

Penyiapan Kemampuan Observasi

Pelihat rekaman aktivitas lokal

Otomatis dengan --trace_to_cloud

Memerlukan tanda --trace_to_cloud

Terbaik Untuk

Pengembangan & pengujian

Agen produksi

Agen produksi

Rekomendasi: Untuk sistem donasi tepercaya ini, sebaiknya gunakan Agent Engine sebagai deployment produksi utama Anda karena memberikan:

  • Infrastruktur yang terkelola sepenuhnya (tidak ada container yang perlu dikelola)
  • Persistensi sesi bawaan melalui VertexAiSessionService
  • Penskalaan otomatis tanpa cold start
  • Deployment yang disederhanakan (tidak memerlukan pengetahuan Docker)
  • Integrasi Cloud Trace yang siap digunakan

Opsi Tambahan: Google Kubernetes Engine (GKE)

Untuk pengguna tingkat lanjut yang memerlukan kontrol tingkat Kubernetes, jaringan kustom, atau orkestrasi multi-layanan, deployment GKE tersedia. Opsi ini memberikan fleksibilitas maksimum, tetapi memerlukan lebih banyak keahlian operasional (pengelolaan cluster, manifes, akun layanan).

Deployment GKE tidak dibahas dalam codelab ini, tetapi didokumentasikan sepenuhnya dalam Panduan Deployment GKE ADK.

Prasyarat

1. Penyiapan Project Google Cloud

Anda memerlukan project Google Cloud dengan penagihan yang diaktifkan. Jika Anda tidak memilikinya:

  1. Buat project: Konsol Google Cloud
  2. Aktifkan penagihan: Aktifkan Penagihan
  3. Catat Project ID Anda (bukan nama atau nomor project)

2. Autentikasi Ulang (Opsional)

Lakukan autentikasi dengan Google Cloud:

gcloud auth application-default login
gcloud config set project YOUR_PROJECT_ID

Ganti YOUR_PROJECT_ID dengan ID project Google Cloud Anda yang sebenarnya.

Verifikasi autentikasi Anda:

gcloud config get-value project
# Should output: YOUR_PROJECT_ID

3. Variabel Lingkungan

Gunakan perintah ini untuk mengisi otomatis file .env Anda:

# Get your current Project ID
PROJECT_ID=$(gcloud config get-value project)
STAGING_BUCKET_VALUE="gs://${PROJECT_ID}-staging"
ENV_FILE=".env"

# Check if STAGING_BUCKET is already set in the .env file
if grep -q "^STAGING_BUCKET=" "${ENV_FILE}"; then
  # If it exists, replace the line
  # The sed command finds the line starting with STAGING_BUCKET= and replaces the entire line.
  # Using | as a delimiter to avoid issues with slashes in the bucket name.
  sed -i "s|^STAGING_BUCKET=.*|STAGING_BUCKET=${STAGING_BUCKET_VALUE}|" "${ENV_FILE}"
  echo "Updated STAGING_BUCKET in ${ENV_FILE}"
else
  # If it doesn't exist, add it to the end of the file
  echo "STAGING_BUCKET=${STAGING_BUCKET_VALUE}" >> "${ENV_FILE}"
  echo "Added STAGING_BUCKET to ${ENV_FILE}"
fi

# Verify it was added or updated correctly
echo "Current STAGING_BUCKET setting:"
grep "^STAGING_BUCKET=" "${ENV_FILE}"

Anda akan melihat:

STAGING_BUCKET=gs://your-actual-project-id-staging

Catatan penting:

  • Ganti YOUR_PROJECT_ID dengan project ID Anda yang sebenarnya (atau gunakan perintah di atas)
  • Untuk GOOGLE_CLOUD_LOCATION, gunakan wilayah yang didukung
  • Bucket penyiapan akan dibuat secara otomatis jika belum ada saat Anda menjalankan skrip deployment

4. Mengaktifkan API yang Diperlukan

Proses deployment memerlukan beberapa Google Cloud API yang diaktifkan. Jalankan perintah ini untuk mengaktifkannya:

gcloud services enable \
    aiplatform.googleapis.com \
    storage.googleapis.com \
    cloudbuild.googleapis.com \
    cloudtrace.googleapis.com \
    compute.googleapis.com

Perintah ini mengaktifkan:

  • AI Platform API - Untuk model Agent Engine dan Vertex AI
  • Cloud Storage API - Untuk bucket penyiapan
  • Cloud Build API - Untuk pembuatan container (Cloud Run)
  • Cloud Trace API - Untuk jalur audit dan kemampuan observasi
  • Compute Engine API - Untuk pengelolaan akun layanan

Langkah 1: Pahami Infrastruktur Deployment

Project Anda menyertakan skrip deployment terpadu (deploy.sh) yang menangani semua mode deployment.

👉 Tinjau skrip deployment (opsional):

cat deploy.sh

Skrip ini menyediakan tiga mode deployment:

  • ./deploy.sh local - Jalankan secara lokal dengan penyimpanan dalam memori
  • ./deploy.sh agent-engine - Deploy ke Vertex AI Agent Engine (direkomendasikan)
  • ./deploy.sh cloud-run - Deploy ke Cloud Run dengan UI opsional

Cara kerjanya di balik layar:

Untuk deployment Agent Engine, skrip akan menjalankan:

adk deploy agent_engine \
  --project=$GOOGLE_CLOUD_PROJECT \
  --region=$GOOGLE_CLOUD_LOCATION \
  --staging_bucket=$STAGING_BUCKET \
  --display_name="Charity Advisor" \
  --trace_to_cloud \
  charity_advisor

Untuk deployment Cloud Run, perintah ini dijalankan:

adk deploy cloud_run \
  --project=$GOOGLE_CLOUD_PROJECT \
  --region=$GOOGLE_CLOUD_LOCATION \
  --service_name="charity-advisor" \
  --app_name="charity_advisor" \
  --with_ui \
  --trace_to_cloud \
  charity_advisor

Flag --trace_to_cloud sangat penting untuk kedua jenis deployment—flag ini mengaktifkan integrasi Cloud Trace untuk jalur pertanggungjawaban yang akan Anda pelajari di Modul 9.

Langkah 2: Siapkan Pembungkus Mesin Agen

Agent Engine memerlukan titik entri tertentu yang membungkus agen Anda untuk runtime terkelola. File ini telah dibuat untuk Anda.

👉 Tinjau

charity_advisor/agent_engine_app.py

:

"""Agent Engine application wrapper.

This file prepares the Charity Advisor agent for deployment to Vertex AI Agent Engine.
"""

from vertexai import agent_engines
from .agent import root_agent

# Wrap the agent in an AdkApp object for Agent Engine deployment
app = agent_engines.AdkApp(
    agent=root_agent,
    enable_tracing=True,  # Enables Cloud Trace integration automatically
)

Alasan file ini diperlukan:

  • Agent Engine memerlukan agen yang di-wrap dalam objek AdkApp
  • Parameter enable_tracing=True mengaktifkan integrasi Cloud Trace secara otomatis
  • Wrapper ini direferensikan oleh ADK CLI selama deployment
  • Mengonfigurasi VertexAiSessionService untuk persistensi sesi otomatis

Agent Engine adalah deployment produksi yang direkomendasikan untuk sistem donasi tepercaya Anda karena menyediakan infrastruktur yang terkelola sepenuhnya dengan persistensi sesi bawaan.

Menjalankan Deployment

Dari root project Anda:

chmod +x deploy.sh
./deploy.sh agent-engine

Tahap Deployment

Tonton skrip yang menjalankan fase ini:

Phase 1: API Enablement
   aiplatform.googleapis.com
   storage.googleapis.com
   cloudbuild.googleapis.com
   cloudtrace.googleapis.com
   compute.googleapis.com

Phase 2: IAM Setup
   Getting project number
   Granting Storage Object Admin
   Granting Vertex AI User
   Granting Cloud Trace Agent

Phase 3: Staging Bucket
   Creating gs://your-project-id-staging (if needed)
   Setting permissions

Phase 4: Validation
   Checking agent.py exists
   Verifying root_agent defined
   Checking agent_engine_app.py exists
   Validating requirements.txt

Phase 5: Build & Deploy
   Packaging agent code
   Uploading to staging bucket
   Creating Agent Engine instance
   Configuring session persistence
   Setting up Cloud Trace integration
   Running health checks

Proses ini memerlukan waktu 5-10 menit karena mengemas agen dan men-deploy-nya ke infrastruktur Vertex AI.

Menyimpan ID Agent Engine Anda

Setelah deployment berhasil:

✅ Agent Engine created successfully!

   Agent Engine ID: 7917477678498709504
   Resource Name: projects/123456789/locations/us-central1/reasoningEngines/7917477678498709504
   Endpoint: https://us-central1-aiplatform.googleapis.com/v1/...

   ⚠️  IMPORTANT: Save the Agent Engine ID from the output above
   Add it to your .env file as:
   AGENT_ENGINE_ID=7917477678498709504

   This ID is required for:
   - Testing the deployed agent
   - Updating the deployment later
   - Accessing logs and traces

Segera perbarui file .env Anda:

echo "AGENT_ENGINE_ID=7917477678498709504" >> .env

Yang Di-deploy

Deployment Agent Engine Anda kini mencakup:

Ketiga agen (Shopping, Merchant, Kredensial) berjalan di runtime terkelola
Logika rantai kredensial lengkap (Perintah → Keranjang → Mandat pembayaran)
Mekanisme izin pengguna dengan alur kerja konfirmasi
Persistensi sesi otomatis melalui VertexAiSessionService
Infrastruktur penskalaan otomatis yang dikelola oleh Google
Integrasi Cloud Trace untuk kemampuan pengamatan lengkap

Langkah 4: Uji Agen yang Di-deploy

Memperbarui Lingkungan Anda

Pastikan .env Anda menyertakan ID Agent Engine:

AGENT_ENGINE_ID=7917477678498709504  # From deployment output
GOOGLE_CLOUD_PROJECT=your-project-id
GOOGLE_CLOUD_LOCATION=us-central1
STAGING_BUCKET=gs://your-project-id-staging

Jalankan Skrip Pengujian

Project Anda menyertakan skrip pengujian khusus untuk deployment Agent Engine.

👉 Jalankan pengujian:

python scripts/test_deployed_agent.py

Output yang Diinginkan

Testing Agent Engine deployment...
Project: your-project-id
Location: us-central1
Agent Engine ID: 7917477678498709504
Endpoint: https://us-central1-aiplatform.googleapis.com/v1/...

Creating session...
✓ Session created: 4857885913439920384

Sending donation query...
✓ Response received:
  Event 1: I'll help you donate $50 to a children's education charity...
  Event 2: Here are some highly-rated children's education charities...
  Event 3: Which charity would you like to support?...

✅ Test completed successfully!

Session ID: 4857885913439920384

This donation generated a trace in Cloud Trace.
View it in Module 9: Observability

To view traces:
https://console.cloud.google.com/traces/list?project=your-project-id

Daftar Periksa Verifikasi

Setelah pengujian, verifikasi:

✅ Agen merespons kueri
✅ Ketiga agen dijalankan secara berurutan (Shopping → Merchant → Kredensial)
✅ Mekanisme izin diaktifkan (konfirmasi diminta)
✅ Sesi tetap ada di seluruh permintaan
✅ Tidak ada error autentikasi
✅ Tidak ada waktu tunggu koneksi

Jika Anda mengalami error:

  • Pastikan variabel lingkungan Anda ditetapkan dengan benar
  • Verifikasi bahwa API diaktifkan: gcloud services list --enabled
  • Periksa log Agent Engine di Konsol Vertex AI
  • Pastikan file agent_engine_app.py ada di folder charity_advisor Anda

Langkah 5: Deploy ke Cloud Run (Opsional)

Meskipun Agent Engine direkomendasikan untuk deployment produksi yang disederhanakan, Cloud Run menawarkan lebih banyak kontrol dan mendukung UI web ADK. Bagian ini bersifat opsional.

Kapan Harus Menggunakan Cloud Run

Pilih Cloud Run jika Anda memerlukan:

  • UI web ADK untuk interaksi pengguna
  • Kontrol penuh atas lingkungan container
  • Konfigurasi database kustom
  • Integrasi dengan layanan Cloud Run yang ada

Menjalankan Deployment

chmod +x deploy.sh
./deploy.sh cloud-run

Yang berbeda:

Skrip akan otomatis:

  • Membangun container Docker dengan kode agen Anda
  • Buat database Cloud SQL PostgreSQL (jika diperlukan)
  • Mengonfigurasi koneksi database
  • Men-deploy dengan UI web ADK yang diaktifkan

Deployment memerlukan waktu 10-15 menit karena penyediaan Cloud SQL.

Pengelolaan Sesi:

  • Menggunakan DatabaseSessionService, bukan VertexAiSessionService
  • Memerlukan kredensial database di .env (atau dibuat otomatis)
  • Status tetap ada di database PostgreSQL

Dukungan UI:

  • UI Web tersedia di: https://charity-advisor-xyz.a.run.app

Menguji Deployment Cloud Run

Jika Anda men-deploy ke Cloud Run dengan --with_ui, Anda dapat melakukan pengujian langsung di browser:

  1. Buka URL Layanan Anda (disediakan di output deployment)
  2. Anda akan melihat antarmuka web ADK. Pilih agen Anda dari menu drop-down.
  3. Mulai donasi percobaan:
   I want to donate $50 to a children's education charity
  1. Amati alur eksekusi:
    • ShoppingAgent menemukan lembaga amal dan menyimpan niat Anda
    • MerchantAgent membuat mandat keranjang
    • CredentialsProvider membuat mandat pembayaran dan meminta konfirmasi
    • Setelah Anda mengonfirmasi, pembayaran akan diproses
  2. Verifikasi bahwa respons mencakup:
    • Rekomendasi lembaga amal
    • Permintaan konfirmasi
    • Pesan keberhasilan setelah persetujuan

Pemecahan masalah

Masalah Umum

Masalah: ERROR: GOOGLE_CLOUD_PROJECT is not set

Solusi: Pastikan file .env Anda memiliki project ID yang benar:

GOOGLE_CLOUD_PROJECT=your-actual-project-id

Masalah: Bucket penyiapan tidak dibuat secara otomatis

Solusi: Skrip akan membuat bucket secara otomatis. Jika belum ada, buat secara manual:

gsutil mb -p $GOOGLE_CLOUD_PROJECT -l $GOOGLE_CLOUD_LOCATION $STAGING_BUCKET

Ringkasan

Anda berhasil:

✅ Memahami infrastruktur deployment yang disediakan oleh deploy.sh
✅ Meninjau konfigurasi wrapper Agent Engine
✅ Men-deploy sistem donasi tepercaya Anda ke Agent Engine (direkomendasikan)
✅ Mengaktifkan integrasi Cloud Trace dengan --trace_to_cloud
✅ Memastikan agen dapat diakses dan berfungsi
✅ Membuat dasar untuk jejak audit di Modul 9

Dalam modul berikutnya, Anda akan melihat persis apa yang dapat dilakukan oleh tanda ini: visibilitas lengkap ke setiap donasi, setiap momen izin, dan setiap langkah dalam rantai kredensial.

9. Kemampuan observasi

banner

rekaman aktivitas grafik

Dalam Modul 1, Anda telah mempelajari masalah mendasar: saat agen AI menangani uang, bagaimana cara membuktikan apa yang terjadi?

Pengguna dapat mengklaim:

  • "Saya tidak pernah memilih lembaga amal itu!"
  • "Saya tidak mengizinkan pembayaran itu!"
  • "Sistem menagih saya tanpa izin saya!"

Dalam sistem AI kotak hitam tradisional, Anda tidak akan dapat membuktikan sebaliknya. Namun, sistem donasi tepercaya Anda berbeda. Di Modul 8, Anda men-deploy dengan tanda --trace_to_cloud, yang berarti setiap donasi kini membuat jalur audit yang lengkap dan anti-rusak di Cloud Trace.

Modul ini mengajarkan Anda cara membaca rekaman aktivitas tersebut dan menggunakannya sebagai bukti. Anda akan mempelajari cara:

  • Menavigasi Cloud Trace Explorer untuk menemukan rekaman aktivitas produksi
  • Membaca tampilan waterfall untuk memahami alur eksekusi
  • Temukan rantai kredensial (Maksud → Keranjang → Otorisasi pembayaran)
  • Menemukan momen izin dengan bukti stempel waktu
  • Menggunakan rekaman aktivitas untuk penyelesaian sengketa
  • Mengekspor rekaman aktivitas untuk kepatuhan dan audit

Inilah yang membedakan sistem yang dapat dipercaya dari sistem yang mumpuni tetapi tidak transparan: kemampuan untuk membuktikan apa yang terjadi dengan presisi forensik.

Memahami Trace dan Span

Sebelum melihat rekaman aktivitas di Cloud Trace, Anda harus memahami apa yang Anda lihat.

Apa itu Trace?

Trace adalah linimasa lengkap agen Anda yang menangani satu permintaan. Latensi ini mencakup semuanya, mulai dari saat pengguna mengirimkan kueri hingga respons akhir dikirimkan.

Setiap rekaman aktivitas menampilkan:

  • Total durasi permintaan
  • Semua operasi yang dijalankan
  • Cara operasi saling terkait (hubungan induk-turunan)
  • Saat setiap operasi dimulai dan berakhir
  • Status berhasil atau gagal

Untuk agen lembaga amal Anda: Satu rekaman aktivitas = satu alur donasi lengkap dari "Saya ingin berdonasi" hingga "Pembayaran berhasil".

Apa itu Span?

Span mewakili satu unit kerja dalam rekaman aktivitas. Anggap rentang sebagai elemen penyusun rekaman aktivitas.

Jenis rentang umum dalam sistem donasi Anda:

Jenis Rentang

Yang Ditampilkan

Contoh

agent_run

Eksekusi agen

ShoppingAgent.run, MerchantAgent.run

call_llm

Permintaan ke model bahasa

gemini-2.5-flash permintaan pemilihan badan amal

execute_tool

Eksekusi fungsi alat

find_charities, create_payment_mandate

state_read

Membaca dari memori sesi

Mengambil intent_mandate dari status

state_write

Menulis ke memori sesi

Menyimpan cart_mandate dalam status

Setiap rentang berisi:

  • Nama: Operasi yang diwakili oleh tindakan ini
  • Durasi yang diperlukan (waktu mulai → waktu berakhir)
  • Atribut: Metadata seperti input alat, respons model, jumlah token
  • Status: Berhasil (OK) atau error (ERROR)
  • Hubungan induk-turunan: Operasi mana yang memicu operasi mana

Cara Rentang Membentuk Rekaman Aktivitas

Rentang bersarang di dalam satu sama lain untuk menunjukkan sebab akibat:

Root Span: CharityAdvisor.run (entire request)
  └─ Child: DonationPipeline.run (sequential workflow)
      ├─ Child: ShoppingAgent.run
         ├─ Grandchild: call_llm (Gemini processes charity search)
         ├─ Grandchild: execute_tool (find_charities)
         └─ Grandchild: execute_tool (save_user_choice)
      ├─ Child: MerchantAgent.run
         ├─ Grandchild: call_llm (Gemini generates cart)
         └─ Grandchild: execute_tool (create_cart_mandate)
      └─ Child: CredentialsProvider.run
          ├─ Grandchild: call_llm (Gemini processes payment)
          └─ Grandchild: execute_tool (create_payment_mandate) [CONSENT!]

Hierarki ini menunjukkan apa yang terjadi dan dalam urutan apa. Anda dapat melihat bahwa mandat pembayaran dibuat setelah mandat keranjang, yang dibuat setelah pengguna memilih lembaga amal.

Langkah 1: Akses Cloud Trace Explorer

Sekarang, mari kita lihat rekaman aktivitas sebenarnya dari agen yang di-deploy.

  1. Buka Konsol Google Cloud: console.cloud.google.com
  2. Pilih project Anda dari dropdown di bagian atas (seharusnya sudah dipilih sebelumnya jika Anda telah mengerjakannya)
  3. Buka Cloud Trace Explorer:

Yang Anda Lihat

Trace Explorer menampilkan daftar semua rekaman aktivitas dari project Anda:

Kolom

Informasi yang Ditampilkan

Permintaan

Metode dan endpoint HTTP (untuk permintaan API)

Waktu Mulai

Saat permintaan dimulai

Latensi

Total durasi permintaan

Spans

Jumlah operasi dalam rekaman aktivitas

Setiap baris mewakili satu permintaan lengkap ke agen yang di-deploy.

Membuat Rekaman Aktivitas Pengujian (Jika Diperlukan)

Jika Anda belum melihat rekaman aktivitas, daftar mungkin kosong karena:

  • Belum ada permintaan yang dibuat ke agen yang di-deploy
  • Pelacakan memerlukan waktu 1-2 menit untuk muncul setelah permintaan

Buat rekaman aktivitas pengujian:

Jika Anda men-deploy ke Cloud Run dengan UI, buka URL layanan Anda dan selesaikan donasi di browser.

Jika Anda men-deploy ke Agent Engine, jalankan skrip pengujian dari Modul 8:

python scripts/test_deployed_agent.py

Tunggu 1-2 menit, lalu muat ulang halaman Cloud Trace Explorer. Sekarang Anda akan melihat rekaman aktivitas.

Memfilter Rekaman Aktivitas

Gunakan opsi filter di bagian atas untuk menemukan rekaman aktivitas tertentu:

  • Rentang waktu: Ubah dari "Satu jam terakhir" menjadi "24 jam terakhir" jika diperlukan
  • Latensi min / Latensi maks: Memfilter permintaan yang lambat
  • Filter permintaan: Telusuri berdasarkan operasi tertentu (misalnya, "DonationPipeline")

Untuk modul ini, fokuslah pada rekaman aktivitas dengan durasi yang lebih lama (>5 detik), karena rekaman aktivitas ini mewakili alur pemberian donasi lengkap dengan ketiga agen yang dieksekusi.

Langkah 2: Periksa Alur Donasi Lengkap

Klik rekaman aktivitas mana pun dalam daftar untuk membuka tampilan waterfall. Di sinilah Anda akan menghabiskan sebagian besar waktu untuk menganalisis perilaku agen.

Memahami Tampilan Waterfall

Tampilan waterfall adalah diagram Gantt yang menampilkan linimasa eksekusi lengkap:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
              Timeline (horizontal = time) 
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

invocation                           ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ 8.2s
  agent_run: CharityAdvisor          ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ 8.1s
    agent_run: DonationPipeline      ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ 7.9s
      agent_run: ShoppingAgent       ▓▓▓▓▓▓ 2.1s
        call_llm: gemini-2.5-flash   ▓▓▓▓ 1.2s
        execute_tool: find_charities ▓▓ 0.5s
        execute_tool: save_user_choice  0.3s
      agent_run: MerchantAgent       ▓▓▓ 1.8s
        call_llm: gemini-2.5-flash   ▓▓ 0.9s
        execute_tool: create_cart_mandate  0.7s
      agent_run: CredentialsProvider ▓▓▓▓▓▓▓▓ 4.0s
        call_llm: gemini-2.5-flash   ▓▓ 0.8s
        execute_tool: create_payment_mandate ▓▓▓▓▓ 3.0s [CONSENT]

Membaca Diagram

Setiap batang mewakili rentang:

  • Posisi horizontal: Kapan dimulai
  • Durasi: Berapa lama waktu yang dibutuhkan
  • Indentasi: Menunjukkan hubungan induk-turunan
  • Warna: Biasanya biru untuk normal, merah untuk error

Pengamatan utama dari rekaman aktivitas contoh ini:

Durasi total: 8,2 detik
Eksekusi berurutan: ShoppingAgent selesai sebelum MerchantAgent dimulai
MerchantAgent selesai

sebelum

CredentialsProvider dimulai
Consent adalah operasi terlama: 3,0 detik untuk create_payment_mandate (karena menunggu konfirmasi pengguna)
Panggilan LLM terlihat: Setiap agen membuat satu permintaan Gemini
Panggilan alat dicatat: Keenam alat berhasil dijalankan

Visual ini akan langsung menunjukkan tempat waktu dihabiskan dan urutan operasi yang dijalankan.

Mengklik Rentang untuk Melihat Detail

Klik rentang invocation (rentang root di bagian atas). Di panel kanan, Anda akan melihat atribut mendetail:

{
  "http.method": "POST",
  "http.status_code": 200,
  "http.url": "https://charity-advisor-xyz.a.run.app/api/run",
  "user_id": "test_user_123",
  "session_id": "4857885913439920384",
  "trace_id": "a1b2c3d4e5f6...",
  "span_id": "1234567890abcdef"
}

Atribut ini memberikan konteks tentang seluruh permintaan.

Langkah 3: Temukan Rantai Kredensial

Sistem tepercaya Anda menggunakan rantai kredensial untuk membuktikan otorisasi di setiap langkah:

IntentMandate (User chose charity)
    ↓
CartMandate (Merchant created cart, signed IntentMandate)
    ↓
PaymentMandate (Payment provider created payment, signed CartMandate)

Mari kita temukan setiap perintah dalam rekaman aktivitas.

Menemukan IntentMandate

Klik rentang execute_tool: save_user_choice (di bagian ShoppingAgent).

Di panel atribut, Anda akan melihat:

{
  "tool.name": "save_user_choice",
  "tool.input.charity_name": "Save the Children",
  "tool.input.amount": 50,
  "tool.output.status": "success",
  "tool.output.intent_mandate": {
    "charity_name": "Save the Children",
    "amount": 50,
    "timestamp": "2024-11-08T15:30:12.345Z",
    "signature": "a3f7b9c1d2e4..."
  }
}

Hal ini membuktikan:

  • ✅ Pengguna memilih "Save the Children"
  • ✅ Jumlahnya adalah $50
  • ✅ Pilihan direkam pada 15.30.12 UTC
  • ✅ Tanda tangan dibuat (dalam produksi, tanda tangan ini akan bersifat kriptografis)

IntentMandate kini berada dalam status sesi dan tersedia untuk agen berikutnya.

Menemukan CartMandate

Klik rentang execute_tool: create_cart_mandate (di bagian MerchantAgent).

Di panel atribut:

{
  "tool.name": "create_cart_mandate",
  "tool.input.intent_mandate": {
    "charity_name": "Save the Children",
    "amount": 50,
    "signature": "a3f7b9c1d2e4..."
  },
  "tool.output.status": "success",
  "tool.output.cart_mandate": {
    "cart_id": "cart_7893",
    "intent_signature": "a3f7b9c1d2e4...",
    "cart_signature": "e8f2a9b3c7d1...",
    "timestamp": "2024-11-08T15:30:14.789Z"
  }
}

Hal ini membuktikan:

  • ✅ MerchantAgent menerima IntentMandate (input menunjukkannya)
  • ✅ Keranjang dibuat dengan ID "cart_7893"
  • ✅ Tanda tangan keranjang merujuk ke tanda tangan IntentMandate (link berantai!)
  • ✅ Dibuat pada 15.30.14 UTC (2,4 detik setelah intent)

CartMandate kini mereferensikan IntentMandate, yang membentuk rantai.

Menemukan PaymentMandate

Klik rentang execute_tool: create_payment_mandate (di bagian CredentialsProvider).

Di panel atribut:

{
  "tool.name": "create_payment_mandate",
  "tool.input.cart_mandate": {
    "cart_id": "cart_7893",
    "intent_signature": "a3f7b9c1d2e4...",
    "cart_signature": "e8f2a9b3c7d1..."
  },
  "tool.confirmation_required": true,
  "tool.confirmation_timestamp": "2024-11-08T15:30:17.891Z",
  "tool.user_response": "CONFIRMED",
  "tool.wait_duration_ms": 29168,
  "tool.output.status": "success",
  "tool.output.payment_mandate": {
    "payment_id": "pay_9821",
    "cart_signature": "e8f2a9b3c7d1...",
    "payment_signature": "b4c9e2a7f8d3...",
    "timestamp": "2024-11-08T15:30:47.059Z"
  }
}

Hal ini membuktikan rantai lengkap:

  • ✅ CredentialsProvider menerima CartMandate (input menunjukkannya)
  • ✅ Pembayaran merujuk pada tanda tangan CartMandate (link rantai)
  • Konfirmasi diperlukan (confirmation_required: true)
  • Pengguna dikonfirmasi pada 15.30.17 UTC
  • Sistem menunggu 29,2 detik untuk keputusan pengguna
  • ✅ Pembayaran dibuat setelah konfirmasi (stempel waktu: 15.30.47)

Memvisualisasikan Rantai

Rekaman aktivitas membuktikan bahwa rantai kredensial dieksekusi dengan benar:

15:30:12 UTC  IntentMandate created (signature: a3f7...)
                  
15:30:14 UTC  CartMandate created (references: a3f7...)
                  
15:30:17 UTC  User consent requested
                  
15:30:47 UTC  PaymentMandate created (references: e8f2...)

Setiap mandat mereferensikan tanda tangan mandat sebelumnya. Ini tampak jelas jika dirusak - Anda dapat memverifikasi rantai dengan memeriksa apakah tanda tangan cocok.

Langkah 4: Menganalisis Performa dan Hambatan

Cloud Trace tidak hanya membuktikan apa yang terjadi, tetapi juga menunjukkan penggunaan waktu sehingga Anda dapat mengoptimalkan.

Mengidentifikasi Jalur Kritis

Dalam tampilan waterfall, cari rentang terpanjang dalam tumpukan vertikal. Bagian ini menunjukkan hambatan performa Anda.

Dari rekaman aktivitas contoh kita:

Total: 8.2 seconds

Breakdown:
  - ShoppingAgent:         2.1s (26%)
  - MerchantAgent:         1.8s (22%)
  - CredentialsProvider:   4.0s (49%)   Bottleneck
  - Other overhead:        0.3s (3%)

Insight penting: CredentialsProvider menyumbang 49% dari total waktu. Mengapa?

Lihat perincian rentang CredentialsProvider:

CredentialsProvider: 4.0s
  - call_llm:              0.8s (20%)
  - create_payment_mandate: 3.0s (75%)   User consent wait
  - Other:                 0.2s (5%)

Penundaan 3,0 detik sudah diperkirakan dan baik - pengguna sedang mempertimbangkan sebelum mengonfirmasi. Ini bukan masalah performa; ini adalah bukti izin yang bijaksana.

Melacak Biaya LLM

Klik rentang call_llm untuk melihat penggunaan token:

{
  "llm.model": "gemini-2.5-flash",
  "llm.usage.prompt_tokens": 487,
  "llm.usage.completion_tokens": 156,
  "llm.usage.total_tokens": 643,
  "llm.response_time_ms": 1243
}

Gunakan ini untuk:

  • Melacak biaya per permintaan (token × harga model)
  • Mengidentifikasi perintah yang terlalu panjang
  • Membandingkan performa model (Flash vs. Pro)
  • Mengoptimalkan latensi vs. kualitas

Contoh penghitungan:

Gemini 2.5 Flash pricing (as of Nov 2024):
  Input:  $0.075 per 1M tokens
  Output: $0.30 per 1M tokens

This request:
  Input:  487 tokens × $0.075 / 1M = $0.000037
  Output: 156 tokens × $0.30 / 1M  = $0.000047
  Total:                            = $0.000084 (~$0.00008)

For 10,000 donations/month:
  10,000 × 3 agents × $0.00008 = $2.40/month in LLM costs

Visibilitas terperinci ini membantu Anda membuat keputusan berbasis data tentang pemilihan model.

Membandingkan Antar-Rekaman Aktivitas

Memfilter beberapa rekaman aktivitas dan membandingkan durasi:

Trace 1: 8.2s  (with consent wait: 3.0s)
Trace 2: 12.5s (with consent wait: 7.8s)  ← User took longer
Trace 3: 5.1s  (with consent wait: 0.2s)  ← User clicked fast
Trace 4: 6.3s  (with consent wait: 1.5s)

Insight: Sebagian besar variasi berasal dari waktu pengambilan keputusan pengguna, bukan performa sistem. Eksekusi agen inti (tanpa izin) konsisten pada ~5 detik.

Hal ini menunjukkan bahwa sistem berfungsi dengan andal.

Untuk sistem produksi, siapkan pemberitahuan untuk mendeteksi masalah sebelum pengguna mengeluh.

Peringatan tentang Tingkat Error Tinggi

Buat pemberitahuan saat >5% rekaman aktivitas berisi error:

  1. Buka Cloud Monitoring
  2. Klik "Pemberitahuan""Buat Kebijakan"
  3. Konfigurasi:
    Resource: Cloud Trace Span
    Metric: Span error count
    Condition: Rate > 5% over 5 minutes
    Notification: Email your-team@example.com
    

Pemberitahuan Latensi Tinggi

Buat pemberitahuan saat latensi p95 melebihi 15 detik:

Resource: Cloud Trace
Metric: Span duration (95th percentile)
Condition: > 15000ms for 5 minutes
Notification: PagerDuty

Hal ini akan mendeteksi penurunan performa sebelum memengaruhi pengalaman pengguna.

Buat pemberitahuan jika ada pembayaran yang diproses tanpa konfirmasi:

Resource: Cloud Trace Span
Filter: tool.name="create_payment_mandate" AND tool.confirmation_required!=true
Condition: Any match
Notification: Critical alert to security team

Ini adalah pendeteksi pelanggaran keamanan - jika diaktifkan, ada yang sangat salah dengan mekanisme izin Anda.

Yang Telah Anda Pelajari

Melalui Cloud Trace, Anda kini memahami cara:

Menavigasi Cloud Trace Explorer untuk menemukan rekaman aktivitas produksi
Membaca tampilan waterfall untuk melihat alur eksekusi lengkap
Melacak rantai kredensial melalui IntentMandate → CartMandate → PaymentMandate ✅ Menggunakan rekaman aktivitas sebagai bukti untuk penyelesaian sengketa
Menganalisis performa untuk mengidentifikasi bottleneck
Melacak biaya LLM pada tingkat perincian

Perbedaan yang Ditimbulkan

Bandingkan dua sistem yang menangani keluhan "Saya tidak pernah mengizinkan ini!" yang sama:

Sistem Tanpa Kemampuan Observasi

User: "I never authorized that $50 donation!"
You:  "Our logs show the transaction completed successfully."
User: "But I didn't approve it!"
You:  "The system requires confirmation before processing."
User: "I never saw any confirmation!"
You:  "..." [no way to prove what happened]

Result: Refund issued, trust lost, user never returns.

Sistem Dengan Cloud Trace

User: "I never authorized that $50 donation!"
You:  "Let me pull up the trace from your session..."
      [Shows waterfall with consent span]
You:  "Here's the evidence:
       - 15:30:17 UTC: System asked for confirmation
       - Message shown: 'You are about to donate $50...'
       - 15:30:47 UTC: You clicked 'CONFIRM'
       - Wait time: 29.2 seconds
       
       The system waited almost 30 seconds for your decision.
       Here's the exact timestamp of your confirmation."
       
User: "Oh... I remember now. My mistake. Sorry!"

Result: Trust preserved, no refund needed, user continues using service.

Inilah kekuatan jejak akuntabilitas. Anda beralih dari "percayalah pada kami" menjadi "izinkan kami menunjukkan dengan tepat apa yang terjadi".

Langkah Berikutnya

Anda telah menyelesaikan inti teknis untuk membangun agen yang tepercaya:

Modul 1-6: Merancang arsitektur yang tepercaya (peran, kredensial, izin)
Modul 7: Mengatur alur kerja yang kompleks (SequentialAgent)
Modul 8: Men-deploy dengan kemampuan observasi yang diaktifkan
Modul 9: Mempelajari cara membaca dan menggunakan jejak audit

Arsitektur yang telah Anda buat—pemisahan peran, rantai kredensial, mekanisme izin, kemampuan pengamatan lengkap—ditransfer langsung ke sistem produksi yang menangani uang sungguhan, data sungguhan, dan konsekuensi sungguhan.

10. Perjalanan Anda ke Depan

Yang Telah Anda Buat

Anda memulai workshop ini dengan pertanyaan: "Bagaimana cara membangun agen AI yang benar-benar dapat saya percayai untuk mengelola uang?"

Sekarang Anda sudah mendapatkan jawabannya.

Tempat Anda memulai (Modul 3):

simple_agent = Agent(
    model="gemini-2.5-flash",
    instruction="Find charities and donate",
    tools=[google_search]
)

Posisi Anda saat ini (Modul 10):

  • ✅ Tiga agen khusus dengan pemisahan peran
  • ✅ Tiga kredensial yang dapat diverifikasi (Maksud → Keranjang → Otorisasi pembayaran)
  • ✅ Menyelesaikan rantai kredensial dengan validasi masa berlaku di setiap langkah
  • ✅ Mekanisme izin eksplisit dengan bukti stempel waktu
  • ✅ Deployment produksi ke Agent Engine dengan kemampuan observasi
  • ✅ Jejak audit lengkap di Cloud Trace
  • ✅ Bukti forensik untuk penyelesaian sengketa

Workshop vs. Produksi: Perbedaan

Sistem Anda menunjukkan arsitektur dan pola yang benar, tetapi menggunakan penyederhanaan edukasional yang harus diupgrade untuk uang sungguhan dan pengguna sungguhan.

Berikut adalah hal yang disederhanakan dan yang diperlukan untuk produksi:

Komponen

Implementasi Workshop

Persyaratan Produksi

Tanda tangan

Hash SHA-256 (SIG_abc123) untuk demonstrasi

Tanda tangan kriptografi nyata menggunakan PKI atau JWT dengan kunci pribadi

Pemrosesan Pembayaran

Simulasi pengembalian (flag simulation: True)

Integrasi dengan API pembayaran sebenarnya (Stripe, PayPal, Square)

Autentikasi Pengguna

Kepercayaan implisit (tidak perlu login)

OAuth 2.0, WebAuthn, atau pengelolaan sesi

Pengelolaan Secret

Variabel lingkungan dalam file .env

Google Secret Manager atau Cloud KMS dengan enkripsi

Database Lembaga Amal

File JSON tiruan dengan 9 lembaga amal

Integrasi API langsung (IRS Tax Exempt Organization Search, Charity Navigator API)

Penanganan Error

Try-catch dasar dengan pesan error

Logika percobaan ulang dengan backoff eksponensial, pemutus arus listrik, antrean pesan yang tidak terkirim

Pengujian

Verifikasi manual melalui skrip

Suite pengujian unit/integrasi/E2E yang komprehensif dengan CI/CD

Persistensi Sesi

Dalam memori (lokal) atau otomatis (Agent Engine)

Database produksi dengan pencadangan dan pemulihan dari bencana

Pembatasan Kecepatan

Tidak ada (lingkungan pendidikan)

Batas laju per pengguna, pembatasan berbasis IP, deteksi penyalahgunaan

Pola Arsitektur Utama yang Anda Kuasai

Pola yang Anda pelajari dalam workshop ini adalah pola produksi. Jangan meragukan mereka.

Pemisahan Peran (Prinsip AP2 #1)

Setiap agen memiliki SATU tugas yang jelas dan HANYA melihat apa yang dibutuhkannya. Jika satu agen disusupi, penyerang tidak dapat mengakses data agen lain. Hal ini membatasi radius efek gangguan.

Sistem produksi yang menggunakan ini: Pemrosesan pembayaran, alur kerja dokumen, rantai persetujuan, formulir multi-langkah dengan gerbang validasi.

Kredensial yang Dapat Diverifikasi (Prinsip AP2 #2)

Setiap kredensial memiliki waktu habis masa berlaku, mereferensikan kredensial sebelumnya, dan memerlukan validasi sebelum langkah berikutnya. Tindakan ini akan membuat rantai audit yang tahan terhadap gangguan.

Nilai produksi: Bukti lengkap tentang apa yang terjadi, kapan, dan dalam urutan apa. Penting untuk penyelesaian sengketa dan kepatuhan terhadap peraturan.

Bukti stempel waktu bahwa pengguna menyetujui tindakan. Tidak dapat disengketakan.

Nilai produksi: Persyaratan hukum untuk transaksi keuangan. Melindungi pengguna dan perusahaan.

Orkestrasi Berurutan (Pola ADK)

Menerapkan urutan eksekusi yang benar. Mencegah langkah-langkah dilewati. Memastikan setiap agen melihat output agen sebelumnya.

Nilai produksi: Sempurna untuk sistem human-in-the-loop di mana pengguna mengharapkan hasil langsung. Ini adalah pola yang tepat untuk alur donasi, proses checkout, dan rantai persetujuan.

Kemampuan Observasi Lengkap (OpenTelemetry + Cloud Trace)

Setiap keputusan, panggilan alat, momen izin, dan penyerahan kredensial dicatat secara otomatis.

Nilai produksi: Bukti forensik untuk sengketa. Data pengoptimalan performa. Jejak audit kepatuhan. Debug masalah produksi dengan presisi.

Sumber Daya untuk Pembelajaran Berkelanjutan

Dokumentasi ADK:

AP2 & Standar Terkait:

Layanan Google Cloud:

Membersihkan Resource

Untuk menghindari tagihan berkelanjutan, hapus deployment Anda:

Agent Engine: Ikuti langkah-langkah dalam dokumen Agent Engine

Cloud Run (jika di-deploy):

gcloud run services delete charity-advisor \
    --region=$GOOGLE_CLOUD_LOCATION

Bucket Penyimpanan:

gsutil -m rm -r gs://$GOOGLE_CLOUD_PROJECT-staging
gsutil -m rm -r gs://$GOOGLE_CLOUD_PROJECT-artifacts

Perjalanan Anda Berlanjut

Anda memulai dengan pertanyaan sederhana dan membuat jawaban lengkap. Anda telah menguasai pola dasar untuk agen AI yang tepercaya. Pola ini ditransfer ke domain mana pun tempat agen AI menangani operasi sensitif—transaksi keuangan, keputusan layanan kesehatan, dokumen hukum, operasi rantai pasokan.

Prinsip-prinsipnya ditransfer. Model kepercayaan berfungsi.

Sekarang, buat sesuatu yang dapat dipercaya. ❤️

banner