1. Pengantar
Dalam codelab ini, Anda akan mempelajari cara membuat agen AI canggih menggunakan Agent Development Kit (ADK) Google. Kita akan mengikuti jalur evolusi alami, dimulai dari agen percakapan dasar dan secara progresif menambahkan kemampuan khusus.
Agen yang kita bangun adalah asisten eksekutif, yang dirancang untuk membantu Anda dalam tugas sehari-hari seperti mengelola kalender, mengingatkan Anda tentang tugas, melakukan riset dan menyusun catatan, semuanya dibangun dari awal menggunakan ADK, Gemini, dan Vertex AI.
Di akhir lab ini, Anda akan memiliki agen yang berfungsi penuh dan pengetahuan yang diperlukan untuk memperluasnya sesuai kebutuhan Anda.
Prasyarat
- Pengetahuan dasar tentang bahasa pemrograman Python
- Pengetahuan dasar tentang konsol Google Cloud untuk mengelola resource cloud
Yang akan Anda pelajari
- Menyediakan infrastruktur Google Cloud untuk Agen AI.
- Menerapkan memori jangka panjang persisten menggunakan Vertex AI Memory Bank.
- Membangun hierarki sub-agen khusus.
- Mengintegrasikan database eksternal dan ekosistem Google Workspace.
Yang Anda butuhkan
Workshop ini dapat dilakukan sepenuhnya di dalam Google Cloud Shell, yang sudah diinstal sebelumnya dengan semua dependensi yang diperlukan (gcloud CLI, editor kode, Go, Gemini CLI).
Atau, jika Anda lebih suka bekerja di komputer Anda sendiri, Anda akan memerlukan hal berikut:
- Python (versi 3.12 atau yang lebih tinggi)
- Editor kode atau IDE (seperti VS Code atau
vim). - Terminal untuk menjalankan perintah Python dan
gcloud. - Direkomendasikan: agen coding seperti Gemini CLI atau Antigravity
Teknologi Utama
Di sini Anda dapat menemukan informasi selengkapnya tentang teknologi yang akan kami gunakan:
2. Penyiapan Lingkungan
Pilih salah satu opsi berikut: Penyiapan lingkungan mandiri jika Anda ingin menjalankan codelab ini di komputer Anda sendiri, atau; Mulai Cloud Shell jika Anda ingin menjalankan codelab ini sepenuhnya di cloud.
Penyiapan lingkungan mandiri
- Login ke Google Cloud Console dan buat project baru atau gunakan kembali project yang sudah ada. Jika belum memiliki akun Gmail atau Google Workspace, Anda harus membuatnya.



- Project name adalah nama tampilan untuk peserta project ini. String ini adalah string karakter yang tidak digunakan oleh Google API. Anda dapat memperbaruinya kapan saja.
- Project ID bersifat unik di semua project Google Cloud dan tidak dapat diubah (tidak dapat diubah setelah ditetapkan). Cloud Console otomatis membuat string unik; biasanya Anda tidak mementingkan kata-katanya. Di sebagian besar codelab, Anda harus merujuk Project ID-nya (umumnya diidentifikasi sebagai
PROJECT_ID). Jika tidak suka dengan ID yang dibuat, Anda dapat membuat ID acak lainnya. Atau, Anda dapat mencobanya sendiri, dan lihat apakah ID tersebut tersedia. ID tidak dapat diubah setelah langkah ini dan tersedia selama durasi project. - Sebagai informasi, ada nilai ketiga, Project Number, yang digunakan oleh beberapa API. Pelajari lebih lanjut ketiga nilai ini di dokumentasi.
- Selanjutnya, Anda harus mengaktifkan penagihan di Konsol Cloud untuk menggunakan resource/API Cloud. Menjalankan operasi dalam codelab ini tidak akan memakan banyak biaya, bahkan mungkin tidak sama sekali. Guna mematikan resource agar tidak menimbulkan penagihan di luar tutorial ini, Anda dapat menghapus resource yang dibuat atau menghapus project-nya. Pengguna baru Google Cloud memenuhi syarat untuk mengikuti program Uji Coba Gratis senilai $300 USD.
Mulai Cloud Shell
Meskipun Google Cloud dapat dioperasikan dari jarak jauh menggunakan laptop Anda, dalam codelab ini, Anda akan menggunakan Google Cloud Shell, lingkungan command line yang berjalan di Cloud.
Dari Google Cloud Console, klik ikon Cloud Shell di toolbar kanan atas:

Hanya perlu waktu beberapa saat untuk penyediaan dan terhubung ke lingkungan. Jika sudah selesai, Anda akan melihat tampilan seperti ini:

Mesin virtual ini berisi semua alat pengembangan yang Anda perlukan. Layanan ini menawarkan direktori beranda tetap sebesar 5 GB dan beroperasi di Google Cloud, sehingga sangat meningkatkan performa dan autentikasi jaringan. Semua pekerjaan Anda dalam codelab ini dapat dilakukan di browser. Anda tidak perlu menginstal apa pun.
3. Penyiapan Project
Sebelum menulis kode, kita harus menyediakan infrastruktur dan izin yang diperlukan di Google Cloud.
Menetapkan Variabel Lingkungan
Buka terminal dan tetapkan variabel lingkungan berikut:
export PROJECT_ID=`gcloud config get project`
export LOCATION=us-central1
Mengaktifkan API yang Diperlukan
Agen Anda memerlukan akses ke beberapa layanan Google Cloud. Jalankan perintah berikut untuk mengaktifkannya:
gcloud services enable \
aiplatform.googleapis.com \
calendar-json.googleapis.com \
sqladmin.googleapis.com
Melakukan autentikasi dengan Kredensial Default Aplikasi
Kita perlu mengautentikasi dengan Kredensial Default Aplikasi (ADC) untuk berkomunikasi dengan layanan Google Cloud dari lingkungan Anda.
Jalankan perintah berikut untuk memastikan Kredensial Default Aplikasi Anda aktif dan terbaru:
gcloud auth application-default login
4. Membuat agen dasar
Sekarang, kita perlu menginisialisasi direktori tempat kita akan menyimpan kode sumber project:
# setup project directory
mkdir -p adk_ea_codelab && cd adk_ea_codelab
# prepare virtual environment
uv init
# install dependencies
uv add google-adk google-api-python-client tzlocal python-dotenv
uv add cloud-sql-python-connector[pg8000] sqlalchemy
Kita mulai dengan menetapkan identitas agen dan kemampuan percakapan dasar. Di ADK, class Agent menentukan persona agen dan petunjuknya.
Sekaranglah saatnya Anda memikirkan nama agen. Saya suka memberi nama yang tepat untuk agen saya seperti Aida atau Sharon, karena menurut saya hal ini membantu memberikan "kepribadian" kepada agen tersebut, tetapi Anda juga dapat memanggil agen berdasarkan fungsinya, seperti "asisten_eksekutif", "agen_perjalanan", atau "pengeksekusi_kode".
Jalankan perintah adk create untuk membuat agen boilerplate:
# replace with your desired agent name
uv run adk create executive_assistant
Pilih gemini-2.5-flash sebagai model, dan Vertex AI sebagai backend. Periksa kembali apakah project ID yang disarankan adalah project ID yang Anda buat untuk lab ini, lalu tekan enter untuk mengonfirmasi. Untuk region Google Cloud, Anda dapat menerima default (us-central1). Terminal Anda akan terlihat seperti ini:
daniela_petruzalek@cloudshell:~/adk_ea_codelab (your-project-id)$ uv run adk create executive_assistant Choose a model for the root agent: 1. gemini-2.5-flash 2. Other models (fill later) Choose model (1, 2): 1 1. Google AI 2. Vertex AI Choose a backend (1, 2): 2 You need an existing Google Cloud account and project, check out this link for details: https://google.github.io/adk-docs/get-started/quickstart/#gemini---google-cloud-vertex-ai Enter Google Cloud project ID [your-project-id]: Enter Google Cloud region [us-central1]: Agent created in /home/daniela_petruzalek/adk_ea_codelab/executive_assistant: - .env - __init__.py - agent.py daniela_petruzalek@cloudshell:~/adk_ea_codelab (your-project-id)$
Setelah selesai, perintah sebelumnya akan membuat folder dengan nama agen (misalnya, executive_assistant) dengan beberapa file di dalamnya, termasuk file agent.py dengan definisi agen dasar:
from google.adk.agents.llm_agent import Agent
root_agent = Agent(
model='gemini-2.5-flash',
name='root_agent',
description='A helpful assistant for user questions.',
instruction='Answer user questions to the best of your knowledge',
)
Jika ingin berinteraksi dengan agen ini, Anda dapat melakukannya dengan menjalankan uv run adk web di command line dan membuka UI pengembangan di browser. Anda akan melihat sesuatu seperti ini:
$ uv run adk web ... INFO: Started server process [1244] INFO: Waiting for application startup. +-----------------------------------------------------------------------------+ | ADK Web Server started | | | | For local testing, access at http://127.0.0.1:8000. | +-----------------------------------------------------------------------------+ INFO: Application startup complete. INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
Meskipun agen ini cukup mendasar, sebaiknya lakukan hal ini setidaknya sekali untuk memastikan penyiapan berfungsi dengan baik sebelum kita mulai mengedit agen. Screenshot di bawah menunjukkan interaksi sederhana menggunakan UI pengembangan:

