# 上下文工程導論
> 💡 **學習指南**：提示詞工程解決的是「怎麼把話說清楚」，上下文工程解決的是「讓模型在合適的時刻看到合適的資訊」。本章節會圍繞一個問題展開：**在有限的上下文視窗裡，如何既讓模型懂你，又不把錢燒光？**

在開始之前，建議你先補兩塊「基礎磚」：

- **Token 是什麼**：可以先閱讀 [大語言模型入門](./llm-principles.md) 的「分詞 & Token」部分。
- **Prompt 是什麼**：如果你還不熟悉 System / User / Assistant 的基本結構，可以先看 [提示詞工程](./prompt-engineering/)。

---

## 0. 引言：聊著聊著，它就忘事，還越來越貴的動機

<AgentContextFlow />

很多人在實際使用大模型時都會遇到類似的情況：

- 聊到一半，模型突然「忘記」之前說過的關鍵條件；
- 長對話裡，前後回答自相矛盾，很難保持同一套設定；
- 對話輪次一多，帳單像打車計價一樣不斷往上走。

直覺上，我們會以為是：**「這個模型記性不好」**。
但大多數時候，問題並不在於模型「不會記」，而在於我們**沒有設計好它能看到的上下文**。

<IntroProblemReasonSolution />

面對這些挑戰，單純依靠「寫好提示詞」已經捉襟見肘。我們需要一套更系統的工程方法，來在有限的視窗和預算內，讓模型始終獲得最關鍵的資訊。這正是**上下文工程**試圖解決的問題。

---

## 1. 上下文工程 概述

先給一個簡短的工作定義，再看幾個典型場景。

> 上下文工程，是一門為 LLM 構建和管理「資訊環境」的工程方法，決定模型「看到什麼、忽略什麼、什麼時候看到」，從而在有限的上下文視窗內穩定完成任務。

你可以簡單地把它理解成三件事：整理資訊、控制視窗、管理成本。
常見會用到它的場景包括：

- 對話型 Agent 和客服機器人
- 程式碼 / 文件助手
- 多輪工具調用和長流程編排

接下來，我們就從一個真實團隊的「血淚教訓」出發，看看他們是怎麼一點點從「只會寫 Prompt」進化到「會做上下文工程」的。

---

## 2. 從「血淚教訓」說起：Manus 團隊踩過的坑

本章案例來自 **Manus**（一款通用 AI Agent）。
與普通對話不同，Manus 需要自主規劃並調用工具完成長任務（涉及幾十甚至上百輪互動）。

這帶來了核心矛盾：
- **如果不記**：關鍵資訊丟失，任務中斷。
- **全記**：成本和延遲爆炸，甚至超出視窗限制。

Manus 團隊經歷過多次架構重構，才明白一個道理：**上下文不能只靠「寫」，而要靠「設計」。**

### 2.1 四次重構教會我們的範圍界定

Manus 的聯合創辦人季逸超分享過他們的「踩坑史」：

| 階段 | 遇到的問題 | 當時的想法 | 結果 |
| :--- | :--- | :--- | :--- |
| **第一次** | AI 聊著聊著就忘事 | 「多寫點提示詞就好了」 | 越寫越長，越寫越貴 |
| **第二次** | 重要資訊總被擠掉 | 「把重要的多複製幾遍」 | 文本更長，成本更高 |
| **第三次** | 帳單高得嚇人 | 「能不能複用之前的計算？」 | 找到降低重複計算成本的方式 |
| **第四次** | 長文件處理不了 | 「能不能需要時再查？」 | 建立「圖書館+按需檢索」的方案 |

**核心領悟**：**不是記得越多越好，而是記得越巧越好**。

### 2.2 AI 的「記性」到底像的範圍界定

**傳統電腦記憶體** = **硬碟**：
- 容量大：可以長期保存大量資料；
- 價格低：存放一年成本較低；
- 讀寫速度相對較慢，尋找資訊需要一定時間。

