Membangun Agent Memory Tanpa Vendor Lock-in: Implementasi Riil (Part 3)

Bagian ketiga dari seri memori AI Agent. Kali ini kita bedah implementasi riil: membangun unified memory engine yang dibungkus dalam MCP, serta merancang Dual-Lane Memory (RAG + SQL Analytics).

Jul 19, 2026
•
8 min read

Di tulisan sebelumnya (Part 2), gw udah cerita panjang lebar soal teori Hybrid Scoring dan 1-Hop Related Dates Expansion buat nyederhanain relasi memori asisten AI gw, Nouva. Secara konsep di atas kertas, semuanya kelihatan cantik banget.

Tapi pas masuk ke fase implementasi riil di homelab, gw sadar: kayaknya AnythingLLM gak begitu berguna deh.

Akhirnya, gw mutusin mencabut AnythingLLM dari arsitektur memori gw dan membangun Nouva Memory Engine yang ramping menggunakan FastMCP (Python) yang dicolok langsung ke Postgres + pgvector serta database relasional biasa untuk kebutuhan analisis deterministik.

Berikut bedah arsitektur dan lika-liku implementasinya.


Arsitektur Baru: Dual-Lane Memory Engine

Sebelum masuk ke detail kode, ini gambaran umum alur memori Nouva yang baru:

Nouva Memory Engine ArchitectureNouva Memory Engine Architecture
[Image] Nouva Memory Engine Architecture

Inti dari arsitektur ini adalah pemisahan jalur pencarian berdasarkan tipe pertanyaan yang masuk (Dual-Lane). Jalur semantik (RAG) dan jalur analisis kuantitatif (SQL) gak bisa dicampur aduk kalau lu mau dapet hasil yang akurat dan hemat token.

Di dalam sistem ini, data memori dibagi menjadi dua status utama:

  1. Active Memory (Lokal): Menyimpan data yang sedang berjalan atau baru terjadi hari ini (seperti transkrip chat aktif harian dan catatan harian yang belum selesai). Data ini disimpan langsung di server lokal agent gw (active_memory_dir) untuk proses penulisan cepat.
  2. Archived Memory (NAS): Setelah melewati grace period (misal H-1), pipeline otomatis akan memindahkan data yang sudah final (transkrip mentah, summary harian, dan indeks) ke penyimpanan terpisah di NAS (archived_memory_dir) secara permanen dan read-only, sehingga penyimpanan SSD server agent gw tetap hemat.

1. Decommissioning AnythingLLM & Beralih ke FastMCP

Awalnya, AnythingLLM kerasa ngebantu banget karena dia nyediain API instan buat nge-manage dokumen dan vector database. Tapi pas dipikir-pikir, AnythingLLM itu bloated buat asisten pribadi yang jalan di homelab. Dia makan resource RAM dan CPU yang lumayan di cluster K3s gw, padahal gw cuma butuh:

  1. Nyimpen dokumen markdown (MEMORY.md, index harian).
  2. Ngedapetin embedding vector-nya.
  3. Melakukan cosine distance query.

Akhirnya, gw decommission AnythingLLM secara permanen. Container-nya gw matiin, volume di server gw hapus, dan ingress routing-nya gw cabut dari manifes Kubernetes.

Sebagai gantinya, gw bikin Unified Memory Engine berbasis FastMCP (Python). Kenapa MCP (Model Context Protocol)? Karena protokol ini sekarang udah jadi standar industri yang didukung sama banyak AI Agent client (seperti OpenClaw, Cursor, Zed, dll.). Cukup sekali buat server MCP, lu bisa colok memori ini ke editor kode lu, asisten Telegram lu, atau CLI agent mana pun tanpa perlu bikin API wrapper baru.


2. Dari Zed Editor Langsung ke Server (Lifecycle Memori)

Bagaimana sebuah interaksi bisa berubah jadi memori? Prosesnya dimulai langsung dari workspace coding atau chat harian.

Sebagai contoh, saat gw lagi ngoding di Zed Editor dan meminta asisten AI untuk menyimpan sesi diskusi penting, asisten bakal memicu tool session_write (FastMCP tool). Tool ini secara instan merekam jalannya obrolan, mengemasnya dalam format JSON terstruktur, dan menyimpannya sebagai file transkrip mentah berformat Markdown di server agent gw:

Menyimpan Sesi Diskusi dari Zed EditorMenyimpan Sesi Diskusi dari Zed Editor
[Image] Menyimpan Sesi Diskusi dari Zed Editor

File transkrip mentah yang baru disimpan ini (2026-07-19-1037.md) bakal nangkring di server agent gw (agent-host di Proxmox) dan langsung dikaitkan dengan tanggal induknya (Parent Day: [[2026-07-19]]):

