回到頂部

📦 Context Window 管理

128K tokens 不夠用?長文處理、對話記憶、Token 優化全攻略。

📦 什麼是 Context Window?

💡 一句話理解 Context Window = AI 一次能「看到」多少內容。就像工作桌面大小——桌子越大,能同時攤開的資料越多。

2026 主流模型 Context Window

模型Context Window約等於
GPT-4o128K tokens~10 萬字中文
Claude Sonnet 4.6200K tokens~15 萬字中文
Gemini 2.5 Pro1M tokens~75 萬字中文
DeepSeek V41M tokens~75 萬字中文
GPT-4o-mini128K tokens~10 萬字中文
Llama 3.1 405B128K tokens~10 萬字中文

⚠️ 能放不代表品質一樣好。研究顯示,當 context 超過 64K tokens,大部分模型在「中間位置」的資訊理解力會明顯下降(“Lost in the Middle” 問題)。


🤔 什麼時候 Context 不夠用?

場景需要的 context問題
分析一本書50-200K tokens大部分模型放得下,但品質不穩
分析一整個 codebase200K-2M tokens超過大部分模型上限
長對話(100+ 輪)50K+ tokensToken 費用爆炸
知識庫問答無限文件不可能全塞進去

🔧 四大策略

策略 1:RAG(最常用)

不把所有資料塞進 context——只檢索最相關的段落。

全部文件(10 萬份)
       ↓ Embedding + 向量搜尋
最相關的 5 段(2000 tokens)
       ↓ 和問題一起送給 LLM
精準回答

適合: 知識庫問答、客服、文件搜尋

優點: context 用量極小、可擴展到無限文件量

缺點: 檢索品質決定回答品質

→ 詳見 RAG 完全指南


策略 2:Map-Reduce(處理超長文件)

把長文件切段,每段分別處理,最後合併結果。

def map_reduce_summarize(long_text, chunk_size=3000):
    """Map-Reduce 摘要:處理超長文件"""

    # 1. Map:把長文切成小段,各自摘要
    chunks = split_text(long_text, chunk_size)
    summaries = []

    for i, chunk in enumerate(chunks):
        summary = client.chat.completions.create(
            model="gpt-4o-mini",  # 用便宜的模型做 Map
            messages=[{
                "role": "user",
                "content": f"請摘要以下段落的重點(150 字內):\n\n{chunk}"
            }],
            max_tokens=200
        ).choices[0].message.content
        summaries.append(summary)
        print(f"Map {i+1}/{len(chunks)} 完成")

    # 2. Reduce:合併所有摘要,生成最終摘要
    combined = "\n\n".join(summaries)
    final = client.chat.completions.create(
        model="gpt-4o",  # 用強模型做 Reduce
        messages=[{
            "role": "user",
            "content": f"以下是一份文件各段落的摘要,請整合成一篇完整的摘要:\n\n{combined}"
        }],
        max_tokens=1000
    ).choices[0].message.content

    return final

適合: 超長文件摘要、報告分析

優點: 理論上無文件長度限制

缺點: 各段獨立處理,可能遺漏跨段落的關聯


策略 3:Refine(迭代精練)

逐段讀取,每讀完一段就更新當前的理解。像人讀書一樣——邊讀邊記筆記。

def refine_analysis(chunks, question):
    """Refine:逐步精練回答"""

    current_answer = "目前還沒有足夠資訊回答。"

    for i, chunk in enumerate(chunks):
        current_answer = client.chat.completions.create(
            model="gpt-4o",
            messages=[{
                "role": "user",
                "content": f"""根據以下新的資訊,更新你的回答。

## 問題
{question}

## 目前的回答
{current_answer}

## 新的資訊(第 {i+1} 段)
{chunk}

請整合新資訊,更新回答。如果新資訊和問題無關,保持現有回答不變。"""
            }],
            max_tokens=1000
        ).choices[0].message.content

        print(f"Refine {i+1}/{len(chunks)} 完成")

    return current_answer

適合: 需要跨段落理解的深度分析

優點: 保持上下文連貫性

缺點: 逐段處理速度慢、前面段落資訊可能被稀釋


策略 4:對話記憶管理

長對話(100+ 輪)的 token 會快速膨脹,需要策略管理。

Sliding Window(滑動窗口)

