# Stage 2 — Prompt 設計(Prompt Engineering)
> **繁體中文** | [简体中文](./02-prompt-engineering.zh-Hans.md) | [English](./02-prompt-engineering.en.md)
⏱ **時間估算**:1-2 週(約 5-12 小時)
> 👋 **從 [Stage 1](01-llm-basics.md) 來的**:好,你會呼叫 API 了——這 5-12 小時:寫出可重用的結構化 prompt、用 few-shot 跟 chain-of-thought 解難題、用 eval 量化 prompt 改善幅度。**直接從這裡開始的**:先確認你會呼叫 LLM API、會用 token 算成本——做不到請先回 [Stage 1](01-llm-basics.md)。
> 💡 用語不熟(prompt / few-shot / CoT / system prompt⋯)→ 翻 [`resources/glossary.md`](../resources/glossary.md)。
> 📋 **本章組成**:學習目標 → 進入條件 → 必修閱讀 →〔可選 · 概念地圖〕→ 動手練習 → 精選 Projects → 自我檢查
> 🔑 **關鍵名詞**:見 [`resources/glossary.md`](../resources/glossary.md)(每 stage 用到的術語都收在那裡)
## 📌 學習目標
走完這個階段後你會:
- 寫出結構化 prompt(角色 + 任務 + 格式 + 範例)
- 應用 few-shot prompting,並知道什麼時候有用
- 在推理任務上使用 chain-of-thought(CoT)
- 反覆迭代修改一個 prompt 並衡量改善
- 看出什麼時候 prompt 已經到極限了(這時你需要 tool / agent)
## 🚪 進入條件
你應該已經:
- 會呼叫 LLM API(Stage 1)
- 會解析 / 走訪 API 回應
## 📚 必修閱讀
1. [**anthropics/prompt-eng-interactive-tutorial**](https://github.com/anthropics/prompt-eng-interactive-tutorial) ⭐⭐⭐⭐⭐ ★ 35k+ — **Anthropic 官方互動教學**、9 章 Jupyter notebook(basic / intermediate / advanced + appendix),含 playground 跟 answer key。用 Claude 3 Haiku(最便宜)跑得起來、**Stage 2 的 canonical 動手教材**。也是 [**anthropics/courses**](https://github.com/anthropics/courses) 5 course umbrella 的 module 2,想看更廣(含 API Fundamentals / Real World Prompting / Eval / Tool Use)直接看 umbrella
2. [**anthropics/courses — Real World Prompting**](https://github.com/anthropics/courses) ⭐⭐⭐⭐ ★ 21k+ — 同 umbrella 的 module 3,**「真實情境下怎麼用 prompting」**:chatbot / legal / financial / coding 案例 walk-through。看完 #1 再來看 #2
3. [**Anthropic Prompt Engineering Guide**](https://docs.anthropic.com/en/docs/build-with-claude/prompt-engineering/overview) — 官方 docs、配合上面 #1 一起讀
3. [**OpenAI Prompt Engineering**](https://platform.openai.com/docs/guides/prompt-engineering) — OpenAI 觀點
4. [**dair-ai Prompt Engineering Guide**](https://www.promptingguide.ai/) — 學術風,深入
5. [**Anthropic — Prompting Best Practices**](https://docs.anthropic.com/en/docs/build-with-claude/prompt-engineering/be-clear-and-direct) — 直接清楚
**🎥 中文影片補充(強烈推薦)**:
- [**李宏毅 — 生成式 AI 導論(2024 春台大課程)**](https://speech.ee.ntu.edu.tw/~hylee/genai/2024-spring.php) ⭐⭐⭐ — 中後段集數講 prompt engineering(few-shot、CoT、in-context learning)+ 對應 lab。中文圈最完整的 prompting 學術級教學。最新整合版見 [**GenAI-ML 2025 秋**](https://speech.ee.ntu.edu.tw/~hylee/GenAI-ML/2025-fall.php)
- [**李宏毅 — 機器學習 2025 春(含 prompt + LLM 章節)**](https://speech.ee.ntu.edu.tw/~hylee/ml/2025-spring.php) — 適合想看 ML 完整背景的人
## 🛠 動手練習
> 🦙 **本 stage 預設用 Ollama gemma4:e4b**(成本考量、$0/run)。Prompt engineering 對小 model 更有教學價值——小 model 對 prompt 質量敏感、能讓你看清楚 system prompt / few-shot / CoT / refinement 各自帶來多少改善。每個練習都有 Path A(Ollama、預設)+ Path B(Anthropic、選擇性)。
>
> 💰 **Stage 2 預算估算**(全 4 練習各跑 3-5 次):**全本機 = $0**、**全 haiku ≈ $0.20**、**全 sonnet ≈ $0.60**。Few-shot 分類任務的 12 calls × 5 reps ≈ $0.30 haiku / $0.90 sonnet。完整預算見 [`examples/README.md#推薦-llm-清單`](../examples/README.md#推薦-llm-清單)。
>
> 完整 3 路 trade-off 見 [`examples/README.md`](../examples/README.md#三條路徑--預設用-ollama成本考量)。
### 練習 1:System Prompt
同樣的 user message,三個不同的 system prompt。觀察人格 / 輸出格式怎麼變。
📋 起手碼 — Path A(本機 Ollama gemma4:e4b、預設)(複製到 practice_1.py)
```python
# 需要:pip install openai
# 前置:ollama pull gemma4:e4b && ollama serve
import sys, json
if hasattr(sys.stdout, "reconfigure"):
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
from openai import OpenAI
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
# 同一個 user message、3 個不同 system prompt
SYSTEM_PROMPTS = {
"嚴肅律師": "你是嚴謹的合約律師。回答要精準、引用法條編號、避免任何主觀形容詞。",
"幼兒園老師": "你是溫柔的幼兒園老師、要對 5 歲小孩說話。用比喻、口語、少於 80 字。",
"JSON 機器": "你只回 JSON。schema: {\"answer\": string, \"confidence\": float}",
}
USER_MSG = "請幫我解釋什麼是租賃合約。"
outputs = {}
for label, system in SYSTEM_PROMPTS.items():
# Note: Ollama 把 system 放 messages 第一筆(不像 Anthropic 用 system= 參數)
r = client.chat.completions.create(
model="gemma4:e4b",
max_tokens=200,
messages=[
{"role": "system", "content": system},
{"role": "user", "content": USER_MSG},
],
)
outputs[label] = r.choices[0].message.content
print(f"\n--- [{label}] ---")
print(outputs[label])
# === 自我驗證 ===
json_output = outputs["JSON 機器"]
assert "{" in json_output and "}" in json_output, "JSON 機器版輸出應該含 JSON braces"
try:
parsed = json.loads(json_output.strip().split("\n")[-1] if "\n" in json_output else json_output)
assert "answer" in parsed, "JSON schema 應包含 answer 欄位"
except json.JSONDecodeError:
pass # 容許 model 回 JSON 含解釋文字、最後一筆才是 JSON
print(f"\n✅ 練習 1 通過 — 同一個問題、3 種人格 / 格式 / 語氣")
print("💡 觀察:律師長、老師短、JSON 機器一定是 {...}")
```
**預期輸出**(樣本、gemma4:e4b 對 system prompt 遵循度 OK 但不如 Claude 嚴謹):
```
--- [嚴肅律師] ---
依民法第 421 條...
--- [幼兒園老師] ---
租賃合約就像借玩具給朋友、講好什麼時候還、要付多少糖果...
--- [JSON 機器] ---
{"answer": "租賃合約是當事人約定一方以物租與他方使用...", "confidence": 0.85}
```
📋 起手碼 — Path B(Anthropic API、選擇性)(複製到 practice_1_anthropic.py)
```python
# 需要:pip install anthropic
import sys, json
if hasattr(sys.stdout, "reconfigure"):
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
import anthropic
client = anthropic.Anthropic()
SYSTEM_PROMPTS = {
"嚴肅律師": "你是嚴謹的合約律師。回答要精準、引用法條編號、避免任何主觀形容詞。",
"幼兒園老師": "你是溫柔的幼兒園老師、要對 5 歲小孩說話。用比喻、口語、少於 80 字。",
"JSON 機器": "你只回 JSON。schema: {\"answer\": string, \"confidence\": float}",
}
USER_MSG = "請幫我解釋什麼是租賃合約。"
outputs = {}
for label, system in SYSTEM_PROMPTS.items():
# Anthropic 用 system= 參數(不放 messages 內)
msg = client.messages.create(model="claude-haiku-4-5", max_tokens=200,
system=system, messages=[{"role": "user", "content": USER_MSG}])
outputs[label] = msg.content[0].text
print(f"\n--- [{label}] ---")
print(outputs[label])
# 同樣的 JSON assert(schema 跨 backend 通用)
json_output = outputs["JSON 機器"]
assert "{" in json_output and "}" in json_output
print(f"\n✅ 練習 1 通過(Anthropic)")
```
**主要差異**:
- Anthropic: `system=...` 參數
- Ollama / OpenAI-compatible: `messages=[{"role": "system", ...}, ...]`
**Anthropic 對 system prompt 遵循度通常比 4B 小 model 更嚴謹**——「嚴肅律師」會真的引用法條編號。
### 練習 2:Few-Shot
挑一個分類任務。先用 0-shot 跑,再用 3-shot 跑。量一下準確率差多少。
📋 起手碼 — Path A(本機 Ollama gemma4:e4b、預設)(複製到 practice_2.py)
```python
# 需要:pip install openai
# 前置:ollama pull gemma4:e4b && ollama serve
import sys
if hasattr(sys.stdout, "reconfigure"):
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
from openai import OpenAI
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
# 中文情緒分類(正面 / 負面 / 中立)
TEST_SET = [
("這部電影超讚、看完想再看一次!", "正面"),
("劇情無聊、演員演技尷尬。", "負面"),
("這是一部 2019 年的電影。", "中立"),
("我不確定喜不喜歡、可能再想想。", "中立"),
("第一集很不錯但第二集就崩了。", "負面"),
("看完心情很好、推薦!", "正面"),
]
FEW_SHOT_EXAMPLES = """範例:
input: 這家餐廳的牛排好吃到讓我哭出來。
output: 正面
input: 服務生態度很差、我再也不會來了。
output: 負面
input: 這家店位於新北市三重區。
output: 中立
"""
def classify(text: str, *, use_few_shot: bool) -> str:
prefix = FEW_SHOT_EXAMPLES + "\n" if use_few_shot else ""
prompt = f"{prefix}input: {text}\noutput:"
r = client.chat.completions.create(
model="gemma4:e4b",
max_tokens=10,
messages=[{"role": "user", "content": prompt}],
)
return r.choices[0].message.content.strip().splitlines()[0]
def evaluate(use_few_shot: bool) -> tuple[int, int]:
correct = 0
for text, label in TEST_SET:
pred = classify(text, use_few_shot=use_few_shot)
ok = label in pred
print(f" {'✓' if ok else '✗'} [{label}] {text[:30]}... → '{pred}'")
if ok:
correct += 1
return correct, len(TEST_SET)
print("=== 0-shot ===")
c0, n = evaluate(use_few_shot=False)
print(f"正確 {c0}/{n} = {c0/n:.0%}")
print("\n=== 3-shot ===")
c3, _ = evaluate(use_few_shot=True)
print(f"正確 {c3}/{n} = {c3/n:.0%}")
# === 自我驗證 ===
assert c3 >= c0, f"預期 3-shot 不比 0-shot 差、實際 {c3} < {c0}(小 model 樣本小、跑幾次比較)"
print(f"\n✅ 練習 2 通過 — 0-shot {c0}/{n}、3-shot {c3}/{n}(本機 $0)")
print("💡 觀察:'中立' 在 0-shot 容易被誤判成正面或負面、3-shot 後改善明顯")
print("💡 小 model(gemma4:e4b)通常 0-shot 表現比 Claude 差更多、所以 few-shot 改善幅度更大")
```
📋 起手碼 — Path B(Anthropic API、選擇性)(複製到 practice_2_anthropic.py)
```python
# 需要:pip install anthropic
# 把 starter Path A 的 client 跟 classify() 改成:
import anthropic
client = anthropic.Anthropic()
def classify(text: str, *, use_few_shot: bool) -> str:
prefix = FEW_SHOT_EXAMPLES + "\n" if use_few_shot else ""
msg = client.messages.create(
model="claude-haiku-4-5",
max_tokens=10,
messages=[{"role": "user", "content": f"{prefix}input: {text}\noutput:"}],
)
return msg.content[0].text.strip().splitlines()[0]
# 其餘 TEST_SET / FEW_SHOT_EXAMPLES / evaluate() 跟 Path A 一樣
```
**成本**:6 題 × 2 條件 = 12 次 ≈ $0.005。**Claude 通常 0-shot 已經有不錯準確率**、所以 few-shot 改善幅度比小 model 小。
### 練習 3:CoT
挑一個數學文字題,比較:
- 純 prompt
- 純 prompt + 「Let's think step by step」
- 純 prompt + 一個展示 CoT 的範例
📋 起手碼 — Path A(本機 Ollama gemma4:e4b、預設)(複製到 practice_3.py)
```python
# 需要:pip install openai
# 前置:ollama pull gemma4:e4b && ollama serve
import sys, re
if hasattr(sys.stdout, "reconfigure"):
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
from openai import OpenAI
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
QUESTION = "小明有 3 顆蘋果。他給了小華 1 顆、又從媽媽那邊拿到 5 顆、然後吃了 2 顆。請問現在剩幾顆?"
ANSWER = 5 # 3 - 1 + 5 - 2 = 5
COT_EXAMPLE = """範例:
Q: 一隻雞有 2 隻腳。3 隻雞跟 1 個人共有幾隻腳?
A: 讓我一步一步算。3 隻雞 × 2 隻腳 = 6 隻腳。1 個人有 2 隻腳。總共 6 + 2 = 8 隻腳。答案是 8。
"""
def ask(prompt: str) -> str:
r = client.chat.completions.create(
model="gemma4:e4b",
max_tokens=300,
messages=[{"role": "user", "content": prompt}],
)
return r.choices[0].message.content
def extract_number(text: str) -> int | None:
nums = re.findall(r"-?\d+", text)
return int(nums[-1]) if nums else None
# A. 純 prompt
out_a = ask(QUESTION); ans_a = extract_number(out_a)
# B. + Let's think step by step
out_b = ask(QUESTION + "\nLet's think step by step."); ans_b = extract_number(out_b)
# C. + CoT example
out_c = ask(COT_EXAMPLE + "\n\nQ: " + QUESTION + "\nA:"); ans_c = extract_number(out_c)
for label, out, ans in [("A 純 prompt", out_a, ans_a), ("B +step-by-step", out_b, ans_b), ("C +CoT example", out_c, ans_c)]:
print(f"\n--- [{label}] 答案={ans} {'✓' if ans == ANSWER else '✗'} ---")
print(out[:200])
# === 自我驗證 ===
correct = sum(1 for a in (ans_a, ans_b, ans_c) if a == ANSWER)
assert correct >= 1, f"3 種 prompt 至少要 1 種答對、實際 {correct}/3"
# 小 model 對 CoT 依賴性更高、放寬條件:B 或 C 至少 1 對(vs Anthropic Path B 要求嚴格)
assert ans_b == ANSWER or ans_c == ANSWER, "B (step-by-step) 或 C (CoT example) 至少一種要答對 — CoT 對小 model 是基本功"
print(f"\n✅ 練習 3 通過 — {correct}/3 答對(本機 $0)")
print(f"💡 觀察小 model:A 純 prompt 通常答錯、B/C 加 CoT 後明顯改善——比 Claude 更能凸顯 CoT 重要性")
```
📋 起手碼 — Path B(Anthropic API、選擇性)(複製到 practice_3_anthropic.py)
把 Path A 的 client + ask() 改成:
```python
import anthropic
client = anthropic.Anthropic()
def ask(prompt: str) -> str:
msg = client.messages.create(model="claude-haiku-4-5", max_tokens=300,
messages=[{"role": "user", "content": prompt}])
return msg.content[0].text
```
**Claude 通常 3/3 全對**(包括 A 純 prompt)—— 對照 gemma4:e4b 可能只 1-2/3 對,能看到 CoT 對小 model 的價值。
### 練習 4:Iterative Refinement
拿一個模糊的 prompt,refine 5 次。把每一輪記下來。觀察哪些改動會提升品質。
📋 起手碼 — Path A(本機 Ollama gemma4:e4b、預設)(複製到 practice_4.py)— 這題沒有「對錯」、重點是觀察過程
```python
# 需要:pip install openai
# 前置:ollama pull gemma4:e4b && ollama serve
import sys
if hasattr(sys.stdout, "reconfigure"):
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
from openai import OpenAI
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
# 5 個 iteration、每一輪 prompt 都比前一輪更具體
PROMPTS = {
"v1 模糊": "寫一段介紹 ReAct 的文字。",
"v2 加目標讀者": "寫一段介紹 ReAct 的文字、給寫過 Python 的軟體工程師看。",
"v3 加格式": "寫一段介紹 ReAct 的文字、給寫過 Python 的軟體工程師看。100 字以內、用一個段落。",
"v4 加 example 要求": "寫一段介紹 ReAct 的文字、給寫過 Python 的軟體工程師看。100 字以內、用一個段落、結尾舉一個具體例子(譬如查天氣)。",
"v5 加禁忌": "寫一段介紹 ReAct 的文字、給寫過 Python 的軟體工程師看。100 字以內、用一個段落、結尾舉一個具體例子(譬如查天氣)。不要用「賦能」「驅動」「智能」這類空泛詞彙。",
}
outputs = {}
for label, prompt in PROMPTS.items():
r = client.chat.completions.create(
model="gemma4:e4b",
max_tokens=200,
messages=[{"role": "user", "content": prompt}],
)
text = r.choices[0].message.content
outputs[label] = text
print(f"\n--- [{label}] ({len(text)} chars) ---")
print(text)
# === 自我驗證 ===
v1_len, v5_len = len(outputs["v1 模糊"]), len(outputs["v5 加禁忌"])
banned_words = ("賦能", "驅動", "智能")
v5_has_banned = any(w in outputs["v5 加禁忌"] for w in banned_words)
assert v5_len > 0, "v5 必須有輸出"
assert not v5_has_banned, f"v5 應該避免禁忌詞、實際含: {[w for w in banned_words if w in outputs['v5 加禁忌']]}"
print(f"\n✅ 練習 4 通過 — v5 長度 {v5_len}、無禁忌詞(本機 $0)")
print(f"💡 觀察:v1 ({v1_len} chars) 通常比 v5 ({v5_len} chars) 「鬆」、加約束會逼 prompt 收斂")
print("💡 用 gemma4:e4b 跑這題特別有感——小 model 對 prompt 質量極敏感、5 輪 refine 的差距會比 Claude 更明顯")
```
📋 起手碼 — Path B(Anthropic API、選擇性)(複製到 practice_4_anthropic.py)
把 Path A 的 client + 迴圈內 `client.chat.completions.create(...)` 改成:
```python
import anthropic
client = anthropic.Anthropic()
# 迴圈內:
msg = client.messages.create(model="claude-haiku-4-5", max_tokens=200,
messages=[{"role": "user", "content": prompt}])
text = msg.content[0].text
```
其餘 PROMPTS / outputs / assert 邏輯完全相同。**成本**:5 次 ≈ $0.002。
**Claude vs gemma4 對 prompt 細緻度的差別**:Claude haiku 通常 v1 已能寫出 OK 段落、v5 加上約束後優化幅度較小;小 model v1 常空泛無用、v5 加禁忌後才開始能讀。
**進階做法**:把這 5 輪輸出全存進 csv、Stage 7 練習 2 會教怎麼把這變成 eval harness(評估腳手架、即「跑評估用的外圍程式 / 控制層」、完整定義見下面 進階:prompt → context → harness 三層 engineering)量化「prompt 改善了多少」。
## 🎯 精選 Projects
按用途分 4 類、9 個項目一張表搞定。**挑入口看「適合誰」、想深入點連結看 repo / 網站**。
| 分類 | Project | ⭐ | 適合誰 | 為什麼推薦 / 備註 |
|---|---|---|---|---|
| **學術 / 教學風 guide**
(先看這個) | [dair-ai/Prompt-Engineering-Guide](https://github.com/dair-ai/Prompt-Engineering-Guide) | ⭐⭐⭐⭐⭐ | 當參考書、需要某技巧再來查 | 從基礎到進階(CoT / ToT / ReAct / RAG)端到端,★ 74k+、MIT |
| | [PromptingGuide.ai](https://www.promptingguide.ai/) | ⭐⭐⭐⭐ | 手機閱讀、想要可跑範例 | 跟 dair-ai GitHub 同樣內容、做成網站 + 可跑範例 |
| | [NirDiamant/Prompt_Engineering](https://github.com/NirDiamant/Prompt_Engineering) | ⭐⭐⭐⭐ | 偏好「邊跑邊學」 | 22 種技巧(zero-shot → CoT → ReAct → constitutional)獨立 notebook,★ 7k+。比 dair-ai 更動手(⚠️ NOASSERTION 自訂條款、研究/非商用為主)|
| **官方 cookbook** | [Anthropic Cookbook — Prompt patterns](https://github.com/anthropics/claude-cookbooks) | ⭐⭐⭐⭐⭐ | Claude 進階 prompting(含 prompt caching / multimodal)| Stage 1 已介紹、本 stage 重點看 `misc/prompt_caching.ipynb` 跟 `multimodal/` |
| | [GoogleCloudPlatform/generative-ai](https://github.com/GoogleCloudPlatform/generative-ai) | ⭐⭐⭐ | 用 Google 技術棧(PaLM / Gemini)| Google Cloud 的 prompting cookbook、跨廠商觀點 |
| **靈感 collection**
(找模式、不要照抄)| [f/awesome-chatgpt-prompts](https://github.com/f/awesome-chatgpt-prompts) | ⭐⭐⭐ | 卡關時找靈感 | 上百個「Act as a [角色]...」prompt,★ 162k+、CC0。**把模式拿出來改寫、不要照抄** |
| **Production 管理**
(規模化)| [microsoft/prompt-engine](https://github.com/microsoft/prompt-engine) | ⭐⭐⭐ | production 要管很多 prompt 時 | TypeScript library、管理樣板 + 對話歷史 |
| | [microsoft/promptflow](https://github.com/microsoft/promptflow) | ⭐⭐⭐ | 團隊型應用、需要 eval | 視覺化 prompt 設計 + 評估工具,★ 11k+ |
| | [stanfordnlp/dspy](https://github.com/stanfordnlp/dspy) ⭐ **Stage 2 → 3 橋** | ⭐⭐⭐⭐⭐ | 跑完 dair-ai 想規模化 prompt | 把 prompt 當 code 寫——define signature / module、用 compiler 自動最佳化,★ 34k+、MIT。**framework 非 tutorial、門檻較高、搭配 dspy.ai 官方 tutorial 讀** |
> 💡 **建議閱讀路徑**:dair-ai guide 入手(理論) → Anthropic Cookbook 看 Claude 實作 → NirDiamant 邊跑邊學 → 進 production 時讀 dspy。
## 🔭 進階:prompt → context → harness 三層 engineering
LLM-powered system 的工程實踐分成 **3 層 stack**(不是 1 次 call vs N 次 call)。每一層工程的對象**不一樣**:
- **Prompt Engineering**(本 stage)= 工程「**送進模型的那段字串**」
- **Context Engineering**(Stage 6)= 工程「**每次 call 時、 context window 裡裝什麼資訊**」——把 RAG retrieve 結果、memory、tool definitions、對話 history 動態組裝
- **Harness Engineering**(Stage 7)= 工程「**模型外面的 runtime / scaffolding**」——agent loop、retry、sandbox、observability、deployment 等所有非 LLM 程式碼
→ 三層**正交**:一次 call 的 RAG app 也在做 context engineering(重點是組 context、不是 call 幾次);50 次 call 但沒做 retrieval 的 chatbot 仍只在做 prompt engineering。
**完整三層 lineage(本路線的學習進度)**:
| Discipline | 工程「什麼」 | 在哪一 stage 完整學 |
|---|---|---|
| **1. Prompt Engineering** | 送進 LLM 的字串本身(system prompt / few-shot / format) | **本 stage(Stage 2)** |
| **2. Context Engineering** | context window 裡裝什麼資訊(RAG / memory / tool defs / history) | [Stage 6 — Memory · RAG · Context Engineering](06-memory-rag.md) |
| **3. Harness Engineering** | LLM 外面的 runtime scaffolding(agent loop / retry / sandbox / observability) | [**Stage 7 Harness Engineering**](07-multi-agent-production.md#-harness-engineering--production-agent-runtime-的工程設計--本-stage-核心概念) ⭐ 完整對照表 |
> 💡 **Karpathy 2025-06**:「context engineering 是把對下一步有用的資訊**剛好填進** context window 的精細藝術」(it's about *what goes in the window*)。
>
> 💡 **Simon Willison / Addy Osmani**:「coding agent = LLM + harness」、harness = 所有不是 model 本身的程式碼。[OpenAI 2026-02 也使用 "Harness Engineering" 這個說法](https://openai.com/index/harness-engineering)。
**這個 stage 不用學完後兩層**,只是給方向性提示——進入 Stage 6 / 7 時會接續這個 lineage。
延伸閱讀(不必修、未來想深挖時看):
- [`Meirtz/Awesome-Context-Engineering`](https://github.com/Meirtz/Awesome-Context-Engineering)(★ 3k+)——從 prompt engineering 一路推到 production agent 的 survey
- [`Windy3f3f3f3f/how-claude-code-works`](https://github.com/Windy3f3f3f3f/how-claude-code-works)(★ 2.4k+)——Claude Code 內部解析,含 context engineering 章節
## ✅ 進 Stage 3 前的自我檢查
你能不能:
- [ ] 寫一個有 system message + user message + 3 個範例 message 的 prompt(few-shot)
- [ ] 示範 CoT 在某個推理任務上提升準確率
- [ ] 反覆 refine 一個 prompt 5 次,每一版都留下記錄
- [ ] 看出 prompt 不是對的工具的時候(這時要用 tool use)
如果可以 → 進 [Stage 3 — Tool Use & Agent 入門](03-tool-use-and-hello-agent.md)。這是最重要的一個階段——prompt 不要急著跳過去,但也不要卡在這裡。