Memberi agent kemampuan mengingat: membedakan short-term dan long-term memory, mempersistensikan percakapan ke database, dan mengelola conversation state. Kalian membangun agent yang ingat konteks lintas sesi tanpa membocorkan tokentype: text

Agent dari episode 11 punya satu kelemahan besar: amnesia total. Setiap percakapan baru dimulai dari nol. Episode ini memperbaiki itu dengan memory & state management — kemampuan agent mengingat apa yang dibicarakan, di dalam satu sesi (short-term) maupun lintas sesi (long-term).
Mengapa penting? Karena produk AI nyata (customer support, asisten pribadi) tidak berfungsi tanpa konteks berkelanjutan. Tapi — dan ini kuncinya — menyimpan semua riwayat lalu memasukkan semuanya ke prompt adalah bencana biaya. Memory yang baik adalah tentang menyimpan utuh dan mengambil secukupnya.
| Jenis | Rentang | Simpan Di | Isi |
|---|---|---|---|
| Short-term | Dalam satu sesi percakapan | Riwayat pesan (di-prompt) | Pesan user/assistant terakhir |
| Long-term | Lintas sesi | Database / vector DB | Ringkasan, preferensi, fakta pengguna |
Kesalahannya: memasukkan long-term memory utuh ke prompt. Benarnya: simpan utuh di database, masukkan secukupnya ke prompt — persis prinsip yang kita bahas di episode 8.
Short-term memory adalah daftar pesan yang kita kirim ke model. Kelolanya dengan anggaran token (episode 8): trim pesan lama, ringkas yang perlu, sisakan detail untuk yang terakhir.
def build_prompt(history: list[dict], max_tokens: int = 4000) -> list[dict]:
# ringkas pesan tertua, pertahankan detail terbaru
system = [m for m in history if m["role"] == "system"]
rest = history[len(system):]
if est_tokens(rest) > max_tokens: # placeholder
rest = [{"role": "system",
"content": "Ringkasan: " + summarize(rest[:-6])}] + rest[-6:]
return system + restUntuk mengingat lintas sesi, simpan percakapan di database dengan struktur sederhana:
CREATE TABLE conversations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id TEXT NOT NULL,
title TEXT,
created_at TEXT DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
conversation_id INTEGER NOT NULL REFERENCES conversations(id),
role TEXT NOT NULL, -- system / user / assistant / tool
content TEXT NOT NULL,
created_at TEXT DEFAULT CURRENT_TIMESTAMP
);import sqlite3
from pathlib import Path
DB = Path.home() / "ai-engineer-lab" / "memory.db"
def save_message(conv_id: int, role: str, content: str) -> None:
with sqlite3.connect(DB) as conn:
conn.execute(
"INSERT INTO messages (conversation_id, role, content) VALUES (?, ?, ?)",
(conv_id, role, content))
def load_history(conv_id: int, limit: int = 40) -> list[dict]:
with sqlite3.connect(DB) as conn:
rows = conn.execute(
"SELECT role, content FROM messages WHERE conversation_id = ? "
"ORDER BY id DESC LIMIT ?", (conv_id, limit)).fetchall()
return [{"role": r, "content": c} for r, c in reversed(rows)]
def new_conversation(user_id: str) -> int:
with sqlite3.connect(DB) as conn:
cur = conn.execute("INSERT INTO conversations (user_id) VALUES (?)",
(user_id,))
return cur.lastrowidPola ini memungkinkan agent "melanjutkan" percakapan yang terputus — user kembali minggu depan dan agent masih tahu konteksnya.
State percakapan bukan hanya pesan. Banyak aplikasi butuh structured state: nama pengguna, langkah wizard yang sedang berjalan, preferensi, keranjang belanja. Simpan sebagai JSON terpisah, bukan hanya dalam riwayat:
{
"user_id": "u-4821",
"langkah": "konfirmasi_alamat",
"profil": {"nama": "Rina", "preferensi": "produk bayi"},
"keranjang": [{"sku": "HD-01", "qty": 2}]
}Bedanya dengan riwayat: state adalah data terstruktur yang bisa dibaca kode — misal untuk memvalidasi "apakah user sudah mengisi alamat" tanpa perlu memanggil LLM untuk menebak dari teks.
Untuk memori jangka panjang yang scalable, perlakukan memori seperti knowledge base: simpan fakta penting sebagai vector + metadata, ambil yang relevan saat dibutuhkan (pola RAG):
import chromadb
mem_col = chromadb.Client().get_or_create_collection("memori-user")
def remember(user_id: str, fakta: str) -> None:
mem_col.upsert(ids=[f"{user_id}-{hash(fakta)}"],
documents=[fakta],
metadatas=[{"user": user_id}])
def recall(user_id: str, query: str, k: int = 5) -> list[str]:
hasil = mem_col.query(query_texts=[query], n_results=k,
where={"user": user_id})
return hasil["documents"][0]Perhatikan: memori per-user (filter where={"user": user_id}) — ini juga garis pertahanan privasi yang kita perkuat di episode 20. Jangan pernah mencampur memori antar pengguna.
Important
"Mengingat" bukan "menumpuk". Setiap request hanya boleh membawa memori yang relevan. Ambil 3-5 fakta paling relevan, bukan 500 fakta. Ini menjaga biaya tetap rendah dan kualitas tetap tinggi.
user_id adalah kebocoran data antar pengguna.Inti yang harus dibawa pulang:
Di episode 13 selanjutnya kita melampaui teks: multimodal & vision AI — model yang membaca gambar dan audio, pipeline multimodal, dan membangun aplikasi vision pertama. Sampai jumpa di episode 13!