Skip to content

拆解 Claude Code 的 API Request:System Prompt 與 Message 結構全解析

Posted on:2026年4月2日

拆解 Claude Code 的 API Request:System Prompt 與 Message 結構全解析

前言

使用 Claude Code 時,底層實際上是透過 Anthropic Messages API 與 Claude 模型溝通 但 Claude Code 在發送 request 之前,會注入大量的 system prompt、工具定義、動態上下文等資訊 這些都不是使用者直接寫的——而是 Claude Code 自動組裝的

本文從一份實際發送給 Anthropic Claude API 的 request body —— 拆解其完整結構 理解 Claude Code 是怎麼把「使用者的一句話」包裝成一個完整的 API 請求


一、Request 的整體結構

一個 Claude Code 發出的 API request 大致長這樣:

{
  "model": "claude-opus-4-6",
  "system": [ ... ],      // System Prompt(核心行為指令)
  "messages": [ ... ],    // 對話歷史(包含動態注入的 system-reminder)
  "tools": [ ... ]        // 工具定義(JSON Schema)
}

四大區塊各司其職:

區塊角色特性
model指定使用的模型固定值
system核心 system prompt穩定、可快取
messages對話歷史 + 動態注入每輪變動
tools可用工具的 schema 定義預載入的工具

二、System Prompt — 核心行為指令

system 欄位是 Anthropic Messages API 原生支援的頂層參數,模型會將其視為最高優先級的指令。system 包含 3 個 text block

2.1 三個 Text Block

Block 1:Billing Metadata

{
  "type": "text",
  "text": "x-anthropic-billing-header: cc_version=2.1.83.c50; cc_entrypoint=cli; cch=4abe5;"
}

不是給模型看的指令,而是用於計費與版本追蹤的 metadata。記錄了 Claude Code 的版本號、入口點等資訊

Block 2:角色定義(一句話)

{
  "type": "text",
  "text": "You are Claude Code, Anthropic's official CLI for Claude.",
  "cache_control": { "type": "ephemeral", "ttl": "1h" }
}

簡短的角色宣告,注意這裡帶了 cache_control

Block 3:完整行為規範(約 400+ 行)

這是整個 system prompt 的主體,內容非常龐大。以下列出主要章節:

# System                       — 基本行為規則(輸出格式、工具權限、hook 機制)
# Doing tasks                  — 任務執行原則(先讀再改、不過度工程)
# Executing actions with care  — 謹慎執行(不可逆操作要確認)
# Using your tools             — 工具使用規則(用 Read 不用 cat、用 Grep 不用 grep)
# Tone and style               — 風格(簡潔、不用 emoji、用 file:line 引用)
# Output efficiency            — 輸出效率(直奔重點、不廢話)
# auto memory                  — Memory 持久化系統(四種記憶類型的讀寫規則)
# Environment                  — 環境資訊(工作目錄、平台、shell、模型版本)

幾個值得注意的設計:

2.2 cache_control 的作用

Block 2 和 Block 3 都帶了 cache_control

"cache_control": { "type": "ephemeral", "ttl": "1h" }

這是 Anthropic API 的 Prompt Caching 功能 由於 system prompt 在多輪對話中幾乎不變,加上 cache 可以避免重複處理相同的 token,降低延遲與成本

ephemeral 表示快取存活 1 小時,過期後重新計算


三、System Reminder — 動態注入的上下文

3.1 什麼是 System Reminder?

除了正式的 system 欄位,Claude Code 還會在 messages 中注入 <system-reminder> 標籤。它的形式是普通的 user message text,但被 <system-reminder> 標籤包裹:

{
  "role": "user",
  "content": [
    { "type": "text", "text": "<system-reminder>...</system-reminder>" },
    { "type": "text", "text": "今天 MLB 有比賽嗎" } // 真正的使用者輸入
  ]
}

為什麼不直接放在 system 裡?

system 欄位的內容在整個對話中是固定的(且已加了 cache),但有些資訊需要每一輪動態更新——例如可用工具清單可能隨著對話進展而變化、當前日期每天不同 把這些動態資訊以 <system-reminder> 的形式注入 messages,既不影響 system prompt 的快取,又能確保模型拿到最新的上下文

