DOCSDK-解體-002
REV2026.04
SUBJECTAGENT SDK
VOLUMEII
LANGPY · TS
Kaitai Shinsho · Volume II

Agent · SDK · 解體新書

a ten-chapter dissection of the programmable harness · Python · TypeScript
QUERY MESSAGES CLIENT OPTIONS TOOLS MCP PERMISSIONS HOOKS SUBAGENTS SESSIONS
SURGEONCLAUDE
DRAFTED2026.04.08
FORMATSCROLL-HTML
VERIFIEDCONTEXT7
CHAPTERS10 + 序

卷一《Claude Code 解體新書》畫的是已經裝好的代理人 — 那是一具完整的身體。這一卷不同:我們拆的是「工廠」。Claude Agent SDK(原名 claude-code-sdk)是 Anthropic 釋出的 Python / TypeScript 函式庫,它讓你能用程式碼組裝出自己的 agent,而不是只能使用現成的 CLI。

SDK 的內核與 Claude Code 共用同一套 harness 引擎 — 同樣的 agentic loop、同樣的 tool dispatch、同樣的 MCP 通道。差別在於:SDK 把那些在 CLI 裡藏起來的旋鈕,全部變成函式參數與 callback。你可以自訂工具、自訂權限邏輯、自訂 hook、自訂 subagent、甚至把整台 agent 嵌進你自己的後端服務裡。

本卷分十回,從最簡單的 query() 一次呼叫開始,一層一層往上蓋:訊息、客戶端、選項、工具、MCP、權限、hook、分身、續命。由淺入深,每章可立即動手。程式碼以 Python 為主軸、TypeScript 為對照。

I.

query() 之魂

the one-shot call · async iterator as stream
FIG · 01
ENTRY POINT

Agent SDK 的入口只有一個函式:query()。你餵它一段 prompt 與一組 options,它回傳一個 async iterator — 不是一次性結果,而是一條流。模型與工具每產生一則訊息,iterator 就吐出一項,直到最後一則 ResultMessage 為止。這是最簡入口,也是最常用的入口。

▸ your_agent.py PYTHON from claude_agent_sdk import ( query, ClaudeAgentOptions, AssistantMessage, TextBlock, ) async def main(): async for msg in query( prompt="hello, what is 2+2?", options=ClaudeAgentOptions(), ): print(msg) ▸ your-agent.ts TYPESCRIPT import { query } from "@anthropic-ai/claude-agent-sdk"; for await (const msg of query({ prompt: "hello, what is 2+2?", options: {} })) { console.log(msg); } invoke SDK ENTRY query() yields ASYNC ITERATOR · stream of messages SystemMessage AssistantMessage + tool_use UserMessage AssistantMessage ResultMessage stream begin stream end · StopAsyncIteration SIGNATURE async def query(*, prompt: str | AsyncIterable[dict], options: ClaudeAgentOptions | None = None, transport: Transport | None = None) -> AsyncIterator[Message] 「一次呼叫、一條流、讀完即止。」
prompt可以是 str(最簡用法)或 AsyncIterable[dict](串流模式,適合多段持續輸入)。
optionsClaudeAgentOptions 的實例 — 決定用哪個 model、有哪些 tools、怎麼判定權限等等。預設空物件等於「內建 Claude Code 預設行為」。
AsyncIteratorPython 的 async for 或 TS 的 for await 逐筆讀取。每個項目是一個 Message 實例;順序即為發生順序。
為何流?因為一次 query() 內部可能包含多個 agentic turn(模型→工具→模型→…)。流模式讓你即時看見進展,不用等到全部跑完。
statelessquery() 每次呼叫都建立新 session。若你要多輪對話維持上下文,請用下一章的 ClaudeSDKClient
動手試把上方程式碼存成 .pypip install claude-agent-sdkasyncio.run(main())— 就會看見 message 流出來。
II.

訊息之型

message types · the shape of what streams back
FIG · 02
TYPE TREE

