Zep: Bikin AI Agent Punya Memori Jangka Panjang dengan Temporal Knowledge Graph
Temen-temen, pernah ngobrol sama chatbot yang tiap kali kamu buka percakapan baru dia lupa total sama siapa kamu? Kamu udah cerita panjang lebar soal preferensi, nama proyek, sampai detail teknis yang penting, eh besoknya dia nanya lagi kayak kenalan pertama kali. Nyebelin banget kan. Nah, masalah ini muncul karena secara default Large Language Model (LLM) itu stateless. Dia cuma tau apa yang ada di dalam satu window konteks. Begitu percakapan panjang atau sesi baru dibuka, semua ingatan itu hilang.
Di tutorial ini aku mau ngajak kalian kenalan sama Zep, sebuah memory layer buat AI agent yang menurutku ngerapihin banget cara kita ngasih "ingatan" ke agent. Bukan sekadar nyimpen riwayat chat mentah-mentah, tapi Zep bikin sesuatu yang disebut temporal knowledge graph. Jadi Zep bakal ngambil fakta-fakta penting dari percakapan, nyusun jadi graph relasi, plus nyatet kapan fakta itu berlaku dan kapan berubah. Waktu agent-mu butuh konteks, Zep ngasih ringkasan yang relevan buat dimasukin ke prompt. Praktis dan hemat token.
Aku bakal bahas dari nol banget: kenapa memori itu penting buat agent, gimana daftar Zep Cloud dan ambil API key, cara install, bikin user dan session, nambahin pesan, ngambil memori dan konteks, nyari fakta di graph, sampai akhirnya kita rakit jadi satu loop chatbot sederhana yang beneran inget kamu. Semua contoh pakai Python biar gampang diikutin. Yuk kita mulai.
Introduction
Kenapa memori itu penting buat AI agent
Coba bayangin kamu punya asisten pribadi manusia. Nilai plus utamanya bukan cuma dia bisa jawab pertanyaan, tapi dia inget konteks kamu. Dia tau kamu alergi kacang, tau kamu lagi ngerjain proyek migrasi database, tau kamu lebih suka penjelasan yang to the point. Asisten yang inget hal-hal ini terasa jauh lebih pintar dan personal dibanding yang harus dijelasin ulang tiap hari.
AI agent yang kita bangun juga gitu. Tanpa memori, agent kita cuma reaktif dan generik. Dengan memori jangka panjang, agent bisa:
- Personalisasi jawaban berdasarkan histori interaksi user.
- Ngelanjutin tugas multi-sesi tanpa user harus ngulang konteks.
- Ngambil keputusan yang lebih konsisten karena tau preferensi dan fakta sebelumnya.
- Ngurangin biaya token, karena kita nggak perlu nyeret seluruh transkrip panjang ke tiap request.
Poin terakhir itu sering diremehin temen-temen. Kalau kamu naif dan tiap kali ngirim semua histori percakapan ke LLM, token cost kamu bakal meledak dan latency makin lama seiring percakapan makin panjang. Belum lagi ada limit context window. Jadi kita butuh sistem yang cerdas milih mana yang relevan, bukan asal timbun.
Apa itu Zep dan bedanya sama nyimpen histori biasa
Cara paling primitif buat ngasih memori ke agent adalah nyimpen seluruh array pesan di database, terus di-load semua tiap percakapan. Ini gampang tapi nggak scalable. Pendekatan kedua yang lumayan populer adalah RAG di atas riwayat chat: kita embed tiap pesan, simpan ke vector store, terus retrieve pesan yang mirip. Ini lebih baik, tapi masih ngambil potongan mentah percakapan tanpa ngerti fakta apa yang sebenarnya penting.
Zep ngambil pendekatan yang lebih pinter. Di belakang layar, Zep ngejalanin proses yang mereka sebut ekstraksi dan pembangunan knowledge graph. Setiap kali kamu nambahin pesan ke Zep, sistemnya bakal:
Kata "temporal" di sini kunci banget. Dunia berubah, preferensi user berubah, status proyek berubah. Zep nyimpen dimensi waktu ini jadi agent-mu nggak cuma tau fakta terbaru, tapi juga bisa tau riwayat perubahan fakta. Ini yang bikin Zep beda dari sekadar key-value store atau vector search biasa.
Waktu kita minta konteks ke Zep buat sebuah sesi, dia ngasih blok teks ringkas berisi fakta-fakta relevan yang tinggal kita tempel ke system prompt. Jadi agent kita dapet "ingatan" yang padat dan relevan tanpa harus baca ribuan token transkrip.
Konsep inti yang perlu kamu pahami
Sebelum ngoding, ada beberapa istilah yang bakal sering muncul:
- User: entitas yang mewakili orang atau akun. Semua memori bisa dikaitin ke satu user.
- Session/Thread: satu utas percakapan milik user. Satu user bisa punya banyak session.
- Message: satu pesan dalam thread, punya role (user atau assistant) dan konten.
- Memory/Context: ringkasan konteks yang Zep hasilkan buat diinjeksi ke prompt.
- Graph: kumpulan fakta (edges) dan entitas (nodes) yang bisa kita cari.
Oke, konsepnya udah kepegang. Sekarang kita masuk ke bagian teknis.
Instalasi
Daftar Zep Cloud dan ambil API key
Zep punya dua opsi: self-hosted (open source) dan Zep Cloud (managed). Buat tutorial ini aku pakai Zep Cloud karena paling cepet buat mulai dan kita nggak perlu ngurus infrastruktur graph database sendiri. Langkahnya gampang:
API key inilah yang bakal kita pakai buat otentikasi dari kode Python. Jangan pernah hardcode key ini di kode yang kamu commit ke Git. Nanti aku tunjukin cara amannya pakai environment variable.
Install library
Zep punya SDK resmi buat Python. Install-nya satu baris aja:
pip install zep-cloud
Aku saranin kamu bikin virtual environment dulu biar rapi dan nggak nabrak dependency proyek lain:
python -m venv venv
source venv/bin/activate # di Windows: venv\Scripts\activate
pip install zep-cloud python-dotenv
Aku sekalian install python-dotenv biar gampang baca API key dari file .env. Bikin file .env di folder proyek kamu:
ZEPAPIKEY=zepkeypunyamudisini
Terus jangan lupa masukin .env ke .gitignore biar nggak kebawa ke repository.
Inisialisasi client
Sekarang kita tes koneksi. Bikin file zepsetup.py:
import os
from dotenv import loaddotenv
from zepcloud.client import Zep
loaddotenv()
client = Zep(apikey=os.environ.get("ZEPAPIKEY"))
print("Zep client berhasil diinisialisasi")
Kalau kamu jalanin dan nggak ada error, berarti library kepasang bener dan key kebaca. Zep juga nyediain versi async lewat AsyncZep kalau kamu bangun aplikasi berbasis asyncio. Buat tutorial ini kita fokus ke versi sinkron dulu biar gampang dicerna.
Satu catatan penting: API Zep kadang berevolusi antar versi mayor. Contoh-contoh di tutorial ini nyoba pakai pola yang umum dan stabil, tapi kalau kamu nemu nama method sedikit beda, selalu cek dokumentasi resmi versi yang kamu pasang. Cara cepat lihat method yang tersedia:
print([m for m in dir(client) if not m.startswith("")])
Itu bakal nampilin resource utama kayak user, thread, graph, dan sebagainya.
Basic Usage
Bikin user
Langkah pertama yang logis adalah bikin user. User ini wadah buat semua memori orang tertentu. Kita kasih userid yang unik, misalnya ID dari sistem kita sendiri, plus metadata opsional kayak email dan nama.
import os
from dotenv import loaddotenv
from zepcloud.client import Zep
loaddotenv()
client = Zep(apikey=os.environ.get("ZEPAPIKEY"))
userid = "user-ruby-001"
client.user.add(
userid=userid,
email="ruby@example.com",
firstname="Ruby",
lastname="Abdullah",
metadata={"plan": "pro", "bahasa": "id"},
)
print(f"User {userid} berhasil dibuat")
userid sebaiknya deterministik dan konsisten dari sistemmu, jadi kamu gampang narik ulang memori orang yang sama. Metadata bebas kamu isi apa aja yang relevan, misalnya tier langganan atau preferensi bahasa.
Kalau mau baca ulang data user:
user = client.user.get(userid="user-ruby-001")
print(user.first
name, user.email, user.metadata)
Bikin session atau thread
Setelah punya user, kita bikin thread percakapan. Anggap satu thread itu satu utas obrolan. Kamu bisa bikin thread baru tiap sesi harian, atau satu thread panjang buat satu topik. Yang penting tiap thread dikaitin ke user.
import uuid
threadid = f"thread-{uuid.uuid4().hex[:8]}"
client.thread.create(
threadid=threadid,
userid="user-ruby-001",
)
print(f"Thread {threadid} dibuat untuk user-ruby-001")
Aku pakai uuid biar threadid-nya unik. Di aplikasi beneran, biasanya threadid ini kamu simpan di database kamu sendiri, dikaitin ke sesi chat user.
Nambahin pesan ke thread
Nah ini inti interaksinya. Tiap kali user ngomong atau assistant jawab, kita simpan pesannya ke Zep. Zep bakal nyimpen riwayat mentah sekaligus mulai proses ekstraksi fakta di belakang layar.
from zepcloud.types import Message
messages = [
Message(
role="user",
name="Ruby",
content="Halo, aku Ruby. Aku lagi ngerjain proyek migrasi dari MySQL ke PostgreSQL.",
),
Message(
role="assistant",
name="Assistant",
content="Halo Ruby, senang bisa bantu proyek migrasi database kamu.",
),
]
client.thread.addmessages(
threadid=threadid,
messages=messages,
)
print("Pesan berhasil ditambahkan")
Perhatiin tiap pesan punya role (user atau assistant) dan content. Field name opsional tapi berguna buat nandain siapa yang ngomong. Setelah kamu add pesan kayak gini, Zep bakal mulai ngekstrak fakta, contohnya "Ruby sedang mengerjakan migrasi MySQL ke PostgreSQL". Fakta ini yang nanti bisa kita ambil lagi.
Perlu diinget, ekstraksi graph itu proses asinkron di sisi server Zep. Jadi kadang butuh beberapa detik sebelum fakta baru muncul di hasil pencarian graph. Buat riwayat pesan mentah sih langsung kebaca.
Ngambil memori dan konteks
Sekarang bagian yang paling kepake: narik konteks buat diinjeksi ke prompt LLM. Zep nyediain method buat ngambil ringkasan konteks sebuah thread. Hasilnya udah dalam bentuk blok teks yang siap ditempel ke system prompt.
memory = client.thread.getusercontext(threadid=threadid)
print(memory.context)
memory.context isinya ringkasan padat berisi fakta relevan tentang user dan percakapan. Contoh outputnya kira-kira kayak deskripsi terstruktur: siapa user-nya, fakta apa yang berlaku, preferensi apa yang tercatat. Blok teks inilah amunisi kita buat bikin agent yang "inget".
Kalau kamu mau ngambil daftar pesan mentah dari sebuah thread, biasanya ada method get yang ngembaliin list message beserta metadata:
threaddata = client.thread.get(threadid=threadid)
for msg in threaddata.messages:
print(f"{msg.role}: {msg.content}")
Jadi ada dua level: level mentah (list pesan) dan level konteks yang udah dicerna (ringkasan fakta). Buat feed ke LLM, hampir selalu kita pakai level konteks karena lebih padat dan relevan.
Rakit jadi chatbot sederhana
Yuk kita gabung semua jadi satu loop chatbot yang beneran inget user. Di contoh ini aku pakai OpenAI sebagai LLM-nya, tapi konsepnya sama buat provider mana pun. Alurnya: ambil konteks dari Zep, tempel ke system prompt, panggil LLM, terus simpan balik pesan user dan jawaban assistant ke Zep.
import os
from dotenv import loaddotenv
from zepcloud.client import Zep
from zepcloud.types import Message
from openai import OpenAI
loaddotenv()
zep = Zep(apikey=os.environ.get("ZEPAPIKEY"))
oai = OpenAI(apikey=os.environ.get("OPENAIAPIKEY"))
USERID = "user-ruby-001"
THREADID = "thread-chatbot-demo"
def ensuresetup():
try:
zep.user.add(userid=USERID, firstname="Ruby")
except Exception:
pass
try:
zep.thread.create(threadid=THREADID, userid=USERID)
except Exception:
pass
def chat(userinput: str) -> str:
# 1. Ambil konteks memori dari Zep
memory = zep.thread.getusercontext(threadid=THREADID)
contextblock = memory.context or "Belum ada konteks sebelumnya."
systemprompt = (
"Kamu asisten yang ramah dan inget detail tentang user. "
"Gunakan konteks memori berikut untuk menjawab secara personal.\n\n"
f"KONTEKS MEMORI:\n{contextblock}"
)
# 2. Panggil LLM
completion = oai.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": systemprompt},
{"role": "user", "content": userinput},
],
)
answer = completion.choices[0].message.content
# 3. Simpan pesan balik ke Zep
zep.thread.addmessages(
threadid=THREADID,
messages=[
Message(role="user", name="Ruby", content=userinput),
Message(role="assistant", name="Assistant", content=answer),
],
)
return answer
if name == "main":
ensuresetup()
print("Chatbot siap. Ketik 'exit' untuk keluar.")
while True:
userinput = input("Kamu: ")
if userinput.strip().lower() in ("exit", "quit"):
break
response = chat(userinput)
print(f"Assistant: {response}")
Coba jalanin dan ngobrol beberapa kali. Sesi pertama, bilang aja preferensi kamu, misal "aku lebih suka jawaban singkat" atau "panggil aku Bang Ruby". Terus di giliran-giliran berikutnya, Zep bakal udah nangkep fakta itu dan menyuntikkannya lewat contextblock, jadi assistant otomatis nyesuain. Yang keren, kalau kamu tutup program dan buka lagi dengan THREADID yang sama, memorinya masih ada. Itulah inti dari memori jangka panjang.
Advanced Usage
Nyari fakta di knowledge graph
Selain narik konteks per thread, Zep ngasih kita akses langsung ke knowledge graph buat pencarian yang lebih spesifik. Ini berguna banget kalau agent-mu butuh nyari fakta tertentu lintas percakapan, bukan cuma ringkasan thread aktif.
Ada dua tipe pencarian utama: nyari edges (fakta atau relasi) dan nyari nodes (entitas). Contoh nyari fakta relevan buat sebuah query:
results = zep.graph.search(
userid="user-ruby-001",
query="preferensi database dan bahasa pemrograman",
scope="edges",
limit=5,
)
for edge in results.edges or []:
print("Fakta:", edge.fact)
Di sini kita nyari di seluruh graph milik user, bukan cuma satu thread. scope="edges" artinya kita cari fakta/relasi. Kalau kita mau cari entitas, ganti jadi scope="nodes":
noderesults = zep.graph.search(
user
id="user-ruby-001",
query="proyek yang sedang dikerjakan",
scope="nodes",
limit=5,
)
for node in noderesults.nodes or []:
print("Entitas:", node.name, "-", node.summary)
Pencarian graph ini yang bikin Zep terasa kayak "otak" beneran. Agent-mu bisa nanya "apa aja preferensi user soal komunikasi?" terus dapet fakta-fakta relevan yang udah dikumpulin dari banyak percakapan sebelumnya.
Nambahin data langsung ke graph
Memori nggak harus selalu datang dari percakapan chat. Kadang kamu punya data terstruktur atau dokumen yang mau kamu masukin ke memori user, misal profil dari CRM atau catatan meeting. Zep ngizinin kita nambahin data mentah langsung ke graph.
# Menambahkan teks bebas sebagai sumber fakta
zep.graph.add(
userid="user-ruby-001",
type="text",
data="Ruby adalah lead engineer di tim data. Dia ngatur 3 orang junior "
"dan bertanggung jawab atas pipeline ETL harian.",
)
Menambahkan data JSON terstruktur
import json
profile = {
"role": "Lead Data Engineer",
"tools": ["Airflow", "dbt", "PostgreSQL"],
"timezone": "Asia/Jakarta",
}
zep.graph.add(
userid="user-ruby-001",
type="json",
data=json.dumps(profile),
)
print("Data tambahan berhasil dimasukkan ke graph")
Dengan begini, kamu bisa nyeed memori agent pakai data yang udah kamu punya, bukan nunggu user cerita semuanya dari nol. Zep bakal ngekstrak fakta dari teks atau JSON itu dan nyatuinnya ke graph yang sama.
Ngatur banyak session per user
Di aplikasi nyata, satu user bisa punya banyak thread: obrolan kemarin, obrolan hari ini, obrolan soal topik A, obrolan soal topik B. Yang bagus dari model Zep, walau thread-nya beda-beda, knowledge graph itu dikaitin ke level user. Jadi fakta yang dipelajari di thread A tetap bisa dipakai waktu agent ngelayanin thread B, selama masih user yang sama.
# Thread baru buat topik lain, tetap user yang sama
zep.thread.create(threadid="thread-topik-karier", userid="user-ruby-001")
zep.thread.addmessages(
threadid="thread-topik-karier",
messages=[
Message(role="user", name="Ruby",
content="Aku lagi mikir buat pindah ke role MLOps tahun depan."),
],
)
Waktu narik konteks thread lama, fakta karier ini bisa muncul juga
ctx = zep.thread.getusercontext(threadid="thread-chatbot-demo")
print(ctx.context)
Ini beda banget sama nyimpen histori per-thread doang. Zep ngasih memori yang menyatu di level user, jadi agent-mu punya gambaran utuh soal orangnya, bukan cuma potongan per obrolan.
Nangani fakta yang berubah seiring waktu
Balik lagi ke kata "temporal". Misal user awalnya bilang tinggal di Jakarta, terus beberapa bulan kemudian bilang udah pindah ke Bandung. Zep bakal nyatet dua-duanya dengan info temporal: fakta Jakarta valid dari tanggal sekian sampai tanggal pindah, dan fakta Bandung valid dari tanggal pindah sampai sekarang. Agent yang nanya "user tinggal di mana sekarang?" bakal dapet Bandung, tapi kalau kamu butuh histori, datanya tetap ada.
# Simulasi update fakta
zep.thread.addmessages(
threadid="thread-chatbot-demo",
messages=[
Message(role="user", name="Ruby",
content="Oh iya, sekarang aku udah pindah domisili ke Bandung."),
],
)
Cari fakta domisili terbaru
res = zep.graph.search(
userid="user-ruby-001",
query="domisili tempat tinggal user saat ini",
scope="edges",
limit=3,
)
for edge in res.edges or []:
print("Fakta:", edge.fact)
Kemampuan menangani perubahan fakta ini yang bikin Zep cocok buat agent produksi yang hidup lama. User berubah, dan memori agent-mu harus ikut update tanpa kehilangan konteks historis.
Best Practices
Setelah beberapa kali aku eksperimen sama Zep, ada beberapa hal yang aku rasa penting kamu pegang biar implementasimu rapi dan hemat.
Gunakan userid yang stabil dan deterministik. Jangan generate random tiap kali. Pakai ID dari sistem autentikasimu sendiri supaya kamu selalu bisa narik ulang memori orang yang sama lintas device dan lintas sesi. Ini fondasi biar memori jangka panjang beneran jalan. Simpan pesan secara konsisten, jangan cuma sebagian. Kalau kamu cuma nyimpen pesan user tapi lupa nyimpen jawaban assistant, kualitas ekstraksi fakta bakal turun karena Zep kehilangan setengah konteks percakapan. Selalu simpan dua sisi obrolan. Injeksi konteks, bukan seluruh transkrip. Godaan buat nyeret semua histori ke prompt itu besar, tapi jangan. Pakaigetusercontext buat dapet ringkasan padat. Ini yang bikin biaya token terkendali dan latency stabil walau percakapan udah ribuan turn. Justru inilah keunggulan utama Zep dibanding numpuk transkrip.
Sadari sifat asinkron ekstraksi graph. Fakta baru butuh sedikit waktu buat diproses di sisi Zep. Jangan bikin logika yang langsung nge-assert fakta baru muncul persis sedetik setelah add pesan. Kalau kamu butuh nge-tes, kasih jeda beberapa detik atau retry.
Lindungi API key. Selalu taruh di environment variable atau secret manager, jangan pernah di-hardcode atau di-commit. Rotasi key kalau kecurigaan bocor. Ini basic tapi sering kelupaan waktu ngejar deadline.
Rancang strategi threading yang jelas. Putuskan dari awal apakah kamu mau satu thread panjang per user, atau thread baru per sesi. Buat kebanyakan kasus chatbot, satu thread per sesi percakapan itu masuk akal, dan biarin knowledge graph di level user yang nyatuin semuanya.
Manfaatin graph.add buat seeding. Jangan nunggu user cerita semuanya. Kalau kamu udah punya profil user dari sumber lain, masukin ke graph biar agent langsung punya konteks awal yang kaya. Ini bikin pengalaman first interaction jauh lebih personal.
Tangani error dengan anggun. Panggilan jaringan bisa gagal. Bungkus operasi Zep dalam try/except dan siapin fallback, misal kalau getusercontext gagal, agent tetap bisa jawab pakai konteks kosong daripada crash total. Chatbot yang tetap jalan walau memori sesaat nggak kebaca itu lebih baik daripada yang mati mendadak.
Perhatikan privasi dan retensi data. Karena Zep nyimpen fakta personal tentang user, pastiin kamu punya kebijakan retensi dan cara buat ngapus data user kalau diminta. Ini penting buat kepatuhan seperti GDPR. Zep nyediain operasi buat ngapus user beserta memorinya, jadi manfaatin itu di alur penghapusan akun.
Conclusion
Oke temen-temen, kita udah nempuh perjalanan lumayan panjang. Kita mulai dari kenapa memori itu krusial buat AI agent, terus kenalan sama konsep temporal knowledge graph yang bikin Zep beda dari sekadar nyimpen histori chat. Kita daftar Zep Cloud, ambil API key, install zep-cloud, terus praktik bikin user, bikin thread, nambah pesan, narik konteks, sampai nyari fakta di graph. Puncaknya kita rakit satu chatbot sederhana yang beneran inget siapa kamu antar sesi.
Yang pengen aku tekanin, kekuatan Zep bukan cuma "nyimpen data". Kekuatannya ada di cara dia ngubah percakapan mentah jadi fakta terstruktur yang punya dimensi waktu, terus nyajiin itu balik dalam bentuk konteks padat yang tinggal kamu injeksi ke prompt. Ini ngatasin tiga masalah sekaligus: agent jadi personal, biaya token terkendali, dan fakta yang berubah tetap kerekam rapi.
Kalau kamu lagi bangun asisten AI, agent customer support, atau tutor personal, coba deh integrasiin memori kayak gini dari awal. Percaya deh, agent yang inget user itu terasa beda kelas dibanding yang amnesia tiap sesi. Langkah selanjutnya yang aku saranin: eksperimen dengan graph.add buat seeding profil user, coba pisahin banyak thread per user, dan mainin pencarian graph buat use case retrieval yang lebih spesifik. Selamat ngoding, dan semoga agent buatan kamu makin pinter inget temen-temennya. Sampai ketemu di tutorial berikutnya.