3.2 三種 System Reminder 的用途

第一次 user message 會發送包含 3 個 system-reminder:

Reminder 1:Deferred Tools 清單

<system-reminder>
The following deferred tools are now available via ToolSearch:
AskUserQuestion, CronCreate, CronDelete, CronList,
EnterPlanMode, ExitPlanMode, NotebookEdit, WebFetch, WebSearch...
</system-reminder>

告訴 Claude:「這些工具存在,但 schema 還沒載入。需要時先用 ToolSearch 取得完整定義。」這是一種 lazy loading 機制

Reminder 2:Skills 清單

<system-reminder>
The following skills are available for use with the Skill tool:
- update-config: ...
- keybindings-help: ...
- simplify: ...
- loop: ...
- schedule: ...
- claude-api: ...
</system-reminder>

告訴 Claude 有哪些 slash command(如 /commit/simplify)可以呼叫

Reminder 3:當前日期

<system-reminder>
Today's date is 2026-03-26.
</system-reminder>

LLM 本身沒有即時時間概念,需要外部注入

3.3 System vs System Reminder 對照

system(API 欄位)<system-reminder>(messages 內)
API 層級原生 system prompt普通 user message text
模型如何看待最高優先級指令被訓練為系統資訊對待
內容特性穩定、長期不變動態、每輪可能更新
快取cache_control無(不需要,因為會變動)
典型內容角色定義、行為規範、SOP可用工具、skills、日期

四、Messages — 一次對話的完整流程

4.1 對話背景

使用者問了:「今天 MLB 有比賽嗎」& 「轉換成 +8 時區顯示」。整個對話產生了 8 個 messages

4.2 逐步拆解

Message 1 — User 提問

role: user
content: [system-reminder x3] + "今天 MLB 有比賽嗎"

Claude Code 在使用者輸入前面注入了 3 個 system-reminder,然後才是真正的使用者問題

Message 2 — Assistant 呼叫 ToolSearch

role: assistant
content: [thinking] + ToolSearch({ query: "WebSearch", max_results: 1 })

Claude 判斷需要搜尋網路,但 WebSearch 是 deferred tool——只知道名字,沒有完整的 JSON Schema,無法直接呼叫。所以先呼叫 ToolSearch 來取得 WebSearch 的完整定義

Message 3 — ToolSearch 結果回傳

role: user
content: tool_result(tool_reference: WebSearch) + "Tool loaded."

Harness 回傳 WebSearch 的 schema 已載入 注意這裡的 role: user 不是真正的使用者——所有 tool result 在 Anthropic API 中都必須以 role: user 回傳,這是 API 的設計規範

Continue the conversation by sending a new message with the role of user, and a content block containing the tool_result type and the following information

Message 4 — Assistant 呼叫 WebSearch

role: assistant
content: WebSearch({ query: "MLB games today March 26 2026" })

現在 WebSearch 的 schema 已載入,Claude 正式發出搜尋請求

Message 5 — WebSearch 結果回傳

role: user
content: tool_result(搜尋結果:11 場比賽、連結清單、轉播資訊)

Harness 執行實際的網路搜尋,把結果以 tool_result 的形式回傳

Message 6 — Assistant 整理回覆

role: assistant
content: 用中文整理的比賽表格(含隊伍、時間、轉播平台)

Claude 將搜尋結果整理成結構化的中文回答,不需要呼叫任何工具

Message 7 — User 追問

role: user
content: "轉換成 +8 時區顯示"

使用者追問,要求時區轉換

Message 8 — Assistant 直接回覆

role: assistant
content: [thinking] + 時區轉換後的表格

這次不需要工具,Claude 直接計算 ET → UTC+8(+12 小時)並回覆。thinking block 中的推理過程被加密簽名(signature 欄位),外部無法查看具體內容

4.3 Deferred Tools 與 Lazy Loading 機制

整個 Message 2-3 的 ToolSearch 流程,體現了 Claude Code 的 lazy loading 設計

預載入的工具(在 tools 欄位中有完整 schema):
  Agent, Bash, Edit, Glob, Grep, Read, Write, Skill, ToolSearch

