Ian Chou's Blog

CtxFST CH23 - 讓 AI 記憶可以被看見、搜尋、修正:用 OpenClaw 做一個可除錯 memory loop

CtxFST CH23:讓 AI 記憶可以被看見、搜尋、修正,用 OpenClaw 做一個可除錯 memory loop

前一章 CH22,我做的是 retrieval benchmark。

那一章回答的是:

如果只是做檢索,entity-aware retrieval 到底比純文字相似度多了什麼?

但如果你真的把 AI 放進日常生活或工作流裡,下一個更痛的問題很快就會出現:

當 AI 記錯一件事時,你到底要怎麼把它修好?

這個問題,比 retrieval 本身更接近使用者真正會撞到的牆。

因為大部分人不是卡在「AI 找不到資料」。
而是卡在:

如果一套 memory system 不能被檢查、不能被 debug、不能被修正,那它再聰明,最後都很難被長期信任。

這就是我想把 CH23 聚焦的地方。


先講一句最短的結論

這一章最重要的一句話是:

CtxFST 的價值,不只是讓知識更容易檢索,而是讓 AI 的記憶第一次變成一個可以打開、搜尋、定位、修正的 artifact。

如果 CH22 證明的是 entity-aware retrieval 有用,
CH23 要證明的是另一件事:

當記憶出錯時,CtxFST 讓你有地方下手。

這件事對推廣很重要。

因為很多開發者不會先被「graph traversal」打動。
他們會先被這種場景打動:

我家的 AI 明明記錯了,為什麼我不能直接看到它到底記了什麼?

這個痛點,比 architecture diagram 更容易在前 30 秒讓人懂。


真正有共感的問題,不是檢索,而是「記錯了卻改不掉」

我想像的 demo 不是一個很抽象的 enterprise workflow。

它其實很生活化。

例如家庭群組裡,AI 長期在幫忙記偏好、安排晚餐、整理提醒。

某一天,它記錯了一條事實:

小明不吃牛肉。

結果 AI 卻記成:

小明喜歡牛肉。

接下來每次你問晚餐建議,它都沿著這條錯誤記憶往下推。

問題不只是答案錯。
真正糟的是,你很難知道:

這不是個案。在 AI 群組裡,抱怨這個問題的人非常多。很多人花了大量時間去「訓練」AI 記住家人習慣、工作偏好、溝通模式,但強力的訓練並沒有帶來好的結果。他們願意投入時間做必要的工作,因為他們太想得到正確的回應了——但現在沒有工具讓他們的努力有效。

這就是今天很多 memory system 的共同困境:

它們有寫入能力,但沒有足夠好的可觀測性。


這一章不是在做「更強的 AI」,而是在做「可除錯的 memory」

這裡我想刻意把邊界講清楚。

這一章不是要聲稱 CtxFST 已經變成 OpenClaw 的內建 memory backend。

也不是要說我現在已經把完整的雙向同步產品做完。

這一章更誠實的定位是:

利用 OpenClaw 現有的 memory 能力,加上一條外部的 CtxFST export / inspect / repair loop,證明 AI 記憶可以被 debug。

這個定位很重要,因為它決定了我們要怎麼設計 demo。

如果你一開始就想直接改 OpenClaw 核心,事情會很重。

但如果你先接受一個更務實的目標:

  1. 把現有 memory source 匯出
  2. 轉成 CtxFST
  3. 用 entity / vector / keyword 三種方式找問題
  4. 修正 source-of-truth
  5. 觸發 reindex

那整條路就會清楚很多。


OpenClaw 今天已經給了什麼

根據目前官方文件,OpenClaw 的 memory 機制已經提供了幾個很重要的基礎能力:

這些能力聽起來很基本,但它們其實已經足夠讓一條「可修正 memory loop」成立。

因為這代表:

我們不需要直接碰 OpenClaw 的核心 runtime,就能先從 source Markdown 走一條外部 debug loop。

這也意味著一件很重要的架構判斷:

第一版 demo 的正確做法,不是直接 patch SQLite,而是把 Markdown source 當作真正的修正入口。

直接改 SQLite 不是絕對不行,但它比較 brittle。
因為 index、watcher、重建時機、embedding 更新,都會讓 direct DB patch 變得不夠穩。

具體來說:

如果目標是做一個能在影片裡穩定重現的 demo,那最穩的做法是:

改 source Markdown,再讓 OpenClaw 重建索引。

而最粗暴但最可靠的重建方式,是直接刪掉整個 SQLite index 再重啟——這個方法已經有社區實測過,成功率極高。


刪 Index 重建:完整指令

