
1. 為什么我在本地寫了個“context-mode”來解決上下文管理問題作為一個常年跟 AI 輔助開發(fā)工具打交道的人我最崩潰的場景不是模型答錯而是同一個會話里模型明明幾輪前還記得的關鍵設定說忘就忘。你反復強調別動 service 層之后它下一輪照樣給你生成一個改了 service 層接口的代碼。你放進去的依賴版本、接口協(xié)議、目錄結構它在十輪對話之后就像從來沒看過一樣。這類問題的根源其實不在模型能力上而在上下文管理策略上。絕大多數人是把所有內容一股腦塞進對話里覺得塞得越多模型就越懂。實際情況恰恰相反上下文窗口是有限的塞進去的內容會互相稀釋。放了兩萬字的接口文檔模型記住的可能是文檔里無關緊要的日志格式而不是你最在意的幾條約束。真正該被模型記住的核心指令和那些只用一兩次就能丟棄的臨時信息被丟進了同一個池子里。我用過很多現(xiàn)成方案比如給會話寫固定 preamble、做角色設定、整理項目規(guī)范文檔但都不夠系統(tǒng)。后來我干脆自己寫了一個叫 context-mode 的小工具核心思路是把上下文分成長期記憶區(qū)和短期工作區(qū)并根據當前任務類型動態(tài)調整兩側的比例和權重。用大白話說就是給對話上一套記憶管理策略——該記住的死死摁住不該記住的用完就扔。這個工具不是傳統(tǒng)意義上的插件不需要改模型推理代碼也不依賴某個特定 AI 產品。它是一層運行在你和模型之間的上下文調度層在把請求發(fā)給模型之前由它來決定哪些信息必須帶上哪些可以打折哪些干脆丟棄。用完這套東西之后我在代碼生成、文檔總結、架構設計這幾類任務上的返工率明顯下降尤其是那種上一輪說的約定下一輪就忘的問題基本被根治了。這個項目適合誰如果你經常用 AI 做長對話、需要跨多輪保持一致的開發(fā)規(guī)范或者你處理的任務類型跨度很大一會兒寫代碼、一會兒寫方案、一會兒查文檔那么 context-mode 的思路和實現(xiàn)方式你肯定用得上。即使你不打算復刻我的代碼光把上下文分區(qū)這個思考模型拿走就能顯著改善你跟模型對話的效率。2. context-mode 的整體設計思路為什么分區(qū)模式比塞滿窗口更好用2.1 一個生活化的類比你的大腦不可能同時記住所有事先打個比方。你上班的時候不會把過去十年的工作經歷全部放在腦子里最前的位置你只會臨時把今天要用的資料攤在桌面上而把那些重要但當前用不到的東西收進抽屜里。桌面上這張紙就是短期工作區(qū)抽屜里那些文件就是長期記憶區(qū)。如果今天只是寫一封郵件桌面只需要一張紙就夠了抽屜里那些資料壓根不用打開。如果你今天要寫一份年度規(guī)劃那就得從抽屜里翻出好幾份過往數據桌面攤開的內容就多。AI 對話也是同一個道理。模型的工作記憶是有限的每輪生成回復都要基于當前可見的全部內容。如果一上來就把公司背景、代碼倉庫結構、歷史決策記錄、本次需求、臨時備注全部塞進去那模型每次都要消化大量低權重信息反應變慢不說關鍵約束還容易被淹沒。context-mode 最核心的設計就是把可見上下文顯式分成兩個區(qū)域A 區(qū)長期指令區(qū)存放那些每輪對話都必須遵守的穩(wěn)定規(guī)則比如編碼規(guī)范、禁止觸碰的模塊、輸出格式要求、項目關鍵路徑。B 區(qū)臨時工作區(qū)存放當前任務相關的材料比如這次要修的問題描述、上游接口文檔、報錯日志、相關代碼片段。每次發(fā)起請求前工具會重新計算兩個區(qū)域的內容。A 區(qū)幾乎恒定不變B 區(qū)則跟隨任務推進持續(xù)滾動更新過期的信息自動出隊新的信息按優(yōu)先級入隊。這樣模型永遠在一個干凈、聚焦的上下文里做推理而不是在雜物堆里猜重點。2.2 三種內建模式分別解決什么場景光有分區(qū)還不夠因為不同任務對上下文的消耗方式完全不同。context-mode 內置了三套模式分別對應我在實際開發(fā)中最常遇到的三種任務類型。第一套是速戰(zhàn)速決模式short-context mode。典型的場景是只問一個小問題比如這個函數的正則表達式哪里寫錯了或者這個報錯是什么意思。這種任務根本不需要把項目背景全塞進去只需要把報錯信息和相關十幾行代碼放進去就夠了。這個模式下B 區(qū)會限制得非常小A 區(qū)也只保留最基礎的角色設定保證模型拿到的是最小可用上下文響應速度最快、最不容易被無關信息干擾。第二套是深度任務模式deep-work mode。適用于需要多輪迭代的復雜任務比如實現(xiàn)一個用戶認證模塊或者重構整個數據層。這種任務要求模型跨多輪保持高度一致A 區(qū)必須包含完整的項目結構、技術選型、約束條件B 區(qū)則要支撐不斷增大的中期產出比如已經生成的接口定義、數據結構、依賴版本。這個模式下上下文窗口的利用率最高但代價是單輪請求的 token 消耗會大不少。第三套是緊急搶救模式debug mode。專門為線上出了個 bug 你只想趕緊定位這種高壓場景設計的。它會自動壓低 A 區(qū)的占比把盡可能多的空間讓給 B 區(qū)的報錯棧信息、日志片段、線上配置。因為在跑救火任務時你的核心訴求是讓模型集中精力看現(xiàn)場而不是反復強調代碼規(guī)范。換句話說這種模式允許你暫時犧牲規(guī)則約束換取上下文聚焦上的極限收益。三種模式的本質是把上下文分配做成一個可調策略而不是一刀切。這也是我認為 context-mode 跟簡單拼 prompt最大的區(qū)別后者是靜態(tài)的前者是有彈性的。2.3 上下文調度的核心流程從輸入到請求的四步流水線context-mode 在向模型發(fā)起請求之前會走一套四步流水線。我把它寫在項目 README 的第一行因為理解這四步你就理解了整個工具的核心。第一步叫清洗sanitize。把輸入里的廢話、重復內容、格式雜亂的日志壓縮成結構化信息。比如你把一整段 JSON 日志丟進來工具會把時間戳、非關鍵字段全部剝掉只保留 error message 和堆棧關鍵行。第二步叫歸類classify把清洗后的內容按長期規(guī)則和臨時材料分揀進對應的區(qū)。第三步叫壓縮compress。這一步對 B 區(qū)特別重要因為臨時工作區(qū)如果無限膨脹最終還是會變成新的雜物堆。工具會按照內容的新鮮度和引用頻次做衰減對超過 N 輪沒有被再次引用的信息做摘要壓縮。第四步叫組裝assemble按當前模式指定的比例把 A 區(qū)和 B 區(qū)的內容拼接成最終發(fā)給模型的請求。這套流程單獨看每一步都不復雜但合在一起效果比手工整理 prompt強得多。最關鍵的差異在于它是自動化的、持續(xù)運行的而不是每次對話時靠你手動去復制粘貼。3. 核心功能拆解長期指令權重、上下文衰減與模式切換機制3.1 A 區(qū)長期指令的權重管理讓模型至死不忘三條鐵律A 區(qū)想解決的問題是模型為什么總是忘記我說過的重要要求。我檢查過很多次對話記錄發(fā)現(xiàn)模型忘事分兩種一種是它真的沒有收到那個信息信息壓根沒出現(xiàn)在上下文中另一種是它收到了但上下文里同類信息太多導致它無法判斷哪條優(yōu)先級最高。context-mode 處理第二種問題的手段是給 A 區(qū)的每條指令顯式設置權重。權重最高的指令不僅放在請求的最前部還會在前綴加上強調標記。以當前主流模型對指令的敏感程度來看當若干條約束在上下文中彼此競爭時權重和位置差異就能起到決定性作用。舉個例子我的一個實際項目里有三條鐵律代碼禁止使用 any 類型所有數據庫操作必須走 repository 層生成的注釋必須用中文這三條我會設置為最高權重每次請求都會原樣出現(xiàn)在 A 區(qū)頂部。而像我記得你上次給過一個分頁函數這種偶爾用到的信息權重就低很多放在 A 區(qū)末尾被壓縮的優(yōu)先級也最高。在實際使用中我發(fā)現(xiàn)一個關鍵細節(jié)A 區(qū)的內容不是越多越好。如果 A 區(qū)里塞了三十條重要規(guī)則那模型會把這些規(guī)則平均對待最后沒有一條真正重要。context-mode 的默認策略是 A 區(qū)最多保留約 12 條權重最高的指令超出的部分強制降級到 B 區(qū)。這樣做的效果非常明顯——規(guī)則條目越少模型對每一條的遵從度越高。3.2 上下文衰減機制超過三輪沒被引用對不起請讓位B 區(qū)最大的隱患是陳舊信息堆積。舉個例子你第一輪讓模型分析了某個報錯報錯信息被放進了 B 區(qū)。之后五輪你都在討論解決方案那個報錯原文還躺在 B 區(qū)里占著位置。你要不是刻意清理它可能會一直占據幾百個 token 的空間直到窗口耗盡。context-mode 的衰減機制模仿的是人類記憶規(guī)律一個信息如果在最近幾輪對話中完全沒有被引用它就會被判定為低熱度自動觸發(fā)壓縮流程。默認的熱度衰減系數是每輪 0.7也就是說一個信息如果連續(xù)三輪都沒被模型中任何一條回復引用過它的熱度就會從 1.0 降到大約 0.34這時候它占用的 token 會被壓縮到原來的四分之一只保留摘要。如果連續(xù)六輪沒被引用熱度降到 0.1 以下工具會直接把它從上下文中移除。這里要注意衰減機制不是無腦丟信息。如果某個信息雖然多輪沒被引用但它在 A 區(qū)被標記為會話級必需那它就不會被移除只會被壓縮。所以 B 區(qū)的自動清理本質上只針對那些臨時用一下、用完即棄的材料。我最初實現(xiàn)的時候直接按輪數做衰減后來發(fā)現(xiàn)不準確。因為有的輪次用戶只回了個好字換來的是模型刷新了一整版代碼這時候舊信息的引用熱度其實是被刷新的。后來我改成了引用感知衰減就是當模型回復中出現(xiàn)與舊信息相關的片段時該信息的熱度會被自動重置。這個改動讓衰減機制的誤殺率大幅下降。3.3 模式切換的觸發(fā)策略手動為主自動提示為輔最理想的模式切換是AI 自動識別任務類型并切換但以當前的技術水平純自動切換在復雜對話里經常判斷失誤。所以我采用了比較務實的策略手動切換為主自動提示為輔。用戶輸入的指令里如果包含特定觸發(fā)詞比如重構實現(xiàn)新功能工具就會提示當前任務疑似深度任務模式是否切換如果包含修 bug報錯就會提示當前任務疑似緊急搶救模式是否切換——但最終決定權在用戶手里絕不自動越權。這個設計是有原因的。我在真實使用中發(fā)現(xiàn)模式切換一旦自動化錯誤切換的代價非常高。試過在實現(xiàn)新功能的對話中誤切成短上下文模式結果模型把之前定義的數據結構全忘了所有代碼推倒重來。手動切換雖然多了一步操作但勝在確定性和可控性。工具類軟件最重要的不是聰明而是可預期。4. 實操記錄從零配置一個可用的 context-mode 環(huán)境4.1 環(huán)境配置與依賴準備context-mode 的使用前提是你已經有一個可以調用大模型 API 的開發(fā)環(huán)境。我用的是 Python 3.10 FastAPI 做成本地服務核心依賴只有三個openai 客戶端庫、pydantic 做配置校驗、sqlite 做會話狀態(tài)持久化。你如果不需要做成獨立服務也可以直接把 context-mode 的核心函數嵌入到你自己的腳本里連 FastAPI 都不用裝。安裝依賴的完整命令如下pip install openai pydantic sqlite3注意sqlite3 是 Python 標準庫不需要單獨裝。openai 庫版本建議用 1.x 以上因為 0.x 的老版本接口差異太大我的代碼是基于新接口寫的。配置文件是我建議所有使用者首先看的入口。context-mode 使用一個 YAML 文件來管理所有模式參數核心配置項如下modes: short: a_ratio: 0.2 b_ratio: 0.5 max_tokens: 2000 deep: a_ratio: 0.4 b_ratio: 0.8 max_tokens: 8000 debug: a_ratio: 0.1 b_ratio: 0.9 max_tokens: 4000 decay: factor: 0.7 remove_threshold: 0.1 compress_threshold: 0.34 long_term: max_rules: 12 top_priority_prefix: __RULE__這幾個參數背后都是有講究的。a_ratio 和 b_ratio 表示該模式下 A 區(qū)和 B 區(qū)占上下文窗口的最大比例b_ratio 通常比 a_ratio 高因為大多數任務中臨時材料本來就比長期規(guī)則多。max_tokens 不是模型的完整上下文窗口尺寸而是你允許 context-mode 實際占用的上限留出來的空間給模型生成回復用。4.2 核心實現(xiàn)上下文組裝函數下面這段代碼是 context-mode 最核心的函數負責把兩個區(qū)域的內容按模式比例拼接成一個最終請求。我只保留了最小實現(xiàn)去掉了一些細節(jié)方便你直接理解。def assemble_context(mode: str, long_term: list, short_term: list, config: dict) - str: mode_cfg config[modes][mode] decay_cfg config[decay] # 第一步衰減和壓縮短期工作區(qū) compressed_short [] for item in short_term: if item[recency] decay_cfg[remove_threshold]: continue if item[recency] decay_cfg[compress_threshold]: item[content] summarize(item[content]) compressed_short.append(item) # 第二步按比例分配 token 預算 a_max_tokens mode_cfg[max_tokens] * mode_cfg[a_ratio] b_max_tokens mode_cfg[max_tokens] * mode_cfg[b_ratio] # 第三步組裝長期指令區(qū) result_parts [] used_tokens 0 for rule in long_term[:config[long_term][max_rules]]: prefix config[long_term][top_priority_prefix] if rule[priority] high else rule_text f{prefix}{rule[content]} rule_tokens estimate_tokens(rule_text) if used_tokens rule_tokens a_max_tokens: break result_parts.append(rule_text) used_tokens rule_tokens # 第四步組裝臨時工作區(qū) for item in compressed_short: item_tokens estimate_tokens(item[content]) if used_tokens item_tokens b_max_tokens a_max_tokens: continue result_parts.append(item[content]) used_tokens item_tokens return \n\n---SEPARATOR---\n\n.join(result_parts)這段代碼里幾個細節(jié)值得說。衰減判斷用的是 recency 字段每輪對話結束后全局減一次被引用的條目重置為 1.0。estimate_tokens 是一個估算函數中英文混合場景下我采用中文按 1.5 token/字、英文按 0.3 token/字符的經驗估算雖然不完全準確但用于預算控制足夠了。summarize 函數建議直接調用模型做一次摘要不要把幾百行日志原樣留著。4.3 首次配置的最佳實踐哪些內容進 A 區(qū)哪些進 B 區(qū)配置 context-mode 最讓人犯難的問題就是到底什么東西該放進 A 區(qū)我的建議很簡單只放那些如果你不讓模型遵守它就會犯錯的內容。舉個例子如果你做的是 Java 項目你希望模型生成的類名是駝峰式這屬于 A 區(qū)如果你希望模型在每次回復前先列出一個 TODO 清單這也屬于 A 區(qū)但如果你只是想在一輪對話里讓模型參考一下某個開源項目的寫法這種材料就該在 B 區(qū)用完就走。還有個容易被忽略的點A 區(qū)的內容必須用命令式語氣寫不要用描述性語氣。禁止在代碼中使用 any 類型是命令式項目中通常不會使用 any 類型就是描述式。實測下來模型對命令式指令的遵從度比描述式高很多。這大概是因為命令式指令更像用戶直接給出的要求而描述式指令更像項目文檔摘錄容易被模型歸入參考信息而不是行為約束。首次配置時不要貪多。我建議第一版先只配 3 到 5 條 A 區(qū)規(guī)則跑一周看模型在哪些地方依然反復出錯再逐步補上。一次性配滿 12 條你會很難定位到底是哪條規(guī)則未被遵守因為干擾太多了。4.4 調用接口設計一次完整的帶 context-mode 的對話請求配置好之后實際調用流程就是先更新狀態(tài)再組裝上下文最后發(fā)給模型。下面是用 FastAPI 暴露接口的示例from fastapi import FastAPI from pydantic import BaseModel app FastAPI() session_store {} class ChatRequest(BaseModel): session_id: str user_message: str mode: str deep app.post(/chat) def chat(req: ChatRequest): session session_store.get(req.session_id, {long_term: [], short_term: []}) # 更新短期工作區(qū)把新的用戶消息加進去 session[short_term].append({ content: req.user_message, recency: 1.0, timestamp: time.time() }) # 衰減舊信息 for item in session[short_term]: item[recency] * config[decay][factor] # 組裝上下文 context assemble_context(req.mode, session[long_term], session[short_term], config) # 調用大模型 response client.chat.completions.create( modelyour-model-name, messages[ {role: system, content: 你是該項目的高級開發(fā)助手。}, {role: user, content: context} ] ) # 引用感知如果回復里包含舊的短期信息片段重置其 recency for item in session[short_term]: if item[content][:50] in response.choices[0].message.content: item[recency] 1.0 session_store[req.session_id] session return {reply: response.choices[0].message.content}這個接口描述的是核心回調流程實際部署時建議加上歷史消息的持久化存儲和線程鎖避免并發(fā)請求時狀態(tài)錯亂。5. 踩坑記錄context-mode 落地過程中最常見的五個問題5.1 衰減誤殺頻率低但重要的信息被提前清除這是我遇到的第一個坑。原本的衰減機制只看引用頻率但有些信息雖然引用頻率低重要性卻極高。比如某個數據庫表的結構說明只在最開始討論字段時用過一次后續(xù)十輪都在寫業(yè)務邏輯按照原版衰減規(guī)則它大概率會在第五輪左右被壓縮掉但等到第八輪突然要寫一個關聯(lián)查詢時模型已經把表結構忘干凈了生成出來的 SQL 全是錯的。解決辦法我前面提到過把這類信息手動標記為會話級必需。context-mode 里我給短期工作區(qū)增加了一個 optional 字段optionalfalse 的條目不參與衰減移除只參與壓縮。代價是這類條目會一直占著 token所以標記必需時要想清楚——只有那些后面一定會再用到的材料才值得這樣標。5.2 壓縮摘要導致信息丟失模型復述的能力被削弱壓縮機制雖然節(jié)省了 token但摘要永遠有損。我最開始用摘要替換原始內容的時候遇到一個很尷尬的情況模型知道報錯發(fā)生過但不記得報錯的具體行號導致修復建議一直跑偏。后來我調整了策略摘要里強制保留關鍵結構化字段。對日志類內容摘要必須包含錯誤碼、行號、模塊名對代碼類內容摘要必須包含函數名、入參類型、返回值類型。這個改動之后壓縮的可用性明顯提升。說到底壓縮的目標是去噪不是去信息關鍵信息字段的完整性必須保證。5.3 模式切換錯誤上下文聚焦反而讓模型變蠢有一次我用緊急搶救模式去跑一個本該用深度模式的任務結果非常慘。當時要重構一個核心模塊我圖省事直接用調試模式進入B 區(qū)占比拉滿A 區(qū)長期規(guī)則被壓縮到只剩 10% 的空間結果模型連項目最基本的命名約定都忘了生成了大量風格不統(tǒng)一的代碼。那次之后我徹底改變了思路模式切換刀一定要握在用戶手里工具的自動提示只能當參考不能當替身。5.4 多會話狀態(tài)混亂session 隔離不干凈導致上下文串線早期版本我只有一個全局上下文存儲沒有按會話隔離。有一次同時開兩個會話一個在改前端一個在寫后端文檔結果兩側的內容互相混進對方的上下文里。模型在前端會話里開始輸出后端接口文檔場面一度非常尷尬。后來我把 session 狀態(tài)徹底隔離每個會話獨立維護自己的 A 區(qū)和 B 區(qū)串線問題才徹底解決。這個教訓也提醒我凡是帶狀態(tài)的系統(tǒng)隔離的設計必須放在第一天做不能等出了事故再補。5.5 估算 token 與實際不一致預算控制失真estimate_tokens 函數畢竟只是估算跟真實 API 返回的 token 數經常差 20% 到 30%。如果預算算得太緊上下文會遺漏關鍵材料算得太松又容易觸發(fā)模型的真實上下文窗口溢出。我的解決方案是在每次請求返回后用 API 返回的 usage 信息反向校準估算函數。具體做法是維護一個最近 50 次請求的平均偏差系數估算值乘以偏差系數后再納入預算計算。這樣跑幾輪之后預算控制會越來越貼近真實。老實說做 context-mode 這個工具的過程比工具本身的代碼更有價值。它逼著我去思考一個之前一直忽略的問題我們跟 AI 協(xié)作時效率的瓶頸往往不是模型不夠聰明而是我們沒有給它足夠好的信息結構。A 區(qū)和 B 區(qū)的劃分、衰減機制、模式切換本質上都在做一件事把上下文的布局顯式化讓最重要的信息永遠出現(xiàn)在最該出現(xiàn)的位置。我現(xiàn)在已經把這個思路用在了日常的所有 AI 對話里就算脫離工具本身我也會下意識地做分區(qū)先把核心約束寫清楚再把材料按重要程度排列最后才發(fā)出去。這個習慣的收益比任何工具都大。如果你也在用 AI 輔助工作到長對話我建議你先別急著寫代碼而是試著用手動的方式做三天的上下文分區(qū)把每輪對話前要發(fā)的信息分類整理一次你會有一種豁然開朗的感覺。之后你再決定要不要像這樣寫個工具來固化流程都來得及。