2025 年 9 月 29 日,Anthropic 同一天推了兩個 beta。一個叫 memory tool,一個叫 context editing。當時多數人只注意到前者,因為「讓 AI 有記憶」聽起來比「自動清掉舊的工具結果」性感得多。

十個月後回頭看,這兩個功能的命運差很多。memory tool 已經在 2026 年 2 月 17 日轉正式版、不需要 beta header 了。context editing 到今天還是 beta。中間還冒出第三個主角,把前兩個的定位整個重排了一次。

這篇把這條時間線走完,順便把參數和踩坑點講清楚。

在那之前,這件事是你自己的問題

先講清楚問題長什麼樣,不然後面所有設計都看不出道理。

假設你寫一個 agent,它會呼叫搜尋工具。第一次搜尋回來 3,000 個 token,第二次又 3,000,跑到第三十次的時候,你的 messages 陣列裡塞了九萬個 token 的搜尋結果,其中二十七次的內容早就沒人要看了。可是它們還在,每一輪都要重新送給模型、重新計費。

2025 年 9 月之前,處理這件事完全是你的責任。你要自己算 token、自己決定砍哪幾輪、自己把砍掉的地方補一句說明,還要祈禱砍掉的那段裡面沒有後面會用到的東西。

用桌面來想這件事。你查資料的時候把二十份文件攤在桌上,真正需要同時看的大概三份。差別是實體桌子滿了你看得見,而 context 滿了是一個你事先沒感覺、撞上才知道的邊界。

第一版只會做一件事:把最舊的工具結果丟掉

初版的 context editing 只有一種策略,叫 tool result clearing,type 字串是 clear_tool_uses_20250919。最小的用法只要指定型別,其他全部吃預設值:

1
2
3
4
5
6
7
8
response = client.beta.messages.create(
model="claude-opus-5",
max_tokens=4096,
messages=[{"role": "user", "content": "Search for recent developments in AI"}],
tools=[{"type": "web_search_20250305", "name": "web_search"}],
betas=["context-management-2025-06-27"],
context_management={"edits": [{"type": "clear_tool_uses_20250919"}]},
)

那個 beta header 的日期是 2025-06-27,比功能上線日還早三個月。照抄就好,不要自己「修正」成上線日期,改了會過不去。

五個參數的預設值值得記,因為預設值本身就是一組設計判斷:

trigger 預設 100,000 input tokens,也就是超過十萬才開始清。它可以改用次數計,寫成 {"type": "tool_uses", "value": N}

keep 預設 3。這裡的單位是 tool use 與 result 的配對次數,不是 token,很容易看錯。

clear_at_least 預設沒有值,單位是 token。exclude_tools 預設也沒有值,給它一個工具名稱陣列,裡面的工具永遠不清。

clear_tool_inputs 預設 false,意思是只清結果,模型原本發出的那個工具呼叫參數會留著。

clear_at_least 的語意是全篇最容易誤會的一個。它不是「至少清這麼多,不夠就盡量清」,而是「如果清不到這個量,整個策略就不套用」。這個設計是為了配合 prompt cache:清東西會讓快取的前綴失效,所以官方讓你設一個門檻,划不來就乾脆別動。

清掉的位置不會靜默消失,API 會放一段佔位文字,讓模型知道那裡原本有東西被移除了。清除順序是最舊的先清。

還有一個實作上最容易做錯的地方,官方特別寫了一節在講:你的 client 端不要跟著修改對話歷史。你維持完整的、沒被動過的歷史,清除是送進模型之前在 server 端發生的。你不需要、也不應該去同步那個被編輯過的版本。

完整參數長這樣:

1
2
3
4
5
6
7
8
9
10
11
context_management={
"edits": [
{
"type": "clear_tool_uses_20250919",
"trigger": {"type": "input_tokens", "value": 30000},
"keep": {"type": "tool_uses", "value": 3},
"clear_at_least": {"type": "input_tokens", "value": 5000},
"exclude_tools": ["web_search"],
}
]
}

一個月後,thinking 也可以清了

2025 年 10 月 28 日多了第二種策略,clear_thinking_20251015,管的是 thinking block。