**AI 的上下文** = **小黑板**：
- 讀寫快：模型可以在一次調用中直接看到全部上下文；
- 容量有限：寫滿後不得不擦除舊內容；
- 每寫入一個 token 都會帶來額外計算與費用。

**Manus 的經驗**：**小黑板要用得省，用得巧，別用來存百科全書**。

---

## 3. 第一步：成本結構分析

### 3.1 要先看成本的動機

讓我們看看一次典型的 AI 對話，你的錢是怎麼花的：

```
💰 成本構成（一次對話）：
├─ 70% 重複看舊內容（「剛才聊了什麼？」）
├─ 20% 處理新內容（「現在說什麼？」）
└─ 10% 生成回覆（「怎麼回答？」）
```

**驚人發現**：**70% 的錢花在讓 AI 重新看你之前說過的話！**

### 3.2 KV Cache 概述

在討論價格之前，我們得先搞懂一個核心技術概念：**KV Cache（鍵值快取）**。
別被這個技術名詞嚇到，它其實就是 AI 的「短期記憶速查表」。

- **沒有 KV Cache 時**：AI 每次都要像第一次看到這篇文章一樣，從第一個字開始重新閱讀、理解、計算。
- **有了 KV Cache 時**：AI 會把看過的部分（Pre-fill）計算結果存下來。下次如果開頭的內容沒變，它就直接調取記憶，不用重新算了。

這就好比：
> 你去考場考試。
> **情況 A**：每次都要把整本教材從頭讀一遍，再開始答題。（慢、累、貴）
> **情況 B**：教材內容你已經背得滾瓜爛熟了（Cache），坐下直接答題。（快、輕鬆、便宜）

在雲端廠商的計費表裡，**「背過的書」（Cache Hit）**通常比**「新看的書」（Cache Miss）**便宜 90% 以上。

### 3.3 「背課文」vs「現查現用」的價格差

以 Claude 為例：
- **現查現用**（沒快取）：$3.00 / 百萬字
- **背過再用**（有快取）：$0.30 / 百萬字
- **相差 10 倍**！

**Manus 的實踐**：透過讓 AI「背課文」，他們把成本從 **$0.15 降到 $0.02**，**省了 87%**！

<ContextWindowVisualizer />

### 3.4 避坑指南：別讓時間戳毀了你的「快取」

很多開發者習慣把「當前時間」寫在 System Prompt 的第一句，覺得這樣很嚴謹。
**但這其實是上下文工程中最大的反模式之一。**

想像一下：你背了一整本歷史書（System Prompt），結果書的第一行寫的是「現在的秒數」。
如果這行字每秒都在變，那你上一秒背的所有內容，下一秒就全廢了——你得從頭再背一遍。

這就是**前綴複用（KV Cache）**的死穴：**只要開頭變了，後面全都要重算。**

#### 錯誤示範：把動態資訊放前面
```text
System: 現在是 2024-01-01 12:00:01。你是助手...
(一分鐘後)
System: 現在是 2024-01-01 12:01:01。你是助手...
```
**後果**：雖然只變了幾個字，但因為在開頭，導致後續 99% 的固定內容無法複用快取，每次請求都像第一次一樣慢且貴。

#### 正確姿勢：動靜分離
```text
System: 你是助手... (這裡放幾千字的固定規則、知識庫)
User: (在這裡透過工具調用或使用者訊息傳入當前時間)
```
**好處**：前面的幾千字規則永遠不變，AI 只需要「背」一次。後續請求直接調用記憶，速度極快。

👇 **動手點點看**：
點擊下方的開關，開啟**「背課文加速」**，然後多次點擊「發送新請求」。
觀察一下：當第一塊內容變成「已背過」時，**開口速度（TTFT）**會發生什麼變化？

<KVCacheDemo />

---

## 4. 第二步：滑動視窗機制

