與 LLM 合作越久,我越常發現自己陷入一場永恆的戰爭:上下文要夠多、但又不能太多、Token 浪費與壓縮大戰。看向 Agent 的記憶系統,能發現一些強大的武器可以助戰。
在我觀察的 19 個系統中,一個反覆出現的發現是:更大的上下文視窗不是在解決預算問題,而是在加劇它。一個 200K token 的視窗,並不會平等地關注所有 200K token。在視窗填滿之前,效能早就開始下滑了。這種衰退是不均勻的:長上下文的中間部分,受到的關注度可靠地低於兩端。而且 Agent 的實際任務只佔據了視窗中固定的一小塊,無論視窗有多大,這意味著其他所有東西都是與之爭奪相同注意力預算的開銷。
那些處理得好的系統,最終都收斂到六個機制上。它們沒有一個是花俏的。有幾個甚至簡單得令人尷尬。但那些跳過它們的系統,都付出了代價。
六個機制
壓縮處理
這是最顯而易見的機制。你取一段長對話或一大塊記憶區段,對它進行摘要,然後用摘要替換掉原始內容。MemoryOS 在區段層級上這麼做:當一個對話區段增長超過某個門檻時,它的區段摘要器就會觸發,將其壓縮成一個緊湊的表示形式,以免它排擠掉工作上下文。Karpathy 風格的維基(purpose.md, overview.md)則在知識層級上做類似的事:維基就是 Agent 在某個主題上學到的一切的壓縮形式,並且會跨階段維護。
其代價是資訊損失。壓縮本質上就是一種有損操作。摘要只捕捉了在壓縮當下,摘要器認為相關的內容。如果 Agent 後來需要一個當時被認為不相關的細節,它就消失了。這不是避免壓縮的理由,但卻是不要將其視為唯一機制的理由。
還有第二個容易被忽略的成本。壓縮在運行時也不是免費的。MemoryOS 在一次互動中,可能就需要支付 20 次以上的 LLM 調用,來維護其區段摘要。對於互動頻率高的系統來說,這是一個真實的運營成本。
結果預覽截斷
與其在每次檢索時都返回完整的記憶內容,不如返回一個簡短的預覽,讓 Agent 決定是否要獲取完整記錄。supermemory 公開了片段長度控制項,讓調用者可以調整每個結果返回的文字量。mem9 更進一步:它用三個環境變數(MEM9_SOURCE_TURN_MIN_SCORE, MEM9_SOURCE_TURN_PER_MEMORY_LIMIT, MEM9_SOURCE_TURN_TOTAL_LIMIT)來裝飾來源輪次,讓操作員能夠精確控制出現多少來源輪次以及它們的最低相關性分數。
其代價是一次額外的工具調用。如果 Agent 需要完整內容,它必須明確提出要求。對於大多數檢索模式來說,這是個正確的取捨:Agent 在支付完整閱讀的 token 成本之前,就能獲得足夠的信號來判斷記錄是否相關。
兩步檢索
這是預覽截斷的一個特定且重要的變體。搜尋返回標識符和簡短預覽。當需要時,一個獨立的 GetByID 調用會獲取完整記錄。mem9 的 MemoryRepo 介面就是圍繞這個模式構建的:搜尋和獲取是兩個不同的操作,具有不同的 token 足跡。
數字清楚地說明了這一點。十個匹配結果,每個 1,500 token,那就是 15,000 token 被注入到上下文中,無論 Agent 用不用它們。兩步檢索返回 10 個標識符和簡短預覽,總共大約 450 token,然後只獲取 Agent 實際需要的記錄。在一個會話中的 20 個召回步驟裡,這個差異會累積成大約 200,000 token 的節省。
這是你所能採用的最便宜的紀律。它不需要對記憶儲存進行任何架構更改,不需要額外的 LLM 調用,也沒有資訊損失。它只是一個檢索介面的決策。
分解再召回
與其將完整的用戶查詢發送給檢索層,不如先將其分解為子查詢。SimpleMem 的意圖感知檢索規劃器,會在命中記憶儲存之前,將進來的查詢分解為原子檢索意圖。GitNexus 也做了類似的事,透過其查詢工具分解:複雜的查詢被拆分為有針對性的子查詢,每個子查詢都會檢索記憶圖譜中一個集中的切片。
好處是精確性。分解後的查詢檢索到的無關材料更少,這意味著上下文中的雜訊更少。代價是延遲:分解在檢索開始之前增加了一個規劃步驟。對於互動式 Agent 來說,這很重要。對於批次或後台 Agent 來說,通常則沒什麼問題。
分層儲存作為預算過濾器
如果你已經建立了分層記憶架構(上週文章的主題),你會得到預算過濾作為一個副作用。supermemory 的三層模型意味著,熱門、頻繁存取的資料存在一個會返回緊湊、高信號結果的層級。冷資料則存在一個預設不會被查詢的層級。Hindsight 的觀察層也是同樣的運作方式:原始觀察結果不會直接被注入上下文;它們在被提升到更高層級後,才會成為檢索候選項。
代價是召回的完整性。尚未被提升的資料可能相關,但在標準檢索過程中不會出現。這與壓縮的代價相同,但失敗模式不同:它不是透過摘要丟失資訊,而是透過降級丟失資訊。
自我引導的工具回應
這是最少被討論的機制,也是比較有趣的一個。工具回應本身不讓 Agent 自行決定下一步要做什麼,而是包含一個關於下一步該做什麼的提示。GitNexus 在工具回應後附加了一個 ---
**Next:** 區塊,建議後續操作。mem9 則用結構化元資料裝飾來源輪次,引導 Agent 的下一個檢索步驟。
效果是 Agent 在工具調用之間花費更少的 token 來規劃。工具回應攜帶了足夠的結構,讓下一步變得顯而易見。代價是提示工程的功夫:寫出好的自我引導回應,需要預先知道 Agent 下一步可能需要什麼,而這並不總是可行的。
Tolaria 的極端案例
Tolaria 值得單獨拿出來看,因為它代表了預算紀律走向極致的邏輯終點。ADR-0009 記錄了從系統中完全移除嵌入的決定。Tolaria 只使用子字串搜尋。沒有向量索引,沒有語義檢索,沒有嵌入調用。
理由很直接:最便宜的 token,是你一開始就從未檢索出來的那個。基於嵌入的檢索會返回語義上相似的結果,這意味著它會返回 Agent 沒有明確要求的結果。其中一些結果是有用的。很多是沒用的。而所有這些結果都消耗 token。
Tolaria 的立場是,在整個會話中,不相關但相似的結果所累積的成本,超過了語義召回對其使用案例帶來的好處。這個取捨是否適用於你的系統,取決於你的系統是做什麼的。對於查詢精確且結構化的系統(如程式碼導航、按標識符查找文件),Tolaria 的立場是站得住腳的。對於查詢模糊且探索性的系統,移除嵌入會以一種難以恢復的方式破壞召回。
Tolaria 案例的價值,不在於你應該複製它。而是在於,它讓語義檢索的成本以一種大多數系統都沒有的方式,變得清晰可見。
反對純壓縮系統的理由
在 19 個系統中,有幾個依賴壓縮作為其主要或唯一的預算機制。這些失敗模式值得提出來。
首先,摘要會遺漏那些在壓縮時未被判定為相關,但後來卻變得相關的細節。這不是假設性的:它是任何應用於未來相關性未知的資訊之有損壓縮方案的標準失敗模式。
其次,壓縮是一個熱路徑成本。MemoryOS 在一次互動中支付 20 多次 LLM 調用,對於壓縮密集型系統來說並不罕見。在規模擴大時,這個成本是不可忽視的。
第三點,也是最微妙的一點,沒有逃生艙的壓縮就是緩慢的遺忘。如果減少上下文大小的唯一方法是摘要,而摘要又是有損的,那麼系統就在持續地丟棄資訊,並且沒有任何方法可以恢復它。兩步檢索、分層儲存和結果預覽截斷都保留了原始記錄。壓縮則不會。
這些都不意味著壓縮是錯的。它只意味著僅靠壓縮是不夠的。
近期性加權與持久佇列
兩個不完全符合上述六個類別的機制,值得注意。
graymatter 使用了帶有近期性半權重的 RRF 融合。嚴格來說,這不是一個預算機制,但它起到了這樣的作用:透過在檢索排名中降低較舊資料的權重,它降低了過時、低信號記錄排擠掉近期、高信號記錄的可能性。其效果是透過排名權重實現的軟分層,而非明確的層級提升。
llm-wiki 的 540 行攝入佇列狀態機則採取了一種不同的方法。該佇列將攝入操作序列化,並在任何內容進入記憶儲存之前應用一個四信號相關性排序器。預算控制發生在寫入時,而非讀取時。未通過相關性門檻的資料不會被儲存,這意味著它無法被檢索,也無法消耗上下文。這是一種間接的預算控制,但它是持久的:節省下來的資源會在每個未來的會話中累積。
設計良好的系統有哪些共同點
綜觀這 19 個系統,那些處理上下文預算得當的系統,共享一些特點。
它們將檢索視為一個兩步驟操作,而非一次性注入。它們在返回完整記錄之前先返回預覽。它們保留原始記錄,而不是用摘要取而代之。它們透過明確的參數讓操作員可以控制檢索量,而不是使用硬編碼的默認值。而且它們同時在寫入時和讀取時考慮預算。
那些處理得不好的系統,則傾向於依賴單一機制(通常是壓縮),並將上下文視窗視為一個需要填滿的緩衝區,而不是一個需要管理的資源。
這項研究的最終結論很簡單。更大的視窗需要更多的紀律,而不是更少。這不是因為填滿它們原則上有錯,而是因為用錯誤的材料填滿它們,比讓空間空著要付出更高的代價。
在我的下一篇文章中,我計劃將焦點從「記憶即注入」轉向「記憶即工具」,探討這 19 個系統如何處理哪些內容被自動推入上下文、哪些內容需要 Agent 明確請求的邊界。
一如既往,如果你覺得這篇文章有趣、有用,或者只是想幫忙傳播知識:
請分享





