Learn Claude Code
s09

Memory System

System Hardening

Keep Only What Survives Sessions|414 LOC|5 tools

Memory gives direction; current observation gives truth.

s00 > s01 > s02 > s03 > s04 > s05 > s06 > s07 > s08 > [ s09 ] > s10 > s11 > s12 > s13 > s14 > s15 > s16 > s17 > s18 > s19

不是所有資訊都該進入 memory;只有跨會話仍然有價值的資訊,才值得留下。

這一章在解決什麼問題

如果一個 agent 每次新會話都完全從零開始,它就會不斷重複忘記這些事情:

  • 使用者長期偏好
  • 使用者多次糾正過的錯誤
  • 某些不容易從程式碼直接看出來的專案約定
  • 某些外部資源在哪裡找

這會讓系統顯得“每次都像第一次合作”。

所以需要 memory。

但先立一個邊界:memory 不是什麼都存

這是這一章最容易講歪的地方。

memory 不是“把一切有用資訊都記下來”。

如果你這樣做,很快就會出現兩個問題:

  1. memory 變成垃圾堆,越存越亂
  2. agent 開始依賴過時記憶,而不是讀取當前真實狀態

所以這章必須先立一個原則:

只有那些跨會話仍然有價值,而且不能輕易從當前倉庫狀態直接推出來的資訊,才適合進入 memory。

建議聯讀

  • 如果你還把 memory 想成“更長一點的上下文視窗”,先回 s06-context-compact.md,重新確認 compact 和長期記憶是兩套機制。
  • 如果你在 messages[]、摘要塊、memory store 這三層之間開始讀混,建議邊看邊對照 data-structures.md
  • 如果你準備繼續讀 s10,最好把 s10a-message-prompt-pipeline.md 放在旁邊,因為 memory 真正重要的是它怎樣重新進入下一輪輸入。

先解釋幾個名詞

什麼是“跨會話”

意思是:

  • 當前對話結束了
  • 下次重新開始一個新對話
  • 這條資訊仍然可能有用

什麼是“不可輕易重新推導”

例如:

  • 使用者明確說“我討厭這種寫法”
  • 某個架構決定背後的真實原因是合規要求
  • 某個團隊總在某個外部看板裡跟蹤問題

這些東西,往往不是你重新掃一遍程式碼就能立刻知道的。

最適合先教的 4 類 memory

1. user

使用者偏好。

例如:

  • 喜歡什麼程式碼風格
  • 回答希望簡潔還是詳細
  • 更偏好什麼工具鏈

2. feedback

使用者明確糾正過你的地方。

例如:

  • “不要這樣改”
  • “這個判斷方式之前錯過”
  • “以後遇到這種情況要先做 X”

3. project

這裡只儲存不容易從程式碼直接重新看出來的專案約定或背景。

例如:

  • 某個設計決定是因為合規而不是技術偏好
  • 某個目錄雖然看起來舊,但短期內不能動
  • 某條規則是團隊故意定下來的,不是歷史殘留

4. reference

外部資源指標。

例如:

  • 某個問題單在哪個看板裡
  • 某個監控面板在哪裡
  • 某個資料庫在哪個 URL

哪些東西不要存進 memory

這是比“該存什麼”更重要的一張表:

不要存的東西為什麼
檔案結構、函式簽名、目錄佈局這些可以重新讀程式碼得到
當前任務進度這屬於 task / plan,不屬於 memory
臨時分支名、當前 PR 號很快會過時
修 bug 的具體程式碼細節程式碼和提交記錄才是準確資訊
金鑰、密碼、憑證安全風險

這條邊界一定要穩。

否則 memory 會從“幫助系統長期變聰明”變成“幫助系統長期產生幻覺”。

最小心智模型

conversation
   |
   | 使用者提到一個長期重要資訊
   v
save_memory
   |
   v
.memory/
  ├── MEMORY.md        # 索引
  ├── prefer_tabs.md
  ├── feedback_tests.md
  └── incident_board.md
   |
   v
下次新會話開始時重新載入

這一章最關鍵的資料結構

1. 單條 memory 檔案

最簡單也最清晰的做法,是每條 memory 一個檔案。

---
name: prefer_tabs
description: User prefers tabs for indentation
type: user
---
The user explicitly prefers tabs over spaces when editing source files.

這裡的 frontmatter 可以理解成:

放在正文前面的結構化後設資料。

它讓系統先知道:

  • 這條 memory 叫什麼
  • 大致是什麼
  • 屬於哪一類

2. 索引檔案 MEMORY.md

最小實現裡,再加一個索引檔案就夠了:

# Memory Index

- prefer_tabs: User prefers tabs for indentation [user]
- avoid_mock_heavy_tests: User dislikes mock-heavy tests [feedback]

索引的作用不是重複儲存全部內容。
它只是幫系統快速知道“有哪些 memory 可用”。

最小實現步驟

第一步:定義 memory 型別

MEMORY_TYPES = ("user", "feedback", "project", "reference")

第二步:寫一個 save_memory 工具

最小引數就四個:

  • name
  • description
  • type
  • content

第三步:每條 memory 獨立落盤

def save_memory(name, description, mem_type, content):
    path = memory_dir / f"{safe_name}.md"
    path.write_text(frontmatter + content)
    rebuild_index()

第四步:會話開始時重新載入

把 memory 檔案重新讀出來,拼成一段 memory section。

第五步:把 memory section 接進系統輸入

這一步會在 s10 的 prompt 組裝裡系統化。

