Lesson 06 · 記憶

上下文滿了,學會砍

「The agent can forget strategically and keep working forever.」策略性遺忘 = 工程能力。

⏱ 約 12 分鐘 · 📝 3 個可互動元件 · 🧑‍💻 基於 shareAI-lab · s06_context_compact.py

為什麼要 compact?

agent 跑久了,messages[] 會膨脹:每個 read_file 回幾千 token、每個 bash 回幾百、每輪對話還有模型的思考文字。跑 50 輪,context 能塞到 100K+。兩個後果:

  • 碰到模型上限:到了視窗大小就崩,或是每一次 API 呼叫價格線性上漲。
  • 注意力稀釋:現在手頭上的任務淹沒在 30 輪前的無關 tool_result 裡,模型開始分心。

s06 的想法:讓 agent 主動遺忘不重要的內容,但保留關鍵狀態。三層機制,從輕到重。

Layer 1 · micro_compact(每輪靜默運轉)

最便宜的一層。每次 LLM 呼叫前跑一遍,把超過 3 個的舊 tool_result 替換成佔位符:

# 第 10 轮往前,大部分 tool_result 变成:
{
  "type": "tool_result",
  "tool_use_id": "toolu_01A",
  "content": "[Previous: used bash]"   # 从几千字符缩到几十
}

有個特例:read_file 的結果不壓縮。為什麼?因為 read 的輸出是參考資料,壓了模型就得重讀一遍,反而更貴。

PRESERVE_RESULT_TOOLS = {"read_file"}  # 永不压缩

看 micro_compact 按 turn 吃掉舊結果

下面按步驟模擬 10 輪交互,每一輪前讓 micro_compact 跑一次。看 messages[] 裡舊的 tool_result 變成 [Previous: ...],但最近 3 個保持原樣。

Layer 2 · auto_compact(超過閾值觸發)

即使 micro 一直跑,累到一定規模還是會爆。 s06 設了個閾值(預設 50000 token):

  1. 估算 token 數 len(str(messages)) // 4(粗糙但夠用)。
  2. 超過閾值 → 把完整 transcript 寫盤到 .transcripts/transcript_TIMESTAMP.jsonl(留底)。
  3. 讓 LLM 寫一個 summary給整段對話。
  4. messages 整個換成一條 "[compressed] SUMMARY..."

代價很明顯-失去了具體的工具輸出、對話語氣,只剩下提綱。但 agent 能接著往下乾,這是核心收益。

Layer 3 · 模型自己調 compact tool

auto_compact 是 harness 自動觸發的,模型不知道。 Layer 3 反過來:給模型一個 compact 工具,讓它主動要求壓縮-例如覺得前面的探索已經沒用、要開新階段。

模型呼叫:

tool_use("compact", focus="keep the API design decisions")

觸發和 auto 一樣,但能帶一個 focus 參數告訴總結時重點保留什麼。實戰裡非常實用-模型知道哪些是 "已結束的小任務",比 harness 的啟發式更準。

哪層合適?判斷題

下面幾個場景,決定 micro / auto / manual 哪個觸發比較合理。

Interactive

Widget 1 · Micro Compact · 看 tool_result 按 turn 老化

Step 以步驟推進,觀察舊 tool_result 如何被替換成 [Previous: used X],而最近 3 個保持完整。 read_file 永不壓縮(綠色高亮)。

Turn: 0 · Tokens: ~0
Interactive

Widget 2 · Threshold Simulator · token 上去後哪層被觸發

拖曳滑桿改 token 數,看三層中哪一層活躍。

3000
Interactive

Widget 3 · 哪一層適合 · 6 個場景判斷題

每個場景選取 micro / auto / manual,說說各自適合在何時觸發。

答对 0 / 6