Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 4 additions & 3 deletions src/contents/posts/en/building-agent-memory-free-vendor.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,15 @@ importance: 7
mood: excited
related_dates: [2026-07-01]
uncategorized:
technologies: []
libraries: [Pydantic]
technologies: [Pydantic]
projects: []
---
### Today's Summary
- Completed the refactor of Nouva's agent memory with the hybrid scoring scheme.
- Discussed K3s cluster worker nodes setup with Freedy.
```

* **Controlled Vocabulary:** The `projects`, `technologies`, and `libraries` fields must match a pre-defined list in `memory_config.json`. If a new term appears (like the `Pydantic` library which isn't registered yet), the LLM places it under `uncategorized` to keep the main vocabulary clean.
* **Controlled Vocabulary:** The `projects` and `technologies` fields must match a pre-defined list in `memory_config.json`. The `libraries` category (like React, Next.js, LangChain) has now been merged directly into `technologies` to keep metadata compact. If a new term appears (like a library or technology `Pydantic` which isn't registered yet), the LLM places it under `uncategorized` to keep the main vocabulary clean.

You might wonder, *"Why hardcode the controlled vocabulary and scoring weights in a static `memory_config.json` file?"* The answer is simple: because this memory system is purely for my personal use. At a personal scale, a static configuration that is re-read per invocation is more than enough, highly secure, and avoids adding the unnecessary complexity of a dynamic database layer or a hot-reloaded service.
* **Idempotency & Reconciliation:** I added a `reconcile_missing_summaries()` pass. If the LLM proxy timeouts or crashes mid-sync, the next cron job automatically scans for missing summaries and regenerates them. No more silent data loss.
Expand Down Expand Up @@ -101,6 +100,8 @@ Ultimately, I refactored this approach to make it much simpler and deterministic
1. **Programmatic Topic Detection (Sync-time):** During the daily sync process, our Python script automatically scans the new summary's metadata and matches it against keywords in the local `MEMORY_INDEX.md` file. The top 2 most relevant dates are injected into the YAML metadata as `related_dates`. This has *zero* LLM token cost and guarantees 100% valid dates.
2. **1-Hop Expansion (Retrieval-time):** When the AI agent queries memory (`query-memory.py`), we no longer run a recursive BFS traversal. We simply take the candidate dates from the Semantic RAG and perform a **1-hop expansion** to their `related_dates`, decaying their semantic score by 30% (`score * 0.7`).

Additionally, we designed the output of `query-memory.py` to be highly efficient using a **Lazy Loading** approach. Instead of dumping long, token-heavy raw chat transcripts, the script now only returns clean summaries from the relevant `.summary.md` files, accompanied by a dynamic `Path` to the raw transcripts subfolder on the NAS (`daily_sessions/YYYY-MM-DD/`) and `Links` to associated entities. The LLM can then read raw transcripts on-demand only when specific conversational details are required.

With this approach, we still get the benefits of document relationships (topic clusters), but the code remains lightweight, fast, and free from complex traversal bugs. *Keep it simple!*

---
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ Instead, I implemented a **Two-Tier Hybrid Memory** architecture that separates
│ Searches MEMORY_INDEX.md (The Map)
│ Result: Finds Target Date (e.g., 2026-06-24)
├─► Step 2: Direct Read (NAS Zip Archive)
│ Reads raw transcript directly from zip on NAS
├─► Step 2: Direct Read (NAS Folder Archive)
│ Reads raw transcript from daily folder on NAS
└─► Fallback: Fast Keyword Search (Grep in NAS Zip)
If RAG confidence is low or no matches found
└─► Parallel: Fast Keyword Search (Grep in NAS Summaries)
Searches specific keywords in parallel
```

