:從Token預(yù)算到RAG策略)
1. 上下文管理到底是什么為什么它是大模型應(yīng)用的命門做AI模型落地尤其是做大模型應(yīng)用繞不開一個詞上下文Context。朋友圈里狂熱討論的“長文本大模型”跑分榜上的百萬token窗口本質(zhì)上都是在搶這一個東西——讓模型“記住”更多信息。但真正到了實際業(yè)務(wù)里上下文管理幾乎可以說是決定項目成敗的隱形關(guān)卡。我先解釋清楚這個名詞。上下文管理指的不是讓模型去“理解”每一句話的瑣碎細節(jié)而是解決一個工程問題如何在你與模型交互的有限窗口里既塞進足夠的關(guān)鍵信息又保證模型不會被噪聲帶偏更不會因為超窗而報錯、遺忘甚至產(chǎn)生幻覺。一個最直觀的例子。你在做一個AI客服Bot用戶連續(xù)問了三個問題第一個問“發(fā)票怎么開”你回答完規(guī)則流程第二個問“那電子發(fā)票開不來了怎么辦”你還能接住第三個問“那剛才說的紅沖具體怎么操作”如果你的上下文中沒有保留第一輪和第二輪的原話模型大概率會給你胡編一個紅沖流程。這時候你罵模型智商低其實冤枉它了——是上下文管理沒做好該保留的信息被沖掉了。我用通俗的話來定位它的價值上下文管理是大模型從“玩具”變成“生產(chǎn)工具”的核心分界線。沒有它的應(yīng)用只會是一問一答的機械工具有了它的應(yīng)用才算是一個有記憶、會歸納、能自查的智能體。這篇文章適合誰適合正在做AI應(yīng)用開發(fā)但被對話記憶、Token超長、多輪邏輯搞到頭禿的開發(fā)者也適合產(chǎn)品經(jīng)理、技術(shù)負責(zé)人想建立對AI系統(tǒng)底層機制的判斷力。我會從一個實際做項目的經(jīng)驗視角出發(fā)把上下文管理的原理、方案選型、實現(xiàn)細節(jié)和排查方法全部拆開講盡量做到不給PPT式的空話。2. 底層機制上下文窗口、Token與注意力之間的關(guān)系很多初學(xué)者會把“上下文窗口”誤解為模型的記憶容量認(rèn)為窗口越大模型記住的東西就越多。這個理解只對了一半。上下文窗口嚴(yán)格來說是模型能處理的序列長度上限但這個上限雙制約著輸入和輸出——也就是你一次性發(fā)給它的所有內(nèi)容包括問題、歷史記錄、系統(tǒng)指令加上它即將生成的回答總token數(shù)不能超過窗口值。一旦超了最直接的表現(xiàn)是報錯或粗暴截斷。2.1 Token是上下文的基本計量單位不是字?jǐn)?shù)Token是模型處理文本的最小單位可以粗略理解成一個雙字節(jié)的集裝箱。中英文的Token計算差異極大。一般來說1個英文單詞約等于1.3個Token而1個漢字在多數(shù)大模型上約等于1到2個Token。這個細節(jié)在實戰(zhàn)中極其重要你覺得你只發(fā)了2000字的中文但模型眼里這可能是3000個Token再疊加上系統(tǒng)的角色設(shè)定、歷史記錄可能輕松突破窗口。我見過不少開發(fā)者在Token估算上栽跟頭。有人用“字符數(shù)除以2”來估算中文token發(fā)現(xiàn)精度很差因為在不同模型的分詞器之下同樣一句話的Token數(shù)可能差出20%。實操中我的習(xí)慣是硬編碼一段基準(zhǔn)文本直接在模型服務(wù)里跑一次tokenizer統(tǒng)計然后算出一個經(jīng)驗系數(shù)。比如某模型對中文的經(jīng)驗系數(shù)約為0.6 token/字那1000字中文大約600 Token這個系數(shù)比任何在線估算器都要準(zhǔn)。2.2 注意力機制決定了模型的“關(guān)注范圍”不等于“全窗口范圍”模型處理上下文時并不是把窗口里所有歷史內(nèi)容一視同仁地讀一遍。從原理上來講模型里每一層都有一套注意力權(quán)重它會根據(jù)當(dāng)前的生成任務(wù)動態(tài)分配對歷史各個位置的關(guān)注程度。這就帶來一個非常重要、但被很多人忽略的結(jié)論模型能看到的Token越多并不代表它“在乎”的Token越多。當(dāng)窗口塞入8000個Token時其中真正對當(dāng)前回答起決定作用的可能只有十幾個關(guān)鍵Token。注意力分?jǐn)?shù)就像聚光燈不可能均攤照亮每一個角落。這也是為什么有些人把長篇文檔一股腦塞給模型結(jié)果回答質(zhì)量反而不如只傳關(guān)鍵段落——有效信息被淹沒在海量歷史里聚光燈照不到它該照的地方。2.3 位置編碼與“迷失在中間”效應(yīng)還有一層更隱蔽的機制位置編碼。模型會在處理序列時給每一個Token所在的位置計算一個位置向量幫助它區(qū)分先后順序。早期的位置編碼對長序列不友好Token離當(dāng)前位置越遠位置向量就越容易被“稀釋”。后來有了相對位置編碼如RoPE這類改進技術(shù)情況好轉(zhuǎn)但仍然存在一個工程師圈內(nèi)很經(jīng)典的現(xiàn)象模型對上下文的頭部和尾部內(nèi)容更敏感對中間段的記憶相對模糊。我還特意做過對照實驗。把一段需要精確引用的合同條款放在上下文的前段、中段、后段分別讓模型回答條款細節(jié)。結(jié)果中間段的回答準(zhǔn)確率比前段低約17%。這不是巧合而是注意力分布天然導(dǎo)致的“中間遺忘”現(xiàn)象。所以工作中凡是關(guān)鍵指令、核心約束條件我寧愿把它放在上下文開頭比如系統(tǒng)提示詞里強調(diào)也不放在中部隨波逐流。3. 四大上下文管理策略對應(yīng)不同應(yīng)用場景上下文管理沒有銀彈方案。不同業(yè)務(wù)對歷史信息的精度要求、時效要求、成本預(yù)算完全不同。我從實踐中梳理出四種主流策略你可以把它們當(dāng)成上下文管理的基礎(chǔ)工具箱。3.1 全量傳入最簡單的方案但有限制全量傳入就是把所有對話歷史、文檔內(nèi)容、用戶問題一次性組裝進Prompt丟給模型。它的優(yōu)勢是信息無損模型能看到完整上下文適用于交互輪次少、單次信息密度高的場景比如一次性給模型做長文檔摘要。缺點也很明顯Token消耗高、響應(yīng)延遲長、超窗風(fēng)險大。某些長文檔任務(wù)動輒幾萬Token一次調(diào)用可能就讓用戶的耐心耗盡。全量傳入在目前大模型參數(shù)量膨脹、上下文窗口擴大比如從4K漲到128K的趨勢下曾經(jīng)看起來特別香但工程上很快遇到了瓶頸大窗口意味著高昂的注意力計算開銷在線服務(wù)的首字響應(yīng)時間會被拉長到不可接受。所以即便窗口變大工業(yè)界也不會真的把每輪對話都無腦全量塞進去。3.2 滑動窗口最近N輪的記憶模型滑動窗口是最常用的輕量方案核心思路是只保留最近N輪對話比如最近10輪更早的歷史一律裁剪。它模擬的是人類聊天的短期記憶機制——時間久遠的話說忘了就忘了只關(guān)注當(dāng)下話題。好處在于實現(xiàn)簡單、控制Token穩(wěn)定、延遲可控。壞處是它會喪失長期依賴。典型場景是客服機器人用戶前20分鐘問過“退貨政策是什么”20分鐘后問“那我超期了怎么辦”如果中間夾雜了大量其他輪次超過了滑動窗口范圍模型就完全不記得退貨政策相關(guān)的信息只能重問用戶體驗極差。要緩解這個缺陷我的經(jīng)驗是在做滑動窗口時不能只是生硬地按輪次切而應(yīng)該加入一個語義優(yōu)先級。比如把用戶首次問題、系統(tǒng)回復(fù)的關(guān)鍵結(jié)論抽出來固定保留然后對較舊的內(nèi)容降權(quán)裁剪。也就是“滑動窗口 關(guān)鍵節(jié)點釘住”的組合策略比純滑動窗口的魯棒性高得多。3.3 摘要記憶把歷史壓縮成精華摘要記憶是我個人最看重的策略之一。核心做法是每當(dāng)對話進行到一定輪數(shù)比如每5輪就觸發(fā)一次摘要將之前的所有內(nèi)容壓縮成一個結(jié)構(gòu)化或非結(jié)構(gòu)化的摘要段落放進上下文里作為長期記憶的替代。舉個例子。你和一個AI助手討論今天的日程安排聊到了會議變更、出差航班、餐飲預(yù)訂。如果不做摘要每輪全量保留歷史上下文體積線性膨脹做了摘要之后變成一段話“用戶上午會議從10點改到11點航班改為下午3點中午在機場解決午餐?!边@段精煉摘要既保留了關(guān)鍵事實又極大壓縮了Token占用。要注意的是摘要過程本身也會消耗時間和Token。每做一次摘要就是一次模型調(diào)用如果摘要頻率太高成本就上去了。所以我的落地經(jīng)驗是設(shè)定一個閾值比如總Token達到窗口的30%就觸發(fā)摘要而不是機械地按5輪或10輪。摘要的本質(zhì)是損失重構(gòu)壓縮得越厲害丟失的細節(jié)越多因此在做摘要時我還會強制要求模型保留關(guān)鍵專有名詞、數(shù)字、時間、金額防止“壓縮出幻覺”導(dǎo)致關(guān)鍵信息被改寫。3.4 向量檢索與RAG讓模型擁有“外部記憶庫”當(dāng)上下文的量級從幾輪對話變成幾百頁文檔前三種策略就都不夠用了。向量檢索方案通常以RAG的形式出現(xiàn)是目前最主流的選擇。原理也不復(fù)雜先把文檔切塊每塊通過嵌入模型轉(zhuǎn)成向量存入向量數(shù)據(jù)庫查詢時把用戶問題也轉(zhuǎn)成向量通過相似度檢索找到最相關(guān)的幾個片段拼進Prompt。RAG的最大好處是上下文管理的顆粒度從“全量”變成了“按需提取”理論上你可以裝下一個企業(yè)知識庫的全部文檔但每一輪實際傳輸給模型的只是其中一小撮最相關(guān)的塊。缺點也同樣明顯。首先檢索質(zhì)量直接決定回答質(zhì)量——如果檢索出來的塊不相關(guān)后面模型再怎么努力也答不對其次切塊策略是玄學(xué)切得太碎丟語義切得太粗撈噪聲再次向量相似度并不總能正確捕捉語義相關(guān)性對于隱式表達的語義比如同義改寫、上下文指向檢索經(jīng)常會漏掉真關(guān)聯(lián)。另外RAG的上下文管理不只是檢索放入這一步還包括檢索后如何排序、如何與歷史對話拼接、如何控制多個檢索塊之間的邏輯一致性。3.5 四種策略怎么選一張決策表策略適用場景優(yōu)點缺點成本全量傳入單輪長文檔處理、一次性分析信息無損、實現(xiàn)零成本Token消耗大、超窗風(fēng)險高高滑動窗口輪次少、話題連貫的對話實現(xiàn)簡單、Token穩(wěn)定長期記憶丟失低摘要記憶多輪復(fù)雜對話、需長期跨話題壓縮比高、保留關(guān)鍵信息摘要過程額外開銷、有損中向量檢索RAG知識庫問答、超大文檔對話按需提取、可擴展海量數(shù)據(jù)檢索不確定性、依賴切塊高實際業(yè)務(wù)中這幾種策略并不互斥。我做的某跨平臺系統(tǒng)中就是滑動窗口負責(zé)短期對話上下文摘要記憶負責(zé)每日匯總RAG負責(zé)長期知識引用三者疊在一個Pipeline里才跑出比較穩(wěn)定可靠的效果。4. 實操一個多輪客服場景的上下文管理方案落地說得再漂亮不如跑一個能直接抄作業(yè)的方案。下面我用一個虛構(gòu)的智能客服項目X來演示上下文管理完整鏈路。假設(shè)面臨的需求是用戶會與Bot進行多輪交互主題包括政策咨詢、訂單查詢偶爾還會跨主題比如從售后政策聊到具體訂單狀態(tài)同時線上需要一個知識庫承擔(dān)FAQ問答。4.1 需求分析與方案設(shè)計第一步先拆需求明確幾個約束條件模型上下文窗口為32K Token這是上限實際使用預(yù)算設(shè)定為24K預(yù)留出模型輸出的空間。對話平均輪次在10輪以上用戶經(jīng)?;仡^續(xù)問。需要接入知識庫但知識庫文檔總量超過1萬條。系統(tǒng)存在延遲要求首字響應(yīng)時間目標(biāo)在1.5秒以內(nèi)。基于這四條全量傳入方案直接放棄。判斷理由10輪對話如果每輪約600Token累計6000Token能扛住但知識庫插入后總量完全失控如果每輪把1萬條文檔都塞進去窗口立刻爆掉。所以主要策略鎖定為“滑動窗口 長期摘要 按需RAG”三段式組合。具體設(shè)計為短期上下文對話輪次層面用滑動窗口保留最近8輪原始記錄長期記憶通過每6輪的摘要生成摘要將固化進一個“長期記憶片段”并跟每輪對話一起拼進Prompt知識信息通過RAG檢索Top4塊加入上下文。這樣一個Prompt的結(jié)構(gòu)是核心指令 長期摘要 最近8輪對話 RAG檢索片段 用戶當(dāng)前輸入。4.2 Token預(yù)算的計算過程拿到方案后我們細算預(yù)算。以32K窗口為基準(zhǔn)預(yù)留輸出約4K Token正常對話模型輸出不會太長但保險起見留足余量。剩下28K給輸入。各組成部分的預(yù)算分配核心指令系統(tǒng)提示詞控制在800Token以內(nèi)。這里就要精簡語言把指令寫清楚、少廢話。長期摘要壓縮到1000Token以內(nèi)。最近8輪對話每輪平均約600Token總約4800Token。RAG檢索片段每個塊約300Token取4塊共1200Token。當(dāng)前用戶輸入通常幾百Token??傤A(yù)算約800100048001200幾百算下來大約8K左右距離28K上限還有富余。這個富余很關(guān)鍵一是給極端情況留緩沖比如用戶某條消息特別長二是給Prompt里可能插入的中間結(jié)果例如工具調(diào)用返回預(yù)留空間。于是我在代碼中實現(xiàn)了一個預(yù)算保護器當(dāng)單次組裝上下文時按配額依次填充哪一部分超了優(yōu)先壓縮摘要再往前的策略是截斷更舊的非關(guān)鍵對話而絕不動核心指令和用戶當(dāng)前輸入。理由很簡單核心指令是模型行為的定盤星用戶當(dāng)前輸入是必須要回答的目標(biāo)為了防止預(yù)算溢出而犧牲這兩項就是本末倒置。4.3 核心代碼實現(xiàn)示意下面給出簡化但完整的核心流程代碼展示怎么把幾個模塊串成一個上下文管理器。from dataclasses import dataclass from typing import List, Dict dataclass class Message: role: str # user or assistant content: str timestamp: float class ContextManager: def __init__(self, max_prompt_tokens28000, max_turns8): self.system_prompt self.history: List[Message] [] self.long_summary self.max_prompt_tokens max_prompt_tokens self.max_turns max_turns def add_message(self, role: str, content: str): self.history.append(Message(rolerole, contentcontent)) if len(self.history) self.max_turns * 2: self._maybe_summarize() def _maybe_summarize(self): # 摘要觸發(fā)策略歷史消息過多時壓縮前1/3 old_messages self.history[:len(self.history) // 3] text \n.join([f{m.role}: {m.content} for m in old_messages]) # 實際項目中這層調(diào)用模型接口生成摘要此處省略 self.long_summary self._call_summary_model(text) self.history self.history[len(self.history) // 3:] def search_knowledge(self, query: str, top_k4): # 實際接入向量庫此處示意 return [] def build_prompt(self, user_input: str) - List[Dict]: rag_blocks self.search_knowledge(user_input) prompt [{role: system, content: self.system_prompt}] if self.long_summary: prompt.append({role: system, content: f[長期記憶摘要]\n{self.long_summary}}) for msg in self.history: prompt.append({role: msg.role, content: msg.content}) for block in rag_blocks: prompt.append({role: system, content: f[知識庫參考]\n{block}}) prompt.append({role: user, content: user_input}) # 這里應(yīng)加入Token裁剪檢查超出預(yù)算時按優(yōu)先級降低歷史內(nèi)容 return prompt這段代碼的核心意圖是先保證結(jié)構(gòu)清晰再通過預(yù)算保護器控制Token。還有一個細節(jié)大家容易忽略摘要納入Prompt時我用的角色是system而不是user或assistant。因為摘要本質(zhì)上是系統(tǒng)附加給模型的記憶參考不屬于對話的一方發(fā)言。如果你把它當(dāng)成assistant歷史會讓模型在風(fēng)格遷移時誤以為這是自己的既有回答繼而產(chǎn)生語氣混亂。4.4 切換話題時如何識別上下文邊界多輪客服最常見的問題之一就是用戶突然變換話題。前兩輪還在聊退款第三輪直接問“那物流到哪了”。如果你的上下文管理把兩段話題混在一起模型很容易把退款相關(guān)的規(guī)則錯誤遷移到物流查詢中。我的處理方式是加一層話題邊界檢測。當(dāng)一個新消息到來先用一個輕量分類器來判斷它與最近話題是否一致。如果話題發(fā)生了明顯偏移歷史對話記錄會重新分組舊的隔段就會被收進摘要新段獨立開啟。這個思路等價于給對話打上了場景標(biāo)簽上下文不再是一串無序的歷史而是按主題組織起來的幾個記憶片段。實現(xiàn)起來輕量分類可以用文本嵌入向量的余弦相似度來判定當(dāng)相似度低于閾值時觸發(fā)話題切換。還有一點很實際有的用戶問題是復(fù)合型的一句話同時涉及兩個主題。比如“退款政策里的紅沖流程和物流顯示已簽收但沒收到貨怎么處理”。這時候話題邊界檢測保持連續(xù)但在RAG檢索時就要檢索跨兩個板塊的內(nèi)容。所以我設(shè)計檢索時允許拆分子查詢分別去不同知識板塊撈片段再合并進上下文。這也是上下文管理中容易被低估的難點單一Prompt中的多話題并存時你需要結(jié)構(gòu)化的拼接保障而不是簡單的一句話前綴。5. 上下文壓縮如何在不丟關(guān)鍵信息的前提下瘦身某些場景下既不能粗暴裁剪歷史也不能接受摘要帶來的細節(jié)損失。這時候就得靠更有針對性的壓縮手段。我把實踐中常用的幾種壓縮策略整理出來供大家在具體項目里按需組合。5.1 結(jié)構(gòu)化提取把非結(jié)構(gòu)化文本變成結(jié)構(gòu)化字段很多對話歷史中有大量的問候語、語氣詞、重復(fù)確認(rèn)車轱轆話來回說。上下文壓縮的首選思路不是摘要而是結(jié)構(gòu)化提取。比如下面一組原始對話用戶“你好我想問一下我之前買的那款耳機還在保修期內(nèi)嗎大概是一月份買的。不好意思我記不太清了你幫我看看嘛……”客服“您好請?zhí)峁┮幌履挠唵翁栁襾韼湍悴樵兣?。”用戶“訂單號是A123456麻煩你了?!比绻孔鳛樯舷挛拇嫦聛頃牟簧賂oken。但真正關(guān)鍵的信息只需要提取為用戶購買時間1月產(chǎn)品耳機訂單號A123456待辦保修期查詢。這樣一段結(jié)構(gòu)化信息不僅壓縮了體積而且比原始對話更容易被模型檢索和引用。實戰(zhàn)中我特別推薦用少量樣本微調(diào)一個信息提取器定期從對話中抽取出實體、意圖、所需參數(shù)、情緒狀態(tài)。上游的上下文管理器只保存結(jié)構(gòu)化結(jié)果原始對話則歸檔到外部存儲。這樣一來上下文瘦身的同時還順便給下游的報表分析、用戶行為洞察打了基礎(chǔ)。5.2 漸進式摘要不是一次壓縮到位而是多輪迭代漸進式摘要是一種讓摘要按梯度更新的策略。它不是每6輪一次性把所有歷史拿去摘要而是每次只對新增的幾輪對話做摘要再與之前的摘要做合并。這個思路有個很實在的好處你不需要每次重新完整讀一遍全部歷史摘要調(diào)用的Token開銷大幅下降。我踩過一次坑初期設(shè)計是每6輪重新生成一次全部歷史的摘要。結(jié)果對話到50輪的時候哪怕歷史片段已經(jīng)有大量總結(jié)模型總結(jié)時仍然要把所有文本全部傳一遍單次摘要調(diào)用就吃掉了近20K Token時間開銷也好幾秒。改為漸進式摘要之后每次摘要只需要讀取“舊摘要 新幾輪對話”總量一般不超過3K Token速度快了十倍成本也降了一個量級。漸進式摘要還有個副作用需要注意多次迭代壓縮后早期細節(jié)可能會逐漸被“洗掉”。比如用戶在第3輪提到“不要香菜”經(jīng)過第9輪、第15輪兩次摘要迭代之后這條信息可能就慢慢消失。對于必須長期保留的約束類信息我會在摘要Prompt里強制加入一條規(guī)則“當(dāng)對話中出現(xiàn)否定性約束如不要、必須是、禁止必須原樣保留在摘要中?!边@個細小的規(guī)則幫我解決了不少因摘要丟失約束導(dǎo)致的Bug。5.3 硬截斷與軟截斷什么時候用哪一種截斷是最后手段但也是不得不掌握的手段。硬截斷就是從某個位置直接切斷上下文比如只看最近N輪之前的一律不要。軟截斷則是保留所有歷史但給每一條歷史分配一個權(quán)重超窗時優(yōu)先丟棄低權(quán)重的部分。硬截斷實現(xiàn)簡單但丟失信息的風(fēng)險是“黑天鵝式”的——你永遠不知道被截掉的哪句話里藏著關(guān)鍵事實。軟截斷相對聰明但計算權(quán)重需要額外開銷。我的建議是把軟截斷的權(quán)重分?jǐn)?shù)建立在幾個維度上消息的角色權(quán)重系統(tǒng)指令最高用戶輸入必須保留assistant回復(fù)中帶有工具調(diào)用結(jié)果的次之純閑聊墊底。語義關(guān)聯(lián)度與當(dāng)前Query的向量相似度越高越保留。時效性時間越近越保留。信息類型含數(shù)字、訂單號、時間、金額、協(xié)議條款的高保留優(yōu)先級。在實際產(chǎn)品里我一般用加權(quán)綜合分對歷史消息排序把分?jǐn)?shù)低的先淘汰。這個方案比“一刀切最近N輪”溫和得多實測下來多輪問答的準(zhǔn)確率能提升8到10個百分點。6. 實戰(zhàn)排查上下文相關(guān)問題的高頻故障與解決實錄上下文管理上線后真正的挑戰(zhàn)才開始。我把實際項目里遇到過的典型故障和排查思路整理成一個速查表這些都來自一線的血淚經(jīng)驗不是文檔里能查到的。6.1 模型“突然失憶”不記得之前說過的關(guān)鍵信息這是最常見的故障。用戶對Bot說“我剛才不是說了我的預(yù)算不能超過3000嗎你怎么還推薦5000的”模型不是故意犯錯而是它的上下文里壓根沒有“預(yù)算3000”這條消息了。排查時不要只盯著Prompt看先檢查上下文組裝鏈路。我遇到過一次問題出在滑動窗口的窗口長度是根據(jù)“輪數(shù)”而不是“Token數(shù)”設(shè)定的。用戶每輪中有些話特別長比如發(fā)了大段需求文檔導(dǎo)致8輪的Token總量遠超預(yù)期后半段被預(yù)算保護器裁掉了關(guān)鍵約束信息恰好被裁掉。修復(fù)方式改為按Token數(shù)滑動窗口并且在裁剪時保留“約束類信息抽取結(jié)果”把這個結(jié)果常駐在長期摘要里。我后來還養(yǎng)成了一個經(jīng)驗每輪對話在進入上下文管理器之前先走一個“關(guān)鍵信息檢測器”把含金額、否定詞、時間節(jié)點的句子抽出單獨存放。這樣就算窗口再擠關(guān)鍵約束也不會丟。6.2 回答含混不清上下文里有多個互相矛盾的信息有時候模型回答得含混不是因為信息不足而是因為上下文中存在互相沖突的信息。比如用戶在一開始的對話里說“我要黑色版本的”后來又說“算了換白色吧”。如果兩輪原話都保存在上下文中模型就會看到兩處矛盾的顏色指令最終可能給出“黑色和白色都可以”的模糊回答。這個問題本質(zhì)上是上下文管理沒有做“信息版本控制”。我在實際項目中加了一套狀態(tài)追蹤模塊專門維護用戶的“當(dāng)前偏好快照”。每當(dāng)新消息出現(xiàn)某個關(guān)鍵屬性的更新比如顏色從黑色到白色就去覆蓋快照中的舊值。上下文里始終只保留最新判定舊的矛盾信息則歸入?yún)R總歷史但不會在Prompt里高亮出現(xiàn)。這個改動對客服場景的效果立竿見影用戶明顯感覺到Bot“更懂我了”。6.3 Token預(yù)算超限明明沒聊幾句就報超長錯誤有一種情況讓人極其困惑總共沒聊幾句也沒傳文檔怎么Token就超了排查后你會發(fā)現(xiàn)問題可能出在角落里。比如系統(tǒng)中某個工具調(diào)用的返回結(jié)果被完整存到了歷史里某次調(diào)API返回了8000Token的原始JSON這些信息被當(dāng)成assistant回復(fù)記錄塞入上下文幾輪下來窗口就被幾段冗長JSON撐爆了。解決思路有幾種。首先是對工具調(diào)用返回結(jié)果做截斷處理只保留核心字段比如“訂單狀態(tài)、預(yù)計到達時間”剔除完整的嵌套對象。其次可以考慮把工具返回結(jié)果不放入上下文歷史而是獨立放進一個“工具觀察區(qū)”只保留最近一條調(diào)用的結(jié)果。這個設(shè)計更貼近智能體的設(shè)計模式模型看得到當(dāng)前觀察但不需要把上一輪調(diào)用的垃圾信息永遠背著。6.4 角色錯亂模型開始把摘要當(dāng)作自己的發(fā)言如果你在Prompt里細心地把摘要放在system角色模型通常不會錯亂。但如果你圖省事把摘要直接拼接成歷史消息放在assistant角色模型就會把它當(dāng)成自己曾經(jīng)的發(fā)言進而影響后續(xù)回答的立場和語氣。這類問題不太會報錯但回答質(zhì)量會詭異下降。排查這類問題的方法是打開模型調(diào)試面板檢查每一條消息的role與content是否匹配。我見過有人用流式拼接的方式動態(tài)組裝歷史結(jié)果把system消息的字符插到了user內(nèi)容里導(dǎo)致模型把指令當(dāng)用戶問題處理。這個問題在代碼里看起來只是一個小Bug但對實際回答影響極大排查難度堪比大海撈針。6.5 超長文檔檢索質(zhì)量不佳檢索到了但答非所問另一種高頻故障是文檔確實被檢索進來了但模型給出的答案依然與文檔無關(guān)。這個情況多半是RAG的檢索階段出了問題而不是上下文管理的問題但它與上下文管理密切相關(guān)因為處理不當(dāng)會把噪聲塊帶進來。我總結(jié)過三個排查維度切塊大小是否合適。切得太小比如100字一塊語義不完整切得太大比如2000字一塊可能混入多個主題。一般經(jīng)驗是256到512字符一塊具體根據(jù)內(nèi)容類型調(diào)節(jié)。檢索TopK的數(shù)量是否合適。太少會漏信息太多會引入噪聲。我一般從Top3開始調(diào)用評測集判斷最優(yōu)值。塊與塊之間是否重復(fù)。如果知識庫本身有大量重復(fù)內(nèi)容檢索會返回多個幾乎相同的片段白白占用上下文窗口。這時候要做一遍去重。還有一個容易被忽視的點RAG檢索返回的文本格式。如果不做格式化處理原始PDF中的表格、列表被切塊后可能變成亂序的字符串模型讀起來一頭霧水。我的建議是檢索后做一個輕量級的“上下文重構(gòu)”把離散的塊按邏輯順序重新排列比如規(guī)則類塊在前、示例類塊在后并且給每個塊加上清晰的小標(biāo)題模型理解起來會輕松很多。7. 上下文管理的擴展方向短期緩存、長期記憶與Agent記憶分層前六節(jié)聊的還都是“在一輪請求之內(nèi)把上下文組裝好”的問題。但真正復(fù)雜的AI應(yīng)用尤其是Agent類的應(yīng)用上下文管理遠遠超出了單次請求的范疇。它更像人的記憶系統(tǒng)需要有工作記憶、短期記憶、長期記憶的分層設(shè)計。7.1 會話級緩存減少重復(fù)計算的工程優(yōu)化同一位用戶連續(xù)多輪對話時如果每次請求都把同一個系統(tǒng)指令、同一份長期摘要重新編碼一遍不僅浪費Token還浪費模型服務(wù)的計算資源。工業(yè)級的做法是引入Prompt緩存。某些模型服務(wù)支持在API層面對相同前綴做KV-Cache緩存也就是說如果Prompt前一段完全一致第二次調(diào)用就可以復(fù)用計算過的KV狀態(tài)直接跳過這幾步計算。這帶來的好處非常顯著首字延遲降低Token成本下降。但代價是需要你在設(shè)計Prompt時把不變的靜態(tài)部分系統(tǒng)指令、固定摘要、指令模板統(tǒng)一排在最前面把頻繁變化的內(nèi)容本輪輸入、檢索結(jié)果排到后面。這個順序設(shè)計直接決定了你能不能吃到緩存的紅利。我到后面已經(jīng)把這當(dāng)成一條硬性工程規(guī)范Prompt的靜態(tài)前綴和動態(tài)后綴開發(fā)時就必須強制分離不能隨手拼接。這個節(jié)省在調(diào)用量大的生產(chǎn)環(huán)境里一個月能省出一筆不小的開銷。7.2 Agent記憶分層從“對話記憶”升級為“實體記憶”到了Agent場景上下文管理不能只停留在消息層面需要把信息抽象成實體關(guān)系。比如一個生活助手類的Agent它需要記住的不只是你昨天說了什么還要建立“家庭成員父母、孩子”、“常用偏好口味、時間”和“當(dāng)前待辦”的實體模型。這就催生了結(jié)構(gòu)化長期記憶的概念。實現(xiàn)上的做法是從對話中持續(xù)抽取實體和關(guān)系更新到一個持久化的知識圖譜或JSON結(jié)構(gòu)里。對話時先從這些結(jié)構(gòu)中查詢與當(dāng)前意圖相關(guān)的部分塞進上下文跟RAG合在一起。這套方案比單純的“對話摘要”更接近人的記憶模式也讓Agent具備更強的規(guī)劃與執(zhí)行能力。但實體記憶也有復(fù)雜度。比如用戶說“幫我訂周五晚上七點的餐廳”這里的“周五”“七點”是一次性的命令參數(shù)不應(yīng)該寫入長期記憶但如果用戶說“我每周五晚上都要加班”這就值得進長期記憶了。區(qū)分瞬時指令與持久偏好需要依賴語義判斷規(guī)則也需要依賴模型的推理能力。這個邊界目前業(yè)界仍在持續(xù)打磨不是能一錘定音的方案。7.3 多智能體協(xié)作下的上下文傳遞如果你在做一個多智能體系統(tǒng)比如一個Agent負責(zé)檢索信息另一個Agent負責(zé)生成回復(fù)上下文管理又增加了一層挑戰(zhàn)Agent之間的消息上下文如何高效傳遞全量傳遞效率極低而且會混入不同Agent的關(guān)注噪聲。我目前采用的模式是“消息摘要 指令式傳遞”主控Agent負責(zé)全局上下文管理它會把壓縮后的任務(wù)描述、中間結(jié)果、約束條件分別包裝成結(jié)構(gòu)化消息傳給各個子Agent。每個子Agent不必知道全量歷史只需拿到完成自己子任務(wù)所需的那一段上下文。這個模式跟人類組織的協(xié)作模式很像項目負責(zé)人不會把整份合同細節(jié)丟給每個人而是把跟每個人相關(guān)的部分拆好發(fā)下去。實現(xiàn)中需要嚴(yán)格保證主控Agent的上下文不能過載。所有子Agent返回的結(jié)果必須通過一個結(jié)果清洗模塊做裁剪和標(biāo)準(zhǔn)化再進入主控上下文。一開始做這個設(shè)計時我也圖省事讓所有子Agent完整返回日志結(jié)果主控上下文很快就塞滿了冗余調(diào)試信息。后來強制規(guī)定每個Agent的返回字段只有兩條——“完成狀態(tài)”和“結(jié)果摘要”上下文體積一下子小了一半以上。7.4 上下文管理與評測用一套測試集持續(xù)度量最后分享一個容易被忽略但極其重要的經(jīng)驗上下文管理要可評測。很多團隊改完上下文管理方案的邏輯只靠幾個手工樣例驗證一下就覺得OK了這是大忌。我做項目時一定會在早期構(gòu)建一個上下文評測集包含至少上百個模擬對話場景覆蓋多輪連貫、話題切換、長距離依賴、矛盾信息更新、長文檔檢索等典型情況。每次改動上下文管理邏輯后都跑一遍評測集看回答準(zhǔn)確率、上下文命中率、Token平均消耗等指標(biāo)變化。這個習(xí)慣幫我避開了不少“看似優(yōu)化實則退化”的改動。很多時候你以為自己加了更復(fù)雜的上下文邏輯系統(tǒng)會更好評測數(shù)據(jù)卻告訴你它負優(yōu)化了。沒有數(shù)據(jù)幫你兜底就只能靠玄學(xué)調(diào)參。8. 關(guān)于上下文管理我個人還在踐行的幾條心得上下文管理做得越久越覺得它不只是技術(shù)層面的問題而是對整個AI應(yīng)用架構(gòu)觀的重塑。每次看到有人興致勃勃地說“我們模型窗口很大不用做上下文管理”我都覺得他們大概率還沒經(jīng)歷過生產(chǎn)環(huán)境的毒打。窗口大只能降低管理頻率不能抵消管理的必要性——因為Token成本、響應(yīng)延遲、注意力稀釋、信息版本沖突這些問題不會因為窗口變大而消失。我個人把上下文管理的能力歸納為四個層面能控制控制Token在預(yù)算內(nèi)、能篩選篩選值得進入窗口的信息、能演化能隨著對話動態(tài)更新記憶、能度量能持續(xù)評估方案效果。這四個層面越往后越困難也越是拉開普通應(yīng)用與頂尖應(yīng)用差距的地方。如果你正在從零搭建一個基于大模型的應(yīng)用我建議你從最樸素的滑動窗口開始不要一上來就上高深的RAG和多級記憶。把基礎(chǔ)的對話流跑通用真實數(shù)據(jù)看看出現(xiàn)什么樣的上下文問題再逐步引入摘要、檢索和結(jié)構(gòu)化管理。上下文管理是演進式的工程不是一步到位的方案。最后再分享一個小技巧也是我每次做架構(gòu)評審時都會提的在Prompt里用角色名稱暗示上下文中的信息性質(zhì)。比如用“知識庫參考”而不是直接丟進System或User里用“歷史摘要”而不是跟在User問題后面。這不僅僅是為了模型理解更是為了可觀測和可調(diào)試。當(dāng)你需要排查問題的時候清晰的角色結(jié)構(gòu)能讓你一眼定位是哪一段上下文出了狀況。這個習(xí)慣替我省下過數(shù)不清的排查時間。