AI代理的上下文工程:從構建Manus中學到的經驗
星期六, 7月 19
技術
2025/7/18 --Yichao 'Peak' Ji
Manus項目的最初階段,我的團隊和我面臨一個關鍵決策:我們應該使用開源基礎模型訓練一個端到端的代理模型,還是基於前沿模型的上下文學習能力構建一個代理? 在我從事NLP的第一個十年中,我們沒有這種選擇的奢侈。在遙遠的BERT時代(是的,已經七年了),模型必須在能夠遷移到新任務之前進行微調和評估。即使與今天的LLM相比這些模型很小,這個過程通常也需要每次迭代幾週的時間。對於快速發展的應用,特別是在產品市場契合度(PMF)之前,這種緩慢的反饋循環是一個致命問題。這是我上一個創業公司的慘痛教訓,我在那裡從頭開始訓練模型用於開放信息抽取和語義搜索。然後GPT-3Flan-T5出現了,我的內部模型一夜之間變得無關緊要。諷刺的是,這些相同的模型標誌著上下文學習的開始——以及一條全新的前進道路。 那個來之不易的教訓使選擇變得明確:Manus 將押注於上下文工程。這使我們能夠在幾小時而非幾週內交付改進,並使我們的產品與底層模型保持正交:如果模型進步是上漲的潮水,我們希望 Manus 成為那條船,而不是固定在海床上的柱子。
儘管如此,上下文工程結果卻一點也不簡單。這是一門實驗科學——我們已經重建了我們的代理框架四次,每次都是在發現了更好的塑造上下文的方式之後。我們親切地將這種手動架構搜索、提示調整和經驗猜測的過程稱為「隨機研究生下降法」。這不優雅,但它有效。
這篇文章分享了我們通過自己的「SGD」所達到的局部最優解。如果你正在構建自己的 AI 代理,我希望這些原則能幫助你更快地收斂。

圍繞 KV-Cache 進行設計

如果我必須選擇僅一個指標,我認為KV-cache命中率是生產階段AI代理最重要的單一指標。它直接影響延遲和成本。為了理解原因,讓我們看看典型代理是如何運作的:
在接收用戶輸入後,代理通過一系列工具使用來完成任務。在每次迭代中,模型根據當前上下文從預定義的動作空間選擇一個動作。然後在環境(例如,Manus的虛擬機沙箱)中執行該動作以產生觀察結果。動作和觀察結果被附加到上下文中,形成下一次迭代的輸入。這個循環持續進行,直到任務完成。 正如您可以想像,上下文隨著每一步而增長,而輸出——通常是結構化的函數調用——保持相對較短。這使得代理程序相比聊天機器人的預填充解碼比率高度傾斜。例如在Manus中,平均輸入對輸出的令牌比率約為100:1
幸運的是,具有相同前綴的上下文可以利用KV-cache,這大大減少了首字令牌時間(TTFT)和推理成本——無論您使用的是自託管模型還是調用推理API。我們談論的不是小額節省:例如使用Claude Sonnet時,緩存的輸入令牌成本為0.30美元/百萬令牌,而未緩存的則為3美元/百萬令牌——相差10倍。

從上下文工程的角度來看,提高KV-緩存命中率涉及以下幾個關鍵做法:
1.保持提示前綴穩定。 由於LLM的自回歸特性,即使是單個標記的差異也會使該標記之後的緩存失效。一個常見的錯誤是在系統提示的開頭包含時間戳—特別是精確到秒的時間戳。沒錯,它可以讓模型告訴你當前時間,但它也會降低你的緩存命中率。
2.使你的上下文僅追加。 避免修改先前的操作或觀察。確保你的序列化是確定性的。許多程式語言和庫在序列化JSON對象時不保證穩定的鍵排序,這可能會悄悄破壞緩存。
3.在需要時明確標記緩存斷點。 某些模型提供商或推理框架不支持自動增量前綴緩存,而是需要在上下文中手動插入緩存斷點。在分配這些斷點時,考慮到潛在的緩存過期,並至少確保斷點包括系統提示的結尾。
此外,如果你正在使用像 vLLM 這樣的框架自託管模型,請確保啟用了前綴/提示緩存,並且你正在使用會話 ID 等技術來一致地將請求路由到分佈式工作節點。

