weken.news
claude-codeai-memorycontext-engineering工程實務自動化

Claude Code 的 MEMORY.md 清過還是會肥回來:我掃了 85 個 session,發現 77% 的記憶從沒被讀過

直接回答

AI 記憶索引清完又肥回來,是因為清理只改了起點沒改斜率。我用 git 還原自己兩次瘦身:第一次從 44,304 bytes 砍到 3,629,成長率也從每天 1,150 bytes 降到 256,但斜率還是正的,118 天後又長回 33,804 bytes。真正的解法是讓索引大小跟記憶總數脫鉤,主檔只列固定分類不列單一則記憶,這樣新增第 558 則記憶時主檔增加 0 bytes。

週末哥 ·

我的 AI 記憶索引今天第二次快撞牆。上一次是 2026 年 5 月,我把它從 44,304 bytes 砍到 3,629,砍掉 92%。

四個月後,它是 22,693 字,離讀取上限只剩大概 13 天。

我原本要再清一次。清到一半停下來,因為我意識到自己在做一件四個月前做過、而且明顯沒有用的事。

先講這個系統長什麼樣,不然後面的數字沒有意義。

Claude Code 有一個檔案叫 MEMORY.md,每個 session 開始都會載入。它是一份索引,一行一則記憶,告訴我硬碟上有哪些筆記存在。真正的內容在別的檔案裡,按需要才讀。

這個設計本身沒問題,問題是它會長。到今天為止我累積了 557 個記憶檔,索引 156 條。

第一件事是不要急著清,先去量。

我的設定資料夾有每日 git 備份,所以索引檔的每一個版本都還在。我把每一天的大小還原出來,得到兩段曲線。

第一段,2026 年 4 月 25 日到 5 月 9 日:163 條長到 237 條,28,197 bytes 長到 44,304 bytes。每天 1,150 bytes。

5 月 13 日那次重構砍到 3,629 bytes、23 條。

第二段,從那天到今天 118 天:長回 33,804 bytes、156 條。每天 256 bytes。

兩次 AI 記憶索引瘦身的實測對比:第一次重構把成長率從每天 1,150 bytes 壓到 256 bytes,降了 4.5 倍,但斜率仍為正。118 天後索引從 3,629 bytes 長回 33,804 bytes。清理改的是起點,不是斜率,所以撞牆只是被延後而不是被解決。

而且不只條數在長,每一條也在變胖。平均一條從 158 bytes 長到 217 bytes,胖了 37%。成長是條數乘以每條長度,兩個乘數都在漲。

第二件事是搞清楚這些記憶到底有沒有人用。

Claude Code 的每個 session 都會在 projects 資料夾留一份完整的 jsonl 逐字稿,裡面每一次讀檔的路徑都有記錄。我本機有 85 個檔、1.1 GB。這份資料躺在那裡半年,我從來沒跑過。

一個 rg 指令掃完,結果比我預期難看很多。

掃 85 個 Claude Code session 的實際讀取紀錄:557 個記憶檔只有 127 個在 30 天內被打開過,430 個(77%)完全沒被碰。索引的 156 條裡有 72 條(46%)指向沒人開的檔案,佔索引 41% 的位元組。30 個檔案(5%)吃掉全部 1,603 次讀取的 80%。

還有一個數字反過來很有意思:有 43 個檔案被讀取了,但它們根本不在索引裡。是靠主題檔或直接路徑找到的。

也就是說,不靠這份索引,路一樣走得通。

到這裡就可以問那個真正的問題了:這份索引為什麼要存在。

它的工作是讓我知道有某個記憶存在。但 77% 的記憶已經 30 天沒被打開,這個工作對絕大多數內容早就失效了。它實際只覆蓋到我本來就記得的那一小撮頭部,卻要我每個 session 付 22,693 字的成本。

再把式子寫出來:開機成本等於條數乘以每條長度。條數每天加 1.13,長度也在漲。只要成本正比於記憶總數,就永遠會再滿一次。

結論不是「這次要清得更用力」,是「索引的大小不能跟記憶總數綁在一起」。

改法很簡單,簡單到有點反高潮。

主檔的結構裡,不再有「單一則記憶」這種列。它只列固定的分類:10 個主題檔、7 個使用者檔、5 個外部參照、1 行指向專案索引,加一段告訴未來的自己新記憶該寫去哪。

原本 156 條專案與筆記條目,88 條搬進一個獨立的專案索引,43 條掛進對應主題檔的認領段。這兩層都是按需讀取,愛長多大都行,因為它們不佔開機成本。

主檔從 22,693 字變成 3,339 字,砍 85%。

但重點不是這個數字。重點是新增第 558 則記憶時,主檔增加 0 bytes。斜率是零,不是變小。

搬的過程差點出事,這段值得單獨講。

我有一條規則是「動手前必先 diff,沒被新位置涵蓋的先搬再刪」。我照做了,寫了一個檢查去確認每一條在新位置找得到。