def sliding_window_chat(messages, max_context=20):
    """只保留最近 N 輪對話"""
    system_msg = messages[0]  # 永遠保留 system prompt
    recent = messages[-max_context:]  # 保留最近 20 輪

    return [system_msg] + recent

Summary Memory(摘要記憶)

class SummaryMemory:
    """自動摘要壓縮歷史對話"""

    def __init__(self, max_messages=10):
        self.max_messages = max_messages
        self.summary = ""
        self.recent_messages = []

    def add(self, role, content):
        self.recent_messages.append({"role": role, "content": content})

        # 對話超過上限時,壓縮舊對話為摘要
        if len(self.recent_messages) > self.max_messages:
            old = self.recent_messages[:5]
            self.recent_messages = self.recent_messages[5:]

            old_text = "\n".join(f"{m['role']}: {m['content']}" for m in old)
            self.summary = client.chat.completions.create(
                model="gpt-4o-mini",
                messages=[{
                    "role": "user",
                    "content": f"將以下對話摘要為 100 字:\n\n之前的摘要:{self.summary}\n\n新對話:\n{old_text}"
                }],
                max_tokens=150
            ).choices[0].message.content

    def get_messages(self, system_prompt):
        msgs = [{"role": "system", "content": system_prompt}]
        if self.summary:
            msgs.append({
                "role": "system",
                "content": f"先前對話摘要:{self.summary}"
            })
        msgs.extend(self.recent_messages)
        return msgs

💰 Token 省錢技巧

技巧節省做法
精簡 System Prompt20-40%刪除冗餘指令,用條列取代長文
壓縮歷史對話50-70%用 Summary Memory 取代完整歷史
只傳必要的 context30-50%RAG 只傳 top-3 相關段落
用便宜模型做 Map60-80%Map-Reduce 的 Map 階段用 mini 模型
設定 max_tokens10-30%避免模型產生過長的輸出
Prompt Caching50-90%OpenAI/Anthropic 的 Prompt Cache 功能

Prompt Caching(重大省錢術)

當你的 System Prompt 或 context 很長且不常變時,API 會自動快取,減少 Token 計費。

# OpenAI Prompt Caching(自動啟用)
# 當 system prompt 超過 1024 tokens 且重複使用時
# 快取命中的 token 只收 50% 費用

# Anthropic Prompt Caching(需明確啟用)
response = client.messages.create(
    model="claude-sonnet-4-20260514",
    max_tokens=1000,
    system=[{
        "type": "text",
        "text": LONG_SYSTEM_PROMPT,  # 你的長 system prompt
        "cache_control": {"type": "ephemeral"}  # 啟用快取
    }],
    messages=[{"role": "user", "content": user_input}]
)
# 快取命中的 token 只收 10% 費用!

🆚 Long Context vs RAG:怎麼選?

面向Long ContextRAG
文件數量少量大文件大量文件
成本💰💰💰 每次都送全文💰 只送相關段落
精度可能 Lost in the Middle取決於檢索品質
速度長 context 推理較慢快(context 短)
適合單一文件深度分析知識庫搜尋、客服

💡 實用建議

  • 文件 < 50 頁 → 直接塞進 context(Long Context)
  • 文件 > 50 頁或多份文件 → 用 RAG
  • 需要跨文件比較 → Map-Reduce
  • 兩者結合:RAG 先縮小範圍 → Long Context 做深度分析

❓ FAQ

Context Window 是越大越好嗎?

不一定。大 context window 有兩個問題:1) 成本高——128K tokens 的單次呼叫可能花幾塊美金 2) 品質不穩——“Lost in the Middle” 問題讓中間位置的資訊容易被忽略。針對性地用 RAG 檢索通常品質更好。

中文的 token 怎麼算?

中文大約每字 1-2 個 token。128K tokens ≈ 8-10 萬字中文。可以用 tiktoken 套件精確計算。注意不同模型的 tokenizer 不同,同一段文字的 token 數可能不一樣。

對話太長怎麼辦?

三種方案:1) Sliding Window——只保留最近 N 輪 2) Summary Memory——壓縮舊對話為摘要 3) RAG——把歷史對話存入向量 DB,需要時檢索。推薦 Summary Memory,兼顧成本和連貫性。

📚 延伸閱讀