寫於 2026 年 8 月 16 日(補 8 月 13 日的排程),9 月才上線(部落格的發佈額度 8 月 10 日就用完了,這批稿子要等到九月才發得出去)。文中的版本號與「目前」都指 8 月 16 日查到的狀態,Claude Code 更新很快,你讀到時可能已經又改過了。

compacting conversation… 跑完,畫面回來,Claude 接著就把剛剛講好不要動的那支檔案改掉了。

舊做法只有一招:把那條規則再貼一次。貼完它乖了,再撐兩小時,又壓一次,又貼一次。整個 session 就在「壓縮 → 失憶 → 補講」之間來回。

同一個動作,現在有第二種做法。官方文件把壓縮這件事拆成三塊可以伸手碰的東西:什麼時候壓、壓完誰會自己回來、以及壓縮前後你能插什麼進去。三塊都有對應的設定鍵或 hook 事件,設定完就不用再靠人肉補講。

什麼時候壓:以前只有開跟關

過去能碰的只有一個開關。autoCompactEnabled 預設是 true,官方對它的描述是「context 接近上限時自動壓縮對話」,在 /config 裡顯示成 Auto-compact;想從環境變數關掉就在 env 裡設 DISABLE_AUTO_COMPACT。開或關,就這樣。關掉之後 context 塞滿了不會壓,會直接撞牆。

現在多了一個旋鈕:autoCompactWindow。它管的是「壓縮前 context 可以裝多滿」,單位是 token,官方寫的合法範圍是 1000001000000

這個東西可以在三個地方設,而且三個地方的行為不一樣,值得記一下:

/autocompact 500k,Claude Code 會把值寫進你的 user settings 並且套用到當前 session。啟動時帶 --autocompact 旗標,只覆蓋這一次啟動、不動你存好的設定。腳本或雲端環境則設 CLAUDE_CODE_AUTO_COMPACT_WINDOW 環境變數。

優先序是環境變數最大。官方原話是它「takes precedence over the command, the flag, and the setting」,而且環境變數還在的時候,你跑 /autocompact 它只會回報「被覆蓋了」,不會真的改。另外有個容易踩的差別:如果 managed settings 這種高優先序的 scope 設了這個鍵,/autocompact 指令會存下你的值但 session 仍然吃那個 scope 的視窗;--autocompact 旗標則不受這個限制。

值的格式也分兩套。指令跟旗標吃四種寫法:純數字 200000kM 後綴的 500k1M、還有 100 到 1000 之間的裸數字代表「千」,所以 200 等於 200,000。環境變數只吃純整數,寫 500k 進去是不會動的。

那不設的話預設是多少?文件沒有給你一個數字。它寫的是「不設就壓在模型的 context 上限」,然後列了一串例外:Sonnet 4.6 跟 Opus 4.6 在沒開 extended context 時壓在 200K 邊界,Opus 4.8 與 Opus 5 跑在 200K 視窗(例如在 Amazon Bedrock、Google Cloud’s Agent Platform、Microsoft Foundry 上)時也一樣;Sonnet 5 在 Anthropic API 上一律是 1M 視窗,會在大約 967K 的地方自動壓。settings 文件對這個鍵的說法則是一句「When unset, Claude Code uses a window tuned for your model」。

所以想預測自己什麼時候會被壓,唯一可靠的辦法是自己把它釘死。

壓完誰會回來:以前靠觀察,現在有一張表

以前判斷「這條規則還在不在」的方法很土:看 Claude 的行為。它開始做傻事就代表沒了。

官方後來把整張存活表寫出來了,這是我覺得整篇文件最該印下來貼螢幕的一段:

機制 壓縮後
System prompt 與 output style 不變;本來就不在 message history 裡
專案根目錄 CLAUDE.md 與無 scope 的 rules 從磁碟重新注入
Auto memory 從磁碟重新注入
paths: frontmatter 的 rules 消失,直到再讀到符合的檔案
子目錄裡的 nested CLAUDE.md 消失,直到再讀到那個目錄的檔案
已被叫用過的 skill 本體 重新注入,但有上限
Hooks 不適用;hook 是跑起來的程式碼,不是 context

