寫入一模一樣,讀取完全相反:長期記憶 vs 提醒
LLM 不會學會你——「越用越懂你」得用工程做出來。兩個寫入路徑相同、讀取方向相反的功能,以及為什麼這隻 bot 沒有 recall 工具。
先講老實話
LINE 家庭助手系列第五篇。開頭先還使用者一句誠實聲明:模型不會學會你。 每次回覆只看得到對話最近的 N 則訊息,再往前一則等於從未發生。你的聊天不會訓練權重。就出廠狀態而言,「越用越懂你」是行銷話術,不是機制。
想要一隻問天氣時知道你住花蓮、問日子時記得你老婆生日的 bot——得有人把它做出來。而這件事意外地是個有趣的設計題,因為 bot 上已經有一個長得幾乎一樣、卻必須運作得完全相反的功能。
一個問題,兩個相反的答案
兩個功能都是「現在存一件事,之後派上用場」。設計的分岔點只有一個問題:存下來的東西,該在什麼時候浮上來?
| 寫入 | 讀取 | |
|---|---|---|
| 提醒 | set_reminder 工具 | 背景排程在某一刻主動推播 |
| 記憶 | remember/forget 工具 | 每一次呼叫都注入,無條件 |
提醒是點事件:「晚上九點」到了、push 出去、結束。一個背景 loop 每 60 秒掃 SQLite 找到期的列,推回它們來自的群組、發真 @ 通知、標記完成——該有的維運配料都有(狀態在 SQLite 所以重啟不丟;停機期間錯過的補送並明標「(遲到)」;循環提醒重排下一次而不是關閉)。
記憶是點事件的反面——它是環境。「John 住在花蓮」不是在某個時間戳相關;它對每一則未來的回覆都潛在相關。所以讀取模型是:每次被叫喚,bot 從 SQLite 撈出這個對話的記憶、以【長期記憶】區塊的形式接在 LLM 的脈絡前面。模型不去撈記憶。它就是擁有記憶,跟擁有 system prompt 是同一種擁有。
為什麼沒有 recall 工具
對稱的設計——remember 配一個 LLM 覺得歷史重要時呼叫的 recall 工具——是多數人(以及多數模型,你問它們的話)第一直覺會抓的形狀。它在兩個會疊加的地方更糟:
- 它花一個來回。 工具呼叫、分派、結果、第二次推論——為了一份本來就躺在本地資料表裡的資訊。
- 它要求模型知道自己該問。 一個天氣問題看起來不需要記憶。模型會反問「哪個城市?」——因為問題裡沒有任何線索暗示答案早已建檔。那個「需要時才呼叫」的 recall 工具,在真正需要的時候永遠不會被呼叫。
無條件注入用一些脈絡 token 的代價,同時刪掉這兩種失敗模式——注入量有每對話上限,溢出時附一行「另有較舊的 N 則未列出」,免得模型把視窗誤當全部。通用規則:最好的工具是模型不必呼叫的那個。 應該影響每個答案的資訊就放進每個 prompt;不要讓一個 agent 自己決定要不要去拿。
刪除不需要查詢,理由相同。每一行注入的記憶都帶著它的列 id——#12 「John 住在花蓮」(John 記的)——所以有人說「欸我搬家了,忘掉住哪那件事」時,模型手上已經有把手,直接呼叫 forget(12)。注入格式同時就是編輯介面。
教模型分清楚哪個是哪個
兩個功能長得這麼像,誤路由就是日常故障模式:「10 分鐘後提醒我倒垃圾」絕不能變成一則永久記憶。這道圍籬蓋在 LLM 真正閱讀的地方——工具的 description:
"description": (
"把一件『之後也要記得』的長期事實記下來,跨對話永久保存。"
"當使用者說「記住…」「我的生日是…」「以後叫我…」,"
"交代一個穩定、之後仍適用的事實時呼叫。"
"⚠️ 只記長期穩定的事實(偏好、稱呼、家庭成員、地點、習慣);"
"一次性的待辦(『10 分鐘後提醒我』)用 set_reminder,不要用這個。"
"⚠️ 若這是在更正脈絡【長期記憶】裡已經有的舊事實,"
"先用 forget 刪掉那則舊的(用它的 #id),再 remember 新的。"
)
tools/remember.py — description 負責路由
這跟第四篇匯率工具對加密貨幣圈地是同一課、再上一層:兩個工具在表面措辭上重疊時,description 必須明白地瓜分空間——包括告訴模型「兩個都像的時候怎麼辦」。
群組記憶是公佈欄
記憶依對話隔離——A 群記的,B 群永遠看不到,跟訊息、提醒一致。但在群組內部,模型刻意是共有的:任何人都看得到、也刪得掉任何人記的。每則記憶標著誰寫的,而那是唯一的歸屬標記。
對高信任的家庭群組來說,共享意識就是功能本身:「記住媽媽的班機週四下午三點落地」本來就該讓 bot 在那個房間服務的每個人看到,本來就該讓發現時間改了的人直接更正。往家庭公佈欄裡蓋 per-user 權限系統,是把辦公室的假設搬進廚房。
同一個性質也解釋了為什麼上限設得小、隱私提醒講得直白:單則記憶上限 500 字(記憶是一句耐久的事實,不是檔案庫),而記憶表裡的一切都會進到每次回覆的 prompt——所以 bot 的文件用白話寫著:別叫它記密碼。
收穫
兩個功能寫入方式相同;所有要緊的設計決策都在讀取端。「某一刻浮現」和「永遠在場」產出一個排程器和一個注入器——幾乎不共享任何程式碼。先問存下的資料該何時重現,再問怎麼存。
應該影響每個答案的資訊,屬於每個 prompt,而不是藏在一個模型得記得去呼叫的工具後面。注入用 token 換掉一整類失敗——模型無法忘記查閱一份它從來不需要去拿的東西。
「模型不會學會你」比假裝相反是更好的賣點,因為它指向真正的工程:記憶是一個資料庫功能,配上一條 LLM 形狀的讀取路徑。理解這點的使用者也會理解為什麼更正 bot 立即生效——他們在改資料列,不是在重訓任何東西。