隨著對話越來越長，最先遇到的問題就是：**視窗滿了怎麼辦？**

### 4.1 「先進先出」會出問題的動機

最簡單的記憶管理是**滑動視窗（Sliding Window）**：**新的進來，舊的出去**。
這聽起來很公平，但在實際任務中卻是個災難。

**場景重現**：
```text
對話記錄：
[1] 使用者：我是張三，負責支付系統
[2] 使用者：專案用 Go 語言開發
[3] 使用者：資料庫是 PostgreSQL
...
[20] 使用者：幫我寫個介面
```
**結果**：當聊到第 20 句時，第 1 句「我是張三」已經被擠出了視窗。AI 徹底忘了你是誰，也不知道你在負責什麼系統。

**問題本質**：這種策略把**重要資訊**（身份、技術棧）和**廢話**（「好的」、「收到」）同等對待，一起被踢了出去。

### 4.2 「中間失憶症」- AI 總看不到關鍵資訊的動機

除了「忘得快」，AI 還有一個怪癖：**它也會「看漏」**。
研究發現：**AI 對開頭和結尾最敏感，中間最容易被忽略**。這就是著名的 **Lost in the Middle（中間迷失）**現象。

**U 型記憶曲線**：
```text
位置：開頭 → 中間 → 結尾
記憶： 高  →  低  →  高
```

👇 **動手點點看**：
1. 先試試**「滑動視窗」**：在下面的聊天框裡多發幾條訊息，看看舊的對話是怎麼被無情「擠出去」的。
2. 再看看**「中間迷失」**：觀察一下，當關鍵資訊藏在整段話的中間位置時，檢索成功率是不是最低的？

<SlidingWindowDemo />
<LostInMiddleDemo />

**解決方案**：把關鍵資訊放在**開頭**（系統提示）或**結尾**（使用者問題）。

---

## 5. 第三步：選擇性保留策略

既然「先進先出」不靠譜，那我們該怎麼辦？
Manus 的答案是：**建立「資訊等級制度」**。

### 5.1 要給資訊分等級的動機

不再平等對待每條資訊，而是根據重要程度決定它們的去留：

| 等級 | 資訊類型 | 待遇 | 成本影響 |
| :--- | :--- | :--- | :--- |
| **VIP** | 系統設定、使用者身份 | **永遠保留** | +15% 成本 |
| **重要** | 當前任務目標 | **任務期內保留** | +10% 成本 |
| **一般** | 普通對話歷史 | **最近 5 輪保留** | 基準成本 |
| **可棄** | 可檢索的知識 | **用時再查** | -60% 成本 |

**核心思想**：**用 25% 的成本增加，換取 90% 的關鍵資訊保留**。

### 5.2 「釘釘子」策略

你可以把上下文視窗想像成一面黑板：
- **VIP 資訊**：用釘子死死**釘在**黑板最上面（System Prompt）。
- **重要資訊**：用磁鐵**吸在**黑板中間（Context Injection）。
- **普通對話**：寫在黑板下半部分，滿了就擦掉舊的（Sliding Window）。

👇 **動手點點看**：
試著在下面的演示裡，把某條重要的對話「釘」住。
觀察一下：當你繼續聊天時，被釘住的資訊是不是一直都在，而沒釘住的就被擠走了？

<SelectiveContextDemo />

---

## 6. 第四步：外部知識檢索整合

有時候，我們要處理的資訊太多了（比如幾百頁的技術文件），黑板根本寫不下。這時候就需要外掛大腦——**RAG（檢索增強生成）**。

### 6.1 「小黑板」不夠用的動機

Manus 面對百萬字級的技術文件時，對比了兩種做法：

1.  **全量寫入**：所有內容一次性塞進上下文。
    *   **後果**：黑板瞬間被佔滿，處理極慢，而且根據「中間迷失」理論，AI 根本記不住中間的內容。
    *   **成本**：約 $50/次，等待 15 秒。
