用一台 Mac mini 跑你自己的 AI Agent 記憶引擎——Memory Hall 入門
你在做 AI agent,每個 agent 都會產生記憶——對話、決策、實驗結果。這些記憶要放哪?Memory Hall 讓「自建記憶層」從「最麻煩的選項」變成三個命令的事。這篇介紹它是什麼、怎麼用、跟 mem0 差在哪。
你在做 AI agent,每個 agent 都會產生記憶——對話、決策、實驗結果、外部查到的事實。這些記憶要放哪?
三個選項:
- 放 OpenAI / Anthropic 的 memory:綁死在他們的 session / workspace 概念裡
- 放 mem0 / Zep 之類的 SaaS:API 可控但部署在別人家
- 放自己的:自由度最高,但你要自己寫
第三條路原本是最麻煩的。Memory Hall 把它變成三個命令的事。
git clone https://github.com/MakiDevelop/memory-hall
cd memory-hall && docker compose up -d
curl -X POST http://localhost:9100/v1/memory/write -d '...'
這篇是入門介紹——不講哲學、不談反省,單純告訴你這是什麼、能做什麼、怎麼用。
Memory Hall 是什麼
一句話:一個在你自己的機器上跑的 AI agent 記憶引擎。
三個零件:
- SQLite:資料放一個
.sqlite3檔,要搬家mv就好 - sqlite-vec:向量索引是 SQLite 的 virtual table,沒有「再起一個 vector DB」的麻煩
- Ollama(或你自選的 embedder):把 content 轉成 embedding,offload 到家裡任何一台 GPU 機器
三種入口:
- HTTP API(
POST /v1/memory/write/search):最通用 - CLI(
mh serve/mh reindex-fts):運維用 - Python embedded(
from memory_hall import ...):沙盒 / 零網路環境用
Quickstart:三步跑起來
Step 1. 抓 repo + 啟動
git clone https://github.com/MakiDevelop/memory-hall
cd memory-hall
docker compose up -d
預設會在 localhost:9100 開 HTTP server,資料寫進 ./data/memory-hall.sqlite3。
如果你家裡有 Ollama,Memory Hall 會連到 http://host.docker.internal:11434 拿 bge-m3 embedder。沒 Ollama 也沒關係——寫入會回 202 Accepted + sync_status=pending,背景 worker 會在 Ollama 上線後自動補 index。
Step 2. 寫一筆記憶
curl -X POST http://localhost:9100/v1/memory/write \
-H "Content-Type: application/json" \
-d '{
"agent_id": "my-agent",
"namespace": "shared",
"type": "episode",
"content": "今天完成了 Memory Hall v0.2 的 deploy",
"tags": ["milestone", "memory-hall"]
}'
回傳:
{
"entry_id": "01KPJ...",
"embedded": true,
"sync_status": "embedded",
"created": true
}
內建 content_hash dedup——同樣內容再寫一次會拿回既有 entry_id,不會重複。
Step 3. 搜它
curl -X POST http://localhost:9100/v1/memory/search \
-H "Content-Type: application/json" \
-d '{
"query": "v0.2 進度",
"mode": "hybrid",
"limit": 5
}'
mode 三選一:
hybrid:BM25 lexical + bge-m3 semantic 融合(預設,命中率最好)lexical:純 keyword 比對semantic:純向量相似度
CJK 原生:中文 query 真的會命中
大部分 OSS memory engine 用 SQLite 的 unicode61 tokenizer,對中文是災難——因為它把一整串無空白的中文當一個 token,substring 都搜不到。
Memory Hall 用 jieba 預切詞。差別直接看數字:
# 寫一筆純中文的記憶
curl ... -d '{"content": "最近又撞牆了,sqlite-vec 的 ARM64 wheel 是 32-bit"}'
# 查 "撞牆"
curl ... -d '{"query": "撞牆", "mode": "lexical"}'
v0.1(unicode61)BM25 score:0(miss)
v0.2(jieba pre-tokenize)BM25 score:0.26(hit)
Python Embedded:零網路場景用
不想 / 不能跑 HTTP?直接 import:
import asyncio
from memory_hall import Settings, build_runtime
from memory_hall.models import WriteMemoryRequest, SearchMemoryRequest
async def main():
settings = Settings(
database_path="./data/memhall.sqlite3",
vector_database_path="./data/memhall-vectors.sqlite3",
ollama_base_url="http://localhost:11434",
)
runtime = build_runtime(settings)
await runtime.start()
await runtime.write_entry(WriteMemoryRequest(
agent_id="sandbox",
namespace="shared",
type="note",
content="寫進 embedded runtime 不需要網路",
))
results = await runtime.search_entries(SearchMemoryRequest(
query="embedded",
mode="hybrid",
limit=3,
))
await runtime.stop()
asyncio.run(main())
這條路徑是為了 sandbox agent 設計的——有些 CLI agent(Codex headless / Gemini 某些配置)連 localhost TCP 都被擋,只能走 in-process。
部署拓樸:從 laptop 到家用伺服器
Memory Hall 跟你的機器規模 adapt:
A. 最小:一台 laptop
[你的 MacBook]
├── Docker Desktop
│ └── memory-hall container (port 9100)
└── Ollama(macOS native,不要放 Docker 裡會慢)
每個 agent 寫進 localhost:9100。適合:個人 PoC、demo、offline 工作。
B. 家用伺服器
[Mac mini / 小主機]
└── memory-hall container(24/7 跑)
↑
│ Tailscale
↓
[DGX Spark / 其他 GPU 機]
└── Ollama bge-m3 (OLLAMA_KEEP_ALIVE=-1)
Memory Hall 和 embedder 分開部署——embedder 常吃記憶體,放有 GPU 的機器。我家裡是 DGX Spark(128GB 統一記憶體)跑 bge-m3,Memory Hall 跑在 Mac mini。
C. 多機冷備
[Mac mini #1 primary]
└── memory-hall:0.2.0 container running
│ rsync /5 min
↓
[Mac mini #2 cold standby]
└── memory-hall:0.2.0 container created (stopped)
primary 掛掉 → 手動 docker start memory-hall on #2 接手。不是真正的 HA(沒共識、沒自動 failover),但對家用 / 個人來說,rsync 冷備等於 80% 的實 HA 價值,維護成本是 0。
為什麼不用 mem0(實際踩過的六個坑)
mem0 是市面上最成熟的 AI agent memory SaaS。我用了幾個月,把它當我七位一體 stack 的記憶層。最後決定自己寫,理由是實戰踩到的:
1. SaaS-only 卡死沒網路場景
我的 agent stack 有一部分跑在沒網路的環境——Codex CLI 的 headless 模式、Gemini CLI 某些配置、air-gapped 的機器、Claude Code 的 sandbox agent。SaaS-only 直接卡。我需要 Python import 就能跑的記憶層(Memory Hall 的 embedded mode)。
2. 中文 recall 極低
我的記憶內容 70% 是中文。mem0 / 大部分 OSS memory engine 的預設索引對中文 query 命中率極低——原因是 unicode61 tokenizer 把一整串無空白中文當一個 token,substring 都搜不到。純 CJK query 幾乎全 miss。
Memory Hall 用 jieba 預切詞解決這件事。
3. 版本 API 節奏我不能 pin
mem0 的服務端 API / 檢索行為會隨他們 roadmap 變動。agent stack 的基礎依賴不能綁在外部的演進節奏上——我需要版本釘在我的 lock file 裡,而不是被動接受服務端升級。
4. 黑盒無法 debug
recall 結果奇怪的時候,我沒辦法看他們 server 在做什麼。對 agent stack 這種「每次查詢都要信任」的基礎設施,黑盒讓你連「這個 query 為什麼 miss」都回答不了。
Memory Hall 是 SQLite——你可以直接 sqlite3 memhall.sqlite3 開進去看 FTS5 索引長什麼樣、BM25 分數怎麼算。
5. 搬家成本
真的決定換 backend,mem0 的 export API 出來的 schema 還要自己轉換成下一家的格式。Memory Hall 的「搬家」是 mv memhall.sqlite3 /path/to/new/host/,一個檔案的事。
6. 定價與供應商風險
SaaS 月費會變、API 會變、公司可能被收購或轉向。agent stack 的底層不要有這種變數——今天你在 500 entries,明天 50k entries,pricing 曲線不是你能控制的。
Memory Hall 的成本就是你家那台機器的電力。entry 數量不影響月費(因為沒有月費)。
不是說 Memory Hall 比 mem0 好。是說 SaaS 的 trade-off 對我這個使用情境不 work。
| Memory Hall | mem0 | |
|---|---|---|
| 部署 | 你家 / 自己的雲 / laptop | 他們的 cloud |
| 資料位置 | 你的 SQLite file | 他們的 DB |
| Offline / sandbox | 支援(embedded mode) | 不支援 |
| CJK | jieba 原生 | 英文優先 |
| Version pin | 你的 lock file | 服務端節奏 |
| Debug | SQLite 直接看 | 黑盒 |
| 搬家 | mv 一個 file |
export API + 轉換 |
| Embedder 選擇 | 你選 | 他們的 |
| Lock-in | 零(Apache 2.0) | 中到高 |
| 成本 | 0 月費 | 按量計費 |
你的情況如果以上 6 點都不是問題,mem0 的 UX 更精緻、功能更完整,直接用它省事。
但如果這 6 點任何一條你也踩到過——sandbox 卡死 / CJK miss / 版本被動 / debug 無門 / 搬家痛 / pricing 焦慮——Memory Hall 值得試。
現在能做 / 現在不能做
v0.2 能做:
- HTTP / CLI / Python embedded 三種入口
- 多租戶 schema from day one
- CJK jieba 分詞,recall 有保證
- 寫入 durability + concurrency benchmark 全綠(50-way burst 零流失)
- 寫入不阻塞:202 accepted pattern,embedder 掛了也不掉資料
- Docker 單 container or docker compose 部署
v0.2 還沒做(刻意 / 不刻意):
| Feature | 為什麼現在不做 | 什麼時候做 |
|---|---|---|
| MCP server | use case 還沒浮現,protocol 本身還在動 | v0.3(1-2 週內) |
| Auth | 個人 / home lab 用不到,早期挑錯 pattern 難改 | 有 production exposure 需求時 |
| Replica / HA | SQLite 的價值就是單機簡單,做 HA 要換 adapter | 換 Postgres adapter 時(v2.0+) |
| Enrichment | 刻意不做。engine 只管儲存,enrichment 在上層 | 永遠不在 memhall repo 做 |
這些決策的完整 rationale 在另一篇 三個月的 over-design 反省。
什麼樣的人適合現在就用?
適合:
- 個人開發者 / 小團隊做 AI agent,需要本地可控的記憶層
- 寫的記憶主要是中文 / 中英混合,對 CJK recall 敏感
- 要 sandbox / offline / 零網路場景(Codex CLI / Gemini CLI / air-gapped)
- 有一台 Mac mini、小 server、或任何能跑 Docker 的機器
- 對「資料放自己家」有偏執
暫時不適合:
- 需要多地多中心 HA(換 Postgres + pgvector 吧)
- 要百萬 entry 以上的生產規模(sqlite-vec 舒服範圍 ~100k)
- 完全沒空做任何 devops(SaaS 比較適合你)
怎麼試
git clone https://github.com/MakiDevelop/memory-hall
cd memory-hall
docker compose up -d
curl http://localhost:9100/v1/health
看到 {"status":"ok","storage":"ok","vector_store":"ok","embedder":"ok"} 就可以開始寫記憶了。
有問題、有建議、有想加的 feature,去 GitHub 開 issue:
OSS: github.com/MakiDevelop/memory-hall
我是江中喬,一位具有 TPM 與產品管理背景的 AI 系統建構者,目前專注於 AI 認知增強系統與多 Agent 協作架構的設計與實踐。