它只有一個參數 keep,可以給 "all",也可以給 {"type": "thinking_turns", "value": N},N 必須大於 0。這裡有個容易看錯的單位:thinking_turns 數的是 assistant 的回合數,不是 thinking block 的數量。一個回合可能包含好幾個 thinking block,尤其在 interleaved thinking 的情況下,所以兩者不是一對一。

1
2
3
4
5
6
7
8
context_management={
"edits": [
{
"type": "clear_thinking_20251015",
"keep": {"type": "thinking_turns", "value": 2},
}
]
}

keep 的預設值依模型而異,這是這兩個功能裡唯一跟模型版本有關的細節。Opus 4.5 及之後、Sonnet 4.6 及之後預設保留全部先前的 thinking;Opus 4.1 及更早、Sonnet 4.5 及更早、Haiku 4.5 及更早則預設只保留最後一輪。官方直接給了建議:如果你的程式會跑在不同等級的模型上,把 keep 明寫出來,不要依賴各模型的預設值。

兩種策略可以一起用,但有一條硬規則:clear_thinking_20251015 必須排在 edits 陣列的第一項。這種「順序有意義」的 API 設計不常見,很值得先記下來,因為排錯了不會有語法錯誤幫你擋。

thinking 對快取的影響是雙向的。保留 thinking block 的時候快取是保住的,可以命中、可以省 input token;清除的時候,快取會從清除發生的那個點失效。所以 keep 要設多少,其實是在選「快取效能」還是「context 空間」。

中間那條後來被放棄的路

2025 年 11 月 24 日,Python 和 TypeScript SDK 加了 client-side compaction,在用 tool_runner 的時候自動摘要對話。

這條路現在不建議走了。compaction_control 這個參數已經在 Python、TypeScript、Ruby SDK 標記棄用,啟用時會噴 deprecation warning,未來版本會移除。

它被放棄的理由裡有一個很值得看,因為它是一個真實的計算錯誤。SDK 算對話長度的方式是把 input_tokenscache_creation_input_tokenscache_read_input_tokensoutput_tokens 全加起來。問題是當你用 server-side 的工具時,cache_read_input_tokens 含的是那個工具內部好幾次 API 呼叫的累積值。官方舉的例子是 63,000 加 0 加 270,000 加 1,400 等於 334,400,而真實的 context 可能只有 63,000。結果就是 compaction 被過早觸發,明明還很空就開始摘要。

2026 年 2 月,主角換人

2026 年 2 月 5 日,compaction API 進 beta,edit type 是 compact_20260112,beta header 是 compact-2026-01-12。這次是 server-side 的。

十二天後,2026 年 2 月 17 日,memory tool 轉 GA,不再需要 beta header。

到這裡定位就重排完了。官方在 context editing 文件最上方直接表態:對大部分使用情境來說,server-side compaction 才是長對話的主要策略,而 context editing 這一頁的策略適用於「你需要更細緻地控制清掉哪些內容」的場景。

兩者的差別用一句話講完:一個是清除,一個是摘要。

清桌子跟寫會議記錄不是同一件事。清桌子快、便宜、不會扭曲任何東西,但被清掉的就是沒了。寫會議記錄會保住脈絡,代價是要有人(同一個模型)花時間讀完再寫,而且摘要總是會失真。

實作上還有一個差別很關鍵:context editing 你的 client 不用同步狀態,維持原始歷史就好;compaction 的回應會帶一個 compaction block,你必須原樣傳回。這是兩種完全不同的整合複雜度。

順帶提一個容易誤會的地方。context editing 目前支援的 edit 型別就只有 clear_tool_uses_20250919clear_thinking_20251015 兩種,沒有第三種,也沒有被淘汰的舊型別。compact_20260112 雖然也放在 context_management.edits 裡,但它屬於另一個 feature、另一個 beta header,不算 context editing 的策略。這兩句話很像,混用會寫出錯的東西。

至於支援的模型,官方對 context editing 只有一句「所有支援的 Claude 模型」,沒有列表。memory tool 是「Claude 4 及之後的所有模型」。compaction 那邊倒是有明確清單,但那份清單不能套到 context editing 頭上。

memory tool:模型只會「請你」寫檔案

memory tool 最需要先講清楚的一件事:它是 client-side 的。