2.  **按需檢索（RAG）**：先去圖書館（資料庫）查，只把相關的幾段話抄到黑板上。
    *   **後果**：黑板很清爽，AI 聚焦於關鍵資訊。
    *   **成本**：約 $0.5/次，等待 2 秒。

**省了 99% 的錢，87% 的時間！**

### 6.2 「查資料」的最佳實踐

Manus 的經驗總結：
*   **每本書撕成多大片？** 500-1000 字效果最好。
*   **一次查幾本書？** 3-5 本，多了反而干擾。
*   **多相關的書才查？** 相似度 > 0.7，避免「硬湊」不相關的內容。

👇 **動手點點看**：
在搜尋框裡輸入問題（比如「如何重置密碼」），看看系統是如何從一大堆文件裡只找出最相關的那幾條的。

<RAGSimulationDemo />

---

## 7. 第五步：資訊壓縮技術

如果資訊都很重要，實在刪不掉，又不想查資料怎麼辦？
那就只能**把字寫小點**——這就是**上下文壓縮**。

### 「縮寫」的使用時機判定
*   檢索回來的資料太厚（>2000 字）。
*   對話歷史太囉嗦（佔了 >80% 黑板空間）。
*   需要快速回答，不想讓 AI 讀長篇大論。

### 7.2 「縮寫」的三種境界

| 壓縮方式 | 壓縮率 | 保留什麼 | 適用場景 | 省錢效果 |
| :--- | :--- | :--- | :--- | :--- |
| **總結式** | 70% | 主要意思 | 快速了解 | 省 30% |
| **要點式** | 50% | 關鍵要點 | 結構化輸出 | 省 50% |
| **表格式** | 30% | 核心資料 | 程式處理 | 省 70% |

👇 **動手點點看**：
選擇不同的壓縮策略，看看長篇大論是如何變短、變精煉的。

<ContextCompressionDemo />

---

## 8. 系統整合：打造 AI 的「記憶宮殿」

前面我們像搭積木一樣，學習了各種獨立的策略：
*   **KV Cache**：幫我們省錢（第 3 章）
*   **滑動視窗**：幫我們騰位置（第 4 章）
*   **分級保留**：幫我們留重點（第 5 章）
*   **RAG**：幫我們開外掛（第 6 章）

現在，是時候把這些積木搭成一座完整的城堡了——我們稱之為 Manus 的**「記憶宮殿」**。

### 8.1 像蓋房子一樣組裝上下文

不要把上下文看作一堆亂糟糟的文字，而要把它看作一座分層的建築。每一層都有它獨特的功能和「居住規則」。

👇 **動手點點看**：
點擊「開始建造」，看看我們是如何一層層把這座宮殿蓋起來的。

<MemoryPalaceDemo />

### 8.2 這樣設計最強的動機

這座宮殿的設計哲學，其實就為了解決三個矛盾：

1.  **地基（System Prompt）—— 解決「貴」的問題**
    *   **矛盾**：系統設定（你是誰、規則是什麼）最長，每次都要發。
    *   **解法**：把它放在最底層，利用 **KV Cache** 技術，只要不改動，AI 就能「背誦全文」。後續幾百輪對話，這部分的計算成本幾乎為 **0**。

2.  **支柱（Task Context）—— 解決「忘」的問題**
    *   **矛盾**：對話一長，AI 容易忘了最初的任務目標（比如「寫一個貪吃蛇遊戲」）。
    *   **解法**：利用**分級保留**策略，把任務目標「釘」在第二層。不管聊了多少輪，這層永遠不刪，確保 AI 不忘初心。

3.  **頂層（Chat & RAG）—— 解決「亂」的問題**
    *   **矛盾**：又有新對話，又有查到的資料，混在一起容易暈。
    *   **解法**：
        *   **客廳（對話）**：用**滑動視窗**管理，只留最近 5-10 句熱乎的。
        *   **圖書館（RAG）**：資料用完即走，不佔地方。