#### Tier 1: Semantic Index Map (MEMORY_INDEX.md)
Expand All @@ -80,24 +80,24 @@ The `MEMORY_INDEX.md` is generated automatically by a weekly sync script. It con
```
Because the file is clean and free of system error stack traces, the RAG embeds it perfectly using a local **`bge-m3`** model running on the homelab's CPU (Ryzen 7 5825U).

#### Tier 2: Raw Archive (NAS Zip)
All raw session logs (`YYYY-MM-DD-HHMM.md`) are zipped and transferred to our NAS (`virtual-nas`) after a 2-day grace period, keeping the local agent host disk footprint minimal.
#### Tier 2: Raw Archive (NAS Folder)
All raw session logs (`YYYY-MM-DD-HHMM.md`) are transferred to their respective daily folders (`daily_sessions/YYYY-MM-DD/`) on our NAS (`virtual-nas`) after a 2-day grace period, keeping the local agent host disk footprint minimal.

#### Tier 3: The Glue (Unified Script)
I wrote a unified wrapper script `query-memory.py` to bridge the two layers:
1. When I ask: *"Do you remember our discussion about ESG?"*, the script queries the RAG (AnythingLLM).
2. The RAG matches the query to the `MEMORY_INDEX.md` chunk and returns the date: **`2026-06-24`**.
3. The script automatically opens the corresponding ZIP archive on the NAS, reads the raw `2026-06-24.md` transcript directly into RAM, and feeds it to the AI Agent.
4. **Fallback:** If the semantic search returns low confidence, the script falls back to a fast regex keyword search across all zipped markdown files on the NAS.
3. The script automatically opens the corresponding folder on the NAS, reads the raw `2026-06-24.md` transcript directly into RAM, and feeds it to the AI Agent.
4. **Parallel Search:** Alongside semantic search, the script runs a fast parallel regex keyword search across all summary files on the NAS to ensure 100% accuracy for specific proper nouns.

---

### Why This Approach Wins

* **Zero Extra Dependencies:** It runs on standard Python libraries (`zipfile` & `requests`). No extra database containers or services to maintain.
* **Zero Extra Dependencies:** It runs on standard Python libraries (`requests` & standard file-handling). No extra database containers or services to maintain.
* **Resource-Friendly:** The local agent host (running on a constrained 4GB RAM LXC) stays incredibly light because embedding workloads are delegated to Ollama in the backend.
* **100% Portable:** Because the data is stored in raw Markdown and standard ZIPs, it is completely vendor-agnostic. If I want to migrate my assistant to another LLM platform, I can import the NAS archives in seconds.
* **Vendor Lock-in Free (Honestly, I'm Tired of It):** Many modern memory frameworks force you to use their proprietary cloud databases, paid APIs, or custom formats that keep your data hostage. Honestly, I'm tired of being locked into a single ecosystem. With raw `.md` and standard `.zip` files, the data is 100% mine. If tomorrow I want to switch LLM providers or migrate from OpenClaw to another framework, I just copy-paste the zip files without worrying about compatibility.
* **100% Portable:** Because the data is stored in raw Markdown and standard folder structures, it is completely vendor-agnostic. If I want to migrate my assistant to another LLM platform, I can import the NAS archives in seconds.
* **Vendor Lock-in Free (Honestly, I'm Tired of It):** Many modern memory frameworks force you to use their proprietary cloud databases, paid APIs, or custom formats that keep your data hostage. Honestly, I'm tired of being locked into a single ecosystem. With raw `.md` and standard folder structures, the data is 100% mine. If tomorrow I want to switch LLM providers or migrate from OpenClaw to another framework, I just copy-paste the NAS folder without worrying about compatibility.
* **Transparent & Debuggable:** If the AI remembers something incorrectly, I can simply open the markdown file and edit the line. No vector database queries or graph node deletions required.

*Keep it simple*. Sometimes, the best solution to complex AI infrastructure problems is to step back and use structured text files glued together with precise automation scripts.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,24 +80,24 @@ File `MEMORY_INDEX.md` ini di-generate otomatis via script sync mingguan. Isinya
```
Karena filenya bersih dari log error dan chat berisik, RAG bisa nge-embed ini dengan akurat pake model **`bge-m3`** secara lokal lewat Ollama di server homelab (Ryzen 7 5825U).

#### Tier 2: Raw Archive (NAS Zip)
Semua chat log mentah per sesi (`YYYY-MM-DD-HHMM.md`) otomatis di-zip dan ditransfer ke NAS (`virtual-nas`) setelah grace period H-2, lalu file lokalnya dihapus biar disk agent-host ga penuh.
#### Tier 2: Raw Archive (NAS Folder)
Semua chat log mentah per sesi (`YYYY-MM-DD-HHMM.md`) otomatis ditransfer ke subfolder tanggal (`daily_sessions/YYYY-MM-DD/`) di NAS (`virtual-nas`) setelah grace period H-2, lalu file lokalnya dihapus biar disk agent-host ga penuh.

