// memoryTypes.ts - WHAT_NOT_TO_SAVE_SECTION
'- Code patterns, conventions, architecture, file paths, or project structure — these can be derived by reading the current project state.'
'- Git history, recent changes, or who-changed-what — `git log` / `git blame` are authoritative.'
'- Debugging solutions or fix recipes — the fix is in the code; the commit message has the context.'核心哲學:記憶系統補充可觀測資訊,而非複製它。程式碼、git 歷史、文件架構都是即時可查的「真相來源」;只有無法從這些來源推導的脈絡(用戶偏好、決策理由、外部系統指標、組織知識)才值得記憶。
這個設計防止了「記憶腫脹」——模型傾向於記錄所有事情,但大量冗餘記憶會稀釋真正有用的資訊。
// memoryTypes.ts - TRUSTING_RECALL_SECTION
'A memory that names a specific function, file, or flag is a claim that it existed *when the memory was written*. It may have been renamed, removed, or never merged. Before recommending it: ...'
'"The memory says X exists" is not the same as "X exists now."'// memoryAge.ts
export function memoryFreshnessText(mtimeMs: number): string {
// 超過 1 天的記憶顯示警告
return `This memory is ${d} days old. Memories are point-in-time observations, not live state — claims about code behavior or file:line citations may be outdated.`
}設計動機:使用者回報模型把過時的 file:line 引用當作事實斷言——引用使過時聲明更具說服力,而非更少。「47 days ago」這樣的年齡表示比原始 ISO 時間戳更能觸發模型的過期推理。
// memoryTypes.ts 中的 TRUSTING_RECALL_SECTION 開頭注釋
// H1 (eval 2026-03-17):
// 標題「Before recommending」(行動提示點)→ 3/3
// 標題「Trusting what you recall」(抽象標題)→ 0/3
// 相同的本體文字,只有標題不同// H6(branch-pollution eval #22856, case 5):
// 「ignore」用法的 anti-pattern 描述
// 失敗:模型看到「ignore memory about X」→ 讀取程式碼正確但追加「not Y as noted in memory」
// 修復:明確命名 anti-pattern:「proceed as if MEMORY.md were empty. Do not apply remembered facts, cite, compare against, or mention memory content」設計方法論:Memory 相關的 prompt 文字通過 A/B eval 測試驗證效果,不純憑直覺。標題定位(action trigger vs abstract)、anti-pattern 的明確命名、例子的具體程度都會影響模型行為。
// extractMemories.ts
let lastMemoryMessageUuid: string | undefined // 游標
function countModelVisibleMessagesSince(messages, sinceUuid): number {
// 只計算游標之後的新訊息
}記憶提取系統不每次重新分析整個對話歷史,而是維護一個游標,只處理上次提取之後的新訊息。這讓系統在長對話中保持 O(new messages) 的開銷,而非 O(total messages)。
// extractMemories.ts
if (hasMemoryWritesSince(messages, lastMemoryMessageUuid)) {
// 主代理已自己寫記憶 → 跳過背景提取,只推進游標
return
}主代理的 prompt 包含完整的記憶保存指令;若主代理主動寫記憶,背景提取是冗餘的(可能還有衝突)。這個「誰先寫誰優先,另一方退出」的設計防止了雙重寫入。
| 子系統 | 允許的工具 | 理由 |
|---|---|---|
| ExtractMemories | Read/Grep/Glob(無限制)+ 唯讀 Bash + Write/Edit(限 memory 目錄) | 需要讀取對話脈絡,只能寫自己的記憶空間 |
| AutoDream | 同上(共享 createAutoMemCanUseTool) | 同上 |
| Session Memory | 只允許 Edit(指定文件) | 只更新一個文件 |
| MagicDocs | 只允許 Edit(指定文件) | 只更新被追蹤的 Magic Doc 文件 |
各子系統的工具集根據其最小需求設計,不繼承主代理的全套工具。
// extractMemories.ts, autoDream.ts
const cacheSafeParams = createCacheSafeParams(context)
const result = await runForkedAgent({
cacheSafeParams,
// ...
})所有背景記憶子系統都使用 forked agent 模式,繼承父對話的 system prompt 和 message prefix。這讓 forked agent 能重用父對話的 prompt cache,而非重建整個上下文。
測量結果(extractMemories 日誌):
cache: read=XXXXX create=YYYYY input=ZZZZZ (N% hit)
高 cache hit 率是這些背景子系統在實際使用中保持低成本的關鍵。
// extractMemories/prompts.ts - opener()
`You MUST only use content from the last ~${newMessageCount} messages to update your persistent memories. Do not waste any turns attempting to investigate or verify that content further — no grepping source files, no reading code to confirm a pattern exists, no git commands.`// memdir.ts - DIR_EXISTS_GUIDANCE
'This directory already exists — write to it directly with the Write tool (do not run mkdir or check for its existence).'背景子系統有嚴格的 turn 預算(extractMemories 最多 5 turns),必須防止模型花 turn 在「確認環境」或「驗證資訊」上。Prompt 明確說明哪些探索是被禁止的,harness 透過保證(目錄已存在、只處理指定訊息範圍)支持這些約束。
// memoryTypes.ts - WHAT_NOT_TO_SAVE_SECTION(最後的強制規則)
'These exclusions apply even when the user explicitly asks you to save. If they ask you to save a PR list or activity summary, ask what was *surprising* or *non-obvious* about it — that is the part worth keeping.'即使用戶明確要求,系統也不記錄活動日誌或 PR 清單。引導模型問「什麼是令人驚訝的或非顯而易見的」——這才是值得跨 session 保留的資訊。
這個設計防止記憶系統退化為日誌系統。
ExtractMemories:
- 提取失敗 → 記 debug log,不通知用戶,游標不前進(下次包含失敗的訊息重試)
- 遠端模式 → 不執行
- 主代理已寫 → 跳過,不是錯誤
Team Memory Watcher:
- 永久失敗(no_oauth, 4xx)→ 設定 pushSuppressedReason,停止無意義重試
- 超過 167K push 事件的設計教訓直接影響了抑制機制的誕生
AutoDream:
- 失敗 → 回滾鎖的 mtime(讓時間門檻下次繼續有效)
- 用戶中止 → DreamTask 已回滾,不重複
MEMORY.md 截斷:
- 超限 → 截斷 + 警告,不拒絕載入。模型看到警告後知道索引不完整,可決定是否整理。
記憶可信度的優先序(從高到低):
- 當前程式碼狀態(
git log、讀取文件) - 新鮮記憶(今天/昨天建立的)
- 較舊記憶(有過期警告的)
- 沉澱記憶(活動快照,repository state summaries)
'A memory that summarizes repo state (activity logs, architecture snapshots) is frozen in time. If the user asks about *recent* or *current* state, prefer `git log` or reading the code over recalling the snapshot.'user → 調整溝通方式(服務個人)
feedback → 改變行為(成功和失敗都記錄)
project → 理解動機(for whom, why, by when)
reference → 知道去哪找(指向外部)
這個分類法的設計讓每種記憶有明確的「何時讀取」和「如何使用」:
user:任何需要根據對方背景調整的場合feedback:防止重蹈覆轍(負向)+ 強化已確認有效的方式(正向)project:提供背景和優先序以避免建議與現實脫節reference:避免讓模型浪費 turn 在「不知道去哪找」上
關鍵設計:feedback 同時記錄失敗(「不要」)和成功(「繼續這樣做」)。若只記錄糾正,模型會避免已知的錯誤但逐漸偏離已確認有效的方式。