### 8.3 實戰效果

Manus 團隊把這套架構搬上線後，效果立竿見影：

*   **省錢了**：因為地基被「背」下來了，每輪對話的成本暴跌 **84%**。
*   **變快了**：AI 不用每次都從頭讀幾千字，平均回應時間從 8 秒縮短到 **2 秒**。
*   **更準了**：關鍵資訊被「釘」死，再也不會聊著聊著就忘了自己是幹嘛的。

---

## 9. 實戰模板：直接抄作業

為了讓你更直觀地理解這套機制是如何運作的，我們為你準備了**全鏈路模擬**。

請選擇一個場景，點擊「下一步」，看看從使用者發問到 AI 回答的幾秒鐘內，**記憶宮殿**是如何動態調取、組裝和清理上下文的。

<MemoryPalaceActionDemo />

### 📝 拿來即用的實戰設計

如果你要設計一個類似 Manus 的系統，不要只盯著 Prompt 怎麼寫，更要關注**系統架構如何調度上下文**。

以下是兩個經典場景的**系統設計藍圖**，包含了**提示詞設計**和**程式碼邏輯（虛擬碼）**。

#### 場景 1：全端工程師 Agent（長程記憶型）
> **核心挑戰**：任務週期長，容易忘了最初的需求和專案背景。
> **解決策略**：System 層（身份）+ Task 層（釘死目標）+ Chat 層（滑動視窗）。

**1. 系統提示詞 (Layer 1 & 2)**
```markdown
# Layer 1: 身份設定 (System Prompt) - 永遠不變，利用 KV Cache
你是一名資深的全端工程師，精通 Python 和 Vue3。
程式碼風格：
- 變數命名嚴格遵守 PEP8
- 關鍵邏輯必須包含註解
- 優先使用專案已有的工具函式

# Layer 2: 任務鎖定 (Task Context) - 任務期間不許刪
當前任務：重構支付模組 (payment_module)
核心約束：
1. 必須相容舊版 API 介面 v1.0
2. 資料庫遷移腳本必須是冪等的
3. 截止時間：本週五
```

**2. 上下文組裝邏輯 (Pseudo-Code)**
```python
def build_engineer_context(user_input, chat_history, task_info):
    context = []

    # 1. 地基層：身份設定 (利用 KV Cache 快取)
    # 這部分內容幾百輪對話都不變，計算成本幾乎為 0
    context.append(SYSTEM_PROMPT)

    # 2. 支柱層：任務鎖定 (Pinned)
    # 無論對話多長，這部分永遠插入在 System 之後
    context.append(f"當前任務：{task_info}")

    # 3. 檢索層：程式碼片段 (RAG)
    # 根據使用者的問題，去程式碼庫裡找相關的程式碼
    relevant_code = search_codebase(user_input)
    if relevant_code:
        context.append(f"參考程式碼：\n{relevant_code}")

    # 4. 互動層：對話歷史 (Sliding Window)
    # 只取最近 10 輪，避免撐爆上下文
    recent_chat = chat_history[-10:]
    context.extend(recent_chat)

    # 5. 最新輸入
    context.append(user_input)

    return context
```

#### 場景 2：智慧客服 Agent（精準問答型）
> **核心挑戰**：成本敏感，且絕對不能胡說八道。
> **解決策略**：System 層（強約束）+ RAG 層（動態注入）。

**1. 系統提示詞 (Layer 1)**
```markdown
# Layer 1: 身份設定 (System Prompt)
你是一名專業的電商客服專員。
回覆原則：
1. 語氣溫柔、專業、簡潔
2. **絕對禁止**編造事實，只根據[參考資料]回答
3. 如果資料裡沒有答案，請直接回答「非常抱歉，這個問題我需要轉接人工客服」
```