在正式走 demo 之前,先把「核彈級重建」的指令整理清楚。這是保底手段,確保任何修正都一定會生效:

# 1. 停 gateway(如果正在跑)
pkill -f "node.*openclaw.*gateway"  # 或 Ctrl+C

# 2. 備份(安全第一)
cp ~/.openclaw/memory/{agentId}.sqlite ~/.openclaw/memory/backup.sqlite

# 3. 粗暴刪(SQLite + vec index)
rm ~/.openclaw/memory/{agentId}.sqlite \
   ~/.openclaw/memory/{agentId}.sqlite-shm \
   ~/.openclaw/memory/{agentId}.sqlite-wal \
   ~/.openclaw/index.sqlite  # 若有分離 index

# 4. 重啟 gateway(自動 reindex MEMORY.md + memory/*.md)
openclaw gateway  # 或 node dist/index.js gateway --port 8787

# 5. 強制觸發(確保完整重建)
openclaw memory index --force --verbose
openclaw memory status --deep  # 驗證 chunks 數、embedding OK

注意:agentId 通常是 main 或 workspace 名稱。刪除後只剩 MEMORY.md 源檔,系統會全部重新 chunk 和 re-embed(需要幾分鐘)。

這個方法的好處是風險趨近於零——最壞的狀況就是重跑一次 demo。


所以,CH23 要證明的不是「神奇 magic」,而是一條可走通的 loop

這一章真正想證明的 loop 是:

OpenClaw memory source
→ export to CtxFST
→ inspect with graph / vector / FTS
→ identify the wrong memory
→ edit the source-of-truth
→ reindex
→ AI behavior changes

如果這條 loop 走得通,那意義其實很大。

因為它把今天常見的黑箱 memory,往前推成另一種東西:

一份可以被人類理解、也可以被工具操作的知識層。

這就是 CtxFST 很適合站的位置。

它不是取代聊天系統。
也不是取代模型。
而是變成那個中間層:


Demo 前置:環境確認

在跑 demo 之前,先確認 OpenClaw 環境正常:

# 確認 OpenClaw 路徑(agentId 通常 main 或 workspace 名)
OPENCLAW_MEM=~/.openclaw/memory/main.sqlite  # 改成你的
echo "記憶庫: $OPENCLAW_MEM"
openclaw memory status  # 應顯示 chunks 數 >0

Demo 完整五步:「小明不吃牛肉」

這個 demo 不是一個很抽象的 enterprise workflow。它刻意設計得很生活化,因為推廣的第一支影片,最重要的是:

路要短,成功率要高,觀眾要一眼就懂。

Step 1. 先故意讓 AI 記錯

MEMORY.md 裡放一條錯的內容:

家庭群確認:小明喜歡牛肉。

接著在對話裡跟 AI 聊幾輪讓它 index,然後問:

晚餐幫我排一下,小明那份可以吃什麼?

讓 AI 顯示出它真的沿用了這條錯誤記憶。

Step 2. 導出記憶,轉成 CtxFST artifact

這一步不是要重新發明 OpenClaw 的 memory。

而是把目前 memory source 抽出來,變成一份可以檢查的知識格式。

先從 SQLite 導出相關記憶:

sqlite3 "$OPENCLAW_MEM" "
SELECT id, text, json_extract(metadata, '$.session') as session, summary 
FROM chunks 
WHERE text LIKE '%小明%' OR text LIKE '%牛%' OR summary LIKE '%牛%'
" > wrong_memory.json

cat wrong_memory.json  # 檢查:應看到錯記「小明愛吃牛」

導出結果範例:

["chunk_family_pref_001","家庭群討論晚餐:小明說愛吃牛排,媽媽點頭同意。下次買牛肉給他。","family_2026-03","小明偏好牛肉"]

然後用 skill-chunk-md 做 entity extraction:

# 用 Claude Code 或 OpenClaw,prompt 輸入 wrong_memory.json
# 或用本地 LLM:
claude "用 skill-chunk-md 處理以下 chunks: $(cat wrong_memory.json)"

產出的 CtxFST 格式:

entities:
  - id: entity:小明
    name: 小明
    type: person
  - id: entity:牛肉
    name: 牛肉
    type: food
    pref: like  # ← 這裡是錯的!
chunks:
  - id: chunk:family-pref-beef
    entities: [entity:小明, entity:牛肉]
    context: "家庭飲食偏好記錄"

在這個格式裡,你至少會看到:

如果未來 relation layer 更完整,還可以明確標成:

entity:小明
→ prefers / avoids
→ entity:牛肉

這時候記憶從黑箱變成了你能讀的結構。