遮罩,而非移除

隨著您的代理獲得更多功能,其行動空間自然變得更加複雜——簡單來說,工具數量呈爆炸性增長。最近流行的MCP只會火上澆油。如果您允許用戶自定義工具,相信我:總會有人不可避免地將數百種神秘工具插入您精心策劃的行動空間中。結果,模型更有可能選擇錯誤的行動或採取低效的路徑。簡而言之,您裝備過重的代理變得更加笨拙。
自然的反應是設計一個動態行動空間——也許使用類似RAG的方式按需加載工具。我們在Manus中也嘗試過這種方法。但我們的實驗表明了一個明確的規則:除非絕對必要,避免在迭代過程中動態添加或移除工具。這主要有兩個原因:
1.在大多數LLM中,工具定義在序列化後位於上下文的前部,通常在系統提示之前或之後。因此,任何變更都會使所有後續操作和觀察的KV緩存失效。
2.當先前的操作和觀察仍然引用當前上下文中不再定義的工具時,模型會感到困惑。如果沒有約束解碼,這通常會導致模式違規或幻覺操作
為了解決這個問題,同時仍然改進操作選擇,Manus使用上下文感知的狀態機來管理工具可用性。它不是移除工具,而是在解碼過程中遮蔽標記邏輯分布,以基於當前上下文來防止(或強制)選擇某些操作。

