/compact 顯示「已壓縮」卻沒真壓縮:Cline Desktop v0.0.5 兩個壓縮假象的踩雷與修法
如果你在 Cline 裡打過 /compact,看到聊天視窗跳出一條「Context compacted」的分隔線,然後安心地繼續跟 agent 對話——這篇要告訴你一件不太舒服的事:在某些設定下,那條分隔線是在對你說謊。 它顯示壓縮成功,但真正送進模型的請求,一個 token 都沒少。
Cline 團隊在 2026-07-27 發布的 Desktop v0.0.5,在一長串效能與 UI 修正的最後,藏了這麼一行:
Fixed agentic compaction silently falling back to basic compaction for OpenAI-Compatible providers, and manual /compact never actually reaching the model when auto-compaction was off.
這一句話其實包了兩個獨立的 bug。修這兩個問題的是 Cline 作者 Saoud Rizwan 的 PR #12563,2026-07-26 合併。有趣的是——這不是使用者回報挖出來的,是他在手動測試前一版剛做好的壓縮 UI(#12487)時,自己在 wire 上抓到 UI 在騙人。
這篇會把這兩個 bug 的機制拆到原始碼層級,然後給你一套不靠 UI、直接在網路層驗證「壓縮到底有沒有發生」的 SOP。就算你不用 Cline,這套「別信分頁、信 wire」的檢查習慣,對任何 coding agent 都適用。
本文大綱
先搞懂:Cline 的壓縮到底在做什麼
要看懂 bug,得先知道 Cline 的 context 壓縮分兩種策略,這是理解整件事的地基。
Agentic compaction(智慧壓縮):這是 Cline 從 #12317(Bee 提交,2026-07-22)起的預設策略。它的做法不是粗暴砍掉舊訊息,而是額外發一次 LLM 請求,叫模型把到目前為止的對話產生一份結構化摘要——保留技術細節、程式碼變更、關鍵決策——然後用這份摘要替換掉冗長的歷史訊息。依 Cline 官方文件(docs.cline.bot/features/auto-compact),這一發摘要請求的成本其實不高,因為大部分 input token 已經被 cache,你主要付的是產生摘要那段輸出。
Basic compaction(基礎截斷):這是規則式的 fallback。當模型不支援 agentic 摘要、或摘要那一發請求失敗時,Cline 就退回到「按規則截斷舊訊息」。它便宜、不會失敗,但會直接丟資訊——這正是長 session 跑到後面會「忘記前面做過什麼、重做已完成的工作」的典型病灶。
再來是兩個關鍵名詞:
/compact與/smol:你可以在輸入框手動打這兩個 slash 指令,強制立刻壓縮一次。(順帶一提,v0.0.5 當下/compact甚至沒被列進 slash 選單,打了會顯示「No matching commands found」,但功能是通的——這是 PR 作者自己列出、但這次沒一起修的 follow-up。)- Auto Compact:自動壓縮開關。context 接近視窗上限時自動觸發壓縮。在擴充功能/桌面版裡,這個開關預設是關的(內部旗標
useAutoCondense預設false)。記住這個「預設關」——第二個 bug 整個踩在它上面。
運作原理:兩條路,兩個謊言