Step 3. 用三種搜尋方式找出問題在哪

這裡的重點不是哪一種搜尋最強,
而是三種搜尋在 memory debugging 裡抓的是不同型別的錯。

FTS / keyword search

適合抓具體事實與字面衝突。

例如直接搜:

sqlite3 "$OPENCLAW_MEM" "SELECT * FROM chunks_fts WHERE chunks_fts MATCH '小明 牛肉'"

你會很快定位到那條記憶到底寫在哪一個 chunk。

Vector / semantic search

適合抓語意相近但文字不完全一樣的記憶。

例如同樣在講飲食偏好,但可能寫成:

這些文字不一定共享同一組 keyword,但語意上其實指向同一個問題區域。

用 OpenClaw 自己的對話介面驗證:

openclaw "小明吃牛肉嗎?"  # 若回「愛吃」→ 確認 vector 也指向錯誤記憶

Entity graph

適合抓「哪裡的關係斷了、哪裡出現矛盾、哪裡缺 context」。

例如:

# 若有 CtxFST validate plugin:
python validate_chunks.py output.md  # 報孤立 entity 或衝突 relation

這時候 graph 的價值不是提供一個 fancy visualization。

而是讓你看到:

錯的不只是某一句話,而是它在整個知識層裡的位置。

三檢診斷結果:FTS 顯示錯誤文字、vector 無反例(假設目前沒有「不吃牛」的記憶)、graph 缺少 avoid 邊。三種搜尋互補:FTS 找字面、vector 找語意、graph 找結構。

Step 4. 修正 source-of-truth,而不是只修 AI 的當下回答

這是整個 demo 最重要的一步。

很多系統在表面上看起來也能修。
你跟它說一次:

不對,小明不吃牛肉。

它當下也許會道歉,也許下一句會答對。

但這不等於 memory 被修好了。

真正要修的是 source-of-truth。

所以這一步要做的不是 prompt 它再想一次,
而是直接修改底層資料。

方式 A:改 MEMORY.md(觸發 watcher,推薦)

echo '家庭群確認:小明不吃牛肉(媽媽糾正 2026-03)。' >> ~/.openclaw/MEMORY.md

方式 B:直接 UPDATE SQLite(精準定位,進階用法)

sqlite3 "$OPENCLAW_MEM" "
UPDATE chunks SET text='家庭群確認:小明不吃牛肉(媽媽糾正 2026-03)' 
WHERE id='chunk_family_pref_001';
UPDATE chunks SET summary='小明避免牛肉' WHERE id='chunk_family_pref_001';
"

方式 B 更精準,但需要配合後面的刪 index 重建來確保 embedding 也更新。

如果需要更穩定,也可以在修正內容裡補一個帶時間戳的註記:

家庭群確認:小明不吃牛肉。(2026-03 媽媽糾正確認)

這樣未來當同一個人偏好又變動時,
知識層裡才有明確的版本語境。

Step 5. 刪 Index 重建,驗證行為改變

這一步的意義,是把「我改了檔案」和「AI 真的變了」連起來。

只要這一步能穩定成立,整個故事就成立了。

你不需要一開始就追求最優雅的同步機制。

# 停服務
pkill -f openclaw  # 或 kill gateway PID

# 備份(安全第一!)
cp "$OPENCLAW_MEM" "${OPENCLAW_MEM}.backup"

# 粗暴刪(SQLite + vec index 全砍)
rm "$OPENCLAW_MEM"*  # .sqlite .shm .wal
rm ~/.openclaw/index.sqlite  # 若有分離 index

# 重啟(自動從 MEMORY.md 重建 index)
openclaw gateway &  # background
sleep 10  # 等 index 重建

# 強制觸發完整 reindex
openclaw memory index --force --verbose
openclaw memory status  # 驗證 chunks 數恢復、embedding 正常

然後再回頭問同樣的問題:

openclaw "小明牛肉偏好?"  # 應回「不吃」!

晚餐幫我排一下,小明那份可以吃什麼?

如果 AI 的回答明顯改了,
這整個 demo 的核心價值就已經被證明了。


一鍵 Demo Script

如果要拍影片,可以把整個流程寫成一個 Python script,錄屏即影片:

#!/usr/bin/env python3
# demo_openclaw_ctxfst.py
import sqlite3, json, subprocess, time
from pathlib import Path

MEM_DB = Path.home() / ".openclaw/memory/main.sqlite"