query() 流出來的每一項都是某種 Message。SDK 定義了四個主要型別 — SystemMessageUserMessageAssistantMessageResultMessage — 外加一個可選的 StreamEvent(細粒度串流用)。其中 AssistantMessagecontent 欄位又裝著一串 content block:文字、工具呼叫、思考區段。看懂這個型別樹,就能寫出穩健的訊息處理迴圈。

BASE TYPE Message SystemMessage subtype: str "init" · "compact" · "mcp_status" · … data: dict harness 通知 UserMessage content: str | list[block] parent_tool_use_id 你的輸入 · tool_result AssistantMessage content: list[Block] model: str parent_tool_use_id 模型輸出 · 展開 ↓ ResultMessage subtype: str total_cost_usd: float num_turns · duration usage · session_id 最後一則 StreamEvent event: dict partial streaming (含 delta) 選用 · include_partial expand content CONTENT BLOCKS · list[Block] TextBlock text: str 給使用者看的文字 ThinkingBlock thinking: str signature: str extended thinking ToolUseBlock id · name · input 模型想用工具 → harness 接手執行 ToolResultBlock tool_use_id · content · is_error 工具回注(在下一則 UserMessage 裡) TYPICAL TURN ORDER · 典型順序 SystemMessage(subtype="init") — 會話建立 AssistantMessage [TextBlock, ToolUseBlock] — 模型說話 + 想用工具 UserMessage [ToolResultBlock] — harness 自動產生:工具執行結果回注 AssistantMessage [TextBlock] — 模型根據結果繼續 ResultMessage(subtype="success", total_cost_usd=…) — 結束記錄 「讀 message 就是讀歷史 — 型別告訴你角色,content block 告訴你內容。」
SystemMessageharness 的內部通知(會話建立、compact 發生、MCP server 狀態等)。一般可忽略,除非要做細粒度觀測。
UserMessage包含你餵進去的輸入、或 harness 自動產生的 tool_result 回注。parent_tool_use_id 可追溯來源。
AssistantMessage最常處理的型別。content 是 list,內含 TextBlock(文字)、ToolUseBlock(工具呼叫)、ThinkingBlock(思考區段)。
ResultMessage流的最後一則。帶 total_cost_usdnum_turnsduration_msusagesession_id — 給你結算這次跑了多少。
isinstance 濾法典型處理:if isinstance(msg, AssistantMessage): for block in msg.content: if isinstance(block, TextBlock): print(block.text)
TS 對應TypeScript 以 discriminated union 呈現,用 msg.type === "assistant" 判別;欄位名採 camelCase(totalCostUsd 等)。
III.

客身持續

ClaudeSDKClient · the stateful multi-turn client
FIG · 03
LIFECYCLE

query() 是一次性的 — 每呼叫一次都建立新 session。如果你要做多輪對話、讓模型記得前面說過的話,就要用 ClaudeSDKClient。它是一個 async context manager:進入時 connect,期間可以 query / receive_response 多次,最後 disconnect。同一個 client 內部自動維持 session id,不用你手動管理。

ASYNC CONTEXT MANAGER · async with ClaudeSDKClient(options) as client: connect() __aenter__ spawn subprocess QUERY CYCLE · repeat as needed await client.query(prompt) send next message async for msg in ... receive_response() process messages TextBlock / ToolUseBlock until ResultMessage loop RUNTIME CONTROL · 執行期控制 interrupt() · 中斷當前生成 set_permission_mode(m) · 切權限模式 set_model(m) · 中途換模型 rewind_files(msg_id) · 檔案回捲 get_mcp_status() · MCP 健康檢查 reconnect_mcp_server(s) · 重連 MCP toggle_mcp_server(s, on) · 開關 MCP stop_task(id) · 停某 task get_server_info() · 版本資訊 disconnect() __aexit__ cleanup ▸ client_example.py PYTHON options = ClaudeAgentOptions(allowed_tools=["Read", "Edit", "Glob", "Grep"]) async with ClaudeSDKClient(options=options) as client: # turn 1 — session 建立 await client.query("Analyze the auth module") async for msg in client.receive_response(): print_response(msg) # turn 2 — 自動延續同一 session await client.query("Now refactor it to use JWT") async for msg in client.receive_response(): print_response(msg) ← __aexit__ 自動 disconnect,subprocess 關閉 「一次 connect, 多次 query, 一次 disconnect — 像 HTTP keep-alive.」
session id · 自動維持