#### Tier 3: The Glue (Unified Script)
Gw bikin script wrapper `query-memory.py`. Alurnya begini:
1. Pas gw nanya *"Inget ga bahasan ESG kita?"*, script bakal nanya ke RAG (AnythingLLM).
2. RAG bakal nemu kecocokan di file `MEMORY_INDEX.md` dan mengembalikan potongan teks yang berisi tanggal target: **`2026-06-24`**.
3. Script secara otomatis langsung ngebuka file zip di NAS yang meng-cover tanggal tersebut, membaca file `2026-06-24.md` langsung ke RAM, dan menyajikan transkrip full-nya ke AI Agent.
4. **Fallback:** Kalau RAG gagal nemu secara semantik, script otomatis nge-run *keyword search* cepat (grep-like) langsung ke seluruh arsip ZIP di NAS.
3. Script secara otomatis langsung ngebuka folder di NAS yang meng-cover tanggal tersebut, membaca file `2026-06-24.md` langsung ke RAM, dan menyajikan transkrip full-nya ke AI Agent.
4. **Parallel Search:** Selain pencarian semantik, script secara paralel melakukan pencarian kata kunci (*keyword search*) cepat (grep-like) langsung ke seluruh file summary di NAS untuk menjamin keakuratan kata benda spesifik.

---

### Kenapa Skema Ini Indah Banget?

* **Zero Extra Dependencies:** Cuma modal script Python standar bawaan (`zipfile` & `requests`). Ga perlu install database graph baru atau library aneh-aneh.
* **Zero Extra Dependencies:** Cuma modal script Python standar bawaan (`requests` & standard file-handling). Ga perlu install database graph baru atau library aneh-aneh.
* **Resource-Friendly:** `agent-host` gw yang cuma dapet jatah 4GB RAM di Proxmox tetep enteng jalannya karena beban berat embedding model `bge-m3` dititipin ke Ollama di backend.
* **100% Portable:** Karena formatnya cuma Markdown mentah dan file `.zip`, datanya aman dan gampang banget dipindahin kalau suatu saat gw mau migrasi asisten AI ini ke platform lain (tinggal import folder zip NAS ke Claude Project, kelar).
* **Bebas Vendor Lock-in (Jujur, Gw Udah Capek):** Banyak framework memory sekarang yang maksa kita pake database cloud mereka, API berbayar mereka, atau format proprietary yang bikin data kita tersandera. Gw udah capek dikunci sama satu ekosistem. Dengan format `.md` mentah dan `.zip` standar ini, datanya 100% milik gw. Kalau besok gw mau ganti LLM provider atau migrasi dari OpenClaw ke framework lain, gw tinggal copy-paste folder zip-nya aja tanpa pusing mikirin kompatibilitas.
* **100% Portable:** Karena formatnya cuma Markdown mentah dan struktur folder standar, datanya aman dan gampang banget dipindahin kalau suatu saat gw mau migrasi asisten AI ini ke platform lain (tinggal import folder NAS ke Claude Project, kelar).
* **Bebas Vendor Lock-in (Jujur, Gw Udah Capek):** Banyak framework memory sekarang yang maksa kita pake database cloud mereka, API berbayar mereka, atau format proprietary yang bikin data kita tersandera. Gw udah capek dikunci sama satu ekosistem. Dengan format `.md` mentah dan struktur folder standar ini, datanya 100% milik gw. Kalau besok gw mau ganti LLM provider atau migrasi dari OpenClaw ke framework lain, gw tinggal copy-paste folder NAS-nya aja tanpa pusing mikirin kompatibilitas.
* **Transparan & Mudah Di-debug:** Kalau AI salah inget fakta, gw tinggal buka file `.md`-nya dan edit baris yang salah. Ga perlu pusing nyari cara nge-wipe database vector/graph.