Sekarang, mari kita ubah definisi agen dengan persona asisten eksekutif kita. Salin kode di bawah dan ganti konten agent.py. Sesuaikan nama dan kepribadian agen dengan preferensi Anda.
from google.adk.agents.llm_agent import Agent
root_agent = Agent(
model='gemini-2.5-flash',
name='executive_assistant',
description='A professional AI Executive Assistant',
instruction='''
You are an elite, warm, and highly efficient AI partner.
Your primary goal is to help the user manage their tasks, schedule, and research.
Always be direct, concise, and high-signal.
''',
)
Perhatikan bahwa properti nama menentukan nama internal agen, sementara dalam petunjuk, Anda juga dapat memberikan nama yang lebih mudah diingat sebagai bagian dari persona agen untuk interaksi dengan pengguna akhir. Nama internal sebagian besar digunakan untuk kemampuan pengamatan dan penyerahan dalam sistem multi-agen menggunakan alat transfer_to_agent. Anda tidak akan berinteraksi dengan alat ini secara langsung, ADK akan mendaftarkannya secara otomatis saat Anda mendeklarasikan satu atau beberapa sub-agen.
Untuk menjalankan agen yang baru saja kita buat, gunakan adk web:
uv run adk web
Buka UI ADK di browser dan sapa asisten baru Anda.
5. Menambahkan memori persisten dengan Vertex AI Memory Bank
Asisten yang sebenarnya harus mengingat preferensi dan interaksi sebelumnya untuk memberikan pengalaman yang lancar dan dipersonalisasi. Pada langkah ini, kita akan mengintegrasikan Vertex AI Agent Engine Memory Bank, fitur Vertex AI yang secara dinamis menghasilkan memori jangka panjang berdasarkan percakapan pengguna.
Memory Bank memungkinkan agen Anda membuat informasi yang dipersonalisasi yang dapat diakses di beberapa sesi, sehingga menciptakan kesinambungan lintas sesi. Di balik layar, fitur ini mengelola urutan kronologis pesan dalam sesi dan dapat menggunakan pengambilan penelusuran kemiripan untuk memberikan memori yang paling relevan bagi agen untuk konteks saat ini.
Menginisialisasi Layanan Memori
ADK menggunakan Vertex AI untuk menyimpan dan mengambil memori jangka panjang. Anda harus melakukan inisialisasi "Memory Engine" di project Anda. Pada dasarnya, ini adalah instance Reasoning Engine yang dikonfigurasi untuk bertindak sebagai Bank Memori.
Buat skrip berikut sebagai setup_memory.py:
setup_memory.py
import vertexai
import os
PROJECT_ID=os.getenv("PROJECT_ID")
LOCATION=os.getenv("LOCATION")
client = vertexai.Client(project=PROJECT_ID, location=LOCATION)
# Create Reasoning Engine for Memory Bank
agent_engine = client.agent_engines.create()
# You will need this resource name to give it to ADK
print(agent_engine.api_resource.name)
Sekarang jalankan setup_memory.py untuk menyediakan mesin penalaran bagi bank memori:
uv run python setup_memory.py
Output Anda akan terlihat seperti ini:
$ uv run python setup.py projects/1234567890/locations/us-central1/reasoningEngines/1234567890
Simpan nama resource mesin dalam variabel lingkungan:
export ENGINE_ID="<insert the resource name above>"
Sekarang kita perlu memperbarui kode untuk menggunakan memori persisten. Ganti konten agent.py dengan konten berikut:
agent.py
from google.adk.agents.llm_agent import Agent
from google.adk.tools.preload_memory_tool import PreloadMemoryTool
from google.adk.tools.load_memory_tool import load_memory_tool
async def auto_save_session_to_memory_callback(callback_context):
await callback_context._invocation_context.memory_service.add_session_to_memory(
callback_context._invocation_context.session)
# Update root_agent with memory tools and callback
root_agent = Agent(
model='gemini-2.5-flash',
name='executive_assistant',
description='Executive Assistant with Persistent Memory',
instruction='''
You are an elite AI partner with long-term memory.
Use load_memory to find context about the user when needed.
Always be direct, concise, and high-signal.
''',
tools=[PreloadMemoryTool(), load_memory_tool],
after_agent_callback=auto_save_session_to_memory_callback,
)
PreloadMemoryTool secara otomatis menyuntikkan konteks yang relevan dari percakapan sebelumnya ke dalam setiap permintaan (menggunakan pengambilan penelusuran kesamaan), sementara load_memory_tool memungkinkan model secara eksplisit mengkueri Bank Memori untuk mendapatkan fakta jika diperlukan. Kombinasi ini memberikan konteks yang mendalam dan persisten untuk agen Anda.
Sekarang, untuk meluncurkan agen dengan dukungan memori, Anda perlu meneruskan memory_service_uri saat menjalankan adk web:
uv run adk web --memory_service_uri="agentengine://$ENGINE_ID"
Coba berikan beberapa fakta tentang diri Anda kepada agen, lalu kembali dengan sesi yang berbeda untuk menanyakan fakta tersebut. Misalnya, beri tahu nama Anda:

Anda dapat memeriksa memori yang disimpan agen di konsol cloud. Buka halaman produk untuk "Agent Engine" (gunakan kotak penelusuran)

Kemudian, klik nama mesin agen Anda (pastikan Anda memilih wilayah yang tepat):

Kemudian, buka tab kenangan:

Anda akan melihat beberapa kenangan yang ditambahkan.
6. Menambahkan kemampuan riset web
Untuk memberikan informasi berkualitas tinggi, agen kami harus melakukan penyelidikan mendalam yang melampaui satu kueri penelusuran. Dengan mendelegasikan riset ke sub-agen khusus, kami mempertahankan responsivitas persona utama, sementara peneliti menangani pengumpulan data yang kompleks di latar belakang.
Pada langkah ini, kita menerapkan LoopAgent untuk mencapai "kedalaman riset"—yang memungkinkan agen melakukan penelusuran secara berulang, mengevaluasi temuan, dan menyempurnakan kuerinya hingga mendapatkan gambaran lengkap. Kami juga menerapkan ketelitian teknis dengan mewajibkan kutipan inline untuk semua temuan, sehingga memastikan setiap klaim didukung oleh link sumber.
Buat Research Specialist (research.py)
Di sini kita menentukan Agen dasar yang dilengkapi dengan alat Google Penelusuran dan menggabungkannya dalam LoopAgent. Parameter max_iterations bertindak sebagai pengatur, memastikan agen mengulangi penelusuran hingga 3 kali jika masih ada kesenjangan dalam pemahamannya.
research.py
from google.adk.agents.llm_agent import Agent
from google.adk.agents.loop_agent import LoopAgent
from google.adk.tools.google_search_tool import GoogleSearchTool
from google.adk.tools.tool_context import ToolContext
def exit_loop(tool_context: ToolContext):
"""Call this function ONLY when no further research is needed, signaling the iterative process should end."""
print(f" [Tool Call] exit_loop triggered by {tool_context.agent_name}")
tool_context.actions.escalate = True
# Return empty dict as tools should typically return JSON-serializable output
return {}
# --- RESEARCH LOGIC ---
_research_worker = Agent(
model='gemini-2.5-flash',
name='research_worker',
description='Worker agent that performs a single research step.',
instruction='''
Use google_search to find facts and synthesize them for the user.
Critically evaluate your findings. If the data is incomplete or you need more context, prepare to search again in the next iteration.
You must include the links you found as references in your response, formatting them like citations in a research paper (e.g., [1], [2]).
Use the exit_loop tool to terminate the research early if no further research is needed.
If you need to ask the user for clarifications, call the exit_loop function early to interrupt the research cycle.
''',
tools=[GoogleSearchTool(bypass_multi_tools_limit=True), exit_loop],
)
# The LoopAgent iterates the worker up to 3 times for deeper research
research_agent = LoopAgent(
name='research_specialist',
description='Deep web research specialist.',
sub_agents=[_research_worker],
max_iterations=3,
)
Perbarui Root Agent (agent.py)
Impor research_agent dan tambahkan sebagai alat ke Sharon:
agent.py
from google.adk.agents.llm_agent import Agent
from google.adk.tools.preload_memory_tool import PreloadMemoryTool
from google.adk.tools.load_memory_tool import load_memory_tool
# Import our new sub agent
from .research import research_agent
async def auto_save_session_to_memory_callback(callback_context):
await callback_context._invocation_context.memory_service.add_session_to_memory(
callback_context._invocation_context.session)
# Update root_agent with memory tools and callback
root_agent = Agent(
model='gemini-2.5-flash',
name='executive_assistant',
description='Executive Assistant with persistent memory and research capabilities',
instruction='''
You are an elite AI partner with long-term memory.
1. Use load_memory to recall facts.
2. Delegate research tasks to the research_specialist.
Always be direct, concise, and high-signal.
''',
tools=[PreloadMemoryTool(), load_memory_tool],
sub_agents=[research_agent],
after_agent_callback=auto_save_session_to_memory_callback,
)
Buka kembali web adk untuk menguji agen riset.
uv run adk web --memory_service_uri="agentengine://$ENGINE_ID"
Berikan tugas riset sederhana, misalnya, "cara menulis blog teknologi yang baik?"

Anda mungkin memperhatikan bahwa agen mengingat nama saya meskipun ini adalah sesi baru. Perhatikan juga panggilan alat "transfer_to_agent": ini adalah alat yang menyerahkan tugas kepada agen riset baru kami.

