# 策展 · X (Twitter) 🔥🔥🔥🔥

> 作者：Aparna Dhinakaran (@aparnadhinak) · 平台：X (Twitter) · 日期：2026-04-27

> 原始來源：https://x.com/aparnadhinak/status/2048492731929149929

## 中文摘要

# Agent Harness 中的 Context 管理

每個 Agent harness 都會遇到相同的限制：Context window 對於模型可能需要記住的所有內容來說實在太小了。隨著對話進行，讀取的檔案變多、Sub-agent 呼叫次數增加，以及工具輸出的堆積，harness 必須決定哪些內容保留在工作集 (working set) 中、哪些需要壓縮，以及哪些留待稍後再檢索。

過去兩年我們一直在開發 Arize 的產品內 Agent「Alyx」，並遭遇過這個問題的各種版本。我們看過對話長到讓模型遺失任務目標、檔案讀取佔用了 Context window 一半的空間，以及工具執行結果擠壓了實際對話內容。

重要的問題不再只是「什麼該放進 Prompt」，而是 harness 如何隨著時間管理 Context。最優秀的系統不會把 Context window 當作被動的對話紀錄緩衝區，而是主動進行管理：將高價值狀態保持在手邊、按需求分頁讀取資料、建立索引以搜尋所需內容（grep 就是這樣做的），並以能暗示還有哪些內容可存取的方式截斷內容。

