Context Window Management
上下文視窗管理,包含預算分配、資訊篩選策略、訊息視窗設計,以及 token 的隱藏消耗和成本模型。
設計問題
在 email AI Assistant 裡,context window 很快就會被吃光:最近對話、相關記憶、過去聊過的信件、搜尋結果 snippet、工具定義,全都想擠進去。
LLM 的 context window 是一個有限的資源,就像伺服器的記憶體一樣。你有 128K 個 token 的預算,但不代表你應該把 128K 全部用掉。
更多的 context 不一定 = 更好的回答。
研究顯示 LLM 有「Lost in the Middle」問題。放在 context 中間的資訊,比放在開頭和結尾的更容易被忽略。此外,token 用量直接影響延遲和費用。
核心問題:如何在有限的 token 預算內,塞進最多「有用」的資訊,同時排除「噪音」?
這篇在整個系列中的位置
Retrieval Pipeline 會告訴你怎麼把正確的 email 找出來; Memory Architecture 會告訴你哪些跨對話資訊值得保留; 這篇要解的是:找到之後,哪些內容真的值得塞進模型的 context。
預算分配
第一步是決定每個資訊來源應該分配多少預算:
刻意的節制
我們只用了不到 10K token。這不是因為 context window 不夠大,而是刻意的設計。每一塊都經過篩選,確保是高密度的有用資訊。
資訊篩選策略
策略一:全量 vs 篩選
| 資訊來源 | 全量 | 篩選後 | 節省 |
|---|---|---|---|
| 用戶記憶(50 則) | 5,000 token | 300 token(top 3) | 94% |
| 過去對話(20 段) | 40,000 token | 1,500 token(top 3) | 96% |
| 歷史訊息(40 則) | 8,000 token | 2,000 token(top 10) | 75% |
| Email 搜尋結果 | 5,000 token(10 封全文) | 1,500 token(snippet + 2 封全文) | 70% |
篩選的手段:語意搜尋(找最相關的)+ 數量限制(取 top N)。
策略二:集中配置
所有的限制參數集中在一個配置中,方便調整:
LIMITS = {
memoriesPerRequest: 3, ← 每次注入幾則記憶
recentMessageWindow: 10, ← 保留最近幾則訊息
olderMessagesToUse: 10, ← 從舊訊息中搜出幾則
rerankerCandidates: 30, ← Reranking 的候選數量
maxAgentSteps: 10, ← Agent 最多執行幾步
}這些數字是 trade-off 的具體化:
| 參數增大 | 好處 | 代價 |
|---|---|---|
| memoriesPerRequest ↑ | 更多個人化 | 更多 token、可能引入無關記憶 |
| recentMessageWindow ↑ | 更連貫的對話 | 更多 token |
| olderMessagesToUse ↑ | 更好的長對話支持 | 更多 token、更慢 |
| rerankerCandidates ↑ | 更好的搜尋品質 | 更貴的 reranking |
| maxAgentSteps ↑ | 更複雜的任務 | 更長的延遲、更多 token |
策略三:漸進式揭露
搜尋工具的三步工作流就是漸進式揭露的實踐:
不是一次把所有資訊都塞進 context,而是讓 Agent 自己決定要深入哪些。
訊息視窗的設計
滑動視窗 + 語意搜尋
為什麼這樣切?
最近的訊息必須全量保留。它們是對話的當前脈絡;如果丟掉最近的訊息,LLM 就會不知道用戶在說什麼。
舊的訊息用語意搜尋。大部分舊訊息已經不相關了(「你好」「謝謝」),語意搜尋會找回真正重要的:
- 對話開頭的背景設定
- 中間做出的重要決定
- 提到的約束條件
語意查詢的構造
查詢 = 所有最近訊息 + 最新訊息(重複一次加權)
最新訊息加權的原因:它最可能是決定「需要什麼上下文」的關鍵。如果用戶問「John 那封信最後怎麼說?」,最新訊息中的「John」「那封信」會決定應該把哪些舊訊息和搜尋結果找回來。
System Prompt 的結構
System prompt 的結構設計也影響 context 的有效性:
1. 角色定義(你是誰)
2. 工具使用規則(什麼時候用什麼工具)
3. 記憶(用戶的背景知識)
4. 關聯對話(過去的相關經驗)
5. 當前任務指引把記憶和關聯對話放在 system prompt 而非 user message 中,原因:
- 位置優勢:System prompt 在 context 的開頭,不容易被 "Lost in the Middle"
- 角色區分:LLM 把 system prompt 當作「自己的知識」,而非「用戶說的話」
- 持久性:System prompt 在每輪對話中固定,不會隨著訊息增加而被推到 context 中間
Token 的隱藏消耗
除了你主動放進 context 的內容,還有一些隱藏的 token 消耗:
工具定義
每個工具的 schema(名稱、描述、參數定義)都佔 token。如果你註冊了 20 個工具,可能就吃掉 2,000+ token。
優化:App Selection 機制。用戶沒選中的 App 工具不會被註冊。如果用戶只選了 Calendar,Agent 只看到 Calendar 相關的工具,不會被 Tasks 的工具定義佔用 token。
工具呼叫歷史
Agent 的每次工具呼叫和結果都在 context 中累積。10 步的 Agent 循環,每步搜尋返回 10 個結果,可能累積數千 token。
優化:maxAgentSteps 限制步數。搜尋工具返回 snippet 而非全文。
HITL 流程的文字化
HITL 的 data parts(確認請求、用戶決定、執行結果)在轉為 LLM 可理解的格式時,也佔用 token:
"The assistant requested to run findCalendarEvents with input: {...}"
"The user approved the tool."
"The tool returned this result: {...}"生產考量
動態預算分配
靜態的限制參數(3 則記憶、10 則舊訊息)是最簡單的方案。更進階的做法:
- 根據剩餘 token 預算動態調整
- 根據查詢類型調整(簡單問答少給上下文,複雜分析多給)
- 根據模型的 context window 大小調整
監控
需要監控的指標:
- 每次請求的 input/output token 數量
- Context 的利用率(實際用了多少 / window 大小)
- 記憶命中率(注入的記憶有多少被 Agent 使用了)
- 搜尋的點擊率(Agent 深入查看了幾個搜尋結果)
成本模型
每次對話的成本 ≈ input_tokens × input_price + output_tokens × output_price假設 Gemini Flash 的定價:
- Input: $0.15 / 1M token
- Output: $0.60 / 1M token
一次典型的對話(~7K input + ~500 output):
- Input: 7,000 × 0.00105
- Output: 500 × 0.0003
- 總計 ≈ $0.0014
如果每天 1000 次對話:$1.4/day。context 管理做得好,成本完全可控。
重點總結
| 設計原則 | 實踐方式 |
|---|---|
| 少即是多 | 語意搜尋篩選,只放最相關的 |
| 預算化思維 | 每個來源有明確的 token 上限 |
| 漸進式揭露 | snippet → 判斷 → 全文 |
| 位置敏感 | 重要資訊放 system prompt(開頭) |
| 集中控制 | 所有限制參數集中配置 |
| 成本可預測 | 固定上限 = 可預測的費用 |
Last updated on