官方的說法是「Claude requests file operations, and your application executes them」。模型只是發出請求,實際的檔案操作是你的程式去做,儲存在哪裡、怎麼存、由誰控制,全部是你的。那個 /memories 路徑只是一個前綴,你的 handler 把它對應到真實的儲存體,可以是每個使用者一個目錄,也可以是資料庫裡的一批 key。

宣告方式極簡,整個設定就是這一行,name 必須是 memory,不需要自己定 input schema:

1
tools=[{"type": "memory_20250818", "name": "memory"}]

command 一共六個,這就是全集:viewcreatestr_replaceinsertdeleterename。幾個細節值得知道:str_replacenew_str 是選用的,省略就等於刪掉 old_strinsert 是插在 insert_line 之後,給 0 表示插在檔首;deleterename 都不能對 /memories 本身操作,你的 handler 要自己拒絕。

有意思的是回傳字串不是硬規格。官方寫「Claude reads whatever text your tool result contains」,所以那些 File created successfully at: {path} 之類的訊息只是建議行為,你想回別的也可以。create 也一樣:官方參考實作是「檔案已存在就回錯誤」,但工具描述告訴模型 create 是「creates or overwrites」,所以你會收到對已存在路徑的 create 呼叫,改成覆寫是合理的實作選擇。

不想自己刻的話,四個 SDK 有 helper。Python 和 C# 是繼承 BetaAbstractMemoryTool,TypeScript 用 betaMemoryTool,Java 實作 BetaMemoryToolHandler。Python 和 TypeScript 另外附了現成的本機檔案系統版 BetaLocalFilesystemMemoryTool。Go 和 Ruby 沒有 helper,得自己跑 tool-use loop。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import anthropic
from anthropic.tools import BetaLocalFilesystemMemoryTool

client = anthropic.Anthropic()
memory = BetaLocalFilesystemMemoryTool(base_path="./memory")

runner = client.beta.messages.tool_runner(
model="claude-opus-5",
max_tokens=1024,
messages=[
{
"role": "user",
"content": "Remember that customer Acme Corp prefers email follow-ups.",
}
],
tools=[memory],
)

final_message = runner.until_done()
print(final_message.content)

注意 helper 和 tool_runner 都掛在各 SDK 的 beta namespace 底下,即使 memory tool 本身已經 GA。

還有一件事我覺得最少人知道:只要你的 tools 裡出現 memory tool,API 會自動幫你在 system prompt 加一段 MEMORY PROTOCOL,你不用自己送。內容大意是「做任何事之前先 view 你的記憶目錄」,最後那句寫得很直白:

1
2
ASSUME INTERRUPTION: Your context window might be reset at any moment, so you risk
losing any progress that is not recorded in your memory directory.

假設自己隨時會被中斷。這句話某種程度上就是整個功能的設計哲學。

兩個一起用,才是它們被設計出來的樣子

單獨看,context editing 是省 token 的工具,memory tool 是跨對話記憶的工具。合起來才會出現那個真正的機制:

1
2
3
4
5
6
7
8
response = client.beta.messages.create(
model="claude-opus-5",
max_tokens=4096,
messages=[{"role": "user", "content": "Hello"}],
tools=[{"type": "memory_20250818", "name": "memory"}],
betas=["context-management-2025-06-27"],
context_management={"edits": [{"type": "clear_tool_uses_20250919"}]},
)

官方對這個組合的說明是關鍵:當 context 接近設定的清除門檻時,Claude 會收到一個自動警告,提醒它保存重要資訊,於是它可以在那些工具結果被清掉之前先寫進 memory 檔案。

回到桌子的比喻。清桌子的人要來之前先喊一聲「十分鐘後收桌」,你就有機會把還需要的東西抄到筆記本上。沒有那一聲,清桌子就只是單純的損失;有了那一聲,清除變成一個可以規劃的動作。

這也是為什麼「先學哪個」的答案是兩個一起學。memory tool 和 compaction 官方同樣建議併用:compaction 讓活躍的 context 保持精簡,memory 保住那些必須活過摘要的東西。

官方那三個百分比,容易引錯

網路上到處在傳「39% 和 84%」,但這兩個數字被引用的方式常常是錯的。它們出自 2025 年 9 月 29 日的官方公告,不在 API 文件頁上,而且全部是 Anthropic 內部評測,不是公開 benchmark。

39% 是 memory tool 加上 context editing 併用,相對 baseline 的表現提升。只用 context editing 的話是 29%。所以把 39% 說成「context editing 的效果」是錯的。

