在推出分層保留與混合搜尋三天後,我們就遇到了當初打造持久記憶體伺服器要解決的失敗情境:某台機器的 SQLite 資料庫空無一物,而 Qdrant Cloud 仍保有所有向量。所有內容(每筆記憶的實際文字)只存在 SQLite 裡。向量完好無損,卻因缺少它們編碼的文字而毫無用處。
那次事件促成了 BHGBrain 1.3。此版本讓記憶體伺服器具備對儲存失敗的韌性、為在多台機器上運行 BHGBrain 的團隊新增多裝置支援,並推出可從共享向量存放區重建整個本地資料庫的災難復原工具。
問題:雙重儲存、單一失效點
BHGBrain 的架構使用兩個存放區:SQLite 用於內容、元資料與全文搜尋;Qdrant 用於向量嵌入與語意相似度。每次寫入都會同時寫入兩者。每次搜尋都會跨兩者關聯。
此設計假設 SQLite 是持久的。確實如此,sql.js 透過先寫入暫存檔再重新命名的原子方式寫入磁碟。但如果資料庫檔案遺失、被重新建立,或在不同機器上重新開始,所有內容就消失了。Qdrant 擁有嵌入與部分元資料(標籤、類型、重要性),但沒有實際的記憶文字。依賴 SQLite 關聯的搜尋結果會默默回傳空值。
這正是發生的情況。兩個 BHGBrain 實例,一個在主工作站、一個在 Windows 365 雲端電腦上,指向同一個 Qdrant Cloud 叢集。雲端電腦的 SQLite 是空的。每次回顧都回傳零結果,儘管 Qdrant 持有數十個高信心匹配的向量。
修復 1:內容存於 Qdrant Payload
第一個改動很直接:將完整記憶內容連同向量一起存入 Qdrant payload。
1.3 之前,Qdrant 的 upsert payload 只包含元資料,namespace、type、tags、importance、retention_tier、decay_eligible、expires_at。內容文字僅存於 SQLite。
現在,writeMemory 與 updateMemory 在每次 Qdrant upsert 中加入 content、summary、category、source、created_at 與 device_id。Qdrant 成為內容的冗餘備份。SQLite 仍是查詢、全文搜尋與生命週期管理的主要存放區,但失去它不再意味著失去資料。
修復 2:自動搜尋回退
搜尋服務的 buildSearchResults 方法過去會在 SQLite 中查找每個 Qdrant 結果。若某筆記憶 ID 在本地找不到,就會被丟棄。這正是讓該 bug 難以排查的沉默失敗:Qdrant 回傳了匹配,呼叫端卻看到空結果。
現在,當 Qdrant 結果在 SQLite 找不到對應列時,搜尋服務會直接從 Qdrant payload 構建結果:
Qdrant returns match → SQLite lookup → found? → return full record
→ not found? → payload has content?
→ yes → return from payload
→ no → skip (pre-1.3 memory)
這意味著指向既有 Qdrant 叢集的全新 BHGBrain 實例會立即回傳搜尋結果,無需任何 SQLite 資料。結果包含 payload 中儲存的所有內容:內容、摘要、類型、標籤、保留層級、裝置 ID 與建立時間戳。
修復 3:修復工具
若要完整復原,重建 SQLite 以讓全文搜尋、生命週期管理與存取追蹤正常運作,現在有了 repair MCP 工具:
{
"tool": "bhgbrain.repair",
"params": {
"dry_run": true
}
}
修復工具會:
- 透過 collections API 探索 Qdrant 中所有
bhgbrain_*集合 - 捲動每個集合中的每個點(分批分頁)
- 對每個點:檢查 ID 是否存在於本地 SQLite
- 若缺失且 Qdrant payload 含
content:將完整MemoryRecord插入 SQLite - 若缺失且 payload 無內容:跳過(1.3 前的記憶,內容無法復原)
- 回報統計:掃描集合數、掃描點數、已復原、已跳過、錯誤數
使用 dry_run: true 可預覽將復原的內容而不寫入任何資料。使用 device_id 可將復原範圍限定於特定裝置的記憶。
這是災難復原路徑。它也是新裝置的上線路徑:將全新 BHGBrain 實例指向既有的 Qdrant 叢集,執行 repair,本地 SQLite 就會從共享存放區填入所有內容。
多裝置記憶體
1.3 中更大的架構變更,是對多個 BHGBrain 實例共享單一 Qdrant 後端的一級支援。
架構
Device A (Workstation) Device B (Cloud PC)
┌──────────────────┐ ┌──────────────────┐
│ SQLite (local) │ │ SQLite (local) │
│ device_id: ws-1 │ │ device_id: w365 │
└────────┬─────────┘ └────────┬─────────┘
│ │
└──────────┬───────────────────┘
│
┌──────────▼──────────┐
│ Qdrant Cloud │
│ (shared backend) │
│ content + vectors │
│ device_id index │
└─────────────────────┘
每個裝置維護自己的 SQLite 資料庫。Qdrant 是共享層。每次寫入都將內容存入兩個存放區,並以來源 device_id 標記該筆記憶。
裝置識別
每個實例在啟動時解析穩定的 device_id:
- 明確設定:
config.json中的device.id - 環境變數:
BHGBRAIN_DEVICE_ID - 自動產生:從
os.hostname()衍生,轉小寫並清理
解析後的 ID 會在首次執行時寫入 config.json。它出現在每個 Qdrant payload、每筆 SQLite 記錄與每個搜尋結果中,因此你隨時可知某筆記憶來自哪個裝置。
跨裝置可見性
兩個裝置都能看見所有記憶。當裝置 B 搜尋裝置 A 儲存的內容時,Qdrant 搜尋會回傳匹配。若裝置 B 的 SQLite 沒有該記錄,搜尋回退會從 Qdrant payload 構建結果。沒有資料是不可見的。
| 來源 | 裝置 A 看見 | 裝置 B 看見 |
|---|---|---|
| 裝置 A 的記憶(SQLite) | 完整記錄 | Qdrant 回退 |
| 裝置 B 的記憶(SQLite) | Qdrant 回退 | 完整記錄 |
若要完整本地功能(全文搜尋、生命週期追蹤、存取計數),請在裝置上執行 repair 以從 Qdrant 填入其 SQLite。
設定
將兩個裝置指向同一個 Qdrant 叢集,使用不同裝置 ID:
// Device A
{
"device": { "id": "workstation" },
"qdrant": {
"mode": "external",
"external_url": "https://your-cluster.cloud.qdrant.io",
"api_key_env": "QDRANT_API_KEY"
}
}
// Device B
{
"device": { "id": "cloud-pc" },
"qdrant": {
"mode": "external",
"external_url": "https://your-cluster.cloud.qdrant.io",
"api_key_env": "QDRANT_API_KEY"
}
}
就這樣。兩個實例共享同一個記憶庫。兩者都為寫入標記來源。兩者都能看見所有內容。
文件:8 張 Mermaid 圖
README 現包含涵蓋每個主要子系統的詳細 Mermaid 圖:
- 架構 — 顯示完整伺服器堆疊的元件圖
- 多裝置拓撲 — 共享 Qdrant 搭配每裝置本地 SQLite
- 寫入管線 — 完整去重決策流程圖
- 層級指派 — 優先順序分類邏輯
- 層級生命週期 — 含升級、TTL 與到期流程的狀態圖
- 混合搜尋 — 平行語意/全文路徑經 RRF 融合
- 備份與還原 — 建立與還原流程的時序圖
- 修復流程 — 災難復原流程圖
所有四份翻譯(德文、西班牙文、法文、簡體中文)都已更新至與英文 README 完全同步,包含所有圖表與新的多裝置章節。
升級至 1.3
無需手動遷移。升級後首次啟動時:
- SQLite 新增可為空的
device_id欄位(既有記憶保持null) - Qdrant 集合新增
device_id關鍵字索引 - 設定新增從主機名稱自動解析的
device.id欄位 - 所有新寫入都將內容存入 Qdrant payload
1.3 前不含內容的 Qdrant 記憶仍透過 SQLite 正常運作。它們只是無法在 SQLite 遺失時透過 repair 復原,因為它們寫入時內容並未存入 Qdrant。
結論
SQLite 事件最大的教訓:若你的系統有兩個存放區,且其中一個默默退化,你需要另一個能自動接手而無需操作員介入。BHGBrain 1.3 讓 Qdrant 成為 SQLite 的安全網,反之亦然。搜尋會自動回退。復原只需一次工具呼叫。多裝置共享能運作,是因為兩個存放區都帶有完整資訊。
若你在多台機器上運行 BHGBrain,請升級至 1.3 並在每個裝置上執行 repair。若你只在單機運行,也請升級,因為內容存於 Qdrant 的改變意味著下一次的 SQLite 意外將是虛驚一場,而非資料遺失。
BHGBrain 為開源、MIT 授權,可於 github.com/Big-Hat-Group-Inc/BHGBrain 取得。
Kevin Kaminski 是 Big Hat Group 的負責人,專注於企業 AI 基礎架構、Microsoft 365 與 Windows 365。他為在工作中運行 AI 代理的團隊打造開源工具。