看懂這張表的關鍵在「載入方式」這四個字,不在「重要程度」。

拿冰箱來比喻大概是這樣。從磁碟重新注入的那幾項,等於貼在冰箱門外的便條紙,你把冰箱清空幾次它都還在門上。帶 paths: 的 rule 跟子目錄的 CLAUDE.md 則是放在冰箱裡面的東西,因為它們是「讀到符合的檔案時才載入 message history」,壓縮把 message history 摘要掉,它們就跟著沒了,要等下次再讀到那個目錄的檔案才會重新載入。

也就是說,一條規則活不活得過壓縮,跟你把它寫得多嚴厲完全無關。你可以在 rule 裡寫十次「絕對不要動 migration 檔」,只要它掛著 paths:,壓縮完就是不見。官方給的保命寫法很直接:「If a rule must persist across compaction, drop the paths: frontmatter or move it to the project-root CLAUDE.md.」

Skill 這一列比較細,值得單獨講。被叫用過的 skill 本體會重新注入,但吃兩道上限:每個 skill 最多 5,000 token、全部加起來最多 25,000 token,超過的時候先丟最舊的。而截斷的方式是保留檔案開頭,所以官方直接給了操作建議:把最重要的指令寫在 SKILL.md 最上面。你的 skill 要是把關鍵規則放在第 300 行,壓縮後那幾行就是不會回來。

還有一項不在表上、但影響更大:skill 清單本身。官方講得很白:

Unlike the rest of the startup content, this listing is not re-injected after /compact. Only skills you actually invoked get preserved.

開機時注入的那份「你有哪些 skill 可以用」的一行式清單,壓縮後不會補回去。只有你真的叫用過的那幾個會留著。實務上的意思是,長 session 壓過一次之後,Claude 會忘記自己還有哪些工具可以挑,而這件事不會有任何錯誤訊息告訴你。

壓縮本身:以前是天氣,現在是事件

前面兩節都還是「調參數」。真正把壓縮從被動變成可控的,是它有了 hook。

PreCompactPostCompact 是一對,分別在壓縮前後觸發,共用同一組 matcher,用來篩「是什麼觸發了壓縮」,合法值是 manualauto(分別對應你自己下 /compact 跟自動壓縮)。

兩者最大的差別在能不能擋。官方的 exit code 表裡,PreCompact 那一列寫的是 Blocks compaction,PostCompact 那一列寫的是 Shows stderr to user only。擋得住壓縮的 hook 沒幾個,PreCompact 是其中之一:exit 2 就是不准壓。PostCompact 你怎麼 exit 2 都沒用,事情已經發生了,它只會把 stderr 顯示給你看。

設定寫在 settings.json,可以放在 ~/.claude/settings.json(所有專案)、.claude/settings.json(單一專案、可進 repo)或 .claude/settings.local.json(不進 git)。結構長這樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"hooks": {
"PreCompact": [
{
"matcher": "auto",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/save-notes.sh"
}
]
}
]
}
}

matcher"auto" 代表只在自動壓縮時跑;你手動下 /compact 通常是有意識的,不見得需要同一套備份動作。

腳本這邊,hook 的輸入是從 stdin 進來的一包 JSON。官方列出所有 hook 共用的欄位有 session_idprompt_idtranscript_pathcwdpermission_modeefforthook_event_name,另外在 --agent 或 subagent 裡跑的時候會多 agent_idagent_type。一支能用的落檔腳本大概是:

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env bash
# .claude/hooks/save-notes.sh
# 自動壓縮前,把當前待辦複製一份出來,壓完才有東西可以對照
set -euo pipefail

input=$(cat)
cwd=$(printf '%s' "$input" | jq -r '.cwd')
session=$(printf '%s' "$input" | jq -r '.session_id')