**2. 上下文組裝邏輯 (Pseudo-Code)**
```python
def build_support_context(user_input):
    context = []

    # 1. 地基層：身份設定
    context.append(SYSTEM_PROMPT)

    # 2. 圖書館層：動態檢索 (RAG)
    # 只有客服場景，RAG 才是主角，放在中間位置
    docs = vector_db.search(user_input, top_k=3)

    context.append("【參考資料開始】")
    for doc in docs:
        context.append(doc.content)
    context.append("【參考資料結束】")

    # 3. 互動層：極短的歷史
    # 客服通常不需要太久遠的記憶，保留最近 3 輪即可
    context.extend(get_recent_chat(limit=3))

    context.append(user_input)

    return context
```

---

## 10. 名詞對照表

| 英文術語 | 中文對照 | 解釋 |
| :--- | :--- | :--- |
| **Context Window** | 上下文視窗 | 模型一次性能夠處理的文本最大長度（包括輸入和輸出）。超出限制的內容會被截斷或遺忘。 |
| **Token** | 詞元 | LLM 處理文本的最小單位。通常 1 個 Token 約等於 0.75 個英文單詞或 0.5 個漢字。計費和視窗限制都以此為單位。 |
| **KV Cache** | KV 快取 | 一種推理加速技術，透過快取已經計算過的注意力鍵值對，避免對重複前綴進行重複計算，顯著降低延遲和成本。 |
| **RAG** | 檢索增強生成 | 在回答問題前，先從外部知識庫檢索相關資訊，作為上下文提供給模型，以減少幻覺並擴展知識邊界。 |
| **Sliding Window** | 滑動視窗 | 最基礎的上下文管理策略。保持視窗內 Token 數量恆定，當新內容進入時，自動移除最早的舊內容。 |
| **Lost in Middle** | 中間迷失 | 大模型的一種局限性。研究表明，模型對長上下文開頭和結尾的資訊記憶最深，而容易忽略中間部分的資訊。 |
| **System Prompt** | 系統提示 | 位於對話最開始的指令，用於設定模型的身份、行為規範、回覆風格和核心任務。 |
| **Few-shot** | 少樣本學習 | 在提示詞中提供幾個「問題-答案」的範例，幫助模型快速理解任務模式和輸出格式。 |
| **Chain of Thought** | 思維鏈 | 引導模型在給出最終答案前，先輸出推理步驟。這種方法能顯著提升模型解決複雜邏輯和數學問題的能力。 |
| **Hallucination** | 幻覺 | 模型自信地生成看似合理但實際上錯誤或不存在的資訊的現象。 |
| **Embedding** | 向量化 | 將文本轉換為高維數值向量的技術。語義相似的文本在向量空間中的距離更近，是語義搜尋的基礎。 |
| **Vector DB** | 向量資料庫 | 專門用於儲存和檢索向量資料的資料庫。支援透過相似度搜尋快速找到與查詢最匹配的文件片段。 |
| **Temperature** | 溫度 | 控制模型輸出隨機性的超參數。數值越高（如 0.8）輸出越多樣、有創意；數值越低（如 0.2）輸出越確定、嚴謹。 |
| **TTFT** | 首字延遲 | Time to First Token，即從使用者發送請求到模型輸出第一個 Token 所花費的時間，是衡量互動體驗的關鍵指標。 |

---

## 總結：上下文工程的本質

Manus 的四次重構告訴我們：

**從實踐來看**：不是記得越多越好，而是記得越有結構、越有選擇性越好。

**從成本視角看**：
- 大部分浪費來自對固定前綴的重複計算，需要透過前綴穩定和快取機制解決；
- 重要資訊被誤刪，往往源於「一視同仁」的滑動視窗，需要透過資訊分級與釘住策略解決；
- 面對超長文件和知識庫時，僅依賴增大上下文視窗並不現實，必須結合檢索與壓縮機制。

目標是：在給定的模型與上下文上限下，讓每一個 token 的投入都具備明確的用途。
