七月底 CMU 放出一篇叫 ACM(Agentic Context Management)的論文,講的是怎麼讓 agent 自己管理 context。我維護一套 agent 記憶系統已經幾個月,看到題目的第一反應是想知道學術界處理同一個問題的做法跟我摸索出來的差多少。

對照下來的結果比我預期有意思:元件幾乎一一對得上,但兩邊解的其實是不同軸向的問題,而且論文裡有一組數據,反過來替我當初一個靠直覺做的設計決定提供了證據。

論文在做什麼

論文是 arXiv 2607.23809,程式碼在 lixiaochuan2020/agentic-context-management,MIT 授權。網路上不少轉述寫成「CMU 與 Meta 開源」,但論文首頁腳註寫得很明白:Meta 只擔任顧問角色,沒有實驗、資料收集或處理是在 Meta 的機器上跑的。這是 CMU 的工作。

ACM 的做法是給 agent 兩個工具,然後不設外部監控器:

  • manage_context:agent 自己在推理途中決定要壓縮,由 summarizer LLM 產出摘要放回 context,被壓掉的原始訊息全部存到外部 workspace,每份摘要配一個 ID。
  • query_memory:帶著 summary ID 加一句自然語言問題,由 querier LLM 到存起來的原文裡撈回細節。

論文一直強調「lossless」,準確的意思是原文不刪、可回查。壓縮這個動作本身仍然有損,回查也要靠另一顆 LLM 去檢索,這條路徑會不會漏,論文沒有量測。

比工具設計更關鍵的是後訓練。他們用 Qwen3.5-9B 當學生、Qwen3.5-397B-A17B 當老師,讓學生同時跑「有工具」和「沒工具」兩種軌跡,再由老師雙向標註:對前者刪掉太早壓縮的呼叫、改成實際動作;對後者插入該壓卻沒壓的點。所以模型學的是「何時該壓」跟「何時不該壓」兩件事。

元件對照

我這套系統的骨架放在 openclaw-workspace-template,下面提到的檔案路徑都能在那個 repo 裡找到。

ACM 元件我這邊的對應物實作
manage_context 壓縮curate-memory skill + journal rotationLLM 判斷內容該進 journal、notes/ 還是 MEMORY.md 索引;scripts/memory-archive.py --mode rotate-journal 把超過 5 天的日誌搬進 memory/archive-YYYY-MM/
摘要落盤、原文不刪同一組 rotation原始日誌整份保留在 archive 目錄,--mode archive-timeline 另外把 MEMORY.md 裡過期的月份區塊搬到 timeline-archive.md
摘要 ID ↔ 原文映射MEMORY.md 索引行每條長期記憶就一行,附著指回 notes/memory/ 的檔案路徑
query_memory 回查scripts/memory-search-hybrid.pyBM25 加 jieba 分詞,再套時間衰減與 hall type 加權;輸出每一筆都附 verify: Read <path> 讓 agent 自己回原文核對
壓縮時機由誰決定hook 與 crontemplates/.claude/hooks/memory-search-trigger.py 掛 UserPromptSubmit;歸檔走 launchd 排程
後訓練沒有全部靠 prompt 加 hook

前四列基本上是同一件事的兩種寫法。後兩列是分歧點,也是這篇文章真正想講的。

差異一:兩邊解的不是同一個問題

ACM 處理的是單一 trajectory 內部的爆窗。他們的實驗把 context limit 設在 128K,論文裡那張範例圖是一題 5 條件多跳問題跑了 83 turns,不壓縮的話原始 context 會累積到 222K,早在第 47 turn 就撞上限;壓縮之後實際峰值壓在 98K。

我這套處理的是跨 session 遺忘。今天這個對話結束、明天開新的,昨天講過什麼要能撈回來。單一 session 內的壓縮我碰不到,那是 harness 的 auto-compact 在管,權不在我手上。

同一組工具語意,用在不同的時間尺度上。這也代表兩邊的做法沒有互斥關係,只是我暫時沒有需要往 session 內那一層去做。

差異二:觸發權在誰手上