![](https://pub-75d4fe1e4e80421b9ecb1245a7ae0d1a.r2.dev/curated/1777249722430-iaHG2xrRMaAAALBaMjpg.jpg)

Pi、OpenClaw、Claude Code 和 Letta 在這裡做了不同的選擇，但它們正趨向於相似的底層模式。Context 不再只是「塞得進對話紀錄的東西」，而是系統必須主動管理的資源。真正的設計問題在於：這種管理有多少是在 harness 內部完成的，又有多少是預期由模型自行處理的。

## 賭注：信任模型能自行管理 Context

每一個 Context 管理決策都隱含了對模型行為的假設。關鍵問題在於：harness 應該主動限制 Context 的使用，還是依賴模型自行正確管理預算？

![](https://pub-75d4fe1e4e80421b9ecb1245a7ae0d1a.r2.dev/curated/1777249722423-iaHG2x7hyboAATN1Ojpg.jpg)

檔案讀取讓這個問題變得具體。當模型需要讀取的檔案超過 Context 容量時，必須有人決定保留什麼。這四個 harness 都支援分頁功能的 offset 和 limit 參數。

**Pi (pi-mono)**

Pi 讀取檔案時有 2,000 行或 50KB 的硬性上限（以先達到者為準），即使模型沒有要求分片也是如此。內容會進行頭部截斷，且工具輸出會附加一個明確的接續提示：`[Showing lines 1-2000 of 50000. Use offset=2001 to continue.]`。工具說明也強化了這一點：「輸出被截斷為 2000 行或 50KB。對於大檔案請使用 offset/limit。」

Pi 的方法是「harness 優先」：harness 先保護你，再教導模型如何分頁。

**OpenClaw**

OpenClaw 繼承了 Pi 的讀取工具及其 2K 行 / 50KB 的截斷規則。在一般檔案讀取時行為相同。除此之外，它還為引導檔案（bootstrap files，即對話開始時載入的一次性 Context 檔案）增加了額外上限：每個檔案 12,000 字元，總計 60,000 字元。當引導檔案超過預算時，它會採用 75% 頭部 / 25% 尾部的分割方式：你會看到開頭和結尾，中間被切掉。

工具結果有獨立的預算：16,000 字元或 Context window 的 30%（取較小者）。當尾部看起來「很重要」（如錯誤訊息、JSON 結束括號、摘要關鍵字）時，它會切換到「頭部+尾部」模式；否則就只保留開頭。

OpenClaw 的方法是「縱深防禦」：Pi 的截斷作為第一層，然後是引導檔案注入的額外上限，最後再加上工具結果的預算。

**Claude Code**

Claude Code 對檔案讀取應用了兩層防禦。第一道關卡是 256KB 的位元組上限，在檔案開啟前透過 `stat` 呼叫進行檢查——如果檔案超過此限制，讀取會立即被拒絕，並顯示錯誤訊息引導模型使用 offset/limit 或 grep。第二道關卡在讀取後執行：輸出會針對 25,000 token 的預算進行計算，藉此攔截那些位元組數雖小但 token 密度高的檔案。這兩個限制都可以由 Anthropic 透過 GrowthBook 功能旗標 (feature flags) 遠端調整，無需發布新版本。

即使檔案在上限內，工具預設也只會回傳開頭的 2,000 行，且任何超過 2,000 字元的行都會被截斷。模型必須明確使用 offset 和 limit 參數來請求更多內容。

工具說明是一段完整的多段落 Prompt，解釋了分頁機制、提到大小上限、涵蓋圖片/PDF/筆記本支援，並鼓勵跨多個檔案進行平行讀取。offset 和 limit 參數有各自的說明，告訴模型它們是用於過大的檔案。還有一個條件式指令，會根據功能旗標直接在 Prompt 中顯示 256KB 的上限。

檔案去重 (dedup) 系統也值得一提。如果模型在相同的範圍內重新讀取同一個檔案，且檔案時間沒有改變，Claude Code 會回傳一個存根 (stub) 而非完整內容，避免在 Context 中產生重複的 token。

Claude Code 的方法是「具備遠端調整能力的 harness 優先」：包含預讀位元組閘道、讀後 token 閘道、行數與行長預設值、可操作的錯誤訊息、豐富的工具 Prompt、讀取去重，以及讓 Anthropic 能在伺服器端調整所有設定的功能旗標。

**Letta**

Letta 採取了截然不同的方法。每個上傳的檔案都會被解析、分塊並嵌入 (embedded) 到向量資料庫中，因此 Agent 同時擁有精確搜尋和語意搜尋能力。這賦予它三種檔案工具：`open_files` 用於直接檢視（讀取原始文字）、`grep_files` 用於精確模式匹配（也是原始文字），以及 `semantic_search_files` 用於針對嵌入片段進行基於意義的檢索。

當檔案在 Agent 的 Context 中「開啟」時，其可見內容會被截斷為每個檔案的字元限制，該限制會隨模型 Context window 的五個等級而擴展：8K Context 為 5,000 字元、32K 為 15,000、128K 為 25,000，200K+ 為 40,000。同時開啟的檔案數量也會隨之擴展，小型模型為 3 個，大型模型最多 15 個，預設為 5 個。當超過限制時，會採用 LRU (最近最少使用) 策略來移除檔案。

Letta 的方法是「記憶優先」：檔案同時存在於原始文字和向量資料庫（嵌入區塊）中，Context window 只顯示受管理的視圖，模型則使用工具來存取更多內容。

## 真正的工程挑戰：對話修剪 (Session pruning)

隨著對話增長，每個 harness 都必須決定保留什麼、捨棄什麼。這是設計差異最顯著的地方，因為壓縮策略決定了長期運行的 Agent 是能保持連貫性，還是會逐漸退化。

![](https://pub-75d4fe1e4e80421b9ecb1245a7ae0d1a.r2.dev/curated/1777249722410-iaHG2yRKrbYAAM04Rjpg.jpg)

**Pi (pi-mono)**

Pi 使用壓縮：由 LLM 驅動的摘要功能，由 token 閾值觸發。

- 觸發條件：預估的 Context token 超過 `contextWindow - reserveTokens`（預設保留：16,384 tokens）。
- 保留內容：從對話末端往回走，保留最近約 20,000 個 token 的訊息 (`keepRecentTokens`)。
- 摘要內容：更舊的內容會傳送給 LLM 進行摘要。
- 摘要存放位置：成為一則合成的使用者訊息，置於保留的尾部之前。
- 工具呼叫安全性：絕不會在孤立的工具結果處截斷。會遍歷邊界以確保「工具呼叫/工具結果」對保持完整。

**OpenClaw**

OpenClaw 在 Pi 的壓縮機制之上運行了兩種不同的 Context 管理機制：

- 觸發條件：歷史紀錄超過 Context window 的 50% (`maxHistoryShare`，預設 0.5)。
- 保留內容：將歷史紀錄分割成等量的 token 區塊；丟棄最舊的區塊，其餘部分保留，並修復工具呼叫/結果對。
- 摘要內容：被丟棄的內容會經過分階段的多輪 LLM 摘要，並包含合併步驟。
- 摘要存放位置：與 Pi 相同——合成訊息置於保留的尾部之前。
- 工具呼叫安全性：`repairToolUseResultPairing` 可修復區塊丟棄後任何孤立的工具結果；`splitMessagesByTokenShare` 避免在工具呼叫/結果對內部進行切割。
- 壓縮前刷新：一個靜默的 Agentic 轉向 (turn) 讓 Agent 在歷史紀錄消失前將狀態持久化到記憶檔案中。
- 第二層：工具結果的非破壞性記憶體內修剪（軟修剪，然後硬清除），快取 TTL 為 5 分鐘，在保護持久化對話的同時為當前請求回收 Context。

**Claude Code**

Claude Code 透過查詢前最佳化 (pre-query optimization) 和 LLM 驅動的壓縮來管理 Context。

- 觸發條件：預估 token 超過有效 Context window 減去 13,000 token 的緩衝區（對於 200K Context 的模型，壓縮會在約 167K token 時觸發）。
- 摘要內容：將完整對話傳送給模型，並附帶一個結構化的 9 節 Prompt，涵蓋主要請求、關鍵技術概念、檔案與程式碼、錯誤與修復、問題解決、所有使用者訊息、待辦任務、當前工作以及選用的下一步。
- 摘要存放位置：成為一則使用者訊息，告知模型該對話是從先前因 Context 不足而中斷的對話延續而來。
- 壓縮後恢復：壓縮後，最多 5 個最近讀取的檔案會在 token 預算內被重新附加到 Context 中。
- 摘要器安全性：模型會在獨立的標記區塊中產生分析草稿和最終摘要。草稿在摘要進入 Context 前會被移除，在不增加結果負擔的情況下提高品質。
- Prompt 過長時的備援：如果壓縮呼叫本身觸發了 Context 限制，則會進行確定性的頭部刪除，移除最舊的 API 輪次群組（20% 的群組或足以消除 token 差距的量）。
- 查詢前最佳化（每次 API 呼叫，無論 Context 壓力如何）：在每次模型呼叫前，Claude Code 會運行一個管理工具結果的管線，但保持對話文字不變。過大的工具結果會持久化到磁碟並替換為 2KB 的預覽，每個工具上限為 50,000 字元，每則訊息總上限為 200,000 字元，因此一個 60KB 的 grep 結果在新對話的第一輪就會被卸載。

**Letta**

Letta 透過多種壓縮策略管理 Context，並在主要路徑溢位時提供兩階段的摘要器備援。

- 觸發條件：當預估的 Context 使用量超過 Context window 的 90% 時觸發壓縮。
- 滑動視窗移除：從 30% 的訊息開始（而非 10%），每次迭代增加 10%，直到 token 使用量降至目標以下。保留最近的訊息，移除最舊的。
- 自我壓縮模式：使用 Agent 自身的模型進行摘要，因此無需額外的摘要器成本或配置。
- 摘要器溢位時的兩階段備援：首先，將工具回傳內容限制為 5,000 字元並重試。如果仍然溢位，則對對話紀錄進行中間截斷，保留 30% 頭部和 30% 尾部，丟棄中間部分。
- 警告閾值：在 90% 壓縮觸發前，會有一個獨立的 75% 記憶體警告。

## Sub-agent 的 Context 管理

在我們檢視的 harness 中，Sub-agent 通常與父對話隔離。這裡的例子中，沒有一個會將完整的父對話歷史複製到子對話中。問題在於它們繼承了什麼樣的 workspace Context。

![](https://pub-75d4fe1e4e80421b9ecb1245a7ae0d1a.r2.dev/curated/1777249722453-iaHG20pHfaUAAcDpIjpg.jpg)

Pi 為每個委派任務產生一個帶有記憶體內對話的新程序。子程序僅接收任務字串作為其唯一的使用者訊息，不會傳遞任何父對話歷史。

OpenClaw 預設給予 Sub-agent 全新的隔離對話，沒有父對話紀錄。存在一種 fork 模式，可以將父對話紀錄複製到子對話中，但僅限於相同 Agent 的產生。Workspace Context 被過濾為最小的允許清單（AGENTS.md、TOOLS.md、SOUL.md）。

Claude Code 有兩條路徑。預設的 typed-agent 路徑會建立一個空白對話：委派的 Prompt 成為唯一的使用者訊息，沒有父對話歷史。較新的 fork 路徑會將整個父訊息歷史傳遞給子對話以共享 Prompt 快取，外加一則合成的助理訊息和佔位符工具結果。工具會為工作者重新建立，並擁有各自的權限模式；非同步 Agent 擁有明確的允許清單（Read、Grep、Glob、Shell、Edit、Write、WebSearch 等）。Agent 定義中引用的技能會被積極預載。完整的技能內容會作為使用者訊息注入到初始對話中，而不是按需載入。

Letta 在執行一般工具時根本不會進行 fork。工具在主 Agent 迴圈內運行。歷史 Context 是透過專用的搜尋工具存取的：對話搜尋用於回憶記憶，封存記憶搜尋用於嵌入資料庫。

# 設計趨勢的匯流點

比較這四個程式庫後，最驚人的發現不是它們有多不同，而是它們有多麼一致。

這四個 harness 都對檔案讀取設定了硬性上限。四者都支援 offset/limit 分頁。四者都限制了工具結果的大小。四者都隔離了 Sub-agent 對話。四者都運行由 token 閾值觸發的 LLM 驅動壓縮。四者都估算 Context 使用量並偵測壓力。這些並非巧合，而是針對同一個工程問題的匯流解決方案：一個必須感覺起來無限大的固定大小工作集。

這種匯流不僅僅體現在擁有相同功能，具體的設計選擇也如出一轍。Pi 和 OpenClaw 都對檔案讀取進行頭部截斷並附加接續提示。Claude Code 和 OpenClaw 都將過大的工具結果持久化到磁碟。Pi、OpenClaw 和 Claude Code 在壓縮時都強制執行「工具呼叫/結果」邊界安全性。四者中有三者支援將父對話紀錄 fork 到 Sub-agent 中。這些 harness 正在獨立地得出相同的答案。

這些模式不僅限於程式撰寫 Agent。Arize 自家的 Alyx 助理（專為資料探索而非程式編輯而建）也獨立得出了相同的設計。Alyx 將工具結果限制在 10,000 token 的預算內，並使用二元搜尋來找出符合大小的最大資料集切片。它透過從對話歷史中修剪重複的預覽來對冪等 (idempotent) 工具呼叫進行去重，僅保留最近的一次。它將大型 JSON 負載拆分為壓縮後的 LLM 可見預覽，以及模型可透過 `jq` 深入查詢的完整伺服器端副本，這與 Pi、OpenClaw 和 Claude Code 在檔案讀取上使用的「將過大結果持久化在 Context 之外」的模式相同。它對長儲存格值進行「頭部+尾部」截斷，並附帶完整內容的後向參照。它以 `char/4` 的啟發式方法估算 token 壓力，並在對話超過 50,000 token 時強制執行檢查點，此時模型會在歷史紀錄被修剪前寫下自己的狀態摘要，這結合了 Claude Code 的確定性壓縮觸發器與 OpenClaw 的壓縮前狀態刷新。它的 Sub-agent 啟動方式採用了這四個 harness 都使用的隔離模式。一個為完全不同領域打造的產品，最終匯流到了相同的 Context 管理劇本上。

![](https://pub-75d4fe1e4e80421b9ecb1245a7ae0d1a.r2.dev/curated/1777249722420-iaHG21GZqbEAA1ZzVpng.png)

50 年的電腦科學教導我們，最好的記憶體管理是程式完全不需要考慮的那種。暫存器、快取行、分頁表、交換空間。每一層由系統管理，每一層對上一層都是隱形的。程式只需運行即可。

Agent harness 正朝著同一個方向發展。目標不是向模型展示所有內容，而是要在正確的時間給予它正確的工作集，並允許它動態地做出決策來管理自己的 Context。

## 標籤

Agent, Harness, 記憶系統, Arize, Alyx