在實踐中,大多數模型提供商和推理框架支持某種形式的回應預填,這允許你約束行動空間而無需修改工具定義。一般有三種函數調用模式(我們將使用來自NousResearch的Hermes格式作為示例):
自動 – 模型可以選擇調用函數或不調用。通過僅預填回覆前綴來實現:<|im_start|>assistant
必需 – 模型必須調用函數,但選擇不受約束。通過預填到工具調用標記來實現:<|im_start|>assistant<tool_call>
指定 – 模型必須從特定子集中調用函數。通過預填到函數名稱的開頭來實現:<|im_start|>assistant<tool_call>{"name": "browser_ 使用這個,我們通過直接遮蔽令牌邏輯值來限制動作選擇。例如,當用戶提供新輸入時,Manus必須立即回覆而不是採取動作。我們還特意設計了具有一致前綴的動作名稱—例如,所有瀏覽器相關工具都以browser_開頭,命令行工具以shell_開頭。這使我們能夠輕鬆地強制代理在給定狀態下只從特定工具組中選擇,無需使用有狀態的邏輯處理器
這些設計有助於確保Manus代理循環保持穩定—即使在模型驅動的架構下。

使用文件系統作為上下文

現代前沿大型語言模型現在提供128K令牌或更多的上下文窗口。但在現實世界的代理場景中,這通常不夠,有時甚至是一種負擔。存在三個常見的痛點:
1.觀察可能非常龐大,尤其是當代理與網頁或PDF等非結構化數據交互時。很容易超過上下文限制。
2.模型性能傾向於下降,超過某個上下文長度後,即使技術上支持該窗口。
3.長輸入成本高昂,即使使用前綴緩存。你仍然需要支付傳輸和預填充每個標記的費用。
為了解決這個問題,許多代理系統實施上下文截斷或壓縮策略。但過度激進的壓縮不可避免地導致信息丟失。這個問題是根本性的:代理本質上必須基於所有先前狀態預測下一個動作——而你無法可靠地預測哪個觀察可能在十步之後變得至關重要。從邏輯角度來看,任何不可逆的壓縮都帶有風險。 這就是為什麼我們在Manus中將檔案系統視為最終上下文:大小不受限制,天然持久,並且可由代理本身直接操作。模型學會按需寫入和讀取檔案——不僅將檔案系統用作儲存,還用作結構化的外部記憶體。

我們的壓縮策略始終設計為可恢復的。例如,只要保留URL,網頁的內容就可以從上下文中移除,而如果沙盒中仍然有文檔的路徑,則可以省略文檔的內容。這使Manus能夠縮小上下文長度,而不會永久丟失信息。 在開發這個功能時,我發現自己在想像**狀態空間模型(SSM)**要在代理環境中有效運作需要什麼條件。與 Transformers 不同,SSMs 缺乏完整的注意力機制,並且在處理長範圍的向後依賴關係時遇到困難。但如果它們能夠掌握基於文件的記憶——將長期狀態外部化而不是保留在上下文中——那麼它們的速度和效率可能會開啟一類新的代理。基於代理的 SSMs 可能是神經圖靈機的真正繼承者。

通過復述操縱注意力

如果你使用過 Manus,你可能已經注意到一個有趣的現象:在處理複雜任務時,它傾向於創建一個 todo.md 文件——並在任務進行過程中逐步更新它,勾選已完成的項目。
這不僅僅是可愛的行為——這是一種操縱注意力的刻意機制。

Manus 中的典型任務平均需要約 50 次工具調用。這是一個很長的循環——由於 Manus 依賴 LLM 進行決策,它容易偏離主題或忘記早期目標,尤其是在長上下文或複雜任務中。
通過不斷重寫待辦事項清單,Manus 將其目標重述到上下文的末尾。這將全局計劃推入模型的近期注意力範圍,避免 "迷失在中間" 的問題並減少目標不一致。實際上,它使用自然語言來引導自身關注任務目標——無需特殊的架構變更。

保留錯誤的內容

Agent會犯錯。這不是一個bug——這是現實。語言模型會產生幻覺,環境會返回錯誤,外部工具會出現異常,而意外的邊緣情況隨時都會出現。在多步驟任務中,失敗不是例外;它是循環的一部分。
然而,一個常見的衝動是隱藏這些錯誤:清理追蹤記錄,重試行動,或重置模型的狀態並將其留給神奇的"temperature"。這感覺更安全,更受控制。但這是有代價的:消除失敗會移除證據。而沒有證據,模型就無法適應。

在我們的經驗中,改善代理行為最有效的方法之一出奇簡單:將錯誤的嘗試保留在上下文中。當模型看到失敗的行動—以及由此產生的觀察結果或堆棧跟蹤—它會隱式地更新其內部信念。這會使其先驗概率遠離類似的行動,減少重複同樣錯誤的可能性。
事實上,我們認為錯誤恢復是真正代理行為的最明確指標之一。然而,它在大多數學術工作和公開基準測試中仍然代表性不足,這些測試通常專注於理想條件下的任務成功。

不要被少樣本示例所限制

少樣本提示是提高LLM輸出的常見技術。但在代理系統中,它可能會以微妙的方式適得其反。 語言模型是優秀的模仿者;它們模仿上下文中的行為模式。如果你的上下文充滿了類似的過去行動-觀察對,模型將傾向於遵循這種模式,即使它不再是最優的。
這在涉及重複決策或行動的任務中可能很危險。例如,當使用Manus幫助審查20份簡歷時,代理通常會陷入一種節奏——僅僅因為這是它在上下文中看到的內容而重複類似的行動。這導致了偏移、過度泛化,或有時產生幻覺。

解決方法是增加多樣性。Manus在行動和觀察中引入少量的結構化變化——不同的序列化模板、替代性措辭、順序或格式的微小噪聲。這種受控的隨機性有助於打破模式並調整模型的注意力。 換句話說,不要讓自己陷入少量樣本的窠臼。你的上下文越單一,你的代理就越脆弱。

結論

上下文工程仍然是一門新興科學——但對於代理系統來說,它已經是不可或缺的。模型可能變得更強大、更快速、更便宜,但再多的原始能力也無法取代對記憶、環境和反饋的需求。你如何塑造上下文最終定義了你的代理的行為方式:它運行的速度、恢復的效果以及擴展的程度。
在Manus,我們通過反覆重寫、死胡同和數百萬用戶的真實世界測試學到了這些教訓。我們在這裡分享的內容並非放之四海而皆準的真理——但這些是對我們有效的模式。如果它們能幫助你避免哪怕一次痛苦的迭代,那麼這篇文章就達到了它的目的。
代理化的未來將一次構建一個上下文。好好設計它們吧。
Less structure, more intelligence.