論文對現有做法的批評是:外部門檻觸發的壓縮時機,跟 agent 當下的推理焦點會錯位。門檻到了就壓,但那個瞬間 agent 可能正在追一條需要細節的線索。這個批評成立,而我的系統整套都是外部觸發——hook 看到關鍵字才注入記憶,cron 到點才歸檔。

有趣的是同一篇論文裡有一組數據,讓我對「改成自主觸發」這件事失去興趣。他們把 GPT-5.5 放進 ACM 框架、給它一模一樣的兩個工具,結果 manage_context 平均一題只叫 0.1 次,query_memory 0.6 次。強模型拿到工具,基本上不會主動用。

這剛好解釋了我系統裡一段看起來很笨的設定。我的 CLAUDE.md 有一張「必須先搜尋記憶的硬觸發清單」,還附一張「無效藉口表」,把「這題我應該知道」「先問使用者比較快」這類念頭一條條列出來標記成該搜尋的訊號。當初會寫成這樣,是因為只寫「需要時請搜尋記憶」的版本失敗過太多次——模型永遠覺得自己知道。

論文用實驗數據量到了同一個現象。我原本以為那是我 prompt 寫得不夠好,看來是行為層面的通病,要靠訓練才能改掉。既然我改不了權重,把觸發權從模型手上收回來交給 hook,就是目前能拿到的最好結果。

差異三:回查機制

ACM 的 query_memory 是一顆 querier LLM 去讀原文;我的是 BM25 加關鍵字加權,再從 graphify 建的知識圖譜做 1-hop 關聯擴散。

各有各的短處。BM25 是純詞面匹配,換個說法就打不中;querier LLM 讀得懂語意但每次回查都要付一次推理成本,而且會不會漏沒人量過。我這邊的補償辦法是輸出一律附上原檔路徑,讓 agent 可以自己 Read 回去核對,語意判斷交給主 agent 而不是檢索層。

我這邊有、論文沒有的一項

memory-archive.py 有一組 use-aware pinning 參數:

python3 scripts/memory-archive.py --mode rotate-journal \
  --respect-access-log ~/.claude/state/mem-access.jsonl \
  --access-window-days 30 \
  --access-threshold 3

hook 每次實際注入記憶時會把命中的檔案路徑寫進 access log,歸檔腳本讀這份 log,30 天內被撈出來 3 次以上的檔案就 pin 住不歸檔。等於讓實際使用頻率決定什麼東西留在熱區。

ACM 沒有這個概念。一份摘要被 query_memory 反覆命中,在它的框架裡不會改變任何事。這大概是因為它的生命週期只有一個 trajectory,沒有「跨 session 累積使用統計」的空間。

數字要看清楚再引用

論文摘要寫 BrowseComp-Plus 相對提升 27%,這是真的:0.570 到 0.727。但要注意兩件事。

第一,那是 in-domain。他們從 BrowseComp-Plus 拿 680 題訓練、150 題評測,訓練和評測同一個 benchmark。換到完全沒訓練過的 DeepSearchQA 是 0.367 到 0.425,SWE-Bench Verified 是 0.489 到 0.530,提升幅度小很多。

第二,我看到的轉述說峰值 token 佔用下降約 20%,對照 ReAct baseline 是 63k 降到 54k,14% 左右。20% 那個數字只有拿 ReSum 這類 summary agent 當基準才成立。

還有一個容易被忽略的發現:pass@4(能力上限)只從 73.5 動到 82.0,pass^4(同一題連續 4 次都對)從 34.1 拉到 59.3。主要收益在穩定度,讓已經會的題目穩定答對,能解的題目範圍沒有擴張太多。

結論

對照完我不打算改架構。元件層面該有的都有了,論文能給的增量在後訓練,那條路我走不了。

真正的收穫是 GPT-5.5 那組 0.1 次的數據。我的系統裡有好幾個地方是「不信任模型會自己想起來、用外部機制強制執行」的設計,那些決定當初都是被錯誤累積出來的直覺,沒有量化依據。現在至少其中一條有論文的實驗撐著。

至於哪一天 agent 真的能自己判斷該壓縮了——照論文的結論,那得等有人把這種行為訓進權重裡,prompt 層補不上。