84% 完全是另一回事。它出自一個 100 回合的網路搜尋評測,指的是token 消耗的減少幅度,不是正確率提升。把它跟 39% 並列寫成「效果提升 39%、84%」,等於把兩個不同軸的指標黏在一起。

官方沒有公布這些評測的樣本數、baseline 設定與評測集細節。要引用就把「內部評測」四個字寫在句子裡。

會踩到的地方

prompt cache 是第一個。tool result clearing 只要清了東西,快取前綴就會失效,每次清除都會產生 cache write 成本,之後的請求才能重用新的前綴。這就是 clear_at_least 存在的理由:確認這次清除清得夠多,值得打掉快取。

clear_at_least 的門檻語意再說一次,因為真的很容易寫錯:達不到就整個策略不套用,不是清少一點。

memory tool 這邊最需要小心的是路徑穿越,官方用 Warning 等級標示。像 /memories/../../secrets.env 這種路徑可以摸到目錄外面的檔案,而執行檔案操作的是你的程式,所以驗證每一個 command 的每一個路徑是你的責任。官方建議的做法是所有路徑必須以 /memories 開頭、resolve 成 canonical form 之後確認還在目錄內、拒絕 ../..\、注意 URL 編碼過的 %2e%2e%2f,Python 就用 pathlibresolve()relative_to()

這裡有個很誠實的細節:官方自己的 Go、PHP、Ruby 範例是 in-memory 示範版,跳過了路徑驗證,文件也明寫正式環境的 handler 需要補上這些範例省略掉的驗證。照抄官方範例上線,這一刀就是自己挖的。

另外兩件小事。敏感資訊方面,Claude 通常會拒絕把敏感內容寫進 memory 檔案,但官方建議你要更強的保證就自己加一層過濾再寫檔。檔案大小要自己追蹤設上限,並限制 view 回傳的字元數,讓模型用 view_range 分頁讀。

怎麼確認它真的清了

這是我認為最該先寫進監控的部分。回應裡會多一個 context_management 欄位:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
"context_management": {
"applied_edits": [
{
"type": "clear_thinking_20251015",
"cleared_thinking_turns": 3,
"cleared_input_tokens": 15000
},
{
"type": "clear_tool_uses_20250919",
"cleared_tool_uses": 8,
"cleared_input_tokens": 50000
}
]
}

串流的時候它出現在最後的 message_delta 事件裡,同樣掛在 context_management.applied_edits

上線之前想先估效果的話,token counting 端點吃同一組 context_management 參數,回應會給 context_management.original_input_tokens,跟 input_tokens 相減就是省下來的量。這比直接上生產環境再看帳單便宜得多。

還沒走完的部分

把時間線攤開會看到一個有意思的現象:後上線的 compaction 已經被官方指定為主要策略,而早它四個月上線的 context editing 到今天還掛著 beta。

官方文件目前也還有沒說清楚的地方。context editing 沒有逐一的模型支援清單,只有一句「所有支援的 Claude 模型」。context editing 的策略和 compact_20260112 能不能放進同一個 edits 陣列併用,兩邊的文件都沒有範例,也沒有明文禁止。trigger 改用 tool_uses 型別時的預設次數是多少,文件只給了 input token 的那一個預設值。

這些空白通常會被下一版文件填掉,或者被某個人踩過之後寫在 issue 裡。如果你正在做長時間執行的 agent,那個「清除前的自動警告」值得先接起來試,它是這三個功能裡唯一一個把「遺忘」變成可規劃動作的機制。


誠實邊界:本文的參數、預設值、command 清單、回應欄位與那三個百分比,全部來自官方文件與 2025-09-29 官方公告的原文,查證日期 2026-08-04。我沒有實跑過每一種參數組合,尤其 clear_at_least 達不到門檻時的實際行為、以及跨模型 keep 預設值的差異,都是照文件敘述整理,沒有自己的實測輸出。文中標為「官方文件未說明」的部分就是查不到,不要當成我漏寫。

延伸閱讀:CLI 端的對應機制可以看 Claude Code 的 /compact:對話變慢變鈍前,先搞懂壓縮到底在丟什麼,快取那條線可以看 Prompt Caching:讓 Claude 別再重讀同一份文件

參考來源