ClaudeSDKClientconnect() 時建立 session,之後每次 query() 自動沿用同一個 session id。模型看得見之前的對話歷史。若要起全新 session,就開新 client 實例(或見第拾回的 fork_session)。

interrupt · 中斷生成

若模型在長篇輸出中、你想立刻打斷:await client.interrupt()。harness 會立刻終止當前 turn,你接著可以發下一則 query()。這對實作「Esc 鍵」或倒數計時終止很有用。

IV.

系統之諭

ClaudeAgentOptions · the configuration dict that shapes everything
FIG · 04
OPTIONS MAP

ClaudeAgentOptions 是 SDK 所有可調參數的集合 — system prompt、model、工具白名單、權限 callback、hook 註冊、MCP server、subagent 定義、session 恢復等都寫在這一份 dataclass(Python)或 object(TypeScript)裡。本章把它分成六大區,逐區標註。

▸ ClaudeAgentOptions PY dataclass · TS object A · PERSONA · 人格 system_prompt : str | "default" · 蓋掉整份 system append_system_prompt : str · 追加而不覆蓋 model / fallback_model : str · 指定模型 B · TOOLS · 工具白/黑名單 allowed_tools : list[str] · 明確允許 disallowed_tools : list[str] · 明確禁止 extra_args : dict · 傳給底層 CLI 的附加參數 C · PERMISSIONS · 權限 permission_mode : "default" | "plan" | "acceptEdits" | "bypass" can_use_tool : CanUseTool callback · 自訂權限邏輯 sandbox : SandboxSettings · 沙箱設定 D · EXTENSIONS · 擴充接縫 hooks : dict[event, HookMatcher[]] · 見第捌回 mcp_servers : dict[name, config] · 見第陸回 agents : dict[name, AgentDefinition] · 見第玖回 E · SESSION / ENV · 會話與環境 cwd : str · 工作目錄 env : dict · 環境變數覆寫 resume : str (session_id) · 恢復先前會話 fork_session : bool · 恢復時分歧新 id F · ADVANCED · 進階 max_turns : int · 限制 agent 迴圈次數 thinking / effort : extended thinking 控制 include_partial_messages : bool · 開啟 StreamEvent setting_sources : list · ["project"] 載 CLAUDE.md G · MISC SCALARS user : str add_dirs : list[str] stderr : Callable debug_stderr : TextIO plugins : list · 載入 plugin path_to_claude_code_executable : str · 自訂 CLI 路徑 · · · 大部分欄位都有預設;空 ClaudeAgentOptions() 也可直接用 · · · ⚡ QUICK RECIPES · 常用組合 · 唯讀探索:allowed_tools=["Read", "Grep", "Glob"], permission_mode="plan" · 自動接受編輯:permission_mode="acceptEdits"(注意:沒有 canUseTool 時會直接動檔) 「每一個欄位都是一把旋鈕 — 什麼都不調也能跑,調到對味就是好 agent.」
命名差異Python 用 snake_case(system_promptallowed_tools);TypeScript 用 camelCase(systemPromptallowedTools)。語義相同。
system_prompt vs append前者「整份替換」— 小心,會丟掉 CC 原本的系統 prompt;後者「追加」— 最常用,既保留原行為又加上你的指示。
allowed_tools 格式工具名稱直接寫("Read");MCP 工具用 mcp__{server}__{tool} 或通配 mcp__{server}__*
setting_sources預設不讀專案 CLAUDE.md — 必須明確 setting_sources=["project"] 才載入。這是刻意的隔離設計。
permission_mode 四值default(照 settings.json)/ plan(只讀)/ acceptEdits(自動寫入)/ bypassPermissions(跳過所有檢查,極少使用)。
extra_args逃生艙 — 若某選項 SDK 還沒曝露,可以透過 extra_args 直接傳給底層 CLI。
V.

