踐指南)
1. 從“context-mode”這個(gè)詞說起它到底指什么第一次看到“context-mode”這個(gè)標(biāo)題很多人會(huì)愣一下——它不像“XX管理系統(tǒng)”“XX爬蟲框架”那樣一眼能看出用途。我最初接觸這個(gè)詞是在做對話系統(tǒng)上下文管理的時(shí)候當(dāng)時(shí)團(tuán)隊(duì)里有人提出“給會(huì)話加一個(gè) context-mode 開關(guān)”我才意識到這其實(shí)是一個(gè)描述“上下文處理策略”的抽象概念而不是某個(gè)具體的庫或工具。簡單來說context-mode 指的是一個(gè)系統(tǒng)在處理“上下文”時(shí)采用的模式或策略。這里的“上下文”可以是一段對話的歷史消息、一次請求攜帶的環(huán)境參數(shù)、一個(gè)函數(shù)執(zhí)行時(shí)的作用域鏈也可以是一個(gè) AI 應(yīng)用在生成回復(fù)前需要參考的全部背景信息。而“mode”則意味著面對同樣的上下文系統(tǒng)可以選擇不同的處理方式——全量保留、滑動(dòng)窗口截?cái)唷⒄獕嚎s、按優(yōu)先級篩選、分層緩存等等。它解決的問題非常實(shí)際上下文不是越多越好也不是越少越好而是要在“信息完整性”和“資源消耗”之間找到平衡點(diǎn)。比如一個(gè)客服機(jī)器人如果把用戶過去三個(gè)月的所有對話都塞給模型token 成本會(huì)爆炸響應(yīng)也會(huì)變慢但如果只保留最后一句話機(jī)器人又會(huì)丟失用戶之前說過的訂單號、產(chǎn)品型號等關(guān)鍵信息。context-mode 就是用來定義“到底保留哪些、丟棄哪些、怎么組織”的那套規(guī)則。這篇文章適合三類人看一是正在做對話系統(tǒng)、Agent 應(yīng)用、RAG 管道的開發(fā)者你們一定被上下文長度折磨過二是做后端服務(wù)、需要管理請求級上下文比如鏈路追蹤、多租戶隔離的工程師三是對 AI 應(yīng)用架構(gòu)感興趣、想理解“為什么同樣的大模型不同產(chǎn)品體驗(yàn)差那么多”的技術(shù)愛好者。我會(huì)從概念拆解講到落地實(shí)現(xiàn)把我在實(shí)際項(xiàng)目里踩過的坑和總結(jié)出的模式都攤開來說。需要先說明一點(diǎn)context-mode 目前沒有一個(gè)官方標(biāo)準(zhǔn)不同團(tuán)隊(duì)對它的定義有差異。我下面講的是基于常見工程實(shí)踐歸納出來的一套理解框架你可以把它當(dāng)作設(shè)計(jì)參考而不是照搬的規(guī)范。2. 上下文為什么需要“模式”三種典型困境2.1 困境一窗口有限但信息無限增長大模型的上下文窗口是有硬上限的。早期是 4K token后來到 32K、128K現(xiàn)在有些模型號稱支持百萬級。但窗口變大不等于可以無腦塞。我做過一個(gè)實(shí)測把一個(gè) 8 萬 token 的對話歷史直接喂給模型響應(yīng)延遲從 1.2 秒漲到了 7 秒以上成本翻了十幾倍而回答質(zhì)量并沒有線性提升——因?yàn)榇罅繗v史信息是冗余的反而稀釋了關(guān)鍵內(nèi)容的注意力權(quán)重。這就引出一個(gè)核心矛盾業(yè)務(wù)上希望“記住一切”工程上必須“有所取舍”。context-mode 的第一個(gè)作用就是定義取舍規(guī)則。常見的策略包括滑動(dòng)窗口只保留最近 N 輪、摘要壓縮把舊對話總結(jié)成一段話、關(guān)鍵信息抽取只保留實(shí)體、意圖、槽位、分層保留近期全量 遠(yuǎn)期摘要等。每種策略對應(yīng)一種 mode適用場景不同。2.2 困境二不同任務(wù)對上下文的敏感度差異巨大同一個(gè)系統(tǒng)里不同請求對上下文的需求可能完全相反。我拿一個(gè)實(shí)際項(xiàng)目舉例那是一個(gè)法律咨詢助手用戶會(huì)先描述案情然后連續(xù)追問細(xì)節(jié)。對于“幫我總結(jié)一下剛才說的案情”這類請求需要完整的歷史但對于“勞動(dòng)合同法第幾條規(guī)定了試用期”這種事實(shí)性問題歷史上下文幾乎沒用反而可能干擾模型。如果系統(tǒng)只有一種上下文處理方式就會(huì)出現(xiàn)兩難全量保留則事實(shí)性問題變慢變貴激進(jìn)截?cái)鄤t總結(jié)類請求丟信息。context-mode 的價(jià)值就在于讓“上下文策略”變成可切換的配置根據(jù)請求類型、用戶等級、會(huì)話階段動(dòng)態(tài)選擇。這就像開車時(shí)的駕駛模式——市區(qū)用經(jīng)濟(jì)模式高速用運(yùn)動(dòng)模式?jīng)]有一種模式通吃所有路況。2.3 困境三上下文里混著“噪聲”和“信號”原始上下文往往很臟。用戶可能發(fā)了一堆“嗯”“好的”“稍等”中間夾雜著真正重要的需求描述系統(tǒng)日志里混著無關(guān)的調(diào)試信息多輪工具調(diào)用產(chǎn)生的中間結(jié)果大部分是一次性的。如果不加處理直接使用模型會(huì)被噪聲帶偏。我遇到過最典型的情況用戶在和機(jī)器人聊了二十輪之后突然問“那這個(gè)多少錢”。如果上下文里最近幾輪都在聊別的產(chǎn)品模型很可能答錯(cuò)價(jià)格。這時(shí)候需要的 context-mode 是**“按相關(guān)性重排 時(shí)間衰減”**——不是簡單取最近幾條而是根據(jù)當(dāng)前 query 去檢索歷史中語義相關(guān)的片段再結(jié)合時(shí)間新鮮度加權(quán)。這已經(jīng)接近 RAG 的思路了但作用域是會(huì)話內(nèi)部而非外部知識庫。3. 拆解 context-mode 的核心維度一張?jiān)O(shè)計(jì)決策表要設(shè)計(jì)一套 context-mode 機(jī)制我習(xí)慣從四個(gè)維度去思考。下面這張表是我在多個(gè)項(xiàng)目里反復(fù)用到的決策框架你可以對照自己的場景填一遍基本就能確定需要哪些 mode。維度關(guān)鍵問題常見選項(xiàng)適用場景保留策略保留哪些上下文全量 / 滑動(dòng)窗口 / 摘要 / 檢索式 / 分層全量適合短會(huì)話檢索式適合長會(huì)話組織方式上下文怎么排列時(shí)間正序 / 時(shí)間倒序 / 相關(guān)性排序 / 結(jié)構(gòu)化分區(qū)倒序利于近期優(yōu)先分區(qū)利于多類型信息壓縮手段如何減少 token截?cái)?/ 摘要 / 實(shí)體抽取 / 向量化摘要保語義抽取保精確切換觸發(fā)何時(shí)換 mode輪次閾值 / token 閾值 / 意圖識別 / 手動(dòng)指定意圖識別最智能但成本高3.1 保留策略從“全量”到“檢索式”的連續(xù)譜全量保留是最簡單的 mode適合會(huì)話輪次少比如 5 輪以內(nèi)、上下文總量可控的場景。它的優(yōu)點(diǎn)是零信息損失缺點(diǎn)是成本隨輪次線性增長。我一般把它作為默認(rèn) mode但設(shè)置一個(gè) token 閾值超過就自動(dòng)降級。滑動(dòng)窗口是保留最近 N 輪或 N 個(gè) token。這里有個(gè)容易踩的坑按輪次截?cái)嗪桶?token 截?cái)嘈Ч顒e很大。用戶一輪可能只發(fā)“好的”兩個(gè)字也可能發(fā)一大段案情描述。按輪次截?cái)鄷?huì)導(dǎo)致 token 數(shù)劇烈波動(dòng)按 token 截?cái)喔€(wěn)定但可能把一輪對話從中間切斷。我的做法是按 token 截?cái)嗟WC不切斷單條消息從最舊的消息開始整條丟棄。摘要壓縮是把舊上下文用模型總結(jié)成一段話。這里的關(guān)鍵是摘要的粒度和更新頻率。我試過每 5 輪做一次摘要結(jié)果發(fā)現(xiàn)摘要本身也要消耗 token 和時(shí)間而且多次摘要會(huì)累積誤差。后來改成“當(dāng)上下文超過閾值時(shí)把最舊的一半消息摘要成一段”效果更穩(wěn)。摘要 mode 適合長會(huì)話但要注意摘要可能丟失細(xì)節(jié)所以關(guān)鍵實(shí)體訂單號、金額、日期最好單獨(dú)抽取保留。檢索式是進(jìn)階方案把歷史消息向量化存入臨時(shí)索引每次請求時(shí)用當(dāng)前 query 檢索 top-k 相關(guān)片段。這個(gè) mode 最省 token但引入了檢索延遲和召回率問題。我在一個(gè)知識密集型客服項(xiàng)目里用過配合“最近 3 輪全量 檢索 top-5 歷史片段”的混合策略效果比純滑動(dòng)窗口好很多。3.2 組織方式順序會(huì)影響模型注意力同樣一組上下文排列順序不同模型的表現(xiàn)可能差很多。我做過對比實(shí)驗(yàn)把關(guān)鍵信息放在上下文開頭、中間、結(jié)尾三個(gè)位置模型對結(jié)尾位置的召回率最高開頭次之中間最差——這就是所謂的“l(fā)ost in the middle”現(xiàn)象。所以我的經(jīng)驗(yàn)是把最重要的信息放在上下文的末尾靠近用戶當(dāng)前 query次要信息放開頭最不重要的放中間。如果是多類型信息系統(tǒng)指令、歷史對話、檢索結(jié)果、當(dāng)前問題用明確的分隔符或結(jié)構(gòu)化標(biāo)簽分區(qū)比如用 XML 標(biāo)簽包裹不同區(qū)塊模型對結(jié)構(gòu)的敏感度比純文本高。3.3 壓縮手段摘要和抽取要配合使用摘要擅長保留“語義走向”比如“用戶之前咨詢過退貨政策表示對運(yùn)費(fèi)不滿”。抽取擅長保留“精確事實(shí)”比如訂單號ORD-2024-8871、金額¥299。兩者不是替代關(guān)系而是互補(bǔ)。我的做法是摘要用于對話脈絡(luò)抽取用于關(guān)鍵實(shí)體兩者拼在一起作為壓縮后的上下文。這里有個(gè)實(shí)操細(xì)節(jié)抽取實(shí)體時(shí)不要只抽一次就完事因?yàn)閷?shí)體可能在后文被修正。比如用戶先說“我要退 A 商品”后來說“算了改成 B 商品”。如果只保留第一次抽取的 A就會(huì)出錯(cuò)。所以抽取邏輯要支持覆蓋更新以最后一次出現(xiàn)的值為準(zhǔn)。3.4 切換觸發(fā)什么時(shí)候該換 mode手動(dòng)指定 mode 最可控但用戶體驗(yàn)差。自動(dòng)切換需要觸發(fā)條件常見的有輪次閾值超過 10 輪切換到摘要 modetoken 閾值上下文超過模型窗口的 60% 時(shí)觸發(fā)壓縮意圖識別檢測到“總結(jié)”“回顧”類意圖時(shí)切回全量檢測到事實(shí)性問題時(shí)切到精簡 mode成本預(yù)算按用戶等級或請求優(yōu)先級動(dòng)態(tài)調(diào)整我傾向于組合觸發(fā)token 閾值作為兜底防止超限意圖識別作為優(yōu)化提升體驗(yàn)。純靠意圖識別風(fēng)險(xiǎn)大因?yàn)橐鈭D分類本身可能出錯(cuò)一旦把需要全量的請求誤判為精簡用戶會(huì)明顯感到“機(jī)器人失憶了”。4. 落地實(shí)現(xiàn)一個(gè)可運(yùn)行的 context-mode 管理器光講概念不夠我把自己項(xiàng)目里用的一套簡化實(shí)現(xiàn)拆出來講。這套代碼用 Python 寫核心是一個(gè)ContextManager類支持多種 mode 切換。你可以直接拿去改。4.1 數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)消息、會(huì)話、模式配置先定義基礎(chǔ)結(jié)構(gòu)。每條消息包含角色、內(nèi)容、時(shí)間戳、token 數(shù)、以及一個(gè)可選的pinned標(biāo)記用于標(biāo)記不可丟棄的關(guān)鍵消息。from dataclasses import dataclass, field from typing import List, Optional import time dataclass class Message: role: str # user / assistant / system / tool content: str timestamp: float field(default_factorytime.time) tokens: int 0 pinned: bool False # 關(guān)鍵消息壓縮時(shí)優(yōu)先保留 dataclass class ContextConfig: mode: str sliding # full / sliding / summary / retrieval / hybrid max_tokens: int 4000 # 上下文 token 上限 window_size: int 10 # 滑動(dòng)窗口保留輪次 summary_threshold: int 3000 # 觸發(fā)摘要的 token 閾值 retrieval_top_k: int 5 # 檢索式保留片段數(shù)這里pinned字段很關(guān)鍵。我在實(shí)際項(xiàng)目里發(fā)現(xiàn)有些消息絕對不能丟比如用戶明確說“記住我的訂單號是 XXX”或者系統(tǒng)注入的租戶配置。給這些消息打上 pinned 標(biāo)記任何壓縮策略都要優(yōu)先保留它們。4.2 滑動(dòng)窗口 mode 的實(shí)現(xiàn)與邊界處理滑動(dòng)窗口看似簡單但邊界處理有講究。我的實(shí)現(xiàn)邏輯是從最新消息往前累加 token直到接近上限然后整條保留不切斷消息。def sliding_window(messages: List[Message], max_tokens: int) - List[Message]: selected [] total 0 # 先保留所有 pinned 消息 for msg in messages: if msg.pinned: selected.append(msg) total msg.tokens # 從最新往前加非 pinned 消息 for msg in reversed(messages): if msg.pinned: continue if total msg.tokens max_tokens: break selected.append(msg) total msg.tokens # 恢復(fù)時(shí)間正序 selected.sort(keylambda m: m.timestamp) return selected注意這里有個(gè)細(xì)節(jié)pinned 消息可能很多如果 pinned 本身就超限了怎么辦我的做法是pinned 優(yōu)先超限時(shí)對 pinned 內(nèi)部再做一次摘要而不是直接丟棄。因?yàn)?pinned 之所以被標(biāo)記就是因?yàn)樗匾苯觼G會(huì)出大問題。4.3 摘要 mode什么時(shí)候摘要、摘要什么、怎么拼接摘要 mode 的觸發(fā)時(shí)機(jī)我前面提過是 token 超過閾值。但摘要本身也有講究。我的實(shí)現(xiàn)是把最舊的一半非 pinned 消息交給模型摘要摘要結(jié)果作為一條 system 消息插入上下文開頭。def summarize_old_messages(messages: List[Message], llm_client) - str: # 取最舊的一半非 pinned 消息 non_pinned [m for m in messages if not m.pinned] half len(non_pinned) // 2 to_summarize non_pinned[:half] if not to_summarize: return text \n.join(f{m.role}: {m.content} for m in to_summarize) prompt f請用簡潔的中文總結(jié)以下對話的關(guān)鍵信息保留實(shí)體、數(shù)字和結(jié)論\n{text} return llm_client.generate(prompt)這里有個(gè)坑摘要 prompt 必須明確要求保留實(shí)體和數(shù)字否則模型會(huì)寫成“用戶咨詢了退貨相關(guān)問題”這種沒用的概括。我試過不加這句結(jié)果摘要里訂單號全丟了后面用戶問“我那個(gè)訂單怎么樣了”機(jī)器人完全接不上。拼接時(shí)摘要消息放在最前面然后是 pinned 消息最后是最近的全量消息。這樣模型先看到背景摘要再看到關(guān)鍵事實(shí)最后看到當(dāng)前對話層次清晰。4.4 檢索式 mode會(huì)話內(nèi)向量檢索的輕量方案檢索式 mode 適合超長會(huì)話。完整方案需要向量數(shù)據(jù)庫但會(huì)話內(nèi)檢索其實(shí)可以用輕量方案用 TF-IDF 或 BM25 做關(guān)鍵詞檢索不一定要上 embedding。因?yàn)闀?huì)話內(nèi)消息通常有大量詞匯重疊用戶會(huì)重復(fù)提到同一產(chǎn)品名、訂單號關(guān)鍵詞檢索的召回率已經(jīng)夠用而且零額外成本。from rank_bm25 import BM25Okapi def retrieval_mode(messages: List[Message], query: str, top_k: int) - List[Message]: corpus [m.content for m in messages] tokenized [doc.split() for doc in corpus] bm25 BM25Okapi(tokenized) scores bm25.get_scores(query.split()) # 取 top_k同時(shí)保證最近 3 輪一定在 top_indices sorted(range(len(scores)), keylambda i: scores[i], reverseTrue)[:top_k] recent list(range(max(0, len(messages)-3), len(messages))) final_indices sorted(set(top_indices) | set(recent)) return [messages[i] for i in final_indices]這個(gè)方案我在一個(gè)日均 5 萬次請求的項(xiàng)目里跑過檢索延遲在 10ms 以內(nèi)比 embedding 方案快一個(gè)數(shù)量級。當(dāng)然如果會(huì)話里語義相似但用詞不同的情況多還是得上 embedding。選型要看具體數(shù)據(jù)分布。4.5 混合 mode我的默認(rèn)推薦配置純?nèi)魏我环N mode 都有短板我最終在生產(chǎn)環(huán)境用的是混合 mode最近 3 輪全量 pinned 消息 檢索 top-5 歷史片段 超長時(shí)追加摘要。這個(gè)組合覆蓋了“近期精確”“關(guān)鍵事實(shí)”“相關(guān)歷史”“遠(yuǎn)期脈絡(luò)”四個(gè)層次。配置上我把max_tokens設(shè)為模型窗口的 50%留一半給模型輸出和系統(tǒng)指令。這個(gè)比例是我多次壓測后定的超過 60% 時(shí)延遲明顯上升低于 40% 時(shí)信息不夠用。當(dāng)然不同模型不一樣你需要自己測。5. 踩坑記錄那些讓我加班到凌晨的上下文問題5.1 坑一摘要的“滾雪球”誤差早期我做摘要 mode 時(shí)是每次超限就把全部歷史摘要一次然后新消息繼續(xù)追加下次超限再把“舊摘要新消息”一起摘要。結(jié)果跑了十幾輪之后摘要變得面目全非——最初的訂單號、金額全沒了只?!坝脩糇稍兞耸酆髥栴}”這種廢話。根因摘要是不可逆的有損壓縮多次摘要會(huì)累積誤差就像反復(fù)復(fù)印同一張紙?jiān)接≡侥:?。修?fù)方案改成“分層摘要”——原始消息保留在存儲(chǔ)層不刪摘要只用于構(gòu)建上下文。每次摘要時(shí)從原始消息重新生成而不是在舊摘要上再摘要。這樣雖然多存了數(shù)據(jù)但摘要質(zhì)量穩(wěn)定。存儲(chǔ)成本遠(yuǎn)低于模型調(diào)用成本這筆賬要算清楚。5.2 坑二pinned 消息的“僵尸化”有次用戶反饋機(jī)器人“記不住事”。排查發(fā)現(xiàn)用戶早期說過的訂單號被標(biāo)記為 pinned一直保留在上下文里。但后來用戶改了訂單新訂單號沒有被標(biāo)記 pinned結(jié)果舊訂單號一直占著位置新訂單號反而被滑動(dòng)窗口擠掉了。根因pinned 標(biāo)記是靜態(tài)的沒有隨信息更新而失效。修復(fù)方案引入“實(shí)體版本”概念。同一類實(shí)體如訂單號只保留最新版本舊版本自動(dòng)取消 pinned。實(shí)現(xiàn)上可以用一個(gè)簡單的實(shí)體槽位表每次抽取到新值就覆蓋舊值并更新對應(yīng)消息的 pinned 狀態(tài)。5.3 坑三多租戶場景下的上下文串?dāng)_在一個(gè) SaaS 項(xiàng)目里多個(gè)租戶共用同一套服務(wù)。有次出現(xiàn)嚴(yán)重 bugA 租戶的用戶看到了 B 租戶的上下文片段。排查發(fā)現(xiàn)是緩存 key 設(shè)計(jì)有問題只用了 session_id 做 key而 session_id 在不同租戶間可能重復(fù)。根因上下文隔離維度不足。修復(fù)方案緩存 key 必須包含租戶 ID 用戶 ID 會(huì)話 ID 三級。而且上下文管理器初始化時(shí)就要綁定租戶不能等到寫入時(shí)才檢查。這個(gè)坑讓我意識到context-mode 不只是“保留多少”的問題更是“隔離誰和誰”的問題。多租戶場景下隔離優(yōu)先級高于一切壓縮策略。5.4 坑四工具調(diào)用結(jié)果的“一次性污染”Agent 應(yīng)用里工具調(diào)用會(huì)產(chǎn)生大量中間結(jié)果比如搜索返回的十條網(wǎng)頁摘要、數(shù)據(jù)庫查詢的原始行。這些結(jié)果對當(dāng)前輪有用但下一輪基本是噪聲。我早期沒處理導(dǎo)致上下文迅速膨脹而且模型經(jīng)常被上一輪的搜索結(jié)果帶偏。修復(fù)方案給工具結(jié)果設(shè)置TTL生存輪數(shù)默認(rèn)只保留 1 輪除非被顯式引用。實(shí)現(xiàn)上給消息加一個(gè)expire_after字段構(gòu)建上下文時(shí)過濾掉已過期的工具消息。這個(gè)改動(dòng)讓平均上下文 token 數(shù)下降了 40%響應(yīng)質(zhì)量反而提升。6. 效果驗(yàn)證怎么知道你的 context-mode 選對了6.1 三個(gè)必須監(jiān)控的指標(biāo)選 mode 不能憑感覺要有數(shù)據(jù)。我一般盯三個(gè)指標(biāo)上下文 token 均值與 P95均值看成本P95 看極端情況。如果 P95 遠(yuǎn)超窗口說明壓縮策略沒兜住。關(guān)鍵信息召回率構(gòu)造一批測試用例每個(gè)用例有一個(gè)“必須被模型看到”的關(guān)鍵信息訂單號、日期、金額檢查最終上下文里是否包含。這個(gè)指標(biāo)直接反映壓縮是否過度。端到端響應(yīng)延遲上下文處理本身有開銷摘要、檢索要單獨(dú)計(jì)時(shí)不能混在模型推理里。6.2 一個(gè)簡單的 A/B 測試框架我通常用離線回放做 A/B把歷史真實(shí)會(huì)話拿出來用不同 mode 重新構(gòu)建上下文讓模型生成回復(fù)然后人工或自動(dòng)評估質(zhì)量。評估維度包括事實(shí)準(zhǔn)確性、上下文連貫性、指令遵循度。跑幾百條就能看出明顯差異。這里有個(gè)經(jīng)驗(yàn)不要只看平均分要看方差。有些 mode 平均分不差但方差大意味著不穩(wěn)定線上會(huì)出各種詭異問題。我寧愿選平均分略低但方差小的 mode。6.3 線上灰度與回滾策略新 mode 上線一定要灰度。我的做法是按用戶 ID 哈希分流先放 5% 流量觀察一周?;貪L開關(guān)要能秒級生效而且回滾后要能恢復(fù)到舊 mode 的上下文狀態(tài)——這就要求上下文存儲(chǔ)層和 mode 邏輯解耦mode 只是“視圖”底層消息始終完整保存。7. 不同場景下的 mode 選型建議7.1 客服機(jī)器人混合 mode 強(qiáng) pinned客服場景的特點(diǎn)是會(huì)話中等長度10-30 輪關(guān)鍵實(shí)體多訂單、產(chǎn)品、金額且用戶經(jīng)?;厮荨N彝扑]混合 modepinned 策略要激進(jìn)——所有實(shí)體抽取結(jié)果都標(biāo)記 pinned并做版本管理。摘要用于對話脈絡(luò)檢索用于歷史回溯。7.2 代碼助手滑動(dòng)窗口 文件上下文分區(qū)代碼助手的上下文分兩部分對話歷史和當(dāng)前文件內(nèi)容。文件內(nèi)容通常很大但結(jié)構(gòu)清晰適合用分區(qū)方式組織——把函數(shù)簽名、相關(guān)代碼塊、報(bào)錯(cuò)信息分區(qū)放置。對話歷史用滑動(dòng)窗口即可因?yàn)榇a問題通常聚焦在當(dāng)前幾輪。這里有個(gè)技巧把光標(biāo)位置附近的代碼放在上下文末尾模型對末尾注意力最強(qiáng)。7.3 長文檔問答檢索式為主全量為輔長文檔問答的上下文主要是文檔片段不是對話歷史。這時(shí)候 context-mode 的重點(diǎn)是片段檢索和重排。我的做法是先用向量檢索召回 top-20再用 cross-encoder 重排取 top-5最后按文檔順序排列保持邏輯連貫。對話歷史只保留最近 2 輪因?yàn)槲臋n問答通常是獨(dú)立問題。7.4 多 Agent 協(xié)作結(jié)構(gòu)化上下文 角色隔離多 Agent 場景下每個(gè) Agent 只需要看到與自己角色相關(guān)的上下文。比如規(guī)劃 Agent 需要看全局目標(biāo)執(zhí)行 Agent 只需要看當(dāng)前子任務(wù)。這時(shí)候 context-mode 的核心是按角色過濾 結(jié)構(gòu)化傳遞。我一般用 JSON 結(jié)構(gòu)定義上下文每個(gè)字段標(biāo)注可見角色構(gòu)建時(shí)按角色裁剪。8. 我個(gè)人的幾條實(shí)操心得第一上下文處理邏輯要和業(yè)務(wù)邏輯解耦。我見過太多項(xiàng)目把壓縮邏輯寫死在業(yè)務(wù)代碼里改一個(gè)策略要?jiǎng)邮畮讉€(gè)文件。正確的做法是抽象出 ContextManager業(yè)務(wù)層只負(fù)責(zé)往里面塞消息取的時(shí)候指定 mode。第二永遠(yuǎn)保留原始消息。壓縮后的上下文是給模型看的但存儲(chǔ)層要留全量。排查問題時(shí)原始消息是唯一真相。我吃過虧——有次線上出問題結(jié)果發(fā)現(xiàn)壓縮后的上下文把關(guān)鍵信息丟了但原始消息也沒存根本沒法復(fù)現(xiàn)。第三token 計(jì)數(shù)不要用估算。不同模型的 tokenizer 不一樣估算誤差可能到 20%。我早期用字符數(shù)除以 4 估算結(jié)果中文場景嚴(yán)重低估導(dǎo)致頻繁超限。后來統(tǒng)一用對應(yīng)模型的 tokenizer 精確計(jì)數(shù)雖然慢一點(diǎn)但穩(wěn)定。第四給上下文加“新鮮度權(quán)重”。同樣相關(guān)的兩條歷史消息一條是 5 分鐘前的一條是 3 天前的顯然前者更重要。我在檢索式 mode 里加了時(shí)間衰減因子score 相關(guān)性 × exp(-Δt / τ)τ 取 1 小時(shí)左右。這個(gè)改動(dòng)讓“最近提過但語義匹配度略低”的信息更容易被召回。第五定期 review 上下文內(nèi)容。我每個(gè)月會(huì)抽樣看一批線上真實(shí)上下文經(jīng)常發(fā)現(xiàn)意外情況比如某個(gè)工具返回了超長 JSON、某個(gè)用戶發(fā)了大量重復(fù)內(nèi)容、某個(gè)系統(tǒng)消息被反復(fù)注入。這些問題不看原始數(shù)據(jù)是發(fā)現(xiàn)不了的。Review 之后針對性加過濾規(guī)則比盲目調(diào)參有效得多。context-mode 這個(gè)東西說到底是在資源約束下做信息取舍的工程藝術(shù)。沒有銀彈只有對場景的深刻理解和對數(shù)據(jù)的持續(xù)觀察。我上面給的配置和代碼都是起點(diǎn)真正適合你業(yè)務(wù)的 mode得自己跑數(shù)據(jù)、看指標(biāo)、迭代出來。