USER.md 與 MEMORY.md 為什麼不能合併?
摘要
USER.md 與 MEMORY.md 是 Hermes Agent 長期記憶的"兩份貼身筆記",它們不能合併的原因有三條硬約束:一、容量各自獨立(USER.md 約 1,375 字符 / 500 tokens;MEMORY.md 約 2,200 字符 / 800 tokens),互不擠壓;二、加載策略不同(兩份都每次會話必載,但語義角色完全不同——一份穩定描述"你是誰",一份高頻記錄"我們做過什麼");三、更新語義不同(USER.md 偏好類字段以"覆蓋舊值"為主,MEMORY.md 以"追加 + 按需 consolidate"為主)。合併成一份 NOTES.md 會同時破壞這三條約束,是典型的"看起來更簡單,實際用起來更糟"。
引子:一個正經的反問
《Hermes Agent 為什麼能"記住你"?拆解長期記憶系統的工作原理》裏講過,Hermes 的分層記憶裏,USER.md 和 MEMORY.md 加起來只有 ~3,575 字符 / ~1,300 tokens——放在動輒十萬上下文窗口的時代,這個空間簡直過分節制。
於是很多人第一反應是三個問題:
- 為什麼不合成一個
NOTES.md? - 為什麼不放大到幾十 KB?
- 為什麼這兩個文件的容量還差一截(2,200 vs 1,375)?
這些問題其實指向的是同一個答案:USER.md 和 MEMORY.md 是兩種性質不同的記憶,塞在一起會互相拖累。下面把這條設計原則拆成三層來看。
一、USER.md:穩定、結構化的用户畫像
USER.md 是關於"你"的穩定描述:
- Identity:名字、職業、所在地。
- Preferences:溝通風格、常用技術棧。
- Constraints:作息、語言、禁忌話題。
- Working style:喜歡結論先行還是過程先行。
- Long-term goals:長期在做什麼、追求什麼。
它的關鍵特徵:
- 穩定:幾天甚至幾周才會更新一次。
- 結構化:分節、條目化、字段化。
- 必載:每次會話開始自動加載進系統提示。
- 有硬上限:~1,375 字符 / ~500 tokens。
你可以把 USER.md 想成 Agent 貼在顯示器邊框上的便利貼——不多,但抬眼就看到,抬眼就用得上。
二、MEMORY.md:可增長、按需召回的事實檔案
MEMORY.md 存的是你和 Agent"一起幹過什麼":
- 項目狀態:agent-demo 前端使用本地開發端口。
- 結論決策:上週把 hero 區改為動態漸變。
- 具體事件:上週和 A 團隊定了 Q4 會做 X。
- 特定知識點:這個 API 的鑑權走內部 SDK 而非公開接口。
它的關鍵特徵:
- 動態:幾乎每次實質會話都會寫入 1-2 條。
- 條目式:一條一條追加,方便召回。
- 必載 + 按需 FTS5 命中:常用條目每次會話必載,歷史條目通過 FTS5 命中拉入。
- 典型 8–15 條:目標條目數 8-15 條、總量 ~2,200 字符 / ~800 tokens。
你可以把 MEMORY.md 想成 桌上的日記本——比便利貼多,但仍然寫得剋制,為的是"翻起來快"。
三、"為什麼不合成一個 NOTES.md?"—— 三條硬約束
有人會説:"既然都是每次必載的 Markdown,為什麼不合並?"
不能合併的三條硬約束如下,每條都獨立到足夠讓這個方案破產。
理由 1:容量各自獨立,不互相擠壓
USER.md 500 tokens、MEMORY.md 800 tokens。合併之後,一份 1,300 tokens 的 NOTES.md 會立刻遇到一個問題:當 MEMORY.md 快滿時,Agent 是否要為"再記一條事件"而刪掉一條"用户是誰"的畫像?
答案顯然是不該——但一旦合併,這個決策就必須做,且每次記錄都做。分開之後,兩個字段池各自演化,MEMORY.md 滿了只擠 MEMORY.md 自己,不會污染畫像。
理由 2:加載策略語義不同
兩個文件確實都是每次會話必載,但它們在系統提示裏扮演的角色完全不同:
USER.md段回答的是 "你面對的是誰",用於給 Agent 定調(語氣、結論粒度、技術棧假設)。MEMORY.md段回答的是 "你們一起做過什麼",用於給 Agent 提供事實抓手。
如果合併,兩段內容會互相稀釋——Agent 無法快速判斷"這段話是我要遵循的溝通風格,還是我要引用的過往事實"。
理由 3:更新語義不同
USER.md偏好字段以"覆蓋舊值"為主:用户説"以後別再總結了",直接覆蓋 preference 裏的相關行。MEMORY.md以"追加 + 按需 consolidate"為主:事件是時間序列,歷史不能被隨意抹掉;容量滿了則通過下面的錯誤響應觸發主動整合:
{
"success": false,
"error": "Memory at 2,100/2,200 chars. Consolidate now...",
"current_entries": [...],
"usage": "2,100/2,200"
}合併之後,add / replace / remove 三種原子操作要在同一個文件上並存,實現和用户心智負擔都會同時變糟。
四、一個具體的例子
假設你告訴 Agent:
"我叫 Jasmin,是一名前端工程師,喜歡結論先行的溝通風格。最近在做 agent-demo 項目(一個 AI Agent 應用)的國際站,用 Next.js。上週我們把 hero 區改成了動態漸變。"
一個訓練良好的 Hermes Agent 會做這樣的拆分:
寫入 USER.md:
## Identity
- Name: Jasmin
- Role: 前端工程師
## Preferences
- 溝通風格:結論先行
- 常用技術棧:Next.js寫入 MEMORY.md:
- [2026-07-30] agent-demo 項目:國際站 hero 區改為動態漸變。
- 相關項目:agent-demo / 國際站 (global region)看到區別沒?
- "你是什麼樣的人"進畫像,"我們一起做了什麼"進檔案。
- 前者只會隨着你本人變化(換工作、換團隊)才會改;後者隨着每一天的對話都在增長。
- 分開寫,才能各自演化。
五、跳過寫入的 5 條黑名單
Hermes 也不是什麼信息都往 USER.md 塞。它有一份"跳過黑名單":
- 一次性情緒:"今天頭有點疼"——不是穩定屬性。
- 臨時任務參數:"這次幫我把標題改成 X"——用完即棄。
- 模糊態度:"我大概比較喜歡簡潔一點"——不夠可複用(要麼加限定,要麼不記)。
- 可從對話上下文實時推斷的信息——沒必要落盤。
- 敏感信息(如未經用户顯式確認的郵箱、密碼、地址)——涉及隱私默認不寫。
有了這份黑名單,USER.md 才能維持"500 tokens 且高質量"的密度。
六、這對產品意味着什麼?
對 Agent 產品而言,USER.md / MEMORY.md 的分層不只是工程細節,它決定了三件事:
- 能不能給用户看"你對我的印象" —— 需要一份穩定、可讀、可編輯的畫像(
USER.md的語義)。 - 能不能長期不失憶 —— 需要一份可增長、可召回的事實檔案(
MEMORY.md的語義)。 - 成本能不能被約束 —— 分層加載才能既"記得你是誰"又不燒掉一整個 context。
七、LightVela:把這份"兩筆賬"做成產品
LightVela 內部的 Agent 記憶體系,借鑑的正是這種分層思路。Hermes 的 USER.md / MEMORY.md 是給開發者看的 Markdown,LightVela 把它做成兩件普通用户能直接操作的東西:
- 畫像層可視化:用户能在界面上直接看到"Agent 記得我的哪些畫像"、"這些畫像來自哪次對話",並支持一鍵修正。畫像不再是黑盒
USER.md。 - 事實層可組織:事實記憶不只是一堆條目,而是能被打上項目、任務、時間等標籤,方便召回與清理。團隊場景下還能把"團隊記憶"和"個人記憶"分層管理。
- 最短路徑:如果你被"分層記憶"這套思路打動,但不想自己去改
~/.hermes/USER.md、維護 FTS5、備份 SQLite,LightVela 是把這套思路直接產品化後端給你的最短路徑。
一句話:Hermes 給了"分層記憶"的工程藍本,LightVela 把它做成每個人都用得上的產品體驗。
小結
USER.md:穩定、結構化、每次必載的用户畫像(~500 tokens,8-15 條畫像字段)。MEMORY.md:可增長、按需召回的事實檔案(~800 tokens,8-15 條事件條目)。- 二者不能合併的三條硬約束:容量互不擠壓 / 加載語義不同 / 更新語義不同。
- 分層是好 Agent 記憶系統的底層能力,也是 LightVela 記憶能力產品化的起點。