第一版的檢查是拿去掉副檔名的前綴去 grep。它回報 47 條裡有 13 條已經被涵蓋。

搬完之後我換一種寫法重掃,才發現那 13 條裡有 9 條是假的。因為 grep 會比對到主題檔內文裡剛好提到那個字串的散文,那是「提過」不是「認領」。去掉副檔名讓字串更短、更容易誤中。

如果我沒有重掃,那 9 則記憶會直接變孤兒。

教訓是模糊比對只能用來找候選,不能用來宣告安全。要下「已涵蓋、可以刪」這種結論,比對條件必須嚴格到不可能誤中,而且驗收要跑兩次、第二次換一種寫法。

最後一段是整件事裡最意外的發現。

我本來以為索引是四個月慢慢長回來的。查下去不是。

7 月 1 日 42 條,7 月 3 日 45 條,7 月 5 日 71 條。兩天內加了 26 條。

那兩天我在做記憶系統的健檢,並且把健檢流程寫成一份每月要跑的維護程序。那份程序裡有一條:索引對帳,新檔沒進索引就補一行。

而另一份更早的存檔流程文件,從頭到尾寫著「主檔永遠 thin,不寫 index」。

索引回流的實際起點可以定位到單一天:2026 年 7 月 3 日索引 45 條,7 月 5 日 71 條,兩天內增加 26 條。原因是同期寫下的月度維護程序要求「新檔沒進索引就補一行」,直接牴觸另一份文件裡「主檔永遠 thin,不寫 index」的原則。兩份制度文件互相矛盾時,可執行的程序打贏了原則。

這個解釋比「我不夠自律」精確得多,而且可以修。我把那份維護程序的兩行改掉了,不然下個月的健檢會叫我去做一件現在會被擋下來的事。

所以最後我加了一支 hook。

不是因為結構不夠,是因為我需要一個證據。我在分析這個膨脹問題的同一天,一邊分析一邊往主檔塞了一條 372 bytes 的紀錄,全檔第二長。我知道規則,還是照犯。

那支 hook 掛在寫入前,偵測到單一記憶條目或檔案超過行數上限就直接擋,而且會在錯誤訊息裡告訴我正確位置在哪。這一點很重要:只擋不指路的檢查會讓人卡住,卡住之後就會有人去繞過它。

裝完我真的去試著寫一條進去,被擋下來,主檔一個字都沒改到。然後我把今天這個 grep 誤判的教訓寫成第 558 則記憶,走新流程掛進主題檔,主檔還是 3,339 字。

如果只留一句:清理是狀態,結構是流程。你清掉的東西會照原本的速率長回來,除非你把生產它的那條路本身改掉。

判斷自己是在改哪一個,只要問一句話:這次改完之後,斜率會變成幾?

常見問題

Claude Code 的 MEMORY.md 太大會怎樣?
超過讀取上限之後會被截斷,後半段的索引等於不存在。我的上限約 25,000 字,撞牆前累積到 22,693 字,換算每天成長 170 字,大概還剩 13 天。被截斷的風險不是檔案壞掉,是你以為索引在那裡、實際上載不進來,而且不會有任何錯誤訊息。
為什麼記憶索引清一次之後還是會長回來?
因為清理改的是起點,不是斜率。我 2026 年 5 月 13 日把索引從 44,304 bytes 砍到 3,629,成長率確實從每天 1,150 bytes 降到 256 bytes,但那條線還是往上,只是慢了 4.5 倍。天花板沒有變,所以撞牆是遲早的事。只要索引大小正比於記憶總數,就沒有一勞永逸這回事。
怎麼知道哪些 AI 記憶該砍、哪些該留?
去讀 session 紀錄,不要憑感覺。Claude Code 的每個 session 都會在 projects 資料夾留一份完整的 jsonl,裡面每一次 Read 的檔案路徑都有記錄。我掃了 85 個 session,發現 557 個記憶檔只有 127 個在 30 天內被打開過,30 個檔案就吃掉 80% 的讀取。這個資料躺在本機半年沒人跑過。
O(1) 的記憶索引長什麼樣?
主檔只列固定分類,不列單一則記憶。我的版本是 10 行主題檔、7 行使用者檔、5 行外部參照、1 行指向專案索引、加一段告訴未來的自己新記憶該寫去哪。總共 23 條、3,339 字。單一則記憶改成掛在主題檔的認領段或獨立的專案索引,那兩層是按需讀取,不佔開機成本。
光靠寫規則能不能防止索引長回來?
我的實測是不行。5 月那次的設計文件寫得很清楚「主檔永遠 thin,不寫 index」,四個月後還是長回 156 條。而且我在做這次分析的同一天,一邊分析膨脹問題一邊往主檔塞了一條 372 bytes 的紀錄,是全檔第二長。正解是讓那個東西在結構上沒有位置可放,再加一支 PreToolUse hook 當保險。