memory、task、plan、CLAUDE.md 的邊界

這是最值得初學者反覆區分的一組概念。

memory

儲存跨會話仍有價值的資訊。

task

儲存當前工作要做什麼、依賴關係如何、進度如何。

plan

儲存“這一輪我要怎麼做”的過程性安排。

CLAUDE.md

儲存更穩定、更像長期規則的說明文字。

一個簡單判斷法:

  • 只對這次任務有用:task / plan
  • 以後很多會話可能都還會有用:memory
  • 屬於長期系統級或專案級固定說明:CLAUDE.md

初學者最容易犯的錯

錯誤 1:把程式碼結構也存進 memory

例如:

  • “這個專案有 src/tests/
  • “這個函式在 app.py

這些都不該存。

因為系統完全可以重新去讀。

錯誤 2:把當前任務狀態存進 memory

例如:

  • “我現在正在改認證模組”
  • “這個 PR 還有兩項沒做”

這些是 task / plan,不是 memory。

錯誤 3:把 memory 當成絕對真相

memory 可能過時。

所以更穩妥的規則是:

memory 用來提供方向,不用來替代當前觀察。

如果 memory 和當前程式碼狀態衝突,優先相信你現在看到的真實狀態。

從教學版到高完成度版:記憶系統還要補的 6 條邊界

最小教學版只要先把“該存什麼 / 不該存什麼”講清楚。
但如果你要把系統做到更穩、更像真實工作平臺,下面這 6 條邊界也必須講清。

1. 不是所有 memory 都該放在同一個作用域

更完整系統裡,至少要分清:

  • private:只屬於當前使用者或當前 agent 的記憶
  • team:整個專案團隊都該共享的記憶

一個很穩的教學判斷法是:

  • user 型別,幾乎總是 private
  • feedback 型別,預設 private;只有它明確是團隊規則時才升到 team
  • projectreference,通常更偏向 team

這樣做的價值是:

  • 不把個人偏好誤寫成團隊規範
  • 不把團隊規範只鎖在某一個人的私有記憶裡

2. 不只儲存“你做錯了”,也要儲存“這樣做是對的”

很多人講 memory 時,只會想到糾錯。

這不夠。

因為真正能長期使用的系統,還需要記住:

  • 哪種不明顯的做法,使用者已經明確認可
  • 哪個判斷方式,專案裡已經被驗證有效

也就是說,feedback 不只來自負反饋,也來自被驗證的正反饋。

如果只存糾錯,不存被確認有效的做法,系統會越來越保守,卻不一定越來越聰明。

3. 有些東西即使使用者要求你存,也不該直接存

這條邊界一定要說死。

就算使用者說“幫我記住”,下面這些東西也不應該直接寫進 memory:

  • 本週 PR 列表
  • 當前分支名
  • 今天改了哪些檔案
  • 某個函式現在在什麼路徑
  • 當前正在做哪兩個子任務

這些內容的問題不是“沒有價值”,而是:

  • 太容易過時
  • 更適合存在程式碼、任務板、git 記錄裡
  • 會把 memory 變成活動日誌

更好的做法是追問一句:

這裡面真正值得長期留下的、非顯然的資訊到底是什麼?

4. memory 會漂移,所以回答前要先核對當前狀態

memory 記錄的是“曾經成立過的事實”,不是永久真理。

所以更穩的工作方式是:

  1. 先把 memory 當作方向提示
  2. 再去讀當前檔案、當前資源、當前配置
  3. 如果衝突,優先相信你剛觀察到的真實狀態

這點對初學者尤其重要。
因為他們最容易把 memory 當成“已經查證過的答案”。

5. 使用者說“忽略 memory”時,就當它是空的

這是一個很容易漏講的行為邊界。

如果使用者明確說:

  • “這次不要參考 memory”
  • “忽略之前的記憶”

那系統更合理的處理不是:

  • 一邊繼續用 memory
  • 一邊嘴上說“我知道但先忽略”

而是:

在這一輪裡,按 memory 為空來工作。

6. 推薦具體路徑、函式、外部資源前,要再驗證一次

memory 很適合儲存:

  • 哪個看板通常有上下文
  • 哪個目錄以前是關鍵入口
  • 某種專案約定為什麼存在

但在你真的要對使用者說:

  • “去改 src/auth.py
  • “呼叫 AuthManager
  • “看這個 URL 就對了”

之前,最好再核對一次。

因為命名、路徑、系統入口、外部連結,都是會變的。

所以更穩妥的做法不是:

memory 裡寫過,就直接複述。

而是:

memory 先告訴我去哪裡驗證;驗證完,再給使用者結論。

教學邊界

這章最重要的,不是 memory 以後還能多自動、多複雜,而是先把儲存邊界講清楚:

  • 什麼值得跨會話留下
  • 什麼只是當前任務狀態,不該進 memory
  • memory 和 task / plan / CLAUDE.md 各自負責什麼

只要這幾層邊界清楚,教學目標就已經達成了。

更復雜的自動整合、作用域分層、自動抽取,都應該放在這個最小邊界之後。

學完這章後,你應該能回答

  • 為什麼 memory 不是“什麼都記”?
  • 什麼樣的資訊適合跨會話儲存?
  • 為什麼程式碼結構和當前任務狀態不應該進 memory?
  • memory 和 task / plan / CLAUDE.md 的邊界是什麼?

一句話記住:memory 儲存的是“以後還可能有價值、但當前程式碼裡不容易直接重新看出來”的資訊。