mkdir -p "$cwd/.claude/state"
[ -f "$cwd/TODO.md" ] && cp "$cwd/TODO.md" "$cwd/.claude/state/todo-$session.md"
exit 0

改完怎麼確認生效?/config 裡看得到 Auto-compact 的狀態,hook 則是設定完重開 session、手動下一次 /compact(記得 matcher 要相應改成 manual 或先拿掉來測),然後去 .claude/state/ 底下看檔案有沒有長出來。檔案有出現就是真的跑了,這比看畫面上有沒有跳訊息可靠。

這裡有個坑我必須先講清楚,因為它會讓一整套「PostCompact 自動回補」的設計直接落空。hook 的 stdout 在多數事件底下只會進 debug log,官方明列的例外只有 UserPromptSubmitUserPromptExpansionSessionStart 這三個事件的純文字 stdout 會變成 Claude 看得到的 context。PostCompact 不在這三個裡面。同時官方文件也沒有給 PostCompact 專屬的 hookSpecificOutput 欄位。

所以「壓縮完用 PostCompact 把待辦 echo 回去」這條路,照目前的文件是走不通的。PostCompact 適合做的是環境層的動作:寫檔、發通知、動一動某個檔案讓 Claude 下次讀到時觸發對應的 rule。真正保證東西會回到 context 的機制,還是上一節那張表裡「從磁碟重新注入」的那兩列。

摘要本身也能指定

還有一層很少人動:摘要的內容。

/compact 可以帶指示,官方範例是 /compact focus on the auth bug fix。文件對這件事的說明是,摘要會保留你指定的東西,而不是自動流程猜的重點。開始一個新的大任務之前先這樣壓一次,比壓完再花五分鐘重講一遍划算。

另外,v2.1.198 起,產摘要的那次請求會繼承你 session 的 extended thinking 設定:session 開著 thinking,摘要就在有推理的情況下產生;關著就關著。官方特別補了一句,thinking 只影響摘要怎麼產生,你的 session 設定事後不會被改掉。

這件事真正換掉的心智

從「被壓縮了再補講」走到「先設好門檻跟鉤子」,表面上是多了幾個設定鍵,實際上換掉的是你對 context 的預設想像。

以前的想像是記憶:東西放進去,希望它留久一點,壓縮是來偷走記憶的小偷。這個想像會讓你把力氣花在錯的地方,例如反覆強調某條規則多重要、或是把 rule 寫得更長更嚴厲。壓縮不看這些。

比較準的想像是開機腳本。context 隨時會被清掉重來,重來之後跑得起來的,只有那些「寫在磁碟上、開機時會自己被讀進來」的東西。所以真正該問的問題不是「Claude 還記得多少」,而是「重開機時有多少會自己回來」。這個問法會直接告訴你答案:把攸關正確性的規則從帶 paths: 的 rule 搬到專案根目錄的 CLAUDE.md、把 SKILL.md 最重要的段落往上搬、把門檻用 /autocompact 釘死在你能預期的數字上。三件事都是改磁碟上的檔案,一次做完,之後每次壓縮都自動生效。

順帶一提,同樣是「東西在 session 中途沒生效」,中途改的 CLAUDE.md 這個 session 不算講的是另一個機制,那篇的結論剛好可以接上這篇:session 中途改 CLAUDE.md 不會生效,但壓縮之後它會從磁碟被重新讀一次。想讓改動當場生效,/compact 反而是個辦法。至於壓縮到底在丟掉什麼、什麼時候該手動壓,站上另有一篇專門在講

最後把話說清楚:這篇的內容全部來自官方文件,我把每個設定鍵、預設值、hook 名稱跟 exit code 語意都回官方頁面對過一次,但我沒有實際掛上 PreCompactPostCompact 跑一輪長 session 去驗證行為。那張存活表我也沒有做對照實驗去量各項的實際 token 數。想照著設之前,建議先在一個不重要的專案上把 hook 掛起來、手動壓一次,確認腳本真的有跑再往正式專案搬。