三個月的 Over-design 反省——Memory Hall 是我第一個刻意保持小的東西

這三個月我做了 mk-council、mk-brain、ops-hub 三個 AI infra——每個都 over-design。今天 ship 的 Memory Hall 是我第一個刻意保持小的東西。48 小時從零上線,v0.2 今天落地。這篇一半講產品,一半講我從前三個 infra 踩的坑。

三個月的 Over-design 反省——Memory Hall 是我第一個刻意保持小的東西

這三個月我在家裡弄了一組「七位一體」的 AI agent stack——Claude、Codex、Gemini、Perplexity Max、SuperGrok、gemma4、加我自己。每個 agent 守不同的活(設計 / 寫 code / 分析 / research / 社群情報 / 本地 brain / 裁決)。

過程中做了幾個基礎建設:mk-council(統一協調)、mk-brain(個人知識倉)、ops-hub(家庭 fleet)。

每一個單看都合理。疊起來我才發現一件事:

我一直在 over-design

今天要講的 Memory Hall,是我第一個刻意保持小的東西。48 小時從零上線,v0.2 今天落地。這篇一半在講產品,一半在講我從前三個 infra 踩的坑。


先認錯:mk-council / mk-brain / ops-hub 的 over-design 樣貌

不是說這三個 infra 失敗。它們都在跑。但每個都有同一個病徵:每個單一功能都合理,疊起來燒注意力

  • mk-council:一開始想做成「統一協調 substrate + dissent queue + circuit breaker + bypass clause + audit log + MCP gateway」——直到 Codex 在 DEC-012 ratification 的 Round 2 點出「你這樣設計,council 壞掉的時候反而把多個故障域收斂成一個」。我們後來補了一條 rules/middleware-not-monopoly.md 收尾。但這是事後修補,不是前置約束
  • mk-brain:Observatory(書籤打分)+ scoring 實驗 + tech-blog angle + market-scan + repo_analysis + roundtable + ghost-publish pipeline——每個功能單看都合理。合起來變成「你要打開儀表板才知道系統在幹嘛」,直接違反我自己寫的四層北極星 L4「保護 Maki 注意力」
  • ops-hub:fleet commander + envelope + router + supergrok integration + 七位一體 runtime code——裡面多少功能今天 daily 真的在用?多少是「看起來該有」?我上週才停下來承認:超過一半沒在用

Over-design 的共同 pattern 我總結出來:

  1. 單一功能正當性陷阱:每個功能單獨看都合理
  2. 完整性誘惑:「這套系統要是完整的話應該有 X」——但沒有「完整」這件事,只有「今天被誰用」
  3. 想像中的未來使用者:「將來某天有人要 X 就會很方便」——通常那天不會來,就算來了需求也跟你想像的不一樣
  4. 技術美學 > 實用:「這樣設計比較乾淨」——乾淨不等於有用

這三個月我反覆踩這些。每踩一次就多寫一條規則(rules/fast-path.mdrules/four-layer-north-star.mdrules/middleware-not-monopoly.md)。但規則是事後補救。

Memory Hall 是第一個前置約束生效的。


Memory Hall 的設計承諾只有一句

docker compose up → curl POST /v1/memory/write → 有記憶

三步。就這樣。

每個被我刪掉的 feature,都是因為加進去會破壞這個承諾。


為什麼要做 Memory Hall

AI Agent 每個人都在做。但記憶層還沒有 canonical 標準——

  • OpenAI Memory 是封閉 API
  • Anthropic Memory Tool 還在 preview
  • mem0 / Zep / LangMem 各有主張、各有取捨
  • MCP / MemoryProtocol 還在醞釀

對今天就要動手做 agent 的人來說,這是選擇障礙 + vendor risk 雙殺。

我用 mem0 幾個月。把它當產品沒意見。但把它當 agent stack 的基礎依賴,幾個結構性問題疊起來之後,我決定自己寫:

  • SaaS-only 部署:我的 agent stack 部分跑在沒網路的 sandbox(Codex CLI headless、Gemini CLI),SaaS-only 直接卡死
  • CJK 斷詞:我的記憶 70% 是中文,預設索引對中文 query 命中率極低
  • 服務節奏外部控制:沒辦法 pin 版本,API 變動被動接受
  • 黑盒:debug recall 問題看不到他們 server 做什麼
  • 搬家成本:真的要換,export API 的資料結構還要自己轉

不是攻擊 mem0。是設計選擇不同

我要的是像 PostgreSQL 一樣的東西:純引擎、開源、本地、我控制一切


Memory Hall vs mem0

面向 mem0 Memory Hall
部署 SaaS-only 單機 / 伺服器 / 多機冷備都行
資料位置 他們 server 你本機 SQLite file
CJK 支援 英文優先 jieba 原生斷詞
Embedder 他們的 你選(Ollama / OpenAI / fine-tuned)
架構 黑盒 SQLite + sqlite-vec + FastAPI
入口 HTTP API HTTP / CLI / Python embedded
License Closed Apache 2.0
Lock-in
月費 按量 0(只吃你家電力)

部署彈性:從 laptop 到多機冷備

這是跟 mem0 最實質的差別。

最小跑法:一台 M1 MacBook,docker compose up,搞定。

我家目前的拓樸

  • Primary:Mac mini M4(家庭 AI 中心 .122),跑 memory-hall:0.2.0 container
  • Cold Standby:另一台 Mac mini .221,rsync 每 5 分鐘從 primary 拉資料,container 停機備用
  • Embedder:offload 到 DGX Spark(128GB),跑 Ollama bge-m3,透過 Tailscale 連到 mini
  • 寫入方:七個 agent 從不同設備寫進同一個 memhall

