為什麼脈絡一長,智慧代理就變笨
把「脈絡腐化」講具體。每一輪都是一次自包含的請求;請求一長、或者塞滿工具往返,答案的準確率就實測下降。這一頁講清請求裡裝著什麼、裝不下時先丟什麼,以及我們為什麼在作答前把它重新算繪一遍,而不是讓模型去總結工具結果。
週一還答得好好的智慧代理,週五開始說話含糊。二十則訊息之前遵守的指令,它不遵守了。它呼叫了工具、拿回正確的資料,然後寫出一個無視其中一半內容的答案。不報錯、不留日誌,對話看起來一切正常。這通常被稱為脈絡腐化(context rot),而最常見的那個解釋——「脈絡視窗滿了」——不是我們測出來的結論。
一輪對話,從頭到尾
一輪從一次請求開始,在工具執行期間變胖,然後在作答之前被重建。中間那個狀態最值得看:絕大多數智慧代理正是拿它來作答的。
每一輪都是一次自包含的請求
模型在兩輪之間不保留任何東西。對面沒有工作階段,不記得一小時前說過什麼,也沒辦法自己去查任何沒交到它手上的東西。每一輪,平台都組裝出一份完整的請求整體送過去;回覆只由這份請求產生,沒有別的來源。
如果你有網路協定方面的直覺,這很接近一個資料報:自包含、送出去一次、帶齊接收方需要的一切,並且受一個發送方必須遵守的大小限制。有兩處這個類比不成立,而且兩處都要緊:
- 傳輸中不會掉東西。 你送的全都到了。變的是模型讀進去了多少——遺失發生在接收方內部,不在線路上。
- 排布會改變答案。 路由器不會因為你重排了酬載就換個行為,模型會。同樣的事實、同一次請求、兩種排法,一種給出正確答案,另一種給出錯的。後面大半內容都是由這個結果引出來的。
請求裡裝著什麼
順序就是模型看到的順序。右側那一欄是裝不下時平台採用的優先順序;名次是相對的,不代表固定丟掉幾塊。
歷史以上的全部內容是同一則系統訊息。這是刻意的:智慧代理的邊界、你的知識庫和回覆語言都不是對話,而模型會因為它們擺在哪裡而區別對待。
裝不下的時候,先丟什麼
超出預算的請求不是從尾巴上截斷,而是按優先順序一層層丟,直到裝得下:
- 最舊的歷史先丟——而且被丟掉的輪次會被壓進滾動摘要,不是扔掉,所以說過的東西留下來了,只是措辭沒留下。
- 先丟分數最低的記憶,再丟分數最低的檢索片段——從尾端開始丟,最相關的材料最後才走。
- 然後是輪廓,再來是摘要。
- 絕不丟智慧代理的指令、你的知識庫和問題本身。 萬一光是問題就撐爆了預算,它會被截斷並標明已截斷——告訴模型,而不是讓它去猜一句話為什麼斷在半個詞上。
這個順序是脈絡預算裡誠實的那一部分:總得有東西要走,而一個不肯說清順序的系統,只會悄悄丟掉碰巧排在最後的那些。
裝得下不等於讀得進
意外正在這裡。我們造了一個測試,每種條件下答題所需的事實都完整在場——沒有任何東西被丟棄、被截斷,哪裡都不缺資訊。唯一變的是請求怎麼排布。任務是在幾百筆記錄裡做多條件過濾,分別打我們自己的本地模型和一個雲端模型,每種條件跑若干次:
| 同樣的事實怎麼排布 | 正確 |
|---|---|
| 一則乾淨的訊息,只裝相關的那幾列 | 8 / 8 |
| 完全相同的那幾列,改由工具結果回傳 | 0 / 8 |
| 整份記錄集,外加五份無關的工具結果 | 0 / 6 |
| 把上面那堆累積重新算繪成一則乾淨訊息 | 8 / 8 |
兩個對照排除了顯而易見的解釋。把那則乾淨請求拆成兩則等長的訊息,仍然 8 / 8,所以變數不是長度;把蒸餾後的那幾列改用工具結果遞回去,仍然 0 / 8,所以也不是「資料得離問題更近」。工具鷹架和無關材料無論擺在哪,都在搶模型的注意力。
雲端模型更穩,但並非免疫:多數任務上它扛住了,可一旦請求裡塞進一輪累積的工具結果,單點查找也掉到 0 / 3。
有兩條限制必須說明。這是我們自己的測試、自己的任務,不是公開基準。而且它不會提升模型的能力上限:同一任務加難一檔後,所有排布下都是零分,因為那個模型根本做不了。重排請求找回的是排布本身害你損失的準確率,買不到模型沒有的能力。
幹活和作答,是兩次不同的請求
這就是頁面開頭那個動畫在講的分界。
幹活需要那份亂的請求。模型必須看見自己的工具呼叫和原始結果,才能決定要不要再呼叫一個。這一段沒變。
作答不需要。工具跑完之後,平台重新組裝一份請求——同樣的系統指令、一小段對話上文、本輪真正查到的事實,以及問題——你收到的回覆由它產生。工具呼叫、原始酬載和中間推理都不會出現在裡面。
上文之所以要留,是因為真實的問題常常就是一句「是的」或者「那第二個呢」。測得最好的那種單則訊息排布是單輪的,而對話不是;上文被折進同一則訊息,而不是還原成獨立的幾輪,實測沒有代價,卻讓指代仍然接得上。
我們刻意不做什麼
對付工具輸出撐爆脈絡,業界常見的解法是讓一個模型先把每筆結果總結一遍。我們不這麼做,理由就是上面那個測試:拿滿分的那一檔用的是原樣資料重新算繪——不需要任何總結步驟就把準確率找回來了。加一個總結步驟,等於請一個模型去判斷哪些事實要緊,而這套系統的整個設計原則就是不把程式能精確完成的活交給模型。一個恰好丟掉了客戶所問那個數字的總結器,失敗時悄無聲息,看起來還像個好答案。
當事實確實裝不下時,它會被截斷,並且在請求裡寫明已截斷,附帶一條不許自行補完缺失部分的指令。一個說「我只看到了前四十筆訂單」的智慧代理,比一個自信地編出其餘部分的智慧代理值錢。
這對你意味著什麼
沒有任何東西需要設定。上面這些就是平台上每一個智慧代理的運作方式。
它在實際使用中改變的是:
- 長對話仍然可用。 第二十輪和第一輪的組裝方式一樣,滑出視窗的部分以摘要形式留下來,而不是憑空消失。
- 回傳大量資料的工具不會毒化回覆。 結果有封頂,而最終答由一份已經不帶原始酬載的請求寫出。
- 呼叫過工具的一輪多花一次模型呼叫——短回覆上大約一秒。跑這一段時聊天介面會顯示它在做什麼,而不是乾坐著。
如果你確實看到智慧代理無視了某個你確信給過它的東西,有用的問題不是「脈絡視窗是不是太小」,而是「它當時在哪一塊裡,請求裡還有什麼和它擠在一起」。一段對話在兩輪之間存在哪裡回答了這一頁的另一半——既然模型什麼都不留,那我們這邊留著什麼,請求又是從什麼組裝出來的。有據可查的回答講檢索到什麼,每位客戶專屬的記憶講記住什麼,工具與 MCP講那些讓單筆工具結果吃不掉整個視窗的預算。