iT邦幫忙

2026 iThome 鐵人賽

DAY 21
0
AI Engineering

Harness Engineering × Pi Agent 實戰:打造可觀測、可評估的 AI Coding Agent系列 第 21 篇

Day21:Context 裝不下時,Agent 該忘掉什麼?拆解 Pi 的 Compaction

  • 分享至 

  • xImage
  •  

迴圈越跑越重

Day2 講過 agent loop 最反直覺的一件事:模型不會「記得」,每一輪都要把目前為止的所有事整包重送。Day14 用帳單證實了它的代價——六成以上的錢花在輸入。

那如果任務很長,長到整包塞不進模型的 context 視窗呢?

這時 harness 只有兩條路:放棄,或是把舊的部分壓縮成摘要。後者就是 compaction。

今天拆 Pi 怎麼做這件事。這是 harness 裡少數「一旦做錯,agent 會突然失憶或整個壞掉」的元件,所以它的規則特別值得看。

什麼時候觸發

Pi 的判斷條件簡單得出乎意料:

contextTokens > contextWindow - reserveTokens

reserveTokens 預設 16,384,用意是替模型的回答留位子。拿這個系列用的 gpt-5.6-luna 來算(視窗 272K):

272,000 - 16,384 = 255,616

超過這個數字就自動 compaction。也可以手動 /compact,還能附上指示,讓摘要聚焦在你在意的東西上。

先說個實測結果:我跑了 130 次執行,compaction 一次都沒有觸發過。 單次請求最大的 context 是 14,050 tokens,只用掉視窗的 5.2%。

這個「沒發生」本身就是資訊:在 272K 的視窗下,一般的修 bug/加 endpoint 任務根本碰不到天花板。 compaction 是為了真正的長任務存在的——跨幾十個檔案的重構、一路 debug 幾十輪、或是把 agent 當成長時間的結對夥伴。

它怎麼決定切在哪

觸發之後,Pi 做五件事:

  1. 找切點:從最新的訊息往回走,累加 token,直到累積量達到 keepRecentTokens(預設 20K)。這裡就是切點。
  2. 取出要摘要的部分:從上次保留的邊界(或 session 開頭)到切點之間的訊息。
  3. 產生摘要:另外呼叫一次模型,用固定格式摘要。如果之前已經摘要過,會把舊摘要一起餵進去,讓它迭代。
  4. 寫入一筆 CompactionEntry,裡面記著摘要和 firstKeptEntryId。
  5. 重建 context:下一次請求 = system prompt + 摘要 + 從 firstKeptEntryId 之後的訊息。

換句話說,舊訊息不會被刪掉——它們還在 session 檔裡,只是不再送給模型。這點很重要:Day6 說 session 記錄是完整的逐字稿,compaction 不會破壞這個保證,它只影響「送出去的那份」。

不能亂切:切點規則

這是我覺得最值得學的一段。Pi 明確規定哪些位置可以當切點:

  • 可以切在:使用者訊息、assistant 訊息、bash 執行訊息、自訂訊息
  • 絕對不能切在工具結果(tool result)

為什麼?因為工具結果和它對應的工具呼叫是一對。模型的 API 要求每個 tool_use 都要有配對的 tool_result。如果切點落在中間,送出去的對話就會出現一個沒有來源的工具結果——這不是「品質變差」,而是請求直接被 API 拒絕。

這就是 harness 設計裡典型的「協定約束」:不是你想怎麼切就怎麼切,資料結構本身有不可分割的單位。任何自己做 context 管理的人都會撞到同一堵牆。

一輪就爆掉怎麼辦:split turn

如果單獨一輪就超過 keepRecentTokens 呢?例如 agent 在一輪裡讀了 20 個大檔案。

這時切點會落在這一輪的中間(切在某個 assistant 訊息上),Pi 稱之為 split turn。它會產生兩份摘要再合併:

  1. 歷史摘要(之前的 context,如果有的話)
  2. 這一輪前半段的摘要

這個設計解決的是一個很實際的窘境:不能為了「保持一輪完整」就把整輪留著(那就壓不下來),也不能硬切在工具結果上(那會壞掉)。於是切在 assistant 訊息、把前半段另外摘要。

摘要長什麼樣

Pi 的摘要不是「請幫我總結上面對話」這種自由發揮,而是固定結構:

## Goal                      這次任務要達成什麼
## Constraints & Preferences 使用者提過的限制與偏好
## Progress
### Done                     已完成
### In Progress              進行中
### Blocked                  卡住的

另外還會累積追蹤檔案操作:哪些檔案讀過、改過,會跨越多次 compaction 一路累積下來。

為什麼要固定格式?因為摘要的讀者是下一輪的模型,不是人。固定欄位讓模型知道去哪裡找「我現在做到哪」,也讓多次摘要之間可以迭代而不會越摘越糊。

這裡有個一般性的原則:給 agent 看的文件,結構比文筆重要。 這跟 Day10 講 skill 描述、Day8 講 AGENTS.md 的道理是同一個。

會反覆發生的那個坑

連續 compaction 有一個容易寫錯的地方,Pi 特別處理了:第二次摘要時,要摘的範圍是從上一次保留的邊界開始,而不是從上一筆 compaction 紀錄開始。

差別在哪?上次保留下來的那些訊息,這次也該一起被摘要進去。如果從 compaction 紀錄之後才開始算,那些訊息會被漏掉——摘要裡不見,又不在送出的訊息裡,等於憑空消失。

agent 突然忘記自己十分鐘前做過什麼,通常就是這類 off-by-one 造成的。

還有一個成本上的副作用

Day14 量到:命中快取的 input 便宜十倍,而且是長迴圈能不能負擔得起的關鍵。

compaction 對快取是毀滅性的:它把送出去的前綴整個換掉(摘要取代了一大段訊息),所以之前累積的 prompt cache 全部失效,下一次請求要重新付全價。而且摘要本身還要額外呼叫一次模型。

所以 compaction 的真實成本有三塊:

  1. 產生摘要的那次模型呼叫
  2. 快取失效,下一次請求從頭付費
  3. 摘要必然有損,agent 可能因此少了某個細節而走錯路

這也是為什麼它被設計成「撞到門檻才做」,而不是「定期整理一下」。

明天

Day22 要把它逼出來:把 reserveTokens 調到讓一個中等長度的任務就會觸發 compaction,然後量三件事——成功率有沒有掉、成本多了多少、以及 agent 會不會在摘要之後忘記自己做過什麼。


上一篇
Day20:接上 Trace 才看見真相:Agent 時間有近一半花在 Harness
下一篇
Day22:Compaction 為何等到 Agent 結束才發生?一次門檻實驗的意外發現
系列文
Harness Engineering × Pi Agent 實戰:打造可觀測、可評估的 AI Coding Agent 共 25 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言