整組完全在我家跑。沒有一個 package 在雲上。Primary 掛掉 → 手動 docker start cold standby 接手。


技術選擇:三個「不需要對方也能活」的元件

  • SQLite WAL:單檔、免 server、busy_timeout + BEGIN IMMEDIATE 處理並發
  • sqlite-vec:向量是 virtual table,進來就是個 .so,沒有「起一個 vector DB」的心智負擔
  • Ollama:embedder 獨立部署,明天想換 OpenAI embeddings?改一個 URL

唯一的非標準零件是 jieba——為了 CJK recall。如果你主要寫英文可以關掉。

順便今天踩到一個 upstream packaging bug:sqlite-vec 0.1.6 在 PyPI 上的 manylinux_2_17_aarch64 wheel 裡其實塞的是 32-bit ARM binaryissue #251)。升到 0.1.9 解決。Dockerfile 加了 build-time smoke test,下次 upstream 再 mis-package,build 階段就炸:

RUN python -c "import sqlite3, sqlite_vec; c = sqlite3.connect(':memory:'); c.enable_load_extension(True); sqlite_vec.load(c); print(c.execute('SELECT vec_version()').fetchone())"

v0.2 今日驗收

指標 v0.1 baseline v0.2 今日
Pure-CJK「撞牆」BM25 0 0.26
Mixed query「sqlite-vec 0.1.9」BM25 0 0.05+
50-way burst 資料流失 0 0
sustained 100/30s 資料流失 0 0
durability(restart / race / dedup)
FTS index unicode61 jieba pre-tokenize
sqlite-vec vec0 ELFCLASS32 fallback v0.1.9 載入 ✅

刻意不做的事(對比 mk-council / mk-brain 的教訓)

這是 Memory Hall 跟我前面三個 infra 最大的差別:我把「不做清單」寫在前面,而不是事後補規則

四個常被問的 feature,我都刻意先不做

MCP server

  • 要裝 MCP client、配 schema、debug protocol——對只想「agent 記東西」的人是過度設計
  • 教訓來自 mk-council:我們最初就想包 MCP,結果 protocol 本身在動(transport / tool schema 變過幾次),包早了就是重寫
  • 留在 v0.3。讓 use case 先浮現再決定 shape

Auth

  • 個人 lab / PoC 階段管 keys / tokens / 輪換是壓倒性負擔
  • 教訓來自 ops-hub:我們很早就想做 fleet-wide auth,花了一週討論 JWT vs API key,最後沒跑起來。原因:家裡七個 agent 都是我控制,根本沒有「誰不是我」的場景
  • 要外部 exposure?放你自己的 gateway(Cloudflare Tunnel / Tailscale ACL / nginx)處理。memhall 不是邊界守門,是內層引擎

Replica / HA

  • SQLite 的核心價值 = 單檔、零維護、簡單。做 replica 要變 Postgres-lite 或依賴 Litestream,兩個都違反我選 SQLite 的理由
  • 教訓來自 mk-brain:我們當初想做 observatory 的 backup pipeline 做了兩週,最後還是 rsync 能 work。複雜方案的 90% 價值 rsync 都給你了
  • 現行 rsync 冷備對個人 / home lab 是 80% 的實 HA 價值

Enrichment worker

  • 每個 enrichment feature(fact extraction / summarization / entity resolution)都是獨立產品,各自要 LLM 預算、失敗語義、opt-in 控制——內建一套 = 鎖一套意見
  • 教訓來自 mem0 本身:它之所以不合我用,部分原因就是它opinionated enrichment 關不掉。我要是重蹈覆徹,就是在重建 mem0
  • Memory Hall 的設計:engine 只管儲存 + 檢索。enrichment 在上層(mk-brain 或你自己的 layer)做。這是 ADR-0003 (engine-vs-platform) 的直接落地

Roadmap 上寫著的不是承諾要補。是「真有人帶著 production 情境來教我怎麼設計時才補」。在那之前,Memory Hall 保留「三步從零到跑」的 promise。


這次跟以前不一樣的地方

做 mk-council / mk-brain / ops-hub 的時候,我先寫規則再發現問題

做 Memory Hall 的時候,我先寫「不做清單」

差別在哪?

寫規則是防禦性的——你預期會出事,所以提前訂規則。但規則只能告訴你不要往哪走,不告訴你該往哪走。結果就是功能堆起來,規則疊起來,你看著自己的 infra 累。

寫「不做清單」是進攻性的——你先決定這東西的形狀是什麼、為什麼。每個新想加的功能,要證明它不違反形狀。通不過就不加。

這有點像雕刻。你不是把黏土堆成型,你是把大理石多餘的部分削掉,留下你要的形狀。

Memory Hall 目前的「形狀」:

一個可以在你任何一台電腦上跑、用三步從零到「有記憶」的開源引擎

MCP 違反這個。Auth 違反這個。Replica 違反這個。Enrichment 違反這個。

所以都不加。


要不要用它?

用 Memory Hall,如果你:

  • 完全控制你的 agent 記憶(部署、備份、搬移、fork)
  • 記憶內容是中文或中英混合
  • 需要零網路 / sandbox / offline 的使用情境
  • 有一台可以跑 Docker 的機器

繼續用 mem0 / Zep / LangMem,如果你:

  • 要馬上跑、不想維護任何東西
  • 記憶內容以英文為主
  • 單一 agent、小規模、SaaS 成本可接受

等 MCP / MemoryProtocol 標準成熟,如果你:

  • 還在 PoC 階段、沒 commit 到任何架構

連結


我是江中喬,一位具有 TPM 與產品管理背景的 AI 系統建構者,目前專注於 AI 認知增強系統與多 Agent 協作架構的設計與實踐。