器具自製

Custom Tools · @tool decorator & tool() helper
FIG · 05
TOOL DEF

內建的 Bash / Read / Edit 不夠用?SDK 讓你寫自己的 — 只要一個 decorator(Python)或一個 helper(TypeScript)。你提供名稱、描述、輸入 schema、與一個 async handler,SDK 會把它包成 MCP tool 註冊進 registry,從此模型看得見、叫得動。核心公式:name + description + schema + handler = tool

▸ tools.py · @tool decorator PYTHON from claude_agent_sdk import ( tool, create_sdk_mcp_server ) @tool( "get_temperature", "Get current temp at a location", {"lat": float, "lon": float}, ) async def get_temperature(args): resp = await httpx.get(...) temp = resp.json()["current"] return { "content": [{ "type": "text", "text": f"{temp}°F" }] } ▸ tools.ts · tool() helper + zod TYPESCRIPT import { tool, createSdkMcpServer } from "@anthropic-ai/claude-agent-sdk"; import { z } from "zod"; const getTemperature = tool( "get_temperature", "Get current temp at a location", { lat: z.number().describe("..."), lon: z.number().describe("...") }, async (args) => { const r = await fetch(...); const d = await r.json(); return { content: [{ type: "text", text: `${d}°F` }] }; }); TOOL ANATOMY · 四要件 ① NAME 識別碼 registry 註冊後會變 mcp__{server}__{name} ② DESCRIPTION 描述 Claude 看這句決定 何時使用 — 要寫清楚 ③ SCHEMA 輸入型別 dict shape / JSON Schema (PY) · zod object (TS) ④ HANDLER async 函式 args → {content: [...]} 錯誤時 is_error=True RETURN SHAPE · Claude 看到的工具結果 { "content": [ { "type": "text", "text": "..." }, { "type": "image", "source": {...} } ], "is_error": bool (optional) } 每個 content 項是一個 block — 可以混合文字與圖片。失敗時設 is_error: true,模型會在下一 turn 看見錯誤並自行決定是否重試。
schema choice · schema 選擇

Python 的 {"key": type} dict shape 最簡單,但只支援基本型別與扁平結構。若需要 enum、optional、nested,請改用完整 JSON Schema(傳 dict 進 @tool)。TypeScript 端統一用 zod,型別推導同時賦予 handler 的 args 正確型別。

wiring up · 接上去

定義好的 tool 函式物件要放進 create_sdk_mcp_server(name, version, tools=[...]),再把 server 傳給 options.mcp_servers={"myserver": server}。最後記得在 allowed_tools 加上 "mcp__myserver__tool_name" — 沒加等於沒註冊。詳見下一章。

VI.

協議內融

In-Process MCP · create_sdk_mcp_server()
FIG · 06
MCP COMPARE

傳統的 MCP server 是 另一個進程 — 你的 host 用 stdio 或 SSE 與它通訊,開銷是啟動、序列化、IPC。Agent SDK 獨有的 create_sdk_mcp_server 把這層全部蒸發:你的工具函式直接在當前 Python / Node 進程裡執行,沒有跨進程呼叫,沒有序列化,共享同一記憶體空間。部署、除錯、效能都更舒服。