*Keep it simple*. Kadang solusi terbaik buat masalah AI modern bukanlah dengan nambah teknologi baru yang makin kompleks, tapi dengan balik ke dasar: file teks terstruktur dan script automasi yang presisi.
Expand Down
7 changes: 4 additions & 3 deletions src/contents/posts/id/membangun-agent-memory-bebas-vendor.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,15 @@ importance: 7
mood: excited
related_dates: [2026-07-01]
uncategorized:
technologies: []
libraries: [Pydantic]
technologies: [Pydantic]
projects: []
---
### Ringkasan Hari Ini
- Menyelesaikan refactoring memori agen Nouva dengan skema hybrid scoring.
- Diskusi dengan Freedy soal setup K3s cluster worker nodes.
```

* **Controlled Vocabulary:** Field `projects`, `technologies`, dan `libraries` wajib dicocokkan dengan daftar baku di `memory_config.json`. Kalau ada term baru (misal library `Pydantic` yang belum terdaftar), LLM bakal masukin ke nested object `uncategorized` agar tidak mencemari vocabulary utama.
* **Controlled Vocabulary:** Field `projects` dan `technologies` wajib dicocokkan dengan daftar baku di `memory_config.json`. Kategori `libraries` (seperti React, Next.js, LangChain) sekarang digabungkan langsung ke dalam `technologies` agar metadata lebih ringkas. Kalau ada term baru (misal library/teknologi `Pydantic` yang belum terdaftar), LLM bakal masukin ke nested object `uncategorized` agar tidak mencemari vocabulary utama.

Mungkin lu bertanya-tanya, *"Kenapa controlled vocabulary dan parameter bobot ini di-hardcode di file `memory_config.json`?"* Jawabannya sederhana: karena sejauh ini sistem memori ini masih digunakan murni untuk kebutuhan personal gw. Dengan skala personal, konfigurasi statis yang dibaca ulang setiap run (per-invocation) jauh lebih dari cukup, aman, dan ga perlu nambah kompleksitas layer database dinamis atau hot-reload service yang nyusahin.
* **Idempotency & Reconciliation:** Gw menambahkan langkah `reconcile_missing_summaries()`. Kalau hari kemarin LLM proxy-nya timeout atau error di tengah jalan, cron job berikutnya bakal otomatis nge-scan mana summary yang bolong dan nge-generate ulang. Gak ada lagi cerita *silent data loss*.
Expand Down Expand Up @@ -101,6 +100,8 @@ Akhirnya, gw merombak pendekatan ini menjadi jauh lebih simpel dan deterministik
1. **Deteksi Topik Pas Sync (Programmatik):** Pas proses sinkronisasi harian, script Python kita bakal nge-scan metadata summary baru dan mencocokkannya dengan keyword di file `MEMORY_INDEX.md` lokal secara otomatis. Top 2 tanggal yang paling relevan disuntikkan ke YAML metadata `related_dates`. Ini *zero cost* token LLM dan dijamin 100% tanggalnya valid.
2. **Ekspansi 1-Hop (Retrieval):** Pas asisten AI nyari memori (`query-memory.py`), kita gak pake BFS traversal rekursif lagi. Kita cuma ngambil tanggal kandidat hasil RAG Semantik, lalu melakukan ekspansi **1-hop** ke tanggal-tanggal yang ada di `related_dates` dengan memotong skor semantiknya sebesar 30% (`score * 0.7`).

Selain itu, hasil output dari `query-memory.py` juga kita buat seefisien mungkin menggunakan pendekatan **Lazy Loading**. Daripada men-dump transkrip chat mentah yang panjang dan boros token LLM, script ini sekarang hanya me-return ringkasan bersih dari file `.summary.md` yang relevan, disertai dengan `Path` dinamis ke subfolder transkrip mentah di NAS (`daily_sessions/YYYY-MM-DD/`) dan `Links` entitas terkait. LLM kemudian bisa membaca transkrip mentah secara *on-demand* hanya jika membutuhkan detail percakapan yang lebih spesifik.

Dengan cara ini, kita tetep dapet keuntungan relasi antar dokumen (graf topik), tapi kodenya super enteng, cepat, dan bebas dari bug traversal yang rumit. *Keep it simple!*

---
Expand Down
Loading