讓長時間的代理作業留在上下文視窗之內
模型一次只能讀取有限的內容,也就是它的上下文視窗。Mythex 的代理會進行長時間的工作:讀取大量檔案、執行指令、把有範圍的任務交給子代理。我們發現長時間工作會以三種方式失敗:子代理成長到模型拒絕所有請求、子代理因為以為空間不夠而提早停止,以及我們對俄文、中文和日文文字的權杖數估算嚴重失準。以下是我們的改變。
先說明幾個名詞
- 權杖(Tokens) 是模型讀取的文字片段。限制與費用都是以權杖計算,而不是以字元或字詞計算。
- 上下文視窗(Context window) 是模型在一次請求中最多能讀取的內容。我們也會為每次請求設定自己較低的預算,讓成本與速度維持在合理範圍。
- 工具結果(Tool results) 是代理從工具取回的內容:它讀取的檔案內容、它執行的指令輸出。在長時間工作中,它們佔了所傳送內容的大部分。
- 子代理(Sub-agent) 是主代理為一個獨立任務啟動的協助者,例如「依照筆記檢查這些章節」。它只拿到一則指示,然後自行進行多次工具呼叫。
問題一:子代理成長到模型拒絕
代理每一步都會把目前為止的對話送給模型。當對話變得太大時,我們的程式會把它修剪,但只在使用者訊息的開頭進行,這樣工具呼叫與其結果就不會被拆開。它會保留第一則訊息,加上一則說明先前對話已被修剪的註記,並保留能放得下的最新完整回合。
子代理只有一則使用者訊息,也就是它的指示。因此沒有可以切割的地方。它每一步都會送出整段歷史,並且持續成長,直到請求大到模型根本無法接受,大約一百萬權杖。在一項包含 8 個這類子代理的測試中,8 個全部都被拒絕。
修正方式:先清除舊的工具結果
現在,請求會分三個步驟符合預算,只有前一步不夠時才會嘗試下一步:
- 清除最舊的工具結果。 它們的內容會被替換成一段簡短說明:為了讓對話維持在模型的上下文之內,該結果已被清除,如果代理需要,可以再次執行該工具。一小時前讀取的檔案通常是請求中最大、用處最小的東西,而且檔案仍然可以再次讀取。最新的結果會保持完整。
- 保留完整的使用者回合。 與先前相同:第一則訊息、一則說明,以及能放得下的最新完整回合。
- 保留完整的工具輪次。 在一個長回合中(子代理只會有這種回合)保留第一則訊息、一則說明,以及最新的完整輪次,即「呼叫一個工具、取得其結果」。
這只會影響送出的內容。代理保存的歷史仍然保留所有內容。在同一項測試中,8 個中有 0 個被拒絕,全部都順利完成。
分批清除,讓快取持續有效
模型供應商可以重複使用請求開頭的部分,前提是它與上一次請求完全相同,這稱為前綴快取。如果我們每次呼叫都多清除一個結果,請求的開頭就會每次都改變,快取就永遠不會有幫助。
因此,最舊的結果是一次清除四個,而不是一個。如此被清除的部分在數次呼叫之間會保持不變。如果一個結果不比說明文字大多少,我們就不去動它:清除它幾乎不會省下什麼,卻仍會改變請求的開頭。保留多少完整內容是依大小決定,而不是依數量:能放進預算四分之一的最新結果會被保留,且至少保留一個。早期版本固定保留六個完整結果,這對短的指令輸出是正確的,但在讀取長檔案時,會讓大部分預算都被舊結果佔據。
拒絕時會說明原因
模型判定為無效的請求不會再被重試,因為重送相同的請求只會得到相同的答案。如果對話對模型來說太長,使用者會看到:「這段對話太長,模型無法一次讀完。請為下一步開啟新的對話,目前已建置的內容都會保留。」這是使用者可以處理的事情,而不是服務中斷,所以不會通知我們的值班人員。
問題二:代理為了「省空間」而停下來
一個被要求閱讀 40 個長章節的子代理,只讀了 2 個就停止了,並說它想省空間。當時它只用了 400,000 權杖預算中的大約 100,000。
它無從得知舊的結果現在會為它被清除。所以我們告訴了它。代理的指示現在說明:當對話成長到超過模型能接受的範圍時,最舊的工具結果會被清除,最新的結果會保持完整,因此不要為了省空間而中止長時間的任務;繼續進行,如果需要已被清除的內容,就再次讀取該檔案。
模型看不見的修正,從模型的角度來看並不是修正。這個機制只有在代理信任它時才會有幫助。
問題三:把所有文字都當成英文來計算權杖
我們在估算權杖時沒有執行分詞器,而是以字元數除以四。這對英文來說是合理的經驗法則,但對其他文字並不適用:俄文大約每個權杖 2.2 個字元,中文或日文大約每個權杖 1 個字元。
因此,一個閱讀俄文章節的子代理送出了約 640,000 權杖的請求,而我們的估算卻顯示低於 400,000 的預算。這個估算同樣可能讓請求超過模型的硬性上限。
現在的估算會依文字系統來計算:純 ASCII 為每個權杖四個字元,中文、日文與韓文為每個權杖一個字元,其他所有文字(西里爾字母、希臘字母、阿拉伯文、帶重音的字母、表情符號)為每個權杖兩個字元。它的設計是寧可估高。以一個俄文章節為例,它現在估算為 43,423 權杖,而實際測量約為 44,600;之前則估算為 25,023。由於一則訊息寫入後就不會再改變,它的估算只會計算一次並記住,而不是每一步都重新計算。
我們的收穫
- 在真正大小所在之處修剪。 在代理工作中,那是工具結果,而不是使用者訊息。
- 告訴代理它自己的安全網。 否則它會依據已不再適用的限制來規劃。
- 「字元數 ÷ 4」是針對英文的假設。 任何為多語言使用者計算權杖的系統,都需要依文字系統計算,或使用真正的分詞器。
- 注意快取。 每次呼叫都在開頭改變的請求,成本更高,執行也更慢。
如果你要在自己的應用程式中加入 AI 功能,我們的什麼是 LLM與如何使用 LLM API指南,從一開始就涵蓋了上下文視窗與權杖。關於子代理在 Mythex 中如何運作,請參閱子代理文件。