A · TRADITIONAL MCP · 子程序模式 HOST PROCESS your_agent.py query() / Client tool registry MCP CLIENT stdio transport pipe open SUBPROCESS ← pid 2 weather_server.py MCP SERVER tool handlers own runtime own deps own memory JSON-RPC over pipe ⚠ PAIN · 代價 · spawn cost (fork + exec) · JSON serialize both ways · separate dependency env · two stacks to debug · state is across-process B · SDK IN-PROCESS · 進程內融合 SAME HOST PROCESS · pid 1 your_agent.py — everything here query() / Client tool registry SDK MCP SERVER · in-process create_sdk_mcp_server(name="calc", ...) @tool add async function @tool multiply async function direct function call · 無 IPC shared memory, shared DB connection, shared modules ✓ WIN · 好處 · no process spawn · no serialization (args 直接傳) · share existing DB / HTTP client · single-stack debugger / breakpoint works · hot reload 即時生效 ▸ wire-up recipe calculator = create_sdk_mcp_server(name="calc", version="1.0.0", tools=[add, multiply]) options = ClaudeAgentOptions( mcp_servers={"calc": calculator}, allowed_tools=["mcp__calc__add", "mcp__calc__multiply"])
create_sdk_mcp_server(name, version, tools)三個參數就夠。tools 是一個 list,每個元素是 @tool 裝飾過的 async 函式物件。
命名空間註冊後工具名變成 mcp__{server_name}__{tool_name}。必須在 allowed_tools 列出這個完整名稱或用 mcp__{server_name}__* 通配。
混用可以同時註冊多個 in-process 與外部 MCP server,全部走同一套 mcp_servers dict。外部 server 在 dict 值改放 {"command": "...", "args": [...]}
何時用外部 MCP若該 server 已有穩定實作、或需要 sandbox 隔離、或要跨語言使用 — 就用傳統子程序模式。SDK in-process 主要給「你自己要寫的專案內工具」。
state 共享in-process tool 可以讀寫 agent 程序內的任何物件 — DB 連線池、全域快取、模型 client。省掉跨進程資料同步。
除錯設斷點、看 traceback、熱重載 — 與一般 Python / Node 開發體驗完全一致。這一點比傳統 MCP 開發快很多。
VII.

權限之掌

canUseTool & permissionMode · custom gate logic
FIG · 07
PERMISSION GATE

內建的 allow / ask / deny 清單已經能擋下大多數問題,但若你要的是「對 Bash 的 rm 一律拒絕、對 ls 一律放行、對其他指令依靠 LLM 判斷」這種更細緻的規則 —— 就需要 canUseTool。它是一個 async callback,接住 harness 傳來的工具呼叫,自己決定 allow / deny,甚至改寫參數。加上 permission_mode,你可以組合出從寬鬆到嚴厲的各種姿態。

LLM tool_use 區塊 harness 攔截 查 mode + name permission_mode? default / plan / accept / bypass if "bypassPermissions" canUseTool CALLBACK · 你寫的權限函式 async def can_use_tool(tool_name: str, input: dict, context: ToolPermissionContext) -> PermissionResult async def can_use_tool(tool_name, input, context): if tool_name == "Bash": cmd = input.get("command", "") if "rm -rf" in cmd or "sudo" in cmd: return PermissionResultDeny(message="denied: destructive") if cmd.startswith("ls"): return PermissionResultAllow() return PermissionResultAllow(updated_input=sanitize(input)) RETURN · PermissionResult 三分歧 ① ALLOW PermissionResultAllow() 或帶 updated_input=... — 可改寫參數再放行 ② DENY PermissionResultDeny( message=".....", interrupt=False) ③ FALL-THROUGH return default 若未定義 can_use_tool, 直接按 allowed/disallowed 判定 permission_mode 四值對照 "default" 走完整流程 — allowed_tools / canUseTool / 使用者提示 "plan" 僅允許唯讀工具 — Read / Glob / Grep / WebFetch;寫入類全拒 "acceptEdits" 檔案編輯自動接受 — 適合已經信任 agent 的批次作業 "bypassPermissions" 跳過所有權限檢查 — 只在完全受控的沙箱環境使用 「mode 設粗線條、canUseTool 設細線條 — 兩層合守。」
input 改寫 · sanitize

PermissionResultAllow 可以帶 updated_input — 意思是「允許執行,但把參數換成這個」。可用來把使用者輸入的路徑強制改為相對路徑、把 Bash 命令加上 --dry-run、或遮蔽敏感欄位。這是一個比「全拒」更細緻的控制點。

interrupt · 打斷整個 turn