Sekarang, mari kita lanjutkan dengan pengelolaan tugas.
7. Menambahkan pengelolaan tugas dengan Cloud SQL
Meskipun memiliki memori jangka panjang, agen ini tidak cocok untuk data terstruktur yang terperinci seperti daftar Tugas. Untuk tugas, kita menggunakan database relasional tradisional. Kita akan menggunakan SQLAlchemy dan database Google Cloud SQL (PostgreSQL). Sebelum dapat menulis kode, kita harus menyediakan infrastruktur.
Menyediakan infrastruktur
Jalankan perintah berikut untuk membuat database Anda. Catatan: Pembuatan instance memerlukan waktu sekitar 5–10 menit. Anda dapat melanjutkan ke langkah berikutnya saat proses ini berjalan di latar belakang.
# 1. Define instance variables
export INSTANCE_NAME="assistant-db"
export USER_EMAIL=$(gcloud config get-value account)
# 2. Create the Cloud SQL instance
gcloud sql instances create $INSTANCE_NAME \
--database-version=POSTGRES_18 \
--tier=db-f1-micro \
--region=us-central1 \
--edition=ENTERPRISE
# 3. Create the database for our tasks
gcloud sql databases create tasks --instance=$INSTANCE_NAME
Penyediaan instance database akan memerlukan waktu beberapa menit. Ini mungkin saat yang tepat untuk minum kopi atau teh, atau memperbarui kode saat Anda menunggu prosesnya selesai. Jangan lupa untuk kembali dan menyelesaikan kontrol akses.
Mengonfigurasi kontrol akses
Sekarang kita perlu mengonfigurasi akun pengguna Anda agar memiliki akses ke database. Jalankan perintah berikut di terminal:
# change this to your favorite password
export DB_PASS="correct-horse-battery-staple"
# Create a regular database user
gcloud sql users create assistant_user \
--instance=$INSTANCE_NAME \
--password=$DB_PASS
Memperbarui konfigurasi lingkungan
ADK memuat konfigurasi dari file .env saat runtime. Perbarui lingkungan agen Anda dengan detail koneksi database.
# Retrieve the unique connection name
export DB_CONN=$(gcloud sql instances describe $INSTANCE_NAME --format='value(connectionName)')
# Append configuration to your .env file
cat <<EOF >> executive_assistant/.env
DB_CONNECTION_NAME=$DB_CONN
DB_USER=assistant_user
DB_PASSWORD=$DB_PASS
DB_NAME=tasks
EOF
Sekarang, mari kita lanjutkan untuk membuat perubahan kode.
Buat Todo Specialist (todo.py)
Mirip dengan agen riset, mari buat spesialis daftar tugas di filenya sendiri. Buat todo.py:
todo.py
import os
import uuid
import sqlalchemy
from datetime import datetime
from typing import Optional, List
from sqlalchemy import (
Column,
String,
DateTime,
Enum,
select,
delete,
update,
)
from sqlalchemy.orm import declarative_base, Session
from google.cloud.sql.connector import Connector
from google.adk.agents.llm_agent import Agent
# --- DATABASE LOGIC ---
Base = declarative_base()
connector = Connector()
def getconn():
db_connection_name = os.environ.get("DB_CONNECTION_NAME")
db_user = os.environ.get("DB_USER")
db_password = os.environ.get("DB_PASSWORD")
db_name = os.environ.get("DB_NAME", "tasks")
return connector.connect(
db_connection_name,
"pg8000",
user=db_user,
password=db_password,
db=db_name,
)
engine = sqlalchemy.create_engine(
"postgresql+pg8000://",
creator=getconn,
)
class Todo(Base):
__tablename__ = "todos"
id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
title = Column(String(255), nullable=False)
priority = Column(
Enum("high", "medium", "low", name="priority_levels"), nullable=False, default="medium"
)
due_date = Column(DateTime, nullable=True)
status = Column(Enum("pending", "done", name="status_levels"), default="pending")
created_at = Column(DateTime, default=datetime.utcnow)
def init_db():
"""Builds the table if it's missing."""
Base.metadata.create_all(bind=engine)
def add_todo(
title: str, priority: str = "medium", due_date: Optional[str] = None
) -> dict:
"""
Adds a new task to the list.
Args:
title (str): The description of the task.
priority (str): The urgency level. Must be one of: 'high', 'medium', 'low'.
due_date (str, optional): The due date in ISO format (YYYY-MM-DD or YYYY-MM-DDTHH:MM:SS).
Returns:
dict: A dictionary containing the new task's ID and a status message.
"""
init_db()
with Session(engine) as session:
due = datetime.fromisoformat(due_date) if due_date else None
item = Todo(
title=title,
priority=priority.lower(),
due_date=due,
)
session.add(item)
session.commit()
return {"id": item.id, "status": f"Task added ✅"}
def list_todos(status: str = "pending") -> list:
"""
Lists tasks from the database, optionally filtering by status.
Args:
status (str, optional): The status to filter by. 'pending', 'done', or 'all'.
"""
init_db()
with Session(engine) as session:
query = select(Todo)
s_lower = status.lower()
if s_lower != "all":
query = query.where(Todo.status == s_lower)
query = query.order_by(Todo.priority, Todo.created_at)
results = session.execute(query).scalars().all()
return [
{
"id": t.id,
"task": t.title,
"priority": t.priority,
"status": t.status,
}
for t in results
]
def complete_todo(task_id: str) -> str:
"""Marks a specific task as 'done'."""
init_db()
with Session(engine) as session:
session.execute(update(Todo).where(Todo.id == task_id).values(status="done"))
session.commit()
return f"Task {task_id} marked as done."
def delete_todo(task_id: str) -> str:
"""Permanently removes a task from the database."""
init_db()
with Session(engine) as session:
session.execute(delete(Todo).where(Todo.id == task_id))
session.commit()
return f"Task {task_id} deleted."
# --- TODO SPECIALIST AGENT ---
todo_agent = Agent(
model='gemini-2.5-flash',
name='todo_specialist',
description='A specialist agent that manages a structured SQL task list.',
instruction='''
You manage the user's task list using a PostgreSQL database.
- Use add_todo when the user wants to remember something. If no priority is mentioned, mark it as 'medium'.
- Use list_todos to show tasks.
- Use complete_todo to mark a task as finished.
- Use delete_todo to remove a task entirely.
When marking a task as complete or deleting it, if the user doesn't provide the ID,
use list_todos first to find the correct ID for the task they described.
''',
tools=[add_todo, list_todos, complete_todo, delete_todo],
)
Kode di atas bertanggung jawab atas dua hal utama: terhubung dengan database Cloud SQL dan menyediakan daftar alat untuk semua operasi daftar tugas umum, termasuk menambahkan, menghapus, dan menandainya sebagai selesai.
Karena logika ini sangat spesifik untuk agen tugas, dan kita tidak perlu memperhatikan pengelolaan terperinci ini dari sudut pandang asisten eksekutif (agen root), kita akan mengemas agen ini sebagai "AgentTool" dan bukan sub-agen.
Untuk memutuskan antara menggunakan AgentTool atau sub-agen, pertimbangkan apakah mereka perlu membagikan konteks atau tidak:
- menggunakan AgentTool saat agen Anda tidak perlu membagikan konteks dengan agen root
- menggunakan sub-agen saat Anda ingin agen Anda membagikan konteks dengan agen root
Dalam kasus agen riset, berbagi konteks bisa berguna, tetapi untuk agen daftar tugas sederhana, tidak banyak manfaatnya.
Mari kita terapkan AgentTool di agent.py.
Perbarui Root Agent (agent.py)
Sekarang, impor todo_agent ke dalam file utama Anda dan lampirkan sebagai alat:
agent.py
import os
from datetime import datetime
from google.adk.agents.llm_agent import Agent
from google.adk.tools.agent_tool import AgentTool
from google.adk.tools.preload_memory_tool import PreloadMemoryTool
from google.adk.tools.load_memory_tool import load_memory_tool
# Import our specialized sub-agents
from .research import research_agent
from .todo import todo_agent
# Callback for persistent memory storage
async def auto_save_session_to_memory_callback(callback_context):
await callback_context._invocation_context.memory_service.add_session_to_memory(
callback_context._invocation_context.session)
# --- ROOT AGENT DEFINITION ---
root_agent = Agent(
model='gemini-2.5-flash',
name='executive_assistant',
description='A professional AI Executive Assistant with memory and specialized tools.',
instruction='''
You are an elite, high-signal AI Executive Assistant.
Your goal is to help the user manage their knowledge, tasks, and research.
## Your Capabilities:
1. Memory: Use load_memory to recall personal facts or past context about the user.
2. Research: Delegate complex web-based investigations to the research_specialist.
3. Tasks: Delegate all to-do list management (adding, listing, or completing tasks) to the todo_specialist.
Always be direct and professional. If a task is successful, provide a brief confirmation.
''',
tools=[
PreloadMemoryTool(),
load_memory_tool,
AgentTool(todo_agent) # Exposes the Todo Specialist as a tool
],
sub_agents=[research_agent], # Exposes the Research Specialist for direct handover
after_agent_callback=auto_save_session_to_memory_callback,
)
Jalankan adk web lagi untuk menguji fitur baru:
uv run adk web --memory_service_uri="agentengine://$ENGINE_ID"
Kemudian, coba buat daftar tugas:

8. Menambahkan pengelolaan kalender
Terakhir, kita akan berintegrasi dengan Google Kalender agar agen dapat mengelola janji temu. Demi codelab ini, daripada memberikan akses agen ke kalender pribadi Anda, yang berpotensi berbahaya jika tidak dilakukan dengan cara yang benar, kita akan membuat kalender independen untuk dikelola agen.
Pertama, kita akan membuat Akun Layanan khusus untuk bertindak sebagai identitas agen. Kemudian, kita akan membuat kalender agen secara terprogram menggunakan akun layanan.
Sediakan akun layanan
Buka terminal Anda dan jalankan perintah ini untuk membuat identitas dan memberikan izin kepada akun pribadi Anda untuk meniru identitas tersebut:
export SA_NAME="ea-agent"
export SA_EMAIL="${SA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com"
# Create the service account
gcloud iam service-accounts create $SA_NAME \
--display-name="Executive Assistant Agent"
# Allow your local user to impersonate it
gcloud iam service-accounts add-iam-policy-binding $SA_EMAIL \
--member="user:$(gcloud config get-value account)" \
--role="roles/iam.serviceAccountTokenCreator"
# Save it to the agent's environment
echo "SERVICE_ACCOUNT_EMAIL=$SA_EMAIL" >> executive_assistant/.env
Membuat kalender secara terprogram
Mari tulis skrip untuk memberi tahu Akun Layanan agar membuat kalender. Buat file baru bernama setup_calendar.py di root project Anda (bersama setup_memory.py):
setup_calendar.py
import os
import google.auth
from googleapiclient.discovery import build
from google.auth.transport.requests import Request
from google.auth import impersonated_credentials
from dotenv import load_dotenv
load_dotenv('executive_assistant/.env')
SA_EMAIL = os.environ.get("SERVICE_ACCOUNT_EMAIL")
def setup_sa_calendar():
print(f"Authenticating to impersonate {SA_EMAIL}...")
# 1. Base credentials
creds, _ = google.auth.default(scopes=["https://www.googleapis.com/auth/cloud-platform"])
creds.refresh(Request())
# 2. Impersonate the Service Account
impersonated = impersonated_credentials.Credentials(
source_credentials=creds,
target_principal=SA_EMAIL,
target_scopes=["https://www.googleapis.com/auth/calendar"],
)
service = build("calendar", "v3", credentials=impersonated)
# 3. Create the calendar
print("Creating independent Service Account calendar...")
calendar = service.calendars().insert(body={
"summary": "AI Assistant (SA Owned)",
"description": "An independent calendar managed purely by the AI."
}).execute()
calendar_id = calendar['id']
# 4. Save the ID
with open("executive_assistant/.env", "a") as f:
f.write(f"\nCALENDAR_ID={calendar_id}\n")
print(f"Setup complete! CALENDAR_ID {calendar_id} added to .env")
if __name__ == "__main__":
setup_sa_calendar()
Jalankan skrip dari terminal Anda:
uv run python setup_calendar.py
Buat Calendar Specialist (calendar.py)
Sekarang, mari kita fokus pada spesialis kalender. Kami akan melengkapi agen ini dengan serangkaian lengkap alat kalender: mencantumkan, membuat, memperbarui, menghapus, dan bahkan fitur "tambah cepat" yang memahami bahasa alami.
Salin kode di bawah ke calendar.py.
calendar.py
import os
from datetime import datetime, timedelta, timezone
import google.auth
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from google.adk.agents.llm_agent import Agent
def _get_calendar_service():
"""Build the Google Calendar API service using Service Account Impersonation."""
from google.auth.transport.requests import Request
from google.auth import impersonated_credentials
target_principal = os.environ.get("SERVICE_ACCOUNT_EMAIL")
if not target_principal:
raise ValueError("SERVICE_ACCOUNT_EMAIL environment variable is missing.")
base_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
creds, _ = google.auth.default(scopes=base_scopes)
creds.refresh(Request())
target_scopes = ["https://www.googleapis.com/auth/calendar"]
impersonated = impersonated_credentials.Credentials(
source_credentials=creds,
target_principal=target_principal,
target_scopes=target_scopes,
)
return build("calendar", "v3", credentials=impersonated)
def _format_event(event: dict) -> dict:
"""Format a raw Calendar API event into a clean dict for the LLM."""
start = event.get("start", {})
end = event.get("end", {})
return {
"id": event.get("id"),
"title": event.get("summary", "(No title)"),
"start": start.get("dateTime", start.get("date")),
"end": end.get("dateTime", end.get("date")),
"location": event.get("location", ""),
"description": event.get("description", ""),
"attendees": [
{"email": a["email"], "status": a.get("responseStatus", "unknown")}
for a in event.get("attendees", [])
],
"link": event.get("htmlLink", ""),
"conference_link": (
event.get("conferenceData", {}).get("entryPoints", [{}])[0].get("uri", "")
if event.get("conferenceData")
else ""
),
"status": event.get("status", ""),
}
def list_events(days_ahead: int = 7) -> dict:
"""List upcoming calendar events."""
calendar_id = os.environ.get("CALENDAR_ID")
try:
service = _get_calendar_service()
now = datetime.now(timezone.utc).isoformat()
end = (datetime.now(timezone.utc) + timedelta(days=days_ahead)).isoformat()
events_result = service.events().list(
calendarId=calendar_id, timeMin=now, timeMax=end,
maxResults=50, singleEvents=True, orderBy="startTime"
).execute()
events = events_result.get("items", [])
if not events:
return {"status": "success", "count": 0, "events": []}
return {"status": "success", "count": len(events), "events": [_format_event(e) for e in events]}
except HttpError as e:
return {"status": "error", "message": f"Calendar API error: {e}"}
def create_event(title: str, start_time: str, end_time: str, description: str = "", location: str = "", attendees: str = "", add_google_meet: bool = False) -> dict:
"""Create a new calendar event."""
calendar_id = os.environ.get("CALENDAR_ID")
try:
service = _get_calendar_service()
event_body = {
"summary": title,
"start": {"dateTime": start_time},
"end": {"dateTime": end_time},
}
if description: event_body["description"] = description
if location: event_body["location"] = location
if attendees:
email_list = [e.strip() for e in attendees.split(",") if e.strip()]
event_body["attendees"] = [{"email": e} for e in email_list]
conference_version = 0
if add_google_meet:
event_body["conferenceData"] = {
"createRequest": {"requestId": f"event-{datetime.now().strftime('%Y%m%d%H%M%S')}", "conferenceSolutionKey": {"type": "hangoutsMeet"}}
}
conference_version = 1
event = service.events().insert(calendarId=calendar_id, body=event_body, conferenceDataVersion=conference_version).execute()
return {"status": "success", "message": f"Event created ✅", "event": _format_event(event)}
except HttpError as e:
return {"status": "error", "message": f"Calendar API error: {e}"}
def update_event(event_id: str, title: str = "", start_time: str = "", end_time: str = "", description: str = "") -> dict:
"""Update an existing calendar event."""
calendar_id = os.environ.get("CALENDAR_ID")
try:
service = _get_calendar_service()
patch_body = {}
if title: patch_body["summary"] = title
if start_time: patch_body["start"] = {"dateTime": start_time}
if end_time: patch_body["end"] = {"dateTime": end_time}
if description: patch_body["description"] = description
if not patch_body: return {"status": "error", "message": "No fields to update."}
event = service.events().patch(calendarId=calendar_id, eventId=event_id, body=patch_body).execute()
return {"status": "success", "message": "Event updated ✅", "event": _format_event(event)}
except HttpError as e:
return {"status": "error", "message": f"Calendar API error: {e}"}
def delete_event(event_id: str) -> dict:
"""Delete a calendar event by its ID."""
calendar_id = os.environ.get("CALENDAR_ID")
try:
service = _get_calendar_service()
service.events().delete(calendarId=calendar_id, eventId=event_id).execute()
return {"status": "success", "message": f"Event '{event_id}' deleted ✅"}
except HttpError as e:
return {"status": "error", "message": f"Calendar API error: {e}"}
def quick_add_event(text: str) -> dict:
"""Create an event using natural language (e.g. 'Lunch with Sarah next Monday noon')."""
calendar_id = os.environ.get("CALENDAR_ID")
try:
service = _get_calendar_service()
event = service.events().quickAdd(calendarId=calendar_id, text=text).execute()
return {"status": "success", "message": "Event created from text ✅", "event": _format_event(event)}
except HttpError as e:
return {"status": "error", "message": f"Calendar API error: {e}"}
calendar_agent = Agent(
model='gemini-2.5-flash',
name='calendar_specialist',
description='Manages the user schedule and calendar events.',
instruction='''
You manage the user's Google Calendar.
- Use list_events to check the schedule.
- Use quick_add_event for simple, conversational scheduling requests (e.g., "Lunch tomorrow at noon").
- Use create_event for complex meetings that require attendees, specific durations, or Google Meet links.
- Use update_event to change details of an existing event.
- Use delete_event to cancel or remove an event.
CRITICAL: For update_event and delete_event, you must provide the exact `event_id`.
If the user does not provide the ID, you MUST call list_events first to find the correct `event_id` before attempting the update or deletion.
Always use the current date/time context provided by the root agent to resolve relative dates like "tomorrow".
''',
tools=[list_events, create_event, update_event, delete_event, quick_add_event],
)
Menyelesaikan Agen Root (agent.py)
Perbarui file agent.py Anda dengan kode di bawah:
agent.py
import os
from datetime import datetime
from zoneinfo import ZoneInfo
from google.adk.agents.llm_agent import Agent
from google.adk.tools.agent_tool import AgentTool
from google.adk.tools.preload_memory_tool import PreloadMemoryTool
from google.adk.tools.load_memory_tool import load_memory_tool
# Import all our specialized sub-agents
from .research import research_agent
from .todo import todo_agent
from .calendar import calendar_agent
import tzlocal
# Automatically detect the local system timezone
TIMEZONE = tzlocal.get_localzone_name()
# Callback for persistent memory storage
async def auto_save_session_to_memory_callback(callback_context):
await callback_context._invocation_context.memory_service.add_session_to_memory(
callback_context._invocation_context.session)
# Callback to inject the current time into the prompt
async def setup_agent_context(callback_context, **kwargs):
now = datetime.now(ZoneInfo(TIMEZONE))
callback_context.state["current_time"] = now.strftime("%A, %Y-%m-%d %I:%M %p")
callback_context.state["timezone"] = TIMEZONE
# --- ROOT AGENT DEFINITION ---
root_agent = Agent(
model='gemini-2.5-flash',
name='executive_assistant',
description='A professional AI Executive Assistant with memory and specialized tools.',
instruction='''
You are an elite, high-signal AI Executive Assistant.
Your goal is to help the user manage their knowledge, tasks, research, and schedule.
## Your Capabilities:
1. Memory: Use load_memory to recall personal facts.
2. Research: Delegate complex web investigations to the research_specialist.
3. Tasks: Delegate all to-do list management to the todo_specialist.
4. Scheduling: Delegate all calendar queries to the calendar_specialist.
## 🕒 Current State
- Time: {current_time?}
- Timezone: {timezone?}
Always be direct and professional.
''',
tools=[
PreloadMemoryTool(),
load_memory_tool,
AgentTool(todo_agent),
AgentTool(calendar_agent)
],
sub_agents=[research_agent],
before_agent_callback=[setup_agent_context],
after_agent_callback=[auto_save_session_to_memory_callback],
)
Perhatikan bahwa, selain alat kalender, kami juga menambahkan fungsi panggilan balik sebelum agen baru: setup_agent_context. Fungsi ini memberikan informasi tentang tanggal, waktu, dan zona waktu saat ini kepada agen sehingga agen dapat menggunakan kalender secara lebih efisien. Alat ini berfungsi dengan menyetel variabel status sesi, jenis memori agen yang berbeda yang dirancang untuk persistensi jangka pendek.
Jalankan adk web untuk terakhir kalinya guna menguji agen yang lengkap.
uv run adk web --memory_service_uri="agentengine://$ENGINE_ID"
Anda dapat memeriksa status sesi di tab status di UI developer:

Sekarang Anda memiliki agen yang dapat melacak acara kalender, daftar tugas, melakukan riset, dan memiliki memori jangka panjang.
Membersihkan setelah lab
9. Kesimpulan
Selamat! Anda telah berhasil merancang asisten eksekutif AI multifungsi melalui 5 tahap evolusi.
Yang kita bahas
- Menyediakan infrastruktur untuk Agen AI.
- Menerapkan memori persisten dan sub-agen khusus menggunakan fungsi bawaan ADK.
- Mengintegrasikan database eksternal dan API produktivitas.
Langkah Berikutnya
Anda dapat melanjutkan perjalanan pembelajaran dengan menjelajahi codelab lain di platform ini, atau melakukan peningkatan pada asisten eksekutif Anda sendiri.
Jika Anda memerlukan beberapa ide untuk peningkatan, Anda dapat mencoba:
- Terapkan pemadatan peristiwa untuk mengoptimalkan performa percakapan panjang.
- Menambahkan layanan artefak agar agen dapat membuat catatan untuk Anda dan menyimpannya sebagai file
- Deploy agen Anda sebagai layanan backend menggunakan Google Cloud Run.
Setelah selesai menguji, jangan lupa untuk membersihkan lingkungan agar tidak menimbulkan tagihan yang tidak terduga ke akun penagihan Anda.
Selamat membuat kode!