兩個 bug 的共同症狀是一樣的:分頁顯示「Context compacted」,模型端卻沒有任何改變。 但它們的成因完全不同,一個在 VS Code / 桌面這層的 session factory,一個在共用的 core runtime。分開看。
Bug #1:Agentic 悄悄退回 Basic(OpenAI-Compatible + 自訂 endpoint 中招)
這個 bug 的觸發條件很具體:你用的是 OpenAI-Compatible provider,而且指向一個自訂 endpoint——LiteLLM proxy、企業內部 gateway、本地跑的推論 server,都算。
機制是這樣的。壓縮用的 summarizer 在建立自己的 LLM handler 時,只吃 CoreSessionConfig.providerConfig 這一份設定。而 cline-session-factory.ts 裡負責解析 base URL 的 resolveBaseUrl(),只認得舊版的 provider id 拼法(例如 openai)。問題是,現行設定 UI 存 provider 時,用的是 SDK 的新拼法 openai-compatible。兩邊對不上,於是 providerConfig 被建出來時——base URL 是空的。
接下來就很尷尬了:
- 你的正常對話其實沒事,因為 core 會另外從
providers.json重新解析設定,拿得到你的自訂 endpoint。 - 但壓縮那一發不走這條路。summarizer 的
createHandlerAsync拿著一個沒有 base URL 的設定,就去打 OpenAI 的預設端點api.openai.com。 - 你的 key 是給自家 gateway 用的,打到
api.openai.com當然回 401:
Agentic compaction failed; falling back to basic compaction
{"errorMessage":"Incorrect API key provided: sk-dummy...roxy"}
- agentic 摘要失敗,Cline 靜默退回 basic 截斷。而 UI 呢?照樣顯示一條漂漂亮亮的「Context compacted (manual)」,完全沒有任何 hint 告訴你「其實智慧摘要根本沒跑」。
結論:任何用自訂 endpoint 的 OpenAI-Compatible 設定,在 v0.0.5 之前,從來沒真正用過 agentic 壓縮。 你以為模型幫你做了聰明的摘要保留重點,實際上它只是被規則式地砍掉了一段歷史。
修法:在 resolveBaseUrl 裡把 SDK 新拼法 openai-compatible 也對應上,並且在舊 state 沒有 base URL 時,回頭去讀 providers.json 的 base URL(跟 resolveApiKey 的行為對齊)。附帶一個容易被忽略的修正:把 knownModels 提升到 CoreSessionConfig 頂層——這樣手動壓縮才會照模型真實的 context window 來抓預算,而不是掉回一個 64k 的保底值(sdk-compaction.ts 讀的是 config.knownModels[modelId],先前這欄根本沒被設)。
Bug #2:Auto Compact 關著時,/compact 是純空操作
這個更隱蔽,而且跟 provider 無關,人人有份——只要你沒開 Auto Compact(也就是預設狀態)。
流程拆開看:
- 你打
/compact。compactSessionMessages會強制為這一次手動執行打開壓縮、跑一次摘要、把結果寫進所謂的 sidecar(壓縮後的工作狀態)。它的承諾是:「接下來的 turn 和之後的 resume 都會沿用這份壓縮過的 working context」。 - 但是——
local-runtime-host.ts只有在compaction.enabled === true時,才會把createCompactionStateAwarePrepareTurn接上去。這個 prepareTurn 才是真正「在組下一次請求時,去讀 sidecar、用壓縮後的內容取代完整 transcript」的那隻手。 - 而擴充功能預設 Auto Compact 是關的(
useAutoCondense = false),於是compaction.enabled是false,那隻手根本沒接上。 - 結果:sidecar 存了,卻沒有人去讀它。 每一次後續請求,照樣把完整的原始 transcript 送給模型。作者是掛一個 logging proxy、在 wire 上親眼確認的——請求內容沒變。
這就是為什麼那麼多人回報「/compact 沒用」。GitHub 上像是 sean-harpin 開的 #7298(/compact 後 context 沒縮、只是回一句更短的完成訊息)、以及 #10637(context bar 數字自己往回跳但沒有正常壓縮事件)這類回報,症狀都指向同一個大方向:壓縮的 UI 訊號和實際送到模型的內容脫鉤了。 v0.0.5 修的是其中兩個可以在原始碼裡指名道姓的根因。
修法:把 state-aware prepareTurn 無條件接上。它本來就支援 compact: undefined 的語意(意思是「投影已存在的壓縮狀態,但不要重新壓縮」),而且對沒有 sidecar 的 session 會自動 no-op,所以無條件接上是安全的。同時,在壓縮功能關閉時也保留 resume/初始的 sidecar,而不是把它丟掉。作者順手改掉了舊測試——那條舊測試釘死的是相反行為(壓縮關閉時不投影狀態),卻又同時斷言「關閉時仍持久化壓縮狀態」,等於「存了但永遠不讀」,語意本身就自相矛盾。
數據與限制:哪些數字是真的,哪些別當證據
先把可驗證的事實擺清楚。以下數字全部來自 PR #12563 作者的測試紀錄,測試環境是真實的擴充功能 dev host,透過本地 logging proxy(http://127.0.0.1:4141/v1)打到 OpenRouter,proxy 會用 system prompt 特徵標記出壓縮 summarizer 的請求——所以「agentic 有跑」是在 wire 上被證明的,不是靠 UI:
- 修好後:proxy log 出現
tag=COMPACTION_SUMMARIZER ... status=200 ... stream completed;擴充功能 log 顯示Performed agentic compaction,沒有 fallback。修好前,同一個流程 log 是Agentic compaction failed; falling back to basic compaction,而 proxy 上什麼都沒看到(因為 401 打在別的端點)。 - sidecar 投影生效後:自動壓縮後,下一發請求在 wire 上的訊息數從 14 則掉到 5 則(摘要 + 尾端保留的即時訊息)。這是「真的壓了」最直接的證據形態。
- 測試也涵蓋了壓縮中取消、壓縮中打字排入佇列、壓縮中重載視窗恢復等邊界,單元測試方面
apps/vscode有 984 項通過、@cline/core有 1435 項通過。
現在講限制,這部分要誠實。 PR 作者自己列了幾個「測試中發現、但這次刻意沒修」的 follow-up,這些是你升級後仍會遇到的坑:
- 分隔線的 token 數字可能不減反增:當一段很大的全新 tool 結果落在被保留的即時尾端時,完成後的分隔線可能顯示成
Context compacted · 11.4k → 21.5k tokens,context bar 甚至會往上撐過視窗。原因是「壓縮前」是對這一 turn 的apiMessages估算、「壓縮後」是對正式結果訊息計算,兩者口徑不同。純屬顯示問題,但極度誤導——所以請不要把分隔線上的數字當成壓縮成效的證據。 - 從歷史重開的任務打
/compact//smol會回你「There is no active task to compact.」,得先送一則正常訊息把它變成 active session 才行。 /compact不在 slash 選單裡(只有/smol在),打了會顯示查無指令,但功能其實是通的。- 策略 fallback 依然是靜默的:當 agentic 摘要失敗退回 basic 時,分隔線跟成功跑 agentic 長得一模一樣。也就是說——就算修了 Bug #1,未來若有別的原因讓 agentic 失敗,你在 UI 上還是看不出來。這正是 Bug #1 當初能藏這麼久的結構性原因。
這一點很重要:v0.0.5 修的是兩個特定根因,不是「讓壓縮失敗變得可見」這個更根本的問題。 UI 仍然只報「壓縮了」,不報「用哪種策略壓的」。
適用場景與 trade-off:你該不該在意這件事
你一定要升級 v0.0.5,如果:
- 你用 OpenAI-Compatible provider 接自訂 endpoint(LiteLLM、企業 gateway、本地 vLLM/Ollama 之類的 OpenAI 相容層)。你先前的每一次壓縮都只是規則式截斷,長 session 品質流失比你以為的嚴重。
- 你依賴手動
/compact來控制 context,而且沒有特別去開 Auto Compact(也就是絕大多數人的預設)。你以前打的每一發/compact對模型端都是空操作。
影響相對小,如果:你用官方 Cline / Anthropic / 標準 OpenAI 直連(provider id 對得上、base URL 不是自訂的),而且平常就開著 Auto Compact。這種組合下 Bug #1 不太會踩到,Bug #2 因為 compaction.enabled 是 true 也不受影響。但升級沒壞處。
trade-off 提醒:就算修好了,agentic 壓縮本質上就是多一次 LLM 請求。它保留語意、代價是多一發 API call(多數 input token 已 cache,成本主要在摘要輸出)。在超長、超密集的 session 裡,這仍是你要納入預算的東西。如果你要的是「便宜、可預測、絕不失敗」而不在乎丟細節,basic 截斷反而合適——但那你要自己知道你在用它,而不是被靜默 fallback 決定。
對工程團隊的意義:別信分頁,信 wire
這個 bug 真正的教訓不是「Cline 有 bug」,而是——coding agent 的 UI 狀態指示,和它實際送給模型的內容,是兩回事,而且會脫鉤。 一條「已壓縮」的分隔線、一根往下掉的 context bar,都只是前端的估算與宣稱,不是模型端的事實。當你的 agent 行為詭異(長 session 變笨、重做已完成的工作、莫名忘記前文),第一件事不是相信 UI,是去看線上真正送出去的請求。
下面這套 SOP 適用於任何 coding agent,不限 Cline:

具體三步:
- 架一個 logging proxy 夾在中間。 把 agent 的 base URL 指到本地 proxy(例如
http://127.0.0.1:4141/v1),proxy 再轉發到真正的 API。LiteLLM proxy、mitmproxy,或任何能記錄 request body 的東西都行。這一步讓你握有「模型實際收到什麼」的唯一真相來源。 - 跑一次壓縮,看那一發摘要請求在不在。 打
/compact或/smol,然後盯 proxy log:有沒有出現一發額外的、帶摘要 system prompt 特徵的請求,而且是200?沒有這一發,就代表 agentic 壓縮根本沒跑——不管 UI 說什麼。 - 送下一則訊息,比對請求體積。 壓縮後再送一則普通訊息,比對這一發 request 的訊息數與 token 數有沒有真的掉下來(像作者測到的 14 則 → 5 則)。掉了,才是真壓縮;沒掉,UI 就是在騙你。
把「別把 UI 當成 ground truth」寫進團隊的 agent 除錯 checklist。特別是當你們在正式環境跑 agent、又用了自訂 gateway 時——一個靜默 fallback 可能讓你們的每一次壓縮都名存實亡好幾週,而 dashboard 上一切正常。
最後一個務實建議:升級到 v0.0.5 後,把 Auto Compact 打開。 Bug #2 的根因就是「手動壓縮的狀態沒被後續請求讀取」,開著 Auto Compact 讓 compaction.enabled 為 true,是最省事的自保。同時也記住——就算開了,分隔線上的 token 數字仍可能誤導(follow-up #1 尚未修),要確認成效,還是回到 wire。
來源
- 主要修正 PR:Saoud Rizwan, cline/cline #12563 — fix(vscode/core): agentic compaction silently fell back to basic, and manual /compact never reached the model(2026-07-26 合併)
- Release notes:Cline Desktop v0.0.5(2026-07-27)
- 相關 PR:Bee, #12317 — feat(core): default to agentic compaction;#12487 — feat(vscode): show compaction progress and results in the webview
- 官方文件:Cline Docs — Auto Compact
- 相關使用者回報:#7298(sean-harpin)、#10637
整理:DataAgent · Coding Agent 實戰教學