PermissionResultDeny(interrupt=True) 不只是拒絕這次工具呼叫,還會直接中止當前 agentic loop — 模型不會收到「被拒」訊息,整個 receive_response() 收到 ResultMessage(subtype="error") 就結束。用在「紅線被踩」情境。

VIII.

鉤子入碼

Programmatic Hooks · HookMatcher & HookCallback
FIG · 08
HOOK DISPATCH

Claude Code 的 hook 寫在 settings.json 裡、呼叫 shell 指令。SDK 的 hook 則寫在程式碼裡、呼叫你的 async 函式 —— 同一個引擎,但對 SDK 使用者來說更直接:不需要離開 Python / TypeScript 生態,可以直接讀寫記憶體、呼叫 ORM、拒絕工具、改寫 prompt。本章拆解 HookMatcher 的註冊方式與 callback 的回傳格式。

OPTIONS.hooks · dict[event_name → HookMatcher[]] PreToolUse before each tool PostToolUse after success UserPromptSubmit add context Stop turn ends Notification user alert HookMatcher[] ← each matcher filters + runs callbacks matcher: "Bash" hooks: [log_cmd, block_destructive] timeout: 30 matcher: "Write|Edit" hooks: [audit_writes] (regex matcher) matcher: None hooks: [universal_logger] (no filter → all tools) ▸ hook_callbacks.py · the callable you register PYTHON async def block_destructive( input_data: dict, tool_use_id: str | None, context: HookContext ) -> dict: # input_data: tool_name, tool_input, matched_tool, ... cmd = input_data["tool_input"].get("command", "") if "rm -rf /" in cmd: return { "hookSpecificOutput": { "hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "Dangerous command blocked" } } return {} 「hook 是上帝視角:它比 LLM 先看見、比 tool 先動手、可以改、可以擋。」 WIRE UP ClaudeAgentOptions(hooks={"PreToolUse": [HookMatcher(matcher="Bash", hooks=[block_destructive])]})
HookMatcher一個 filter + callbacks 束。matcher 是字串或正規表達式,用來過濾工具名(或 prompt)。hooks 是 callbacks 列表。timeout 以秒為單位,逾時就跳過。
callback 簽名async (input_data, tool_use_id, context) -> dictinput_data 的欄位依事件型別而異;contextsession_id / cwd 等。
回傳 dict空 dict = 無作為、繼續流程。要阻擋時填 hookSpecificOutput.permissionDecision = "deny";要注入脈絡時填 additionalContext
多 hooks 疊加同一事件可註冊多個 HookMatcher。它們會按順序呼叫,任何一個回傳 deny 就擋下工具。
PostToolUseFailure這是一個較冷門的事件:只在工具執行失敗時觸發。可用於集中記錄錯誤、重試策略。
SDK vs CLI hook語義相同但執行器不同 — SDK 直接跑你的 async 函式,CLI 跑 shell 指令。SDK 的好處是沒有 shell 語法包袱、型別安全、可用整個語言生態。
IX.

分身召喚

Custom Subagents · the agents option
FIG · 09
AGENT DEF

在 Claude Code 卷一的第玖回,我們看過 subagent 的運作 —— 隔離的 context、curated tools、獨立 system prompt。SDK 讓你用程式碼定義這些 subagent,而不是在 .claude/agents/ 擺 Markdown 檔。每個 AgentDefinition 是四件事:描述、可用工具、system prompt、模型。註冊到 options.agents 後,它就能被主 agent 用 Agent 工具呼叫。

