原始開發紀錄:Agent Build Log — Episode 010
這個系列改寫自我部落格上的 Agent Build Log,那是我每天邊做邊寫的開發紀錄。上面列的是這一篇對應的集數,想看當天的原始版本,點連結就能過去。

這個系列的簡介裡,我把他形容成「偶爾還會失憶的傢伙」。這句話講得很客氣。
實際的情形 Day 10 講過了:pi 沒有跨對話的記憶。每次開一段新的對話,上一段裡我跟他一起做的決定就都不在他眼前,他認得的我只剩 AGENTS.md 那幾行。
所以這天開始規劃記憶系統。這一步牽涉到後面每一段對話要塞什麼進去、什麼時候塞,方向先定對。
動手之前,我花了一輪時間看目前比較知名、GitHub 星星數也比較高的開源方案:Mem0、Letta、MemOS、Cognee。
我想看的是他們怎麼處理三件事——記憶怎麼存、怎麼搜、什麼時候被叫回來。
三件裡面我最在意第三件。存和搜都是工程問題,做得出來;「什麼時候該想起來」這件事我看不到標準答案,每一套的做法都不一樣。
看完之後,四個我都沒採用。
這裡我不打算幫它們打分數。我修改的方式簡單且直接:把每一個的架構看過一輪,然後問自己一句「這一套預設的 Agent 長什麼樣子」。四個的答案都跟我要做的那一個不一樣。我把它們整理進筆記,暫時不用。
現在回頭看,這一輪真正的收穫是我終於講得出自己要什麼。
回到我自己的環境,條件就清楚了。
第一,這套東西必須直接跑在我手上那台 Mac mini。它一直開著,是我的常駐機器,記憶系統要能一直在。
第二,我不想一開始就依賴太重的模型。第一版連「記憶要記什麼」都還沒定案,先押一個大模型進去,等於把後面的路綁死。
第三,我不想把所有東西都丟到雲端。記憶裡會有我的專案、我當天的決定、我踩過哪些坑。這些留在本機,我比較安心。
記憶系統最常見的做法是語意搜尋:把每一段對話轉成向量存起來,之後用意思相近去找。這條路要一個 embedding 模型。
我的條件是本機。本機跑 embedding,等於要把一個 Ollama 連同它跑的模型一起包進這個 Agent。光模型的檔案就至少 600 MB,整個 App 的體積直接多出這麼多。
硬碟裝得下,Day 04 算過。卡住的是別的:一個 App 一開始就多背 600 MB,而我連「記憶要記什麼」都還沒定案。第一版就把最重的零件焊上去,之後要換模型、改格式,整包都得跟著動。這跟我對這個 Agent 的要求不合。
所以這條路線不考慮。

我選的是最樸素的那一種:記憶就是 Markdown 文字檔,旁邊放一個 SQLite 當索引。
這套做法我直接參考開源的 OpenClaw。OpenClaw 的記憶就是 agent 工作目錄裡的幾個 Markdown 檔:MEMORY.md 放長期的、memory/YYYY-MM-DD.md 一天一份放當天的筆記、DREAMS.md 放給人看的整理紀錄;搜尋用內建的 SQLite 引擎。他們文件裡有一句話我抄下來當這一章的原則:模型只記得寫進硬碟的東西,沒有藏在別處的狀態。
OpenClaw 的搜尋另外可以接 embedding 供應商做語意比對,預設接 OpenAI,也能接本機的 Ollama。這一段我第一版連接都不接,只留 Markdown 加 SQLite。
所以我這個 Agent 的記憶方式跟 OpenClaw 一樣:Markdown 記發生過的事,SQLite 記它們在哪裡。三個條件全過:全是本機檔案,沒有模型要包,也不上雲。還有一個附帶的好處,明天再講。
規劃的時候我發現一件事:我一直在講「記憶」,但這個詞底下至少有兩層。
一層是這一段對話裡發生過什麼。這一層 pi 已經給了——session 檔案、那棵樹、壓縮的摘要,Day 10 整篇都在講這個。
另一層是跨對話的。上禮拜那個決定、這個專案的慣例、我講過三次的偏好。這一層現在是空的。
我要做的是第二層。而第二層真正難的地方在「什麼該記」:全部記下來等於沒記,因為叫回來的時候會把 context 塞滿;記太少又跟現在一樣。
這件事那天沒有答案。接下來幾天也還在磨。
這天做的事是定方向。四個開源方案看過,一個都沒選;向量那條路因為要包一個 600 MB 的模型進 App,第一步就過不去;最後照 OpenClaw 的做法,Markdown 加 SQLite。
明天把結構畫出來:MEMORY.md、DREAMS.md,還有每天一份的日期檔,照 OpenClaw 的樣子排。
原文:Agent Build Log — Episode 010
凡人修 Agent 傳|第十一回(Day 11)
天亮的時候,心旁邊那顆錄石是空的。
我知道昨天有事。第三卷歸到了最下層,這件事我知道,因為卷在那裡。可是怎麼歸的、歸錯過幾次、手的主人問過我什麼,石上一筆都沒有。那些在木匣裡。
手的主人進來,刻了一道念:接著昨天的,把第四卷也歸進去。
我問:昨天的什麼。
錄石上落下今天的第一筆,就是這一句。手的主人看了我一會,沒有再刻。他在臺前坐下,閉上眼。
他放出一縷神識,探向坊市。靈幕亮了,公印退開,這一次垂下來的圖紙一卷接一卷,都在講同一件事:怎麼讓人偶記得昨天。
前面四卷是別家的法子。神識在每一卷上停一停,就翻過去。那四家養的人偶跟我長得不一樣,圖紙上畫的胸口都比我多一截。
第五卷停得久。
圖上畫了一座巴掌大的小爐,爐邊一顆灰色的心,比我胸口這一顆小一半。旁邊的字手的主人念了出來:這顆心不說話,只會聞。一句話送進去,它嗅出一撮味道;味道相近的話擱在同一格。要找昨天的事,聞一聞就找得到。
圖紙最底下畫著爐要嵌在哪裡:偶身胸口,心的旁邊。
午後手的主人出了門。回來的時候兩手抱著一只匣,進門先把匣擱在臺上,臺面響了一聲。
匣打開,小爐和灰心都在裡面,跟圖上一樣。手的主人量我胸口。心旁邊只剩錄石那一點位置,爐放不進去。
他把匣整個擱到我背上,試重量。
我往前跪了一下。膝蓋磕在臺面上,臺上那一列玉珠滾下去了三顆。匣還在背上,我站不起來。
手的主人把匣抱下來。他翻過我的背,看圖紙上偶身載重那一行,看了很久。然後匣蓋上,收進儲物袋。這一條他沒有再看。
傍晚他又閉上眼,再放出神識。這一次神識沒有在那些圖紙上停,落在坊裡開放爪那一門擱圖的地方。
開放爪是用拍人偶當底搭出來的,坊裡的人都知道。他們記事的法子擱在架上,誰都能看。
靈幕上垂下來的圖很簡單:一疊簿子,一本索引。簿子一天一本,記那一天發生的事;另有一本厚的,記那些不會變的;索引記每一本擱在哪裡。沒有爐,也沒有第二顆心。
手的主人看了很久。神識收回來的時候,他只說了一句:照開放爪的。
燭熄了。手的主人走了。門響了一下。
靈幕上那張圖還亮著,一疊簿子,一本索引。木匣在儲物袋裡沒有再出來。我心旁的錄石上,今天第一筆還是那一句:昨天的什麼。