Deferred 工具(只有名字,需要時才載入 schema):
  WebSearch, WebFetch, TaskCreate, NotebookEdit, CronCreate...

為什麼要這樣設計?

每個工具的 JSON Schema 定義都會佔用 token。如果把所有工具的完整 schema 都放進 tools 欄位,會大幅增加每次 request 的 token 數量(和成本) Deferred tools 機制讓 Claude 只在需要時才載入特定工具的 schema,節省不必要的 token 開銷

流程圖:

使用者提問
  → Claude 判斷需要 WebSearch
  → WebSearch 是 deferred tool,schema 未載入
  → 呼叫 ToolSearch 取得 schema
  → schema 載入完成
  → 正式呼叫 WebSearch
  → 取得結果、回覆使用者

五、Tools — 工具定義

5.1 工具的結構

每個工具在 tools 陣列中以標準格式定義:

{
  "name": "Bash",
  "description": "Executes a given bash command...",
  "input_schema": {
    "type": "object",
    "properties": {
      "command": { "type": "string", "description": "The command to execute" },
      "timeout": { "type": "number" },
      "description": { "type": "string" }
    },
    "required": ["command"]
  }
}

模型根據 description 判斷何時使用該工具,根據 input_schema 生成合法的參數

5.2 預載入 vs Deferred

類型範例特性
預載入Agent, Bash, Edit, Glob, Grep, Read, Write, Skill, ToolSearch完整 schema 在 tools 中,可直接呼叫
DeferredWebSearch, WebFetch, TaskCreate, NotebookEdit, CronCreate…只有名字在 system-reminder 中,需先用 ToolSearch 載入

預載入的都是高頻工具(讀檔、寫檔、搜尋、執行指令),幾乎每次對話都會用到。Deferred 的則是低頻或情境性工具,按需載入以節省 token

5.3 工具呼叫的 Message 格式

工具呼叫在 messages 中表現為一組 request-response:

// Assistant 發起工具呼叫
{ "role": "assistant", "content": [
    { "type": "tool_use", "id": "toolu_xxx", "name": "WebSearch", "input": { "query": "..." } }
]}

// Harness 回傳結果(注意 role 是 user)
{ "role": "user", "content": [
    { "type": "tool_result", "tool_use_id": "toolu_xxx", "content": "..." }
]}

每次工具呼叫都會消耗兩個 message(一去一回),這也是為什麼一個簡單的問題最終產生了 8 個 messages


附錄:完整 Request 結構示意

{
  "model": "claude-opus-4-6",

  "system": [
    { "text": "billing metadata" },
    { "text": "You are Claude Code...", "cache_control": {...} },
    { "text": "完整行為規範(400+ 行)...", "cache_control": {...} }
  ],

  "messages": [
    { "role": "user",      "content": ["<system-reminder>...deferred tools", "<system-reminder>...skills", "<system-reminder>...date", "今天 MLB 有比賽嗎"] },
    { "role": "assistant",  "content": ["thinking", "ToolSearch(WebSearch)"] },
    { "role": "user",       "content": ["tool_result: WebSearch loaded"] },
    { "role": "assistant",  "content": ["WebSearch(MLB games today)"] },
    { "role": "user",       "content": ["tool_result: 搜尋結果"] },
    { "role": "assistant",  "content": ["整理後的比賽表格"] },
    { "role": "user",       "content": ["轉換成 +8 時區顯示"] },
    { "role": "assistant",  "content": ["thinking", "時區轉換後的表格"] },
  ],

  "tools": [
    { "name": "Agent",     "description": "...", "input_schema": {...} },
    { "name": "Bash",      "description": "...", "input_schema": {...} },
    { "name": "Edit",      "description": "...", "input_schema": {...} },
    { "name": "Glob",      "description": "...", "input_schema": {...} },
    { "name": "Grep",      "description": "...", "input_schema": {...} },
    { "name": "Read",      "description": "...", "input_schema": {...} },
    { "name": "Write",     "description": "...", "input_schema": {...} },
    { "name": "Skill",     "description": "...", "input_schema": {...} },
    { "name": "ToolSearch","description": "...", "input_schema": {...} }
  ]
}