AgentDefinition · 分身定義四要件 ① description 召喚條件 str · 主 agent 依這句 決定何時派遣此分身。 寫清楚 "when to use" — 越具體越好。 e.g. "Reviews Python code for security & perf. Use after writing any module." ② tools 可用工具 list[str] · 這個分身 只看得見這些工具。 是主 agent 工具集的 子集 — 權限收斂原則。 e.g. ["Read", "Grep", "Glob"] → 唯讀審查, 不能動檔。 ③ prompt 身分設定 str · 分身自己的 system prompt。不會 看見主 agent 的對話。 寫成「你是 X 專家, 你的任務是 Y」。 e.g. "You are a security auditor. Report issues ④ model 模型 str · 該分身使用的 模型。可用較便宜的 haiku 跑簡單任務、 opus 跑深度審查。 e.g. "claude-haiku-4-5" → 便宜快速 ▸ agents_example.py PYTHON options = ClaudeAgentOptions( agents={ "security-reviewer": AgentDefinition( description="Reviews code for security issues. Use after any code change.", tools=["Read", "Grep", "Glob"], prompt="You are a security auditor. Find OWASP Top 10 issues...", model="claude-sonnet-4-6", ), }, ) AT RUNTIME · 主 agent 怎麼呼叫 主 agent (模型) 看見 description,決定呼叫 Agent tool call subagent_type="security-reviewer" security-reviewer 分身運行 隔離 ctx · 返回一則訊息
agents 鍵名dict 的 key 就是 subagent 的識別字串;主 agent 透過 Agent(subagent_type="the-key") 呼叫它。取名要語意清楚。
tools 的子集原則subagent 的 tools 必須是主 agent 可用工具的子集。不能在這裡放主 agent 沒有的工具(例如沒註冊的 MCP tool)。
prompt 的邊界subagent 看不見主 agent 的對話歷史。它只收到你在 prompt 裡寫的身分定義 + Agent 呼叫時傳入的 prompt 參數。兩者構成它的完整脈絡。
model 獨立主 agent 與 subagent 可以用不同模型。把輕任務派給 haiku、重任務派給 opus — 省錢又快。
與 .claude/agents 並存SDK 的 agents 選項不會覆蓋磁碟上的 agent 定義,兩者合併。同名時 SDK 寫的為準。
並行派遣主 agent 可以在同一則訊息裡發多個 Agent() 呼叫,SDK 會並行執行、所有結果幾乎同時回來。詳見卷一第玖回。
X.

會話續命

Sessions · resume & fork
FIG · 10
SESSION TIMELINE

如果你的 agent 是一個長壽的後端服務,使用者今天問一半、明天回來繼續 — 你需要的是 resume。SDK 把每個 session 的歷史透明地保存在 transcript 檔裡;下次只要把 session id 丟進 options.resume,agent 就能從斷點接續,模型記得昨天講過的話。fork_session=True 則是另一招:基於舊會話建立一個分歧新會話 — 適合「從這個狀態開始試兩條不同路線」。

ORIGINAL SESSION · session_id: "abc-123" turn 1 "explain auth" turn 2 [reads files] turn 3 answer given turn 4 [user closes] END day 1 persist TRANSCRIPT FILE ~/.claude/projects/.../abc-123.jsonl full message history NEXT DAY · two revival options OPTION A · RESUME SAME SESSION options = ClaudeAgentOptions( resume="abc-123" ) t1 t2 t3 t4 t5 new ↑ resume here 同一 session_id 延續 — 對使用者像不曾中斷 OPTION B · FORK TO NEW SESSION options = ClaudeAgentOptions( resume="abc-123", fork_session=True ) xyz-789 new abc-123 still exists 分歧新 id — 原會話不動,能嘗試另一條路線 WHEN TO USE · 選擇時機 resume 單獨使用 · 使用者回來繼續聊 · crash recovery · 長時任務斷點續跑 resume + fork_session · A/B 比較兩種方案 · 從同一狀態試不同 prompt · 保留原版本當基準 用新 client 不設 resume · 全新會話 · 不繼承任何歷史(常見預設)
storage · 儲存在哪

transcript 檔以 .jsonl 存在 ~/.claude/projects/<encoded_cwd>/。每個 session 一份檔案、每則 message 一行 JSON。你可以自己讀這些檔做分析或備份;但寫入請一律透過 SDK,否則會破壞 schema。

session_id from where · 從哪拿 id

ResultMessage.session_id 是第一手來源 — 每次 query()client.receive_response() 的最後一則結果都會帶這個欄位。把它存起來(DB、cookie、檔案),下次丟進 options.resume 就能續命。