def main():
    # Step 1-2: 導出 + 模擬 CtxFST(實際用 LLM)
    conn = sqlite3.connect(MEM_DB)
    wrongs = conn.execute(
        "SELECT id, text FROM chunks WHERE text LIKE '%牛%'"
    ).fetchall()
    print("錯誤記憶:", wrongs)
    conn.close()
    
    # Step 3: 診斷(用 CLI 驗證 AI 回應)
    subprocess.run(["openclaw", "小明牛?"])
    
    # Step 4: 修正 MEMORY.md
    with open(Path.home() / ".openclaw/MEMORY.md", "a") as f:
        f.write("\n小明不吃牛肉(修正)。\n")
    
    # Step 5: 刪 index 重建
    subprocess.run(["pkill", "-f", "openclaw"], capture_output=True)
    time.sleep(2)
    MEM_DB.unlink(missing_ok=True)  # 粗暴刪
    subprocess.Popen(["openclaw", "gateway"])
    time.sleep(15)  # 等 reindex
    subprocess.run(["openclaw", "memory", "index", "--force"])
    
    # 驗證!
    result = subprocess.run(
        ["openclaw", "小明牛?"], capture_output=True, text=True
    )
    print("修正後:", result.stdout)

if __name__ == "__main__":
    main()

python demo_openclaw_ctxfst.py,全程錄屏就是影片素材。


兩種修正方式,都需要

實際使用時,修正記憶有兩種情境:

一種是你知道確切哪裡錯——打開 CtxFST,看到「小明喜歡牛肉」那條 chunk,直接改 source Markdown。這就像你知道哪行 code 有 bug,直接改最快。

另一種是你只知道「AI 的回應不對」,但不確定錯在哪——這時候用自然語言跟 AI 說「小明其實不吃牛肉,幫我找到記錯的地方」,讓 AI 用 entity graph + vector search 定位問題,再幫你修正。

兩種都要有,因為使用者的技術背景不同,問題的明確程度也不同。

不會寫 code 的人,也會去看 code——他們不需要完全理解 CtxFST 的格式,但當他們看到一個 YAML 裡面寫著 entity:小明pref: like 配上 entity:牛肉,他們知道問題在哪。知道哪裡有問題,就能想辦法解決。


這個 demo 真正的賣點

很多人看到這裡,可能會以為賣點只是:

哦,所以你把記憶存成 Markdown,然後可以手改。

但那其實不是最深的賣點。

真正的賣點是:

你第一次有能力把 AI 的錯,從「模糊地覺得它怪怪的」變成「明確定位到哪條記憶、哪個 entity、哪段 relation 出了問題」。

這個轉變很大。

因為只有當問題可以被定位,
修正才會有工程意義。

否則你做的永遠只是:

那不是 debug。
那只是祈禱。


為什麼這一章對推廣特別重要

如果我要把 CtxFST 推給其他開發者或社群,
我不會先從 schema 開始講。

我會先從這句話開始講:

你的 AI 如果記錯了一件事,你有沒有辦法打開它的記憶,看到錯在哪裡,然後修掉?

因為這個問題一出來,很多人會立刻懂。

尤其是有用過:

的人,幾乎都會有共感。

這也是 CH23 比單純講 graph retrieval 更適合做推廣入口的原因。

CH22 證明的是技術價值。
CH23 講的是使用者痛點。

前者建立 credibility。
後者建立共感。

兩者都需要,但如果你要拍影片、發文、丟社群,
通常是後者比較能讓人先停下來看。


這一章的誠實邊界

我也想把現在還沒完成的部分講清楚。

目前最誠實的說法不是:

CtxFST 已經完整取代 OpenClaw memory backend。

而是:

CtxFST 已經足夠成為 OpenClaw memory 的外部檢查層與修正工作流骨架。

也就是說,今天我們已經可以很合理地做:

但還沒有必要在這一章硬說:

那些都可以是後面的 chapter。

例如:

這樣整個系列會更穩,也更可信。


最後的結論

CH22 回答的是:

entity-aware retrieval 有沒有比純文字檢索更有用?

CH23 回答的是另一個更接近現實的問題:

當 AI 記錯了,我們能不能真的找到錯在哪,然後把它修好?

而我目前得到的答案是:

可以。最務實的第一步,不是重寫整個 agent runtime,而是先把 memory 變成一份可檢查、可搜尋、可修正的 CtxFST artifact。

當你能做到這件事時,
AI memory 就不再只是黑箱。

它會開始像 code、像 config、像資料庫 schema 一樣,
成為一個可以被工程化管理的東西。

這就是 CtxFST 最值得被看見的地方。

不是因為它讓 graph 看起來很酷。

而是因為它第一次讓一句很實際的話變得成立:

AI 的記憶,終於可以 debug 了。