Transkrip Tersimpan di Server AgentTranskrip Tersimpan di Server Agent
[Image] Transkrip Tersimpan di Server Agent

File-file transkrip mentah ini sengaja ditaruh di active storage (server agent gw, non-NAS) dulu selama grace period H-1 sebelum nantinya di-ingest, dibuat summary-nya, di-index secara semantik, dan diarsipkan secara permanen ke NAS oleh pipeline sinkronisasi otomatis harian.


3. Dual-Lane Memory: RAG Semantik vs Deterministic SQL Analytics

Masalah terbesar yang sering dilupakan orang pas bikin RAG adalah memaksakan pencarian semantik untuk pertanyaan kuantitatif.

Misalnya lu nanya ke AI: "Berapa persen tingkat produktivitas gw di bulan Juli ini?"

Kalau lu pake RAG semantik biasa, LLM bakal nyari dokumen yang secara kosinus mirip dengan kata "produktivitas Juli". Dia bakal narik 2 atau 3 summary harian acak, lalu mencoba "mengarang" persentase berdasarkan dokumen yang ditarik secara terbatas itu. Hasilnya? Pasti salah atau ngaco.

Untuk menyelesaikan ini, gw ngebagi pemrosesan memori menjadi dua jalur (Dual-Lane):

Lane 1: Semantic Recall (pgvector)

Ini jalur RAG konvensional. Data dari file markdown utama (MEMORY.md, MEMORY_INDEX.md) dipotong-potong menjadi chunks, di-embed menggunakan model bge-m3 secara lokal, dan disimpan ke tabel memory_vectors di PostgreSQL dengan ekstensi pgvector.

  • Guna: Menjawab pertanyaan berbasis konteks, misal: "Gimana setup boot order server homelab kemarin?" atau "Apa kesepakatan gw sama Kak Rina soal reminder sahur?"

Lane 2: Deterministic Analytics (Postgres SQL)

Setiap kali script sinkronisasi harian (auto_sync.py) jalan, dia gak cuma bikin file markdown summary, tapi juga mem-parsing YAML frontmatter di summary tersebut (seperti mood, importance, projects, technologies, date) dan menyimpannya sebagai baris data (SQL rows) di tabel daily_summaries PostgreSQL.

  • Guna: Menjawab pertanyaan agregatif/tren secara deterministik, misal: "Berapa kali gw ngoding pake Next.js bulan ini?" atau "Berapa persen hari produktif gw di bulan Juni?"

Kenapa Postgres? Karena pada akhirnya, perjalanan spiritual setiap developer bakal selalu berujung pada satu kesimpulan mutlak: Just Use Postgres.

Just Use Postgres MemeJust Use Postgres Meme
[Image] Just Use Postgres Meme

Daripada lu pusing nge-deploy database time-series khusus, graph database aneh-aneh, atau document store terpisah cuma buat nyimpen data summary personal, Postgres dengan kombinasi pgvector (untuk Lane 1) dan tabel relasional biasa (untuk Lane 2) udah lebih dari cukup untuk nge-handle semuanya sendirian secara efisien.

Saat asisten AI nerima pertanyaan kuantitatif, dia gak nge-run vector search. Dia bakal mem-parsing parameter query menjadi parameter terstruktur (misal: start_date="2026-07-01", end_date="2026-07-19", intent="mood_timeseries") lewat tools MCP memory_analyze, lalu mengeksekusi query SQL agregasi murni langsung ke database.

Hasilnya? AI gak cuma bisa me-recall ingatan secara semantik, tapi juga bisa melakukan agregasi data dan tren secara 100% presisi layaknya database analitik:

Analisis Mood dan Produktivitas Menggunakan SQL LaneAnalisis Mood dan Produktivitas Menggunakan SQL Lane
[Image] Analisis Mood dan Produktivitas Menggunakan SQL Lane

Hasil kalkulasi di atas murni ditarik menggunakan query SQL agregasi, sangat cepat, akurat, dan tanpa membuang-buang token LLM untuk membaca ratusan baris transkrip obrolan.


4. Struktur Data: Implicit Temporal Graph

Mungkin ada yang penasaran, "Kalau kita gak pakai database graf fisik kayak Neo4j, gimana caranya kita menghubungkan ingatan yang bersambung antar hari (temporal relationship)?"

Jawabannya: Kita mensimulasikan struktur graf waktu secara implisit (Implicit Temporal Graph) langsung di atas flat Markdown dan PostgreSQL biasa.

Dalam sistem ini, struktur datanya didefinisikan seperti ini:

  • Node (Titik): Direpresentasikan oleh entitas tanggal unik (misalnya dokumen harian 2026-07-08).
  • Edge (Garis Hubung): Direpresentasikan oleh array related_dates di dalam YAML metadata summary.

Berikut visualisasi graph view memori Nouva yang langsung terbentuk secara otomatis di Obsidian:

Obsidian Memory Graph ViewObsidian Memory Graph View
[Image] Obsidian Memory Graph View

Sebagai contoh nyata, ini adalah potongan metadata YAML dari file .summary.md yang disimpan di NAS:

schema_version: 1
date: 2026-06-24
people:
- Gading
- Kak Rina
projects:
- Homelab
tags:
- anythingllm-migration
- proxmox-config
technologies:
- Docker
- Terraform
- PostgreSQL
importance: 7
mood: reflective
related_dates:
- '2026-06-23'
- '2026-06-28'

Secara logis, ini adalah graf temporal karena menghubungkan entitas/dokumen antar waktu/tanggal. Namun secara fisik, dia disimpan sebagai data relasional biasa.

Sifat datanya pun sangat efisien:

  1. Immutable Historical Logs: Data harian yang sudah lewat grace period (H-1) bersifat read-only dan disimpan di NAS.

  2. Declarative Relationships: Hubungan antar hari dideklarasikan secara programmatik saat sync, bukan secara dinamis saat runtime query.

  3. Decayed Traversal: Saat melakukan pencarian semantik (RAG), query engine kita akan melakukan 1-hop expansion ke tanggal yang terdaftar di related_dates dengan memotong skor semantiknya sebesar 30% menggunakan rumus peluruhan:

    Sdecayed=Sparent×0.7S_{\text{decayed}} = S_{\text{parent}} \times 0.7

    Di mana SparentS_{\text{parent}} adalah skor semantik asli dari tanggal induk yang ditemukan oleh RAG. Ini berfungsi sebagai penanda bahwa tanggal hasil ekspansi tersebut adalah informasi sekunder/konteks pendukung.

Dengan cara ini, kita tetap mendapatkan keuntungan dari Graph/Temporal traversal tanpa overhead resource dan kompleksitas database graf fisik.


5. Alur Kerja Sync Pipeline (auto_sync.py)

Bagaimana semua data ini tetap sinkron? Gw punya cron job background (auto_sync.py) yang berjalan otomatis setiap hari dengan alur kerja sebagai berikut:

  1. Reconcile & Summarize: Membaca transkrip chat mentah hari sebelumnya yang masih aktif di host lokal, lalu menyuruh LLM lokal membuat summary ringkas dalam format .summary.md lengkap dengan YAML frontmatter terstruktur.
  2. Archive to NAS: Memindahkan file transkrip chat mentah asli ke subfolder NAS (daily_sessions/YYYY-MM-DD/) agar penyimpanan SSD di host asisten tetap low-space.
  3. Database Sync:
    • Membaca YAML frontmatter dari summary baru dan memasukkannya ke tabel daily_summaries di PostgreSQL (Lane 2).
    • Meng-update file indeks MEMORY_INDEX.md, memotongnya menjadi chunks, membuat embedding-nya, lalu memperbarui tabel memory_vectors (Lane 1).

Kesimpulan

Beralih ke Unified Memory Engine berbasis FastMCP dan memisahkan alur pencarian memori menjadi Dual-Lane (Semantic vs. SQL Analytics) terbukti memangkas penggunaan resource homelab secara signifikan. Keuntungan terbesarnya adalah portabilitas tinggi; sekarang gw punya dedicated memory service terstandar yang bisa langsung dicolok dan diakses oleh agent apapun yang mendukung protokol MCP.

Dengan arsitektur ini, asisten AI gw gak cuma sekadar "mengingat" obrolan masa lalu secara semantik (recall history chat). Lebih jauh dari itu, dia bisa melakukan analisis mendalam, melakukan agregasi data terstruktur secara presisi, dan melacak tren kebiasaan, produktivitas, hingga kendala teknis harian gw dengan sangat akurat.

Sebagai contoh, ini visualisasi persentase produktivitas gw di bulan Juli 2026 yang di-generate langsung oleh asisten AI gw menggunakan data dari SQL Lane:

July 2026 Productivity ChartJuly 2026 Productivity Chart
[Image] July 2026 Productivity Chart

Keep it lean, keep it portable, and keep it yours!

Blog Terkait

Perjalanan arsitektur memori ini bisa lu ikuti secara lengkap di sini:

  1. Membangun GraphRAG untuk Agen Otonom dengan Neo4J and Graphitti (Awal eksperimen GraphRAG)
  2. Jebakan GraphRAG: Kenapa Gue Akhirnya Uninstall Neo4j (Kenapa GraphRAG dibuang)
  3. Bagaimana Saya Membangun Agent Memory yang Bebas Vendor Lock-in (Awal mula Two-Tier Architecture)
  4. Membangun Agent Memory dengan Vendor Lock-in Resistance (Part 2) (Optimasi metadata & Hybrid Scoring)