Bagaimana ADK Agent Mengingat: Sessions, Events, dan Scoped-State
Kebanyakan demo AI agent punya memori yang pendek. Agent-nya bisa melacak percakapan, mengingat preferensi kita, membangun konteks dari…
Bagaimana ADK Agent Mengingat: Sessions, Events, dan Scoped-State
Kebanyakan demo AI agent punya memori yang pendek. Agent-nya bisa melacak percakapan, mengingat preferensi kita, membangun konteks dari setiap giliran percakapan, tapi begitu prosesnya ter-restart, semuanya hilang. Di stateless runtime seperti Cloud Run, ini terjadi setiap kali kontainernya di-recycle. Agent mulai dari nol tanpa ingatan interaksi sebelumnya.
Google’s Agent Development Kit (ADK) punya solusi terencana untuk masalah ini. ADK memisahkan memori agent ke dalam layer-layer berbeda dengan lifetime dan scope yang berbeda-beda, dan memungkinkan kita mengganti storage backend tanpa menyentuh kode agent sama sekali. Sebelum masuk ke contoh implementasi, ada baiknya kita pahami dulu model dasarnya.
Sessions: Kontainer untuk Percakapan
Setiap percakapan di ADK hidup di dalam object Session. Session dibuat saat user memulai percakapan baru, dan melacak dua hal yang berbeda: **events dan `state`**. Keduanya adalah struktur data yang secara fundamental berbeda dan punya tujuan masing-masing. Memahami perbedaan ini adalah kunci untuk membangun agent yang mengingat hal yang tepat dengan cara yang tepat.
Events: Transkrip yang Immutable
Events adalah log kronologis dari semua yang terjadi dalam sebuah percakapan. Setiap pesan user, setiap respon Dari agent, setiap tool call beserta return value-nya dicatat sebagai Event dan di-append ke daftar events milik session. Events bersifat immutable: sekali dicatat, tidak pernah berubah.
Setiap event membawa metadata yang menunjukkan apa yang direpresentasikannya. Field **event.author mengidentifikasi siapa yang menghasilkan event tersebut (user atau nama agent seperti cafe_concierge). Array `event.content.parts** menyimpan payload sebenarnya, yang bisa berupa text, function calls, atau function responses. Danevent.actions` membawa side effects seperti perubahan state dan sinyal control flow.
Saat memproses events secara programmatic (untuk custom runner atau logging), kita bisa mengidentifikasi tipe setiap event:
# Pseudocode: Basic event identification (Python)
async for event in runner.run_async(...):
print(f"Event from: {event.author}")
if event.content and event.content.parts:
if event.get_function_calls():
print(" Type: Tool Call Request")
elif event.get_function_responses():
print(" Type: Tool Result")
elif event.content.parts[0].text:
if event.partial:
print(" Type: Streaming Text Chunk")
else:
print(" Type: Complete Text Message")
elif event.actions and (event.actions.state_delta or event.actions.artifact_delta):
print(" Type: State/Artifact Update")
else:
print(" Type: Control Signal or Other")
Di ADK dev UI, semua ini divisualisasikan. Setiap event menampilkan author, tipe, dan isinya. Klik event tool_call untuk melihat nama function dan argument-nya; klik tool_response yang bersesuaian untuk melihat return value-nya.
State: Scratchpad yang Mutable
Kalau events adalah transkrip lengkap, state adalah data terstruktur berbentuk key-value yang dibaca dan ditulis agent selama percakapan berlangsung. Berbeda dengan events, state bersifat mutable: nilainya berubah seiring percakapan berjalan. State adalah tempat agent menyimpan data terstruktur yang dibutuhkan untuk bertindak, seperti pesanan yang sedang berjalan, preferensi diet pelanggan, atau total harga.
Tools membaca dan menulis state melalui ToolContext, sebuah object yang di-inject ADK secara otomatis ke function tool mana pun yang mendeklarasikannya sebagai parameter. Kita tidak perlu membuatnya sendiri.
Koneksi antara events dan state ini penting. Ketika tool menulis ke tool_context.state, ADK mencatat perubahan tersebut sebagai state_delta di dalam event:
state_delta: {"user:dietary_preferences": ["lactose intolerant"]}
Setiap perubahan state bisa ditelusuri kembali ke event spesifik yang menyebabkannya. Kalau kita perlu men-debug kenapa sebuah preferensi di-set atau pesanan diubah, event log menunjukkan dengan tepat kapan dan apa yang memicunya.
State Prefixes: Mengontrol Scope dan Lifetime
State key menggunakan prefix untuk mengontrol sejauh mana data bisa diakses:
| Prefix | Scope | Survives restart? (with DB) |
|----------|----------------------------|-----------------------------|
| *(none)* | Current session only | Yes |
| `user:` | All sessions for this user | Yes |
| `app:` | All sessions, all users | Yes |
| `temp:` | Current invocation only | No |
Di kode, ini tidak terlihat: baik session-scoped maupun user-scoped state menggunakan dictionary tool_context.state yang sama. Prefix pada nama key-lah yang mengontrol perilakunya. Key seperti current_order (tanpa prefix) hanya ada di session saat ini dan hilang saat percakapan berakhir. Key seperti user:dietary_preferences di-share ke semua session untuk user tersebut.
Contoh Penerapan: Cafe Concierge
Untuk melihat konsep-konsep ini bekerja, saya membuat agent cafe concierge, barista ramah yang menerima pesanan kopi dan mengingat preferensi diet. Agent ini menggunakan dua state scope:
current_order(session-scoped): melacak pesanan dalam satu percakapanuser:dietary_preferences(user-scoped): menyimpan preferensi diet lintas semua percakapan
Berikut tool untuk menempatkan pesanan, yang menulis ke session-scoped state:
def place_order(tool_context: ToolContext, items: list[str]) -> dict:
"""Places an order for the specified menu items.
Use this tool when the customer confirms they want to order something.
Args:
tool_context: Provided automatically by ADK.
items: A list of menu item names the customer wants to order.
"""
valid_items = []
invalid_items = []
total = 0.0
for item in items:
item_lower = item.lower()
if item_lower in CAFE_MENU:
valid_items.append(item_lower)
total += CAFE_MENU[item_lower]["price"]
else:
invalid_items.append(item)
if not valid_items:
return {"error": f"None of these items are on our menu: {invalid_items}"}
order = {"items": valid_items, "total": round(total, 2)}
tool_context.state["current_order"] = order
result = {"order": order}
if invalid_items:
result["warning"] = f"These items are not on our menu: {invalid_items}"
return result
Dan berikut tool preferensi, yang menulis ke user-scoped state lewat prefix user::
def set_dietary_preference(tool_context: ToolContext, preference: str) -> dict:
"""Saves a dietary preference that persists across all conversations.
Use this tool when the customer mentions a dietary restriction or
preference (e.g., "I'm vegan", "I'm lactose intolerant",
"I have a nut allergy").
Args:
tool_context: Provided automatically by ADK.
preference: The dietary preference to save (e.g., "vegan",
"lactose intolerant", "nut allergy").
"""
existing = tool_context.state.get("user:dietary_preferences", [])
if not isinstance(existing, list):
existing = []
preference_lower = preference.lower().strip()
if preference_lower not in existing:
existing.append(preference_lower)
tool_context.state["user:dietary_preferences"] = existing
return {
"saved": preference_lower,
"all_preferences": existing,
}
System prompt agent juga bisa me-reference state secara langsung menggunakan template: {user:dietary_preferences?}. ADK meng-inject nilai saat ini di runtime, dan suffix ? mencegah error ketika key belum ada.
Persistensi Session
Jebakan Local Storage
Secara default, ADK menyimpan semua data session di file SQLite lokal di ***{agent_module}/.adk/session.db***. Ini berfungsi selama development, tapi datanya hilang saat kita menghapus file tersebut atau deploy ke stateless environment. Di Cloud Run, setiap container restart menghapus local filesystem.
Migrasi ke Cloud SQL
Jika kita mengetes ini menggunakan command adk web, perubahannya tidak memerlukan perubahan kode agent sama sekali. DatabaseSessionService milik ADK mengambil alih saat kita memberikan connection URI:
uv run adk web --session_service_uri postgresql+asyncpg://postgres:${DB_PASSWORD}@127.0.0.1:5432/${DB_NAME}
Satu flag. Logika agent, tools, dan operasi state semuanya identik. ADK menangani pembuatan schema secara otomatis, membuat table untuk sessions, events, app states, dan user states:
List of relations
Schema | Name | Type | Owner
--------+-----------------------+-------+----------
public | adk_internal_metadata | table | postgres
public | app_states | table | postgres
public | events | table | postgres
public | sessions | table | postgres
public | user_states | table | postgres
(5 rows)
Setelah me-restart agent dengan backing Cloud SQL, user-scoped state seperti preferensi diet bertahan lintas restart dan session baru. Session-scoped state seperti current_order tetap di-reset setiap percakapan baru, yang memang perilaku yang diharapkan.
Yuk Coba Oprek Sendiri!
Kalau kalian tertarik untuk mencoba dan bereksperimen terkait dengan konsep ingatan agent ini, walkthrough hands-on lengkap, termasuk provisioning Cloud SQL, setup proxy, dan testing lintas session, tersedia di codelab ini: Building Persistent AI Agents with ADK and CloudSQL . Tidak perlu khawatir apabila kalian merasa baru dengan GCP ataupun tidak punya akun billing, kamin sudah mempersiapkan langkah-langkat detail dan memberikan akun billing trial agar kalian visa mencoba langsung! Selamat bereksplorasi!
메타데이터
- post_id
- aff84fae79ca
- slug
- bagaimana-adk-agent-mengingat-sessions-events-dan-scoped-state-aff84fae79ca
- url
- https://medium.com/google-cloud-indonesia/bagaimana-adk-agent-mengingat-sessions-events-dan-scoped-state-aff84fae79ca
- canonical_url
- https://medium.com/google-cloud-indonesia/bagaimana-adk-agent-mengingat-sessions-events-dan-scoped-state-aff84fae79ca
- author_url
- https://medium.com/@alphinside
- status
- ok
- fetched_at
- 2026-06-15 20:49:13