[{"data":1,"prerenderedAt":191},["ShallowReactive",2],{"site-footer-license":3,"site-header":7,"article-memori-ai-agent-vector-store-ke-temporal-graph-with-latest":46},{"name":4,"url":5,"show":6},"CC BY-SA","https:\u002F\u002Fcreativecommons.org\u002Flicenses\u002Fby-sa\u002F4.0\u002F",false,{"navigations":8},[9,24,41],{"collection":10,"item":11},"series",{"__typename":10,"title":12,"slug":13,"children_series":14},"Artikel","all",[15,18,21],{"title":16,"slug":17},"Algoritma dan Struktur Data","algoritma-struktur-data",{"title":19,"slug":20},"Kecerdasan Artifisial","kecerdasan-artifisial",{"title":22,"slug":23},"Konsep Dasar Pemrograman","konsep-dasar-pemrograman",{"collection":10,"item":25},{"__typename":10,"title":26,"slug":27,"children_series":28},"Tutorial","tutorial",[29,32,35,38],{"title":30,"slug":31},"Belajar Java","belajar-java",{"title":33,"slug":34},"Belajar Pascal","belajar-pascal",{"title":36,"slug":37},"Belajar Golang","belajar-golang",{"title":39,"slug":40},"Belajar Python","belajar-python",{"collection":10,"item":42},{"__typename":10,"title":43,"slug":44,"children_series":45},"Lab","lab",[],{"article":47,"renderedBody":128,"renderedTakeaway":129,"latestArticles":130,"toc":171},{"prerequisite":48,"id":49,"title":50,"slug":51,"excerpt":52,"published_at":53,"thumbnail_image":48,"tags":54,"reading_time":59,"takeway":60,"body":61,"authors":62,"series":68},null,"126","Memori AI Agent: dari Vector Store ke Temporal Graph","memori-ai-agent-vector-store-ke-temporal-graph","Dua fakta yang saling bertentangan bikin agent menjawab ngawur. Ini bedanya vector store dan temporal knowledge graph dalam menjaga state jangka panjang.","2026-10-18T13:13:29.458Z",[55,56,57,58],"ai","llm","backend","system-design",7,"- *Vector store* murni gagal saat fakta berubah, karena pencarian kemiripan makna nggak menyimpan informasi soal kapan sebuah fakta masih berlaku dan kapan sudah kedaluwarsa.\n- Memori *agent* modern dibagi tiga lapisan: working memory di jendela konteks, semantic memory di *vector store* untuk pengetahuan tak terstruktur, dan episodic\u002Frelational memory di graf entitas bertanggal.\n- *Temporal knowledge graph* menyimpan fakta sebagai relasi berentang waktu, lalu menandai fakta lama sebagai kedaluwarsa tanpa menghapus riwayatnya.\n- Sejak 2026, graph store dihapus dari paket *open source* Mem0 dan pindah jadi fitur bawaan Platform-nya, sementara Graphiti tetap jadi jalur *open source* untuk graf temporal penuh.\n- Klaim akurasi di halaman *vendor* berasal dari *benchmark* mereka sendiri, jadi angkanya lebih layak dibaca sebagai petunjuk arah.","Dulu, waktu pertama kali bikin *agent* AI, cara menambahkan \"memori\" terasa sepele: potong beberapa giliran obrolan terakhir (`chat_history[-5:]`) lalu tempelkan ke *system prompt*. Selama demonya masih satu sesi dan satu topik, cara itu jalan sempurna.\n\nMasalahnya datang belakangan, saat satu pengguna sudah mengobrol di ratusan sesi selama berbulan-bulan. Jendela konteks nggak cukup, dan biaya *token*-nya naik terus kalau semua riwayat ikut dikirim. Jalan keluar paling populer di industri tuh melempar seluruh rekaman percakapan lama ke *vector database* — Pinecone, Qdrant, Chroma, atau apa pun yang kamu pakai. Setiap ada pesan baru, *agent* mencari potongan teks yang paling mirip maknanya lewat *semantic search*, alias perbandingan *cosine similarity*. Potongan yang dianggap relevan itu lalu disisipkan ke *prompt*.\n\nSampai di sini semuanya terasa beres. Tapi kalau kamu pernah melihat *agent*-mu menjawab ngawur justru saat informasinya *ada* di database, misalnya menyebut kamu masih di pekerjaan lama padahal kamu sudah pindah, itu bukan bug *prompt*. Itu batas arsitektur.\n\n## Kenapa *vector store* gagal saat fakta berubah\n\nPencarian *vector* sangat andal dalam menemukan kemiripan topik. Sayangnya ia **buta terhadap waktu**. Ia juga nggak tahu bahwa satu fakta bisa membatalkan fakta lain yang mirip.\n\nLihat urutan sesi ini, nih. Misalkan pengguna *agent*-nya bernama Andi:\n\n- *Sesi 1 (Januari 2026)* — Andi bilang: *\"Aku baru pindah kerja ke Startup A sebagai Full-Stack Engineer.\"* Tersimpan sebagai potongan *vector* A.\n- *Sesi 2 (Agustus 2026)* — Andi bilang: *\"Sekarang aku memimpin tim developer di Startup B.\"* Tersimpan sebagai potongan *vector* B.\n- *Sesi 3 (September 2026)* — Andi bertanya: *\"Aku sekarang kerja di mana, dan perannya apa?\"*\n\nSekarang kita perhatikan apa yang terjadi di sesi ketiga. Pertanyaan itu punya kemiripan makna yang hampir sama dengan potongan A maupun potongan B, karena keduanya memang membicarakan pekerjaan Andi. Kedua potongan tadi lalu masuk ke konteks secara bersamaan. Model yang menerimanya nggak punya alasan untuk menolak salah satunya, jadi jawaban yang keluar campur aduk: *\"Anda bekerja di Startup A dan Startup B sebagai Full-Stack Lead Developer.\"*\n\n*Vector store* murni memang nggak punya cara untuk tahu bahwa fakta B **menggantikan** fakta A. Nggak ada struktur relasi, nggak ada garis waktu, dan nggak ada status yang bilang mana yang masih berlaku. Yang ia punya cuma jarak antar-*embedding*. Jarak itu nggak menyimpan informasi soal waktu sama sekali.\n\nPertanyaan yang gagal dijawab di sini bukan \"fakta apa yang mirip dengan pertanyaan ini?\". Pertanyaan yang gagal dijawab adalah yang ini: **\"dari semua yang aku tahu soal orang ini, mana yang masih benar sekarang?\"**\n\nBunyinya sih sederhana, tapi menjawabnya butuh tempat penyimpanan yang lebih dari sekadar *embedding*. Strukturnya harus bisa bilang \"ini benar sejak kapan, sampai kapan\".\n\n## Tiga lapisan memori *agent*\n\nUntuk menjawab pertanyaan semacam itu, sistem memori *agent* modern membagi penyimpanannya ke dalam tiga lapisan yang saling melengkapi. Kalau kamu sudah paham bedanya *short-term* dan *long-term memory*, bagian ini aman dilewati. Langsung ke bagian graf di bawah aja.\n\n**1. Working memory** adalah konteks yang sedang dipakai saat ini. Isinya giliran pesan terbaru, dikelola pakai *buffer* percakapan, *sliding window*, atau peringkasan berkala supaya nggak melampaui jendela konteks. Umurnya paling pendek, dan memang begitu seharusnya.\n\n**2. Semantic memory** adalah kumpulan pengetahuan faktual yang relatif statis: dokumen SOP, artikel wiki, arsip panduan internal. Isinya nggak berubah karena percakapan. Pencarian berbasis makna di *vector store* memang pas di sini.\n\n**3. Episodic & relational memory** adalah lapisan tempat kejadian dan hubungan antar-entitas disimpan. Di sinilah perubahan nasib pengguna dicatat: ia bekerja di mana, sejak kapan, sampai kapan. Lapisan ini butuh struktur, bukan sekadar tumpukan *embedding*. Strukturnya berupa graf, dan graf itulah yang bikin waktu bisa diperlakukan sebagai data.\n\n## Bentuk memori yang mengingat waktu\n\nDi dalam *temporal knowledge graph*, memori disimpan sebagai relasi bertanggal. Bentuknya kira-kira seperti ini di Graphiti, *engine* graf yang jalan di atas Neo4j atau FalkorDB:\n\n```cypher\n(:Entity {name: \"Andi\"})-[:RELATES_TO {\n    name: \"WORKS_AT\",\n    fact: \"Andi bekerja di Startup A sebagai Full-Stack Engineer\",\n    valid_at: \"2026-01-01T00:00:00Z\",\n    invalid_at: \"2026-07-31T00:00:00Z\"\n}]->(:Entity {name: \"Startup A\"})\n\n(:Entity {name: \"Andi\"})-[:RELATES_TO {\n    name: \"WORKS_AT\",\n    fact: \"Andi memimpin tim developer di Startup B\",\n    valid_at: \"2026-08-01T00:00:00Z\",\n    invalid_at: null\n}]->(:Entity {name: \"Startup B\"})\n```\n\nIni versi yang disederhanakan, ya. Setiap *edge* di Graphiti juga menyimpan jejak kapan ia kedaluwarsa di sistem, dan episode percakapan mana yang jadi sumbernya.\n\nYang penting buat kita bukan sintaks *Cypher*-nya, tapi tiga sifat yang muncul dari struktur itu.\n\nPertama, *retrieval*-nya jadi terarah. Ketika Andi bertanya soal pekerjaannya sekarang, sistem nggak perlu menebak mana yang relevan. Ia tinggal menelusuri relasi `WORKS_AT` milik Andi dan mengambil yang belum punya `invalid_at`.\n\nKedua, fakta lama nggak dihapus. *Edge* Startup A tetap ada lengkap dengan tanggal berakhirnya. *Agent* masih bisa menjawab \"dulu Andi kerja di mana?\" tanpa kehilangan riwayat. Beda mendasar dari pendekatan yang menimpa (*overwrite*) nilai lama.\n\nKetiga, waktu jadi bagian dari data, bukan *metadata* yang menempel di pinggir. Karena setiap fakta punya rentang berlaku, pertanyaan \"mana yang masih benar\" akhirnya bisa dijawab dengan operasi biasa. Tidak perlu menebak lewat model.\n\n## Peta perkakasnya, dan apa yang berubah di 2026\n\nJadi perkakasnya gimana? Kita punya dua *framework* besar yang mengotomatisasi pola ini, dan keduanya mengambil jalan berbeda dalam menghadapi fakta yang bertentangan.\n\n```mermaid\nflowchart TB\n  subgraph mem0[\"Mem0 — OSS v3 \u002F Platform\"]\n    direction TB\n    A1[\"Pesan baru\"] --> A2[\"Ekstraksi fakta\u003Cbr\u002F>ADD-only, satu panggilan LLM\"]\n    A2 --> A3[\"Vector store + indeks BM25\u003Cbr\u002F>+ tabel entitas\"]\n    A3 --> A4[\"Retrieval hibrida\u003Cbr\u002F>semantik + kata kunci + entitas\"]\n    A4 --> A5[\"Satu skor gabungan\"]\n    A5 --> A6[\"Konteks untuk LLM\"]\n  end\n\n  subgraph zep[\"Zep \u002F Graphiti\"]\n    direction TB\n    B1[\"Episode baru\"] --> B2[\"Ekstraksi entitas + relasi\u003Cbr\u002F>+ resolusi kontradiksi\"]\n    B2 --> B3[\"Graf temporal\u003Cbr\u002F>valid_at \u002F invalid_at\"]\n    B2 --> B4[\"Embedding fakta\"]\n    B3 --> B5[\"Hybrid search\u003Cbr\u002F>semantik + kata kunci + traversal graf\"]\n    B4 --> B5\n    B5 --> B6[\"Fakta yang masih berlaku\"]\n  end\n```\n\nBedanya kelihatan dari arah alirannya: Mem0 menyelesaikan kontradiksi di sisi *retrieval*, Graphiti di sisi penyimpanan.\n\n**Mem0** awalnya menempuh jalur ekstraksi dan pembaruan. Setiap pasangan pesan baru, LLM mengekstrak fakta kandidat. Fase kedua membandingkan fakta itu dengan memori lama, lalu memilih satu dari empat operasi: *ADD*, *UPDATE*, *DELETE*, atau *NOOP*. Dukungan graf opsional dipasang ke *graph database* eksternal seperti Neo4j. Kalau kamu membaca tutorial Mem0 yang beredar di blog-blog, hampir semuanya menggambarkan arsitektur versi itu.\n\nEmpat operasi itu terdengar rapi. Kok bisa arahnya berubah? Versi terbaru algoritmanya cuma melakukan satu operasi: *ADD*, satu kali panggilan LLM, tanpa *UPDATE* maupun *DELETE* sama sekali. Ketika informasinya berubah, kedua fakta tetap disimpan lengkap dengan konteks waktunya. Yang mengurutkan mana yang paling relevan adalah *retrieval*-nya, bukan proses penulisan. *Retrieval*-nya sendiri kini hibrida: skor semantik, kata kunci (BM25), dan kecocokan entitas digabung jadi satu angka.\n\nSejak versi itu pula, `graph_store` dihapus dari paket *open source* Mem0. Graf sekarang dibangun otomatis di sisi Platform dari entitas yang sama-sama disebut antar-memori, jadi urusan menyiapkan Neo4j sendiri sudah hilang. Tapi graf itu menyambungkan memori lewat entitas yang kebetulan beririsan, bukan lewat relasi bertipe dengan rentang berlaku. Nggak ada `valid_at` atau `invalid_at` yang bisa kamu telusuri.\n\n**Graphiti** mengambil jalur sebaliknya, dan lebih dekat ke model yang kita bahas di artikel ini. Ia menyimpan setiap fakta sebagai *edge* yang punya rentang berlaku ganda: kapan fakta itu benar di dunia nyata, dan kapan ia tercatat di sistem. Ketika ada informasi baru yang bertentangan, fakta lama ditandai kedaluwarsa lewat `invalid_at`, bukan dihapus. Setiap fakta juga masih bisa ditelusuri balik ke episode percakapan asalnya. Graphiti jalan sendiri di Neo4j atau FalkorDB, sedangkan versi terkelolanya adalah Zep.\n\nDua-duanya paket Apache-2.0 dan sama-sama ramai dipakai. Diperiksa 17 Oktober 2026, repo Mem0 ada di sekitar 67 ribu bintang dan Graphiti sekitar 31 ribu.\n\nSatu catatan soal angka, dan ini berlaku untuk semua perkakas di daftar ini ⚠️: klaim peningkatan akurasi yang beredar di halaman masing-masing *vendor* berasal dari *benchmark* mereka sendiri. Angka itu boleh jadi petunjuk arah, tapi bukan hasil pengukuran independen.\n\n## Contoh: menaruh *memory layer* di depan FastAPI\n\nYuk lihat bentuknya di backend FastAPI. Cuplikan ini memakai API Mem0 OSS yang berlaku sekarang (paket `mem0ai` 2.x). Versinya penting, karena beberapa hal berubah dari tutorial-tutorial lama:\n\n```python\nfrom fastapi import FastAPI\nfrom pydantic import BaseModel\nfrom mem0 import Memory\n\napp = FastAPI(title=\"Production Agent Memory Service\")\n\n# Konfigurasi OSS sekarang cuma memegang vector store, LLM, embedder, dan reranker.\n# `graph_store` dan `enable_graph` sudah dihapus dari paket open source.\nconfig = {\n    \"vector_store\": {\n        \"provider\": \"qdrant\",\n        \"config\": {\"host\": \"localhost\", \"port\": 6333},\n    },\n}\nmemory = Memory.from_config(config)\n\n# call_llm: helper pemanggil LLM milikmu. Di sini sengaja nggak ditulis detail,\n# karena yang sedang dibahas cuma lalu lintas memori di sekelilingnya.\n\nclass ChatRequest(BaseModel):\n    user_id: str\n    message: str\n\n@app.post(\"\u002Fchat\")\nasync def chat_endpoint(req: ChatRequest):\n    # 1. Ambil fakta yang relevan. Entity ID masuk lewat `filters`, bukan kwarg\n    #    teratas seperti di versi lama -- yang lama sekarang melempar ValueError.\n    hasil = memory.search(\n        req.message,\n        filters={\"user_id\": req.user_id},\n        top_k=5,\n    )\n\n    # 2. Bentuk konteksnya. search() mengembalikan dict dengan key \"results\".\n    fakta = \"\\n\".join(f\"- {m['memory']}\" for m in hasil[\"results\"])\n    system_prompt = (\n        \"Anda adalah AI Assistant. Informasi terkini tentang pengguna:\\n\" + fakta\n    )\n\n    # 3. Panggil LLM utama dengan prompt yang sudah diperkaya fakta.\n    llm_response = await call_llm(system_prompt=system_prompt, user_message=req.message)\n\n    # 4. Rekam giliran ini. Di add(), user_id masih diterima sebagai kwarg teratas.\n    memory.add(\n        [\n            {\"role\": \"user\", \"content\": req.message},\n            {\"role\": \"assistant\", \"content\": llm_response},\n        ],\n        user_id=req.user_id,\n    )\n\n    return {\"response\": llm_response}\n```\n\nKalau kamu menyalin dari tutorial lama, tiga hal ini yang paling sering bikin kaget, deh. Pertama, `user_id` yang dikirim sebagai argumen teratas di `search()` sekarang melempar *error*. Kedua, nama parameternya `top_k`, bukan `limit`. Ketiga, hasilnya dibungkus dalam key `results`, bukan *list* telanjang.\n\nSatu hal lagi yang perlu disadari: konfigurasi di atas cuma punya memori semantik. Pertanyaan \"mana yang masih berlaku\" tetap harus dijawab di atasnya, entah dengan menyimpan *metadata* sendiri lalu memfilternya, atau dengan memakai *engine* yang memang menyimpan rentang waktu di tiap fakta. Di situ pembagian tugasnya kelihatan: *vector store* untuk menemukan hal yang mirip, graf temporal untuk memutuskan hal yang masih benar.\n\n## Memori yang hidup menghasilkan *agent* yang relevan\n\nMembangun *agent* AI yang benar-benar personal bukan perlombaan memperbesar jendela konteks. Yang menentukan kualitasnya adalah seberapa pintar arsitektur sistem memfilter, memperbarui, dan melupakan informasi yang sudah kedaluwarsa. Dan bagian melupakan itu, anehnya, yang paling sering dilupakan 😅\n\nBegitu kecepatan pencarian semantik digabung dengan ketepatan relasi bertanggal, *agent*-mu nggak cuma mengingat kata-kata pengguna. Ia mulai bisa membedakan apa yang dulu benar dan apa yang benar sekarang 👋\n",[63],{"authors_id":64},{"name":65,"slug":66,"role":67,"profile_picture":48},"Daffa Izzuddin","izzudd","Writer",{"id":69,"title":19,"slug":20,"articles":70,"parent_series":120},"8",[71,73,83,93,99,106,113],{"id":49,"title":50,"slug":51,"excerpt":52,"thumbnail_image":48,"tags":72},[55,56,57,58],{"id":74,"title":75,"slug":76,"excerpt":77,"thumbnail_image":48,"tags":78},"125","MCP dibongkar ulang: kabar baik buat kamu yang capek nulis konektor","mcp-dibongkar-ulang-kabar-baik-buat-kamu","Kamu punya beberapa API internal dan satu agent AI, tapi kodenya harus ditulis ulang tiap kali modelnya diganti. MCP datang buat mengakhiri itu, dan tahun ini protokolnya dibongkar ulang supaya server-nya bisa jalan seperti web server biasa. Yuk lihat apa yang berubah. 🔧",[79,80,81,56,57,82],"mcp","model-context-protocol","ai-agent","python",{"id":84,"title":85,"slug":86,"excerpt":87,"thumbnail_image":48,"tags":88},"3","Mengenal JEPA: Saat AI Mulai Belajar Punya \"Akal Sehat\"","kenapa-ai-suka-halu-dan-gimana-jepa-beresin-masalah-ini","AI sering 'halu' karena cuma nebak probabilitas data tanpa paham realita. Arsitektur JEPA mencoba mengubah itu dengan membangun model dunia di kepalanya.",[89,90,91,92],"artificial-intelligence","jepa","world-models","yann-lecun",{"id":94,"title":95,"slug":96,"excerpt":97,"thumbnail_image":48,"tags":98},"2","LLM Bakal Mentok, Terus Gimana Dong?","llm-bakal-mentok-terus-gimana-dong","LLM kayak ChatGPT itu jago ngerangkai kata, tapi nggak beneran paham realitas. Yann LeCun bilang arsitektur ini bakal mentok dan butuh solusi baru.",[89,56,92,90],{"id":100,"title":101,"slug":102,"excerpt":103,"thumbnail_image":48,"tags":104},"1","Kenapa AI Suka 'Halu'? Kenalan sama JEPA, Otak AI yang Punya Akal Sehat","kenapa-ai-suka-halu-kenalan-sama-jepa-otak-ai-yang-punya-akal-sehat","AI generatif sering 'halu' karena cuma jago nebak pola kata. JEPA coba ngerombak itu dengan ngajarin AI cara kerja dunia nyata kayak otak manusia.",[90,89,92,105],"agi",{"id":107,"title":108,"slug":109,"excerpt":110,"thumbnail_image":48,"tags":111},"4","Bukan Sekadar Generative AI, JEPA Coba Kejar Pemahaman Konsep ala Manusia","kenapa-ai-masih-gagal-paham-fisika-dasar-mengenal-arsitektur-jepa","AI biasanya cuma jago nebak piksel, tapi JEPA coba belajar lewat konsep abstrak di ruang laten. Ternyata, cara ini bisa sampai dipakai buat baca gelombang otak.",[90,92,112,105],"machine-learning",{"id":114,"title":115,"slug":116,"excerpt":117,"thumbnail_image":48,"tags":118},"5","Di Balik Dapur JEPA: Rahasia EMA, VICReg, dan Efisiensi Komputasi","di-balik-dapur-jepa-rahasia-ema-vicreg-dan-efisiensi-komputasi","Bosen sama AI yang cuma jago nebak kata atau ngafalin pixel? Kenalan sama JEPA, arsitektur revolusioner yang ngajarin mesin punya \"akal sehat\" secara super efisien. Yuk intip jeroan teknisnya!",[90,119],"artificial-intelligent",{"id":121,"title":12,"slug":13,"parent_series":48,"children_series":122},"9",[123,125,126],{"id":124,"title":16,"slug":17,"cover_image":48},"15",{"id":69,"title":19,"slug":20,"cover_image":48},{"id":127,"title":22,"slug":23,"cover_image":48},"12","\u003Cp>Dulu, waktu pertama kali bikin \u003Cem>agent\u003C\u002Fem> AI, cara menambahkan &quot;memori&quot; terasa sepele: potong beberapa giliran obrolan terakhir (\u003Ccode>chat_history[-5:]\u003C\u002Fcode>) lalu tempelkan ke \u003Cem>system prompt\u003C\u002Fem>. Selama demonya masih satu sesi dan satu topik, cara itu jalan sempurna.\u003C\u002Fp>\n\u003Cp>Masalahnya datang belakangan, saat satu pengguna sudah mengobrol di ratusan sesi selama berbulan-bulan. Jendela konteks nggak cukup, dan biaya \u003Cem>token\u003C\u002Fem>-nya naik terus kalau semua riwayat ikut dikirim. Jalan keluar paling populer di industri tuh melempar seluruh rekaman percakapan lama ke \u003Cem>vector database\u003C\u002Fem> — Pinecone, Qdrant, Chroma, atau apa pun yang kamu pakai. Setiap ada pesan baru, \u003Cem>agent\u003C\u002Fem> mencari potongan teks yang paling mirip maknanya lewat \u003Cem>semantic search\u003C\u002Fem>, alias perbandingan \u003Cem>cosine similarity\u003C\u002Fem>. Potongan yang dianggap relevan itu lalu disisipkan ke \u003Cem>prompt\u003C\u002Fem>.\u003C\u002Fp>\n\u003Cp>Sampai di sini semuanya terasa beres. Tapi kalau kamu pernah melihat \u003Cem>agent\u003C\u002Fem>-mu menjawab ngawur justru saat informasinya \u003Cem>ada\u003C\u002Fem> di database, misalnya menyebut kamu masih di pekerjaan lama padahal kamu sudah pindah, itu bukan bug \u003Cem>prompt\u003C\u002Fem>. Itu batas arsitektur.\u003C\u002Fp>\n\u003Ch2 id=\"kenapa-vector-store-gagal-saat-fakta-berubah\">Kenapa \u003Cem>vector store\u003C\u002Fem> gagal saat fakta berubah\u003C\u002Fh2>\n\u003Cp>Pencarian \u003Cem>vector\u003C\u002Fem> sangat andal dalam menemukan kemiripan topik. Sayangnya ia \u003Cstrong>buta terhadap waktu\u003C\u002Fstrong>. Ia juga nggak tahu bahwa satu fakta bisa membatalkan fakta lain yang mirip.\u003C\u002Fp>\n\u003Cp>Lihat urutan sesi ini, nih. Misalkan pengguna \u003Cem>agent\u003C\u002Fem>-nya bernama Andi:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cem>Sesi 1 (Januari 2026)\u003C\u002Fem> — Andi bilang: \u003Cem>&quot;Aku baru pindah kerja ke Startup A sebagai Full-Stack Engineer.&quot;\u003C\u002Fem> Tersimpan sebagai potongan \u003Cem>vector\u003C\u002Fem> A.\u003C\u002Fli>\n\u003Cli>\u003Cem>Sesi 2 (Agustus 2026)\u003C\u002Fem> — Andi bilang: \u003Cem>&quot;Sekarang aku memimpin tim developer di Startup B.&quot;\u003C\u002Fem> Tersimpan sebagai potongan \u003Cem>vector\u003C\u002Fem> B.\u003C\u002Fli>\n\u003Cli>\u003Cem>Sesi 3 (September 2026)\u003C\u002Fem> — Andi bertanya: \u003Cem>&quot;Aku sekarang kerja di mana, dan perannya apa?&quot;\u003C\u002Fem>\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Sekarang kita perhatikan apa yang terjadi di sesi ketiga. Pertanyaan itu punya kemiripan makna yang hampir sama dengan potongan A maupun potongan B, karena keduanya memang membicarakan pekerjaan Andi. Kedua potongan tadi lalu masuk ke konteks secara bersamaan. Model yang menerimanya nggak punya alasan untuk menolak salah satunya, jadi jawaban yang keluar campur aduk: \u003Cem>&quot;Anda bekerja di Startup A dan Startup B sebagai Full-Stack Lead Developer.&quot;\u003C\u002Fem>\u003C\u002Fp>\n\u003Cp>\u003Cem>Vector store\u003C\u002Fem> murni memang nggak punya cara untuk tahu bahwa fakta B \u003Cstrong>menggantikan\u003C\u002Fstrong> fakta A. Nggak ada struktur relasi, nggak ada garis waktu, dan nggak ada status yang bilang mana yang masih berlaku. Yang ia punya cuma jarak antar-\u003Cem>embedding\u003C\u002Fem>. Jarak itu nggak menyimpan informasi soal waktu sama sekali.\u003C\u002Fp>\n\u003Cp>Pertanyaan yang gagal dijawab di sini bukan &quot;fakta apa yang mirip dengan pertanyaan ini?&quot;. Pertanyaan yang gagal dijawab adalah yang ini: \u003Cstrong>&quot;dari semua yang aku tahu soal orang ini, mana yang masih benar sekarang?&quot;\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cp>Bunyinya sih sederhana, tapi menjawabnya butuh tempat penyimpanan yang lebih dari sekadar \u003Cem>embedding\u003C\u002Fem>. Strukturnya harus bisa bilang &quot;ini benar sejak kapan, sampai kapan&quot;.\u003C\u002Fp>\n\u003Ch2 id=\"tiga-lapisan-memori-agent\">Tiga lapisan memori \u003Cem>agent\u003C\u002Fem>\u003C\u002Fh2>\n\u003Cp>Untuk menjawab pertanyaan semacam itu, sistem memori \u003Cem>agent\u003C\u002Fem> modern membagi penyimpanannya ke dalam tiga lapisan yang saling melengkapi. Kalau kamu sudah paham bedanya \u003Cem>short-term\u003C\u002Fem> dan \u003Cem>long-term memory\u003C\u002Fem>, bagian ini aman dilewati. Langsung ke bagian graf di bawah aja.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>1. Working memory\u003C\u002Fstrong> adalah konteks yang sedang dipakai saat ini. Isinya giliran pesan terbaru, dikelola pakai \u003Cem>buffer\u003C\u002Fem> percakapan, \u003Cem>sliding window\u003C\u002Fem>, atau peringkasan berkala supaya nggak melampaui jendela konteks. Umurnya paling pendek, dan memang begitu seharusnya.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>2. Semantic memory\u003C\u002Fstrong> adalah kumpulan pengetahuan faktual yang relatif statis: dokumen SOP, artikel wiki, arsip panduan internal. Isinya nggak berubah karena percakapan. Pencarian berbasis makna di \u003Cem>vector store\u003C\u002Fem> memang pas di sini.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>3. Episodic &amp; relational memory\u003C\u002Fstrong> adalah lapisan tempat kejadian dan hubungan antar-entitas disimpan. Di sinilah perubahan nasib pengguna dicatat: ia bekerja di mana, sejak kapan, sampai kapan. Lapisan ini butuh struktur, bukan sekadar tumpukan \u003Cem>embedding\u003C\u002Fem>. Strukturnya berupa graf, dan graf itulah yang bikin waktu bisa diperlakukan sebagai data.\u003C\u002Fp>\n\u003Ch2 id=\"bentuk-memori-yang-mengingat-waktu\">Bentuk memori yang mengingat waktu\u003C\u002Fh2>\n\u003Cp>Di dalam \u003Cem>temporal knowledge graph\u003C\u002Fem>, memori disimpan sebagai relasi bertanggal. Bentuknya kira-kira seperti ini di Graphiti, \u003Cem>engine\u003C\u002Fem> graf yang jalan di atas Neo4j atau FalkorDB:\u003C\u002Fp>\n\n\u003Cdiv class=\"code-block-wrapper my-6 not-prose rounded-xl border-2 border-black dark:border-gray-600 shadow-neo overflow-hidden bg-[#24292e] text-[#e1e4e8] transition-all\">\n  \u003Cdiv class=\"code-block-header flex items-center justify-between px-4 py-2.5 bg-[#1f2428] border-b-2 border-black dark:border-gray-600 font-mono text-xs font-bold text-gray-300 select-none\">\n    \u003Cdiv class=\"flex items-center gap-2\">\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ff5f56] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ffbd2e] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#27c93f] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"ml-2 font-mono font-bold text-xs uppercase tracking-wider text-gray-300\">CYPHER\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n    \u003Cbutton type=\"button\" class=\"copy-code-btn flex items-center gap-1.5 px-3 py-1 text-xs font-bold bg-[#2f363d] text-gray-200 border-2 border-black dark:border-gray-500 rounded-lg shadow-neo-sm hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none hover:bg-gray-700 transition-all cursor-pointer\" data-code=\"(%3AEntity%20%7Bname%3A%20%22Andi%22%7D)-%5B%3ARELATES_TO%20%7B%0A%20%20%20%20name%3A%20%22WORKS_AT%22%2C%0A%20%20%20%20fact%3A%20%22Andi%20bekerja%20di%20Startup%20A%20sebagai%20Full-Stack%20Engineer%22%2C%0A%20%20%20%20valid_at%3A%20%222026-01-01T00%3A00%3A00Z%22%2C%0A%20%20%20%20invalid_at%3A%20%222026-07-31T00%3A00%3A00Z%22%0A%7D%5D-%3E(%3AEntity%20%7Bname%3A%20%22Startup%20A%22%7D)%0A%0A(%3AEntity%20%7Bname%3A%20%22Andi%22%7D)-%5B%3ARELATES_TO%20%7B%0A%20%20%20%20name%3A%20%22WORKS_AT%22%2C%0A%20%20%20%20fact%3A%20%22Andi%20memimpin%20tim%20developer%20di%20Startup%20B%22%2C%0A%20%20%20%20valid_at%3A%20%222026-08-01T00%3A00%3A00Z%22%2C%0A%20%20%20%20invalid_at%3A%20null%0A%7D%5D-%3E(%3AEntity%20%7Bname%3A%20%22Startup%20B%22%7D)\" title=\"Salin kode\">\n      \u003Csvg class=\"w-3.5 h-3.5 copy-icon-svg\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n        \u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\">\u003C\u002Fpath>\n      \u003C\u002Fsvg>\n      \u003Cspan class=\"copy-text\">Copy\u003C\u002Fspan>\n    \u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"code-block-content p-4 overflow-x-auto text-sm font-mono leading-relaxed bg-[#24292e]\">\n    \u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\">\u003Ccode>(:Entity {name: \"Andi\"})-[:RELATES_TO {\n    name: \"WORKS_AT\",\n    fact: \"Andi bekerja di Startup A sebagai Full-Stack Engineer\",\n    valid_at: \"2026-01-01T00:00:00Z\",\n    invalid_at: \"2026-07-31T00:00:00Z\"\n}]-&gt;(:Entity {name: \"Startup A\"})\n\n(:Entity {name: \"Andi\"})-[:RELATES_TO {\n    name: \"WORKS_AT\",\n    fact: \"Andi memimpin tim developer di Startup B\",\n    valid_at: \"2026-08-01T00:00:00Z\",\n    invalid_at: null\n}]-&gt;(:Entity {name: \"Startup B\"})\u003C\u002Fcode>\u003C\u002Fpre>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\u003Cp>Ini versi yang disederhanakan, ya. Setiap \u003Cem>edge\u003C\u002Fem> di Graphiti juga menyimpan jejak kapan ia kedaluwarsa di sistem, dan episode percakapan mana yang jadi sumbernya.\u003C\u002Fp>\n\u003Cp>Yang penting buat kita bukan sintaks \u003Cem>Cypher\u003C\u002Fem>-nya, tapi tiga sifat yang muncul dari struktur itu.\u003C\u002Fp>\n\u003Cp>Pertama, \u003Cem>retrieval\u003C\u002Fem>-nya jadi terarah. Ketika Andi bertanya soal pekerjaannya sekarang, sistem nggak perlu menebak mana yang relevan. Ia tinggal menelusuri relasi \u003Ccode>WORKS_AT\u003C\u002Fcode> milik Andi dan mengambil yang belum punya \u003Ccode>invalid_at\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Cp>Kedua, fakta lama nggak dihapus. \u003Cem>Edge\u003C\u002Fem> Startup A tetap ada lengkap dengan tanggal berakhirnya. \u003Cem>Agent\u003C\u002Fem> masih bisa menjawab &quot;dulu Andi kerja di mana?&quot; tanpa kehilangan riwayat. Beda mendasar dari pendekatan yang menimpa (\u003Cem>overwrite\u003C\u002Fem>) nilai lama.\u003C\u002Fp>\n\u003Cp>Ketiga, waktu jadi bagian dari data, bukan \u003Cem>metadata\u003C\u002Fem> yang menempel di pinggir. Karena setiap fakta punya rentang berlaku, pertanyaan &quot;mana yang masih benar&quot; akhirnya bisa dijawab dengan operasi biasa. Tidak perlu menebak lewat model.\u003C\u002Fp>\n\u003Ch2 id=\"peta-perkakasnya-dan-apa-yang-berubah-di-2026\">Peta perkakasnya, dan apa yang berubah di 2026\u003C\u002Fh2>\n\u003Cp>Jadi perkakasnya gimana? Kita punya dua \u003Cem>framework\u003C\u002Fem> besar yang mengotomatisasi pola ini, dan keduanya mengambil jalan berbeda dalam menghadapi fakta yang bertentangan.\u003C\u002Fp>\n\n\u003Cdiv class=\"mermaid-block my-6 not-prose rounded-xl border-2 border-black dark:border-gray-600 shadow-neo overflow-hidden bg-[#24292e]\" data-mermaid-diagram>\n  \u003Cdiv class=\"mermaid-header flex items-center justify-between px-4 py-2.5 bg-[#1f2428] border-b-2 border-black dark:border-gray-600 font-mono text-xs font-bold text-gray-300 select-none\">\n    \u003Cspan class=\"uppercase tracking-wider\">Diagram\u003C\u002Fspan>\n    \u003Cbutton type=\"button\" class=\"copy-code-btn flex items-center gap-1.5 px-3 py-1 text-xs font-bold bg-[#2f363d] text-gray-200 border-2 border-black dark:border-gray-500 rounded-lg shadow-neo-sm hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none hover:bg-gray-700 transition-all cursor-pointer\" data-code=\"flowchart%20TB%0A%20%20subgraph%20mem0%5B%22Mem0%20%E2%80%94%20OSS%20v3%20%2F%20Platform%22%5D%0A%20%20%20%20direction%20TB%0A%20%20%20%20A1%5B%22Pesan%20baru%22%5D%20--%3E%20A2%5B%22Ekstraksi%20fakta%3Cbr%2F%3EADD-only%2C%20satu%20panggilan%20LLM%22%5D%0A%20%20%20%20A2%20--%3E%20A3%5B%22Vector%20store%20%2B%20indeks%20BM25%3Cbr%2F%3E%2B%20tabel%20entitas%22%5D%0A%20%20%20%20A3%20--%3E%20A4%5B%22Retrieval%20hibrida%3Cbr%2F%3Esemantik%20%2B%20kata%20kunci%20%2B%20entitas%22%5D%0A%20%20%20%20A4%20--%3E%20A5%5B%22Satu%20skor%20gabungan%22%5D%0A%20%20%20%20A5%20--%3E%20A6%5B%22Konteks%20untuk%20LLM%22%5D%0A%20%20end%0A%0A%20%20subgraph%20zep%5B%22Zep%20%2F%20Graphiti%22%5D%0A%20%20%20%20direction%20TB%0A%20%20%20%20B1%5B%22Episode%20baru%22%5D%20--%3E%20B2%5B%22Ekstraksi%20entitas%20%2B%20relasi%3Cbr%2F%3E%2B%20resolusi%20kontradiksi%22%5D%0A%20%20%20%20B2%20--%3E%20B3%5B%22Graf%20temporal%3Cbr%2F%3Evalid_at%20%2F%20invalid_at%22%5D%0A%20%20%20%20B2%20--%3E%20B4%5B%22Embedding%20fakta%22%5D%0A%20%20%20%20B3%20--%3E%20B5%5B%22Hybrid%20search%3Cbr%2F%3Esemantik%20%2B%20kata%20kunci%20%2B%20traversal%20graf%22%5D%0A%20%20%20%20B4%20--%3E%20B5%0A%20%20%20%20B5%20--%3E%20B6%5B%22Fakta%20yang%20masih%20berlaku%22%5D%0A%20%20end\" title=\"Salin kode diagram\">\n      \u003Cspan class=\"copy-text\">Copy\u003C\u002Fspan>\n    \u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"mermaid-canvas p-4 overflow-x-auto\">\n    \u003Cdiv class=\"mermaid-source text-sm font-mono leading-relaxed text-[#e1e4e8]\">flowchart TB\n  subgraph mem0[\"Mem0 — OSS v3 \u002F Platform\"]\n    direction TB\n    A1[\"Pesan baru\"] --&gt; A2[\"Ekstraksi fakta&lt;br\u002F&gt;ADD-only, satu panggilan LLM\"]\n    A2 --&gt; A3[\"Vector store + indeks BM25&lt;br\u002F&gt;+ tabel entitas\"]\n    A3 --&gt; A4[\"Retrieval hibrida&lt;br\u002F&gt;semantik + kata kunci + entitas\"]\n    A4 --&gt; A5[\"Satu skor gabungan\"]\n    A5 --&gt; A6[\"Konteks untuk LLM\"]\n  end\n\n  subgraph zep[\"Zep \u002F Graphiti\"]\n    direction TB\n    B1[\"Episode baru\"] --&gt; B2[\"Ekstraksi entitas + relasi&lt;br\u002F&gt;+ resolusi kontradiksi\"]\n    B2 --&gt; B3[\"Graf temporal&lt;br\u002F&gt;valid_at \u002F invalid_at\"]\n    B2 --&gt; B4[\"Embedding fakta\"]\n    B3 --&gt; B5[\"Hybrid search&lt;br\u002F&gt;semantik + kata kunci + traversal graf\"]\n    B4 --&gt; B5\n    B5 --&gt; B6[\"Fakta yang masih berlaku\"]\n  end\u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\u003Cp>Bedanya kelihatan dari arah alirannya: Mem0 menyelesaikan kontradiksi di sisi \u003Cem>retrieval\u003C\u002Fem>, Graphiti di sisi penyimpanan.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Mem0\u003C\u002Fstrong> awalnya menempuh jalur ekstraksi dan pembaruan. Setiap pasangan pesan baru, LLM mengekstrak fakta kandidat. Fase kedua membandingkan fakta itu dengan memori lama, lalu memilih satu dari empat operasi: \u003Cem>ADD\u003C\u002Fem>, \u003Cem>UPDATE\u003C\u002Fem>, \u003Cem>DELETE\u003C\u002Fem>, atau \u003Cem>NOOP\u003C\u002Fem>. Dukungan graf opsional dipasang ke \u003Cem>graph database\u003C\u002Fem> eksternal seperti Neo4j. Kalau kamu membaca tutorial Mem0 yang beredar di blog-blog, hampir semuanya menggambarkan arsitektur versi itu.\u003C\u002Fp>\n\u003Cp>Empat operasi itu terdengar rapi. Kok bisa arahnya berubah? Versi terbaru algoritmanya cuma melakukan satu operasi: \u003Cem>ADD\u003C\u002Fem>, satu kali panggilan LLM, tanpa \u003Cem>UPDATE\u003C\u002Fem> maupun \u003Cem>DELETE\u003C\u002Fem> sama sekali. Ketika informasinya berubah, kedua fakta tetap disimpan lengkap dengan konteks waktunya. Yang mengurutkan mana yang paling relevan adalah \u003Cem>retrieval\u003C\u002Fem>-nya, bukan proses penulisan. \u003Cem>Retrieval\u003C\u002Fem>-nya sendiri kini hibrida: skor semantik, kata kunci (BM25), dan kecocokan entitas digabung jadi satu angka.\u003C\u002Fp>\n\u003Cp>Sejak versi itu pula, \u003Ccode>graph_store\u003C\u002Fcode> dihapus dari paket \u003Cem>open source\u003C\u002Fem> Mem0. Graf sekarang dibangun otomatis di sisi Platform dari entitas yang sama-sama disebut antar-memori, jadi urusan menyiapkan Neo4j sendiri sudah hilang. Tapi graf itu menyambungkan memori lewat entitas yang kebetulan beririsan, bukan lewat relasi bertipe dengan rentang berlaku. Nggak ada \u003Ccode>valid_at\u003C\u002Fcode> atau \u003Ccode>invalid_at\u003C\u002Fcode> yang bisa kamu telusuri.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Graphiti\u003C\u002Fstrong> mengambil jalur sebaliknya, dan lebih dekat ke model yang kita bahas di artikel ini. Ia menyimpan setiap fakta sebagai \u003Cem>edge\u003C\u002Fem> yang punya rentang berlaku ganda: kapan fakta itu benar di dunia nyata, dan kapan ia tercatat di sistem. Ketika ada informasi baru yang bertentangan, fakta lama ditandai kedaluwarsa lewat \u003Ccode>invalid_at\u003C\u002Fcode>, bukan dihapus. Setiap fakta juga masih bisa ditelusuri balik ke episode percakapan asalnya. Graphiti jalan sendiri di Neo4j atau FalkorDB, sedangkan versi terkelolanya adalah Zep.\u003C\u002Fp>\n\u003Cp>Dua-duanya paket Apache-2.0 dan sama-sama ramai dipakai. Diperiksa 17 Oktober 2026, repo Mem0 ada di sekitar 67 ribu bintang dan Graphiti sekitar 31 ribu.\u003C\u002Fp>\n\u003Cp>Satu catatan soal angka, dan ini berlaku untuk semua perkakas di daftar ini ⚠️: klaim peningkatan akurasi yang beredar di halaman masing-masing \u003Cem>vendor\u003C\u002Fem> berasal dari \u003Cem>benchmark\u003C\u002Fem> mereka sendiri. Angka itu boleh jadi petunjuk arah, tapi bukan hasil pengukuran independen.\u003C\u002Fp>\n\u003Ch2 id=\"contoh-menaruh-memory-layer-di-depan-fastapi\">Contoh: menaruh \u003Cem>memory layer\u003C\u002Fem> di depan FastAPI\u003C\u002Fh2>\n\u003Cp>Yuk lihat bentuknya di backend FastAPI. Cuplikan ini memakai API Mem0 OSS yang berlaku sekarang (paket \u003Ccode>mem0ai\u003C\u002Fcode> 2.x). Versinya penting, karena beberapa hal berubah dari tutorial-tutorial lama:\u003C\u002Fp>\n\n\u003Cdiv class=\"code-block-wrapper my-6 not-prose rounded-xl border-2 border-black dark:border-gray-600 shadow-neo overflow-hidden bg-[#24292e] text-[#e1e4e8] transition-all\">\n  \u003Cdiv class=\"code-block-header flex items-center justify-between px-4 py-2.5 bg-[#1f2428] border-b-2 border-black dark:border-gray-600 font-mono text-xs font-bold text-gray-300 select-none\">\n    \u003Cdiv class=\"flex items-center gap-2\">\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ff5f56] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#ffbd2e] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"w-3 h-3 rounded-full bg-[#27c93f] border border-black\u002F40 inline-block\">\u003C\u002Fspan>\n      \u003Cspan class=\"ml-2 font-mono font-bold text-xs uppercase tracking-wider text-gray-300\">PYTHON\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n    \u003Cbutton type=\"button\" class=\"copy-code-btn flex items-center gap-1.5 px-3 py-1 text-xs font-bold bg-[#2f363d] text-gray-200 border-2 border-black dark:border-gray-500 rounded-lg shadow-neo-sm hover:translate-x-0.5 hover:translate-y-0.5 hover:shadow-none hover:bg-gray-700 transition-all cursor-pointer\" data-code=\"from%20fastapi%20import%20FastAPI%0Afrom%20pydantic%20import%20BaseModel%0Afrom%20mem0%20import%20Memory%0A%0Aapp%20%3D%20FastAPI(title%3D%22Production%20Agent%20Memory%20Service%22)%0A%0A%23%20Konfigurasi%20OSS%20sekarang%20cuma%20memegang%20vector%20store%2C%20LLM%2C%20embedder%2C%20dan%20reranker.%0A%23%20%60graph_store%60%20dan%20%60enable_graph%60%20sudah%20dihapus%20dari%20paket%20open%20source.%0Aconfig%20%3D%20%7B%0A%20%20%20%20%22vector_store%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22provider%22%3A%20%22qdrant%22%2C%0A%20%20%20%20%20%20%20%20%22config%22%3A%20%7B%22host%22%3A%20%22localhost%22%2C%20%22port%22%3A%206333%7D%2C%0A%20%20%20%20%7D%2C%0A%7D%0Amemory%20%3D%20Memory.from_config(config)%0A%0A%23%20call_llm%3A%20helper%20pemanggil%20LLM%20milikmu.%20Di%20sini%20sengaja%20nggak%20ditulis%20detail%2C%0A%23%20karena%20yang%20sedang%20dibahas%20cuma%20lalu%20lintas%20memori%20di%20sekelilingnya.%0A%0Aclass%20ChatRequest(BaseModel)%3A%0A%20%20%20%20user_id%3A%20str%0A%20%20%20%20message%3A%20str%0A%0A%40app.post(%22%2Fchat%22)%0Aasync%20def%20chat_endpoint(req%3A%20ChatRequest)%3A%0A%20%20%20%20%23%201.%20Ambil%20fakta%20yang%20relevan.%20Entity%20ID%20masuk%20lewat%20%60filters%60%2C%20bukan%20kwarg%0A%20%20%20%20%23%20%20%20%20teratas%20seperti%20di%20versi%20lama%20--%20yang%20lama%20sekarang%20melempar%20ValueError.%0A%20%20%20%20hasil%20%3D%20memory.search(%0A%20%20%20%20%20%20%20%20req.message%2C%0A%20%20%20%20%20%20%20%20filters%3D%7B%22user_id%22%3A%20req.user_id%7D%2C%0A%20%20%20%20%20%20%20%20top_k%3D5%2C%0A%20%20%20%20)%0A%0A%20%20%20%20%23%202.%20Bentuk%20konteksnya.%20search()%20mengembalikan%20dict%20dengan%20key%20%22results%22.%0A%20%20%20%20fakta%20%3D%20%22%5Cn%22.join(f%22-%20%7Bm%5B'memory'%5D%7D%22%20for%20m%20in%20hasil%5B%22results%22%5D)%0A%20%20%20%20system_prompt%20%3D%20(%0A%20%20%20%20%20%20%20%20%22Anda%20adalah%20AI%20Assistant.%20Informasi%20terkini%20tentang%20pengguna%3A%5Cn%22%20%2B%20fakta%0A%20%20%20%20)%0A%0A%20%20%20%20%23%203.%20Panggil%20LLM%20utama%20dengan%20prompt%20yang%20sudah%20diperkaya%20fakta.%0A%20%20%20%20llm_response%20%3D%20await%20call_llm(system_prompt%3Dsystem_prompt%2C%20user_message%3Dreq.message)%0A%0A%20%20%20%20%23%204.%20Rekam%20giliran%20ini.%20Di%20add()%2C%20user_id%20masih%20diterima%20sebagai%20kwarg%20teratas.%0A%20%20%20%20memory.add(%0A%20%20%20%20%20%20%20%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%22role%22%3A%20%22user%22%2C%20%22content%22%3A%20req.message%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%22role%22%3A%20%22assistant%22%2C%20%22content%22%3A%20llm_response%7D%2C%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20user_id%3Dreq.user_id%2C%0A%20%20%20%20)%0A%0A%20%20%20%20return%20%7B%22response%22%3A%20llm_response%7D\" title=\"Salin kode\">\n      \u003Csvg class=\"w-3.5 h-3.5 copy-icon-svg\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n        \u003Cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\">\u003C\u002Fpath>\n      \u003C\u002Fsvg>\n      \u003Cspan class=\"copy-text\">Copy\u003C\u002Fspan>\n    \u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"code-block-content p-4 overflow-x-auto text-sm font-mono leading-relaxed bg-[#24292e]\">\n    \u003Cpre class=\"shiki github-dark\" style=\"background-color:#24292e;color:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">from\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> fastapi \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">import\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> FastAPI\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">from\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> pydantic \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">import\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> BaseModel\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">from\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> mem0 \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">import\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> Memory\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">app \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> FastAPI(\u003C\u002Fspan>\u003Cspan style=\"color:#FFAB70\">title\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"Production Agent Memory Service\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># Konfigurasi OSS sekarang cuma memegang vector store, LLM, embedder, dan reranker.\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># `graph_store` dan `enable_graph` sudah dihapus dari paket open source.\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">config \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">    \"vector_store\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">        \"provider\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"qdrant\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">        \"config\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: {\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"host\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"localhost\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"port\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">6333\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">},\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    },\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">memory \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> Memory.from_config(config)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># call_llm: helper pemanggil LLM milikmu. Di sini sengaja nggak ditulis detail,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\"># karena yang sedang dibahas cuma lalu lintas memori di sekelilingnya.\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">class\u003C\u002Fspan>\u003Cspan style=\"color:#B392F0\"> ChatRequest\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#B392F0\">BaseModel\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">):\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    user_id: \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">str\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    message: \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">str\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#B392F0\">@app.post\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"\u002Fchat\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">async\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\"> def\u003C\u002Fspan>\u003Cspan style=\"color:#B392F0\"> chat_endpoint\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">(req: ChatRequest):\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">    # 1. Ambil fakta yang relevan. Entity ID masuk lewat `filters`, bukan kwarg\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">    #    teratas seperti di versi lama -- yang lama sekarang melempar ValueError.\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    hasil \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> memory.search(\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        req.message,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFAB70\">        filters\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">{\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"user_id\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: req.user_id},\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFAB70\">        top_k\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">5\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    )\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">    # 2. Bentuk konteksnya. search() mengembalikan dict dengan key \"results\".\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    fakta \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\"> \"\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">\\n\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">.join(\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">f\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"- \u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">{\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">m[\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">'memory'\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">]\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">}\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\"> for\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> m \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">in\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> hasil[\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"results\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">])\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    system_prompt \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> (\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#9ECBFF\">        \"Anda adalah AI Assistant. Informasi terkini tentang pengguna:\u003C\u002Fspan>\u003Cspan style=\"color:#79B8FF\">\\n\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\"> +\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> fakta\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    )\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">    # 3. Panggil LLM utama dengan prompt yang sudah diperkaya fakta.\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    llm_response \u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\"> await\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> call_llm(\u003C\u002Fspan>\u003Cspan style=\"color:#FFAB70\">system_prompt\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">system_prompt, \u003C\u002Fspan>\u003Cspan style=\"color:#FFAB70\">user_message\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">req.message)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D\">    # 4. Rekam giliran ini. Di add(), user_id masih diterima sebagai kwarg teratas.\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    memory.add(\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        [\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">            {\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"role\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"user\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"content\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: req.message},\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">            {\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"role\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"assistant\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"content\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: llm_response},\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">        ],\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#FFAB70\">        user_id\u003C\u002Fspan>\u003Cspan style=\"color:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">req.user_id,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#E1E4E8\">    )\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#F97583\">    return\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\"> {\u003C\u002Fspan>\u003Cspan style=\"color:#9ECBFF\">\"response\"\u003C\u002Fspan>\u003Cspan style=\"color:#E1E4E8\">: llm_response}\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\u003Cp>Kalau kamu menyalin dari tutorial lama, tiga hal ini yang paling sering bikin kaget, deh. Pertama, \u003Ccode>user_id\u003C\u002Fcode> yang dikirim sebagai argumen teratas di \u003Ccode>search()\u003C\u002Fcode> sekarang melempar \u003Cem>error\u003C\u002Fem>. Kedua, nama parameternya \u003Ccode>top_k\u003C\u002Fcode>, bukan \u003Ccode>limit\u003C\u002Fcode>. Ketiga, hasilnya dibungkus dalam key \u003Ccode>results\u003C\u002Fcode>, bukan \u003Cem>list\u003C\u002Fem> telanjang.\u003C\u002Fp>\n\u003Cp>Satu hal lagi yang perlu disadari: konfigurasi di atas cuma punya memori semantik. Pertanyaan &quot;mana yang masih berlaku&quot; tetap harus dijawab di atasnya, entah dengan menyimpan \u003Cem>metadata\u003C\u002Fem> sendiri lalu memfilternya, atau dengan memakai \u003Cem>engine\u003C\u002Fem> yang memang menyimpan rentang waktu di tiap fakta. Di situ pembagian tugasnya kelihatan: \u003Cem>vector store\u003C\u002Fem> untuk menemukan hal yang mirip, graf temporal untuk memutuskan hal yang masih benar.\u003C\u002Fp>\n\u003Ch2 id=\"memori-yang-hidup-menghasilkan-agent-yang-relevan\">Memori yang hidup menghasilkan \u003Cem>agent\u003C\u002Fem> yang relevan\u003C\u002Fh2>\n\u003Cp>Membangun \u003Cem>agent\u003C\u002Fem> AI yang benar-benar personal bukan perlombaan memperbesar jendela konteks. Yang menentukan kualitasnya adalah seberapa pintar arsitektur sistem memfilter, memperbarui, dan melupakan informasi yang sudah kedaluwarsa. Dan bagian melupakan itu, anehnya, yang paling sering dilupakan 😅\u003C\u002Fp>\n\u003Cp>Begitu kecepatan pencarian semantik digabung dengan ketepatan relasi bertanggal, \u003Cem>agent\u003C\u002Fem>-mu nggak cuma mengingat kata-kata pengguna. Ia mulai bisa membedakan apa yang dulu benar dan apa yang benar sekarang 👋\u003C\u002Fp>\n","\u003Cul>\n\u003Cli>\u003Cem>Vector store\u003C\u002Fem> murni gagal saat fakta berubah, karena pencarian kemiripan makna nggak menyimpan informasi soal kapan sebuah fakta masih berlaku dan kapan sudah kedaluwarsa.\u003C\u002Fli>\n\u003Cli>Memori \u003Cem>agent\u003C\u002Fem> modern dibagi tiga lapisan: working memory di jendela konteks, semantic memory di \u003Cem>vector store\u003C\u002Fem> untuk pengetahuan tak terstruktur, dan episodic\u002Frelational memory di graf entitas bertanggal.\u003C\u002Fli>\n\u003Cli>\u003Cem>Temporal knowledge graph\u003C\u002Fem> menyimpan fakta sebagai relasi berentang waktu, lalu menandai fakta lama sebagai kedaluwarsa tanpa menghapus riwayatnya.\u003C\u002Fli>\n\u003Cli>Sejak 2026, graph store dihapus dari paket \u003Cem>open source\u003C\u002Fem> Mem0 dan pindah jadi fitur bawaan Platform-nya, sementara Graphiti tetap jadi jalur \u003Cem>open source\u003C\u002Fem> untuk graf temporal penuh.\u003C\u002Fli>\n\u003Cli>Klaim akurasi di halaman \u003Cem>vendor\u003C\u002Fem> berasal dari \u003Cem>benchmark\u003C\u002Fem> mereka sendiri, jadi angkanya lebih layak dibaca sebagai petunjuk arah.\u003C\u002Fli>\n\u003C\u002Ful>\n",[131,133,135,146,154,163],{"id":49,"title":50,"slug":51,"excerpt":52,"thumbnail_image":48,"tags":132},[55,56,57,58],{"id":74,"title":75,"slug":76,"excerpt":77,"thumbnail_image":48,"tags":134},[79,80,81,56,57,82],{"id":136,"title":137,"slug":138,"excerpt":139,"thumbnail_image":48,"tags":140},"124","Rahasia Zero-Cost Abstraction di Rust, Dibedah dari Assembly","rahasia-zero-cost-abstraction-di-rust","\"Abstraksi tinggi selalu lebih lambat\" itu anggapan lama. Iterator Rust bisa menyamai loop manual, dan assembly-nya bisa kita baca sendiri. Yuk, kita bongkar!",[141,142,143,144,145],"rust","performance","system-programming","compiler","optimasi",{"id":147,"title":148,"slug":149,"excerpt":150,"thumbnail_image":48,"tags":151},"120","Tiap Nota di Kafe Ini Isinya Pesanan yang Sama","tiap-nota-isinya-pesanan-yang-sama","Nota tiap meja isinya sama semua, dan totalnya Rp0 — padahal tiap meja cuma pesan satu barang. Coba tebak kenapa.",[82,152,153,44],"scope","mutasi",{"id":155,"title":156,"slug":157,"excerpt":158,"thumbnail_image":48,"tags":159},"119","Aturan Sintaks: Satu Titik Koma Bikin Program Nggak Jalan","aturan-sintaks-pemrograman","Baru nulis beberapa baris, programnya udah nolak jalan? Seringnya bukan logikanya yang salah, cuma satu karakter yang kurang atau salah tempat.",[160,161,162,82],"syntax","dasar","konsep",{"id":164,"title":165,"slug":166,"excerpt":167,"thumbnail_image":48,"tags":168},"122","Script yang Nggak Bisa Bilang di Mana Salahnya","script-yang-tidak-bilang-di-mana-salahnya","Script yang berhenti di baris jelek pertama cuma melaporkan sebagian masalah yang ada. Ini yang muncul di layar sekarang.",[82,169,170,44],"error-handling","validasi",[172,176,179,182,185,188],{"text":173,"depth":174,"id":175},"Kenapa vector store gagal saat fakta berubah",2,"kenapa-vector-store-gagal-saat-fakta-berubah",{"text":177,"depth":174,"id":178},"Tiga lapisan memori agent","tiga-lapisan-memori-agent",{"text":180,"depth":174,"id":181},"Bentuk memori yang mengingat waktu","bentuk-memori-yang-mengingat-waktu",{"text":183,"depth":174,"id":184},"Peta perkakasnya, dan apa yang berubah di 2026","peta-perkakasnya-dan-apa-yang-berubah-di-2026",{"text":186,"depth":174,"id":187},"Contoh: menaruh memory layer di depan FastAPI","contoh-menaruh-memory-layer-di-depan-fastapi",{"text":189,"depth":174,"id":190},"Memori yang hidup menghasilkan agent yang relevan","memori-yang-hidup-menghasilkan-agent-yang-relevan",1792331688233]