Claude Agent SDK Python 原始碼解讀:用 CLI 子程序架構橋接 Python 與 AI 代理

Claude Agent SDK Python 是 Anthropic 官方提供的 Python SDK,讓開發者能以程式化方式操控 Claude Code 代理。這份原始碼最值得深讀的地方在於:它不是直接呼叫 HTTP API,而是透過 CLI 子程序加上一套自訂的雙向控制協定,將 Claude Code 的完整能力暴露給 Python 生態系。從中你能學到如何設計分層式 Transport 抽象、雙向串流的控制協定、In-Process MCP Server 整合,以及在 Python 中實現 Hook 攔截模式的具體手法。

Claude Agent SDK Python GitHub Repository — 專案原始碼,包含本文解讀的所有設計實作

專案概覽

Claude Agent SDK Python 是一個包裝 Claude Code CLI 的 Python SDK,版本 0.1.27,使用 Python 3.10+ 開發。核心依賴為 anyio(非同步 I/O 框架,同時支援 asyncio 和 trio)和 mcp(Model Context Protocol SDK)。專案透過 hatchling 建構系統打包,並在 wheel 中捆綁 Claude Code CLI 執行檔,讓使用者安裝 pip 套件後即可直接使用,無需額外安裝 CLI。

專案核心功能提供兩種使用模式:query() 用於單次問答,ClaudeSDKClient 用於多輪互動對話。兩者共享同一套 Transport → Query → Message Parser 的分層架構。

架構總覽

src/claude_agent_sdk/
├── __init__.py                    # 公開 API 彙出 + MCP Server 工廠
├── query.py                       # query() 單次查詢公開介面
├── client.py                      # ClaudeSDKClient 互動式客戶端
├── types.py                       # 完整型別定義(500+ 行)
├── _errors.py                     # 階層式錯誤類別
├── _version.py                    # SDK 版本
├── _cli_version.py                # 綁定的 CLI 版本
├── _bundled/                      # 綁定的 CLI 執行檔
└── _internal/                     # 內部實作層
    ├── __init__.py
    ├── client.py                  # InternalClient 協調器
    ├── query.py                   # Query 控制協定處理
    ├── message_parser.py          # 訊息解析器
    └── transport/
        ├── __init__.py            # Transport 抽象基類
        └── subprocess_cli.py      # CLI 子程序 Transport 實作
Code language: PHP (php)

架構分為三個明確的層次:

層級模組職責
Public APIquery.py, client.py面向使用者的介面,提供 query()ClaudeSDKClient
Control Protocol_internal/query.py, _internal/client.py雙向控制協定、Hook 分派、MCP 路由、權限管理
Transport_internal/transport/底層 I/O 抽象,子程序管理、stdin/stdout 串流

核心設計解析

設計一:Transport 抽象與子程序通訊

整個 SDK 最核心的設計決策是不直接呼叫 HTTP API,而是透過 CLI 子程序與 Claude Code 通訊。Transport 作為抽象基類,定義了六個方法的介面契約:

src/claude_agent_sdk/_internal/transport/__init__.py — Transport 抽象基類,定義了所有傳輸層必須實作的介面

class Transport(ABC):
    @abstractmethod
    async def connect(self) -> None: ...

    @abstractmethod
    async def write(self, data: str) -> None: ...

    @abstractmethod
    def read_messages(self) -> AsyncIterator[dict[str, Any]]: ...

    @abstractmethod
    async def close(self) -> None: ...

    @abstractmethod
    def is_ready(self) -> bool: ...

    @abstractmethod
    async def end_input(self) -> None: ...
Code language: CSS (css)

這個設計的精妙之處在於 docstring 中的 WARNING:這是一個「暴露的內部 API」,允許開發者實作自訂 Transport(例如遠端 Claude Code 連線),但明確聲明可能在任何版本中變更。這體現了「開放擴展,但不承諾穩定」的務實態度。

src/claude_agent_sdk/_internal/transport/subprocess_cli.py — SubprocessCLITransport,唯一的內建 Transport 實作

SubprocessCLITransport 的實作揭示了幾個關鍵設計決策:

class SubprocessCLITransport(Transport):
    def __init__(self, prompt, options):
        self._prompt = prompt
        self._is_streaming = not isinstance(prompt, str)
        # ...
        self._max_buffer_size = (
            options.max_buffer_size
            if options.max_buffer_size is not None
            else _DEFAULT_MAX_BUFFER_SIZE  # 1MB
        )
        self._write_lock: anyio.Lock = anyio.Lock()

它根據 prompt 的型別自動判斷模式:字串代表單次查詢(--print 模式),AsyncIterable 代表串流模式(--input-format stream-json)。這是一種 型別驅動行為 的設計——API 呼叫者的型別選擇直接決定了底層的執行策略。

設計二:Speculative JSON Buffering

Transport 層的訊息讀取採用了一種 speculative(投機式)JSON 解析策略:

async def _read_messages_impl(self) -> AsyncIterator[dict[str, Any]]:
    json_buffer = ""
    try:
        async for line in self._stdout_stream:
            line_str = line.strip()
            if not line_str:
                continue
            json_lines = line_str.split("\n")
            for json_line in json_lines:
                json_buffer += json_line
                if len(json_buffer) > self._max_buffer_size:
                    json_buffer = ""
                    raise SDKJSONDecodeError(...)
                try:
                    data = json.loads(json_buffer)
                    json_buffer = ""
                    yield data
                except json.JSONDecodeError:
                    continue  # 繼續累積,等待完整 JSON
Code language: PHP (php)

這個設計源於一個現實問題:TextReceiveStream 可能在長行中間截斷輸出。SDK 不假設每行都是完整的 JSON,而是持續累積 buffer 並在每次追加後嘗試解析。解析成功就 yield,失敗就繼續累積。配合 _max_buffer_size 上限(預設 1MB),在健壯性和記憶體安全之間取得平衡。

設計三:雙層 API — query() 與 ClaudeSDKClient

SDK 提供兩種公開介面,分別對應不同的使用場景:

src/claude_agent_sdk/query.py — 單次查詢的 async generator 介面

async def query(
    *,
    prompt: str | AsyncIterable[dict[str, Any]],
    options: ClaudeAgentOptions | None = None,
    transport: Transport | None = None,
) -> AsyncIterator[Message]:
    if options is None:
        options = ClaudeAgentOptions()
    os.environ["CLAUDE_CODE_ENTRYPOINT"] = "sdk-py"
    client = InternalClient()
    async for message in client.process_query(
        prompt=prompt, options=options, transport=transport
    ):
        yield message
Code language: JavaScript (javascript)

query() 是一個精簡的 async generator,每次呼叫都建立新的 InternalClient,適合 fire-and-forget 場景。

src/claude_agent_sdk/client.py — ClaudeSDKClient 互動式客戶端,支援多輪對話

class ClaudeSDKClient:
    async def connect(self, prompt=None) -> None:
        # Auto-connect with empty async iterable
        async def _empty_stream() -> AsyncIterator[dict[str, Any]]:
            return
            yield {}  # type: ignore[unreachable]
        actual_prompt = _empty_stream() if prompt is None else prompt
        # ...

ClaudeSDKClient 使用 async context manager 模式,連線時如果沒有提供 prompt,會建立一個「永遠不 yield 的空 async generator」來維持連線。注意 return 後面的 yield {}——這行永遠不會執行,但它讓 Python 把這個函式識別為 async generator 而非普通 coroutine。這是一個巧妙的型別系統技巧。

設計四:Control Protocol 雙向通訊

Query 類別實作了完整的雙向控制協定,這是整個 SDK 最複雜也最核心的部分:

src/claude_agent_sdk/_internal/query.py — Query 控制協定處理器,管理雙向通訊和請求路由

class Query:
    def __init__(self, transport, is_streaming_mode, can_use_tool, hooks, sdk_mcp_servers, ...):
        self.pending_control_responses: dict[str, anyio.Event] = {}
        self.pending_control_results: dict[str, dict[str, Any] | Exception] = {}
        self.hook_callbacks: dict[str, Callable[..., Any]] = {}
        self._message_send, self._message_receive = anyio.create_memory_object_stream[
            dict[str, Any]
        ](max_buffer_size=100)

Query 使用了多個並行機制:

  1. Memory Object Stream (anyio.create_memory_object_stream):作為訊息佇列,將讀取到的 SDK 訊息傳遞給消費者
  2. Event + Dict 組合pending_control_responses 儲存 anyio.Eventpending_control_results 儲存結果,實現 request-response 配對
  3. Task Group:使用 anyio.create_task_group() 管理背景任務(讀取訊息、串流輸入)
async def _read_messages(self) -> None:
    async for message in self.transport.read_messages():
        msg_type = message.get("type")
        if msg_type == "control_response":
            # 路由到等待中的控制請求
            response = message.get("response", {})
            request_id = response.get("request_id")
            if request_id in self.pending_control_responses:
                event = self.pending_control_responses[request_id]
                self.pending_control_results[request_id] = response
                event.set()
            continue
        elif msg_type == "control_request":
            # CLI 發來的控制請求,在 task group 中處理
            self._tg.start_soon(self._handle_control_request, request)
            continue
        # 普通 SDK 訊息放入佇列
        await self._message_send.send(message)
Code language: PHP (php)

讀取迴圈充當了一個訊息路由器:control_response 配對到對應的 pending request,control_request 分派到處理函式,其餘訊息推入消費者佇列。這種設計讓 SDK 可以在同一條 stdio 管道上多工處理不同類型的通訊。

設計五:Hook 系統 — Python 關鍵字衝突的優雅處理

Hook 系統讓使用者在代理迴圈的特定時間點注入自訂邏輯。設計上最值得注意的是如何處理 Python 關鍵字衝突:

src/claude_agent_sdk/types.py — 完整的型別定義,包含 Hook 輸出格式的巧妙設計

class SyncHookJSONOutput(TypedDict):
    # Using continue_ to avoid Python keyword (converted to "continue" for CLI)
    continue_: NotRequired[bool]
    suppressOutput: NotRequired[bool]
    stopReason: NotRequired[str]
    # ...
    hookSpecificOutput: NotRequired[HookSpecificOutput]
Code language: CSS (css)

CLI 端期望的 JSON 欄位名為 asynccontinue,但這兩者在 Python 中是保留字。SDK 在型別定義中使用 async_continue_ 作為替代,然後在發送前透過 _convert_hook_output_for_cli() 自動轉換:

def _convert_hook_output_for_cli(hook_output: dict[str, Any]) -> dict[str, Any]:
    converted = {}
    for key, value in hook_output.items():
        if key == "async_":
            converted["async"] = value
        elif key == "continue_":
            converted["continue"] = value
        else:
            converted[key] = value
    return converted
Code language: PHP (php)

這個設計展現了跨語言 SDK 開發的典型挑戰——同一套協定在不同語言中的命名適配。不是簡單地改名,而是在型別層面使用 Python 友善的名稱,在傳輸層自動轉換,讓使用者無感知。

設計六:In-Process MCP Server — 跨越程序邊界的整合

SDK 最具創意的設計是 In-Process MCP Server,讓原本需要獨立程序的 MCP 工具直接在 Python 應用內執行:

src/claude_agent_sdk/__init__.py — 包含 @tool 裝飾器和 create_sdk_mcp_server() 工廠函式

@dataclass
class SdkMcpTool(Generic[T]):
    name: str
    description: str
    input_schema: type[T] | dict[str, Any]
    handler: Callable[[T], Awaitable[dict[str, Any]]]

def tool(name, description, input_schema):
    def decorator(handler):
        return SdkMcpTool(
            name=name, description=description,
            input_schema=input_schema, handler=handler,
        )
    return decorator
Code language: CSS (css)

@tool 裝飾器將一般的 async 函式轉化為 SdkMcpTool dataclass。create_sdk_mcp_server() 再將多個 tool 注冊到一個 MCP Server 實例上,自動處理 schema 轉換:

def create_sdk_mcp_server(name, version="1.0.0", tools=None):
    server = Server(name, version=version)
    if tools:
        tool_map = {tool_def.name: tool_def for tool_def in tools}

        @server.list_tools()
        async def list_tools() -> list[Tool]:
            # 自動將 Python 型別轉換為 JSON Schema
            for param_name, param_type in tool_def.input_schema.items():
                if param_type is str:
                    properties[param_name] = {"type": "string"}
                elif param_type is int:
                    properties[param_name] = {"type": "integer"}
                # ...

        @server.call_tool()
        async def call_tool(name, arguments):
            result = await tool_def.handler(arguments)
            # 將結果轉為 MCP TextContent/ImageContent

    return McpSdkServerConfig(type="sdk", name=name, instance=server)
Code language: PHP (php)

但更精妙的是 SDK 如何將 In-Process Server 與 CLI 橋接。在 Query 類別中,_handle_sdk_mcp_request() 方法手動路由 JSONRPC 請求:

async def _handle_sdk_mcp_request(self, server_name, message):
    server = self.sdk_mcp_servers[server_name]
    method = message.get("method")

    # TODO: Python MCP SDK lacks the Transport abstraction that TypeScript has.
    # TypeScript: server.connect(transport) allows custom transports
    # Python: server.run(read_stream, write_stream) requires actual streams
    #
    # This forces us to manually route methods.

    if method == "tools/list":
        handler = server.request_handlers.get(ListToolsRequest)
        result = await handler(request)
        # 手動轉換回 JSONRPC 格式
Code language: PHP (php)

原始碼中的 TODO 註解坦誠指出了限制:Python MCP SDK 不像 TypeScript 版本提供 Transport 抽象,所以必須手動路由每個 JSONRPC method。這是一個「在框架限制下找到可行方案」的典型案例。

設計七:Tool Permission Callback — 型別安全的權限控制

SDK 提供了程式化的工具權限控制機制,使用 discriminated union 設計:

@dataclass
class PermissionResultAllow:
    behavior: Literal["allow"] = "allow"
    updated_input: dict[str, Any] | None = None
    updated_permissions: list[PermissionUpdate] | None = None

@dataclass
class PermissionResultDeny:
    behavior: Literal["deny"] = "deny"
    message: str = ""
    interrupt: bool = False

PermissionResult = PermissionResultAllow | PermissionResultDeny
Code language: CSS (css)

can_use_tool callback 回傳 PermissionResult union type,不只能允許或拒絕,還能透過 updated_input 修改工具輸入(例如將檔案路徑重導向到安全目錄),以及透過 updated_permissions 動態更新權限規則。這種設計讓權限控制不僅是一個 boolean 開關,而是一個完整的攔截和修改管道。

設計八:CLI 探測與 Bundled Binary 策略

SDK 採用了多重 CLI 探測策略,展現了對部署環境多樣性的考量:

def _find_cli(self) -> str:
    # 優先使用 bundled CLI
    bundled_cli = self._find_bundled_cli()
    if bundled_cli:
        return bundled_cli
    # 其次用 PATH 中的 claude
    if cli := shutil.which("claude"):
        return cli
    # 最後檢查常見安裝位置
    locations = [
        Path.home() / ".npm-global/bin/claude",
        Path("/usr/local/bin/claude"),
        Path.home() / ".local/bin/claude",
        Path.home() / "node_modules/.bin/claude",
        Path.home() / ".yarn/bin/claude",
        Path.home() / ".claude/local/claude",
    ]
Code language: PHP (php)

結合 _check_claude_version() 的版本檢查(帶 2 秒超時),以及建構系統中平台特定的 wheel 打包,整個策略是:pip install 後即可使用,但也容錯各種手動安裝場景

設計九:型別系統 — Discriminated Union 與 TypedDict 的混用

types.py 超過 500 行,展現了如何在 Python 中建構 TypeScript 風格的型別系統:

# Hook 輸入使用 discriminated union
HookInput = (
    PreToolUseHookInput
    | PostToolUseHookInput
    | PostToolUseFailureHookInput
    | UserPromptSubmitHookInput
    | StopHookInput
    | SubagentStopHookInput
    | PreCompactHookInput
)

# 每個變體都有 Literal 鑑別欄位
class PreToolUseHookInput(BaseHookInput):
    hook_event_name: Literal["PreToolUse"]
    tool_name: str
    tool_input: dict[str, Any]

使用 TypedDict 而非 Pydantic model 是刻意的選擇——SDK 的型別定義不需要 validation 或 serialization,只需要靜態型別檢查和 IDE 支援。同時,@dataclass 用於需要實例化的物件(如 TextBlockAssistantMessage),TypedDict 用於字典格式的協定訊息。這種混用策略在效能和開發者體驗之間取得了好的平衡。

設計十:Stream 關閉的時序控制

當 SDK MCP Server 或 Hooks 需要雙向通訊時,stdin 不能在輸入結束後立即關閉,否則 CLI 無法發送控制請求回來:

async def stream_input(self, stream: AsyncIterable[dict[str, Any]]) -> None:
    try:
        async for message in stream:
            if self._closed:
                break
            await self.transport.write(json.dumps(message) + "\n")

        # 有 MCP Server 或 Hooks 時,等待第一個 result 才關 stdin
        has_hooks = bool(self.hooks)
        if self.sdk_mcp_servers or has_hooks:
            try:
                with anyio.move_on_after(self._stream_close_timeout):
                    await self._first_result_event.wait()
            except Exception:
                pass  # 超時也繼續
        await self.transport.end_input()
Code language: PHP (php)

這段程式碼解決了一個微妙的時序問題:在串流模式下,所有使用者輸入發送完畢後,如果立即關閉 stdin,CLI 程序就無法再透過 control_request 與 SDK 通訊。SDK 透過 _first_result_event 事件等待 CLI 回傳第一個結果,確保所有雙向通訊(MCP 工具呼叫、Hook 回調)都有機會完成。

設計理念萃取

  • CLI 子程序作為穩定介面 — SDK 不直接呼叫 API,而是包裝 CLI 二進位檔。這讓 SDK 能繼承 Claude Code 的所有功能(工具、權限、沙箱),而不需要在 Python 端重新實作。代價是多一層程序間通訊,但換來的是功能完整性和維護簡便性。
  • 型別驅動行為分支 — 透過 prompt 參數是 str 還是 AsyncIterable 來自動決定執行模式,讓使用者無需學習不同的 API。這種「看你傳什麼型別,我就知道你要什麼」的設計,比用不同的函式名或 flag 參數更直覺。
  • 分層但不過度抽象 — Transport → Query → Public API 三層清晰,但不創造不必要的中間層。Transport 暴露為可擴展的抽象,但只有一個內建實作。這種「預留接口但不過度設計」的態度值得學習。
  • 跨語言協定的命名適配 — 在 Python 端使用 async_continue_ 等友善名稱,在傳輸層自動轉換為 JavaScript 端的 asynccontinue。這種方式比要求使用者用字串 key 存取更安全,也比完全重新命名更一致。
  • In-Process MCP 的務實橋接 — 面對 Python MCP SDK 缺乏 Transport 抽象的限制,SDK 選擇手動路由 JSONRPC 方法而非等待上游修復。程式碼中的 TODO 註解坦誠記錄了技術債務,這是開源專案中負責任的做法。

延伸思考

這個 SDK 的架構模式可以直接應用到幾個場景:

如果你在包裝第三方 CLI 工具,SubprocessCLITransport 的設計是一個很好的範本——抽象 Transport 介面、投機式 JSON 解析、命令行長度限制處理(超長參數自動寫入暫存檔)、以及多重路徑探測策略。

如果你在設計需要雙向通訊的協定,Query 類別的 request-response 配對機制(Event + Dict)和訊息路由模式可以直接借鑑。特別是它如何在同一條管道上多工處理 control_request、control_response 和普通訊息。

如果你在建構 SDK 的型別系統,types.py 展示了一種混合策略:用 @dataclass 定義需要實例化的物件,用 TypedDict 定義純字典格式的協定訊息,用 union type 實現 discriminated union。這比全用 Pydantic 或全用字典更精準。

Vibe Coding:把觀念變成程式碼

以下是你可以直接給 AI 的 prompt 指令,將本文介紹的設計觀念應用到你的專案中:

Transport 抽象層設計

場景:你正在開發一個需要與外部程式(CLI 工具、子程序、遠端服務)通訊的 Python 套件,想要讓通訊層可替換、可測試。

給 AI 的指令

請幫我設計一個 Transport 抽象層,用於包裝外部 CLI 工具的通訊。參考 Claude Agent SDK 的 Transport 模式,需要:
1. 一個 ABC 基類,定義 connect()、write()、read_messages()(async iterator)、close()、is_ready() 等方法
2. 一個 SubprocessTransport 實作,用 anyio.open_process 管理子程序,透過 stdin/stdout 進行 JSON 訊息交換
3. read_messages() 要實作 speculative JSON buffering——不假設每行是完整 JSON,持續累積 buffer 並嘗試解析,配合 max_buffer_size 上限防止記憶體溢出
4. write() 要加上 anyio.Lock 確保並發安全

我要包裝的 CLI 工具是 [你的工具名稱],它接受 [輸入格式] 並輸出 [輸出格式]。

效果:AI 會產出一個分層清晰的 Transport 架構,包含抽象基類和具體實作,你可以在不修改上層邏輯的情況下替換通訊方式(例如從子程序改為 HTTP 或 WebSocket)。

雙向控制協定的 Request-Response 配對

場景:你的應用需要在同一條通訊管道上處理多種訊息類型(普通訊息、控制請求、控制回應),需要一個訊息路由機制。

給 AI 的指令

我正在設計一個雙向通訊協定,需要在同一條 stdio 管道上多工處理不同類型的訊息。參考 Claude Agent SDK 的 Query 類別的控制協定模式,請幫我實作:
1. 一個訊息路由器:讀取迴圈根據 message type 分流——control_response 配對到 pending request,control_request 分派到處理函式,普通訊息推入消費者佇列
2. Request-Response 配對機制:用 dict[str, anyio.Event] 追蹤 pending requests,用另一個 dict 儲存結果,發送請求時產生 unique ID,等待對應的 Event 被 set
3. 使用 anyio.create_memory_object_stream 作為普通訊息的非同步佇列
4. 使用 anyio.create_task_group 管理背景的讀取任務和控制請求處理

我的訊息類型有:[列出你的訊息類型]。

效果:AI 會產出一個完整的雙向協定處理器,支援在單一管道上同時進行 request-response 配對和訊息串流,可直接整合到你的通訊架構中。

Hook 攔截系統

場景:你正在建構一個有多步驟流程的應用(例如 CI/CD pipeline、資料處理管線、AI Agent 迴圈),想讓使用者在流程的特定時間點注入自訂邏輯。

給 AI 的指令

請幫我設計一個 Hook 系統,讓使用者可以在我的應用流程中注入回調函式。參考 Claude Agent SDK 的 Hook 模式,需要:
1. 定義 Hook 事件的 TypedDict 輸入型別,每個事件用 Literal 欄位作為 discriminated union 的鑑別器
2. Hook 回傳值使用 TypedDict,支援 continue(是否繼續流程)、修改後的輸入等控制欄位
3. 如果 Hook 輸出的欄位名與 Python 保留字衝突(如 async、continue),用底線後綴命名(async_、continue_),並在發送到外部系統前自動轉換回原始名稱
4. Hook 的註冊和分派機制,支援同步和非同步回調

我的流程步驟是:[列出你的流程步驟,例如 pre_execute, post_execute, on_error]。

效果:AI 會產出型別安全的 Hook 系統,包含事件定義、回調註冊、分派邏輯,以及跨語言命名衝突的自動轉換機制。

In-Process MCP Server 與 @tool 裝飾器

場景:你想為 Claude Agent SDK 建立自訂工具,讓 AI 代理可以呼叫你的 Python 函式,而不需要啟動獨立的 MCP Server 程序。

給 AI 的指令

我想用 Claude Agent SDK 的 In-Process MCP Server 功能,為我的應用建立自訂工具。請幫我:
1. 用 @tool 裝飾器定義 [工具數量] 個工具,每個工具需要有 name、description 和 input_schema
2. 用 create_sdk_mcp_server() 將所有工具註冊到一個 MCP Server
3. 建立 ClaudeSDKClient,設定 mcp_servers 和 allowed_tools
4. 工具的輸入 schema 用 Python dict 定義(如 {“query”: str, “limit”: int}),SDK 會自動轉換為 JSON Schema

我需要的工具功能:
– [工具 1]:[描述功能和參數]
– [工具 2]:[描述功能和參數]

效果:AI 會產出可直接執行的 In-Process MCP 工具定義和客戶端程式碼,你的 Python 函式會在同一個程序內被 Claude 代理呼叫,無需管理額外的程序。

Discriminated Union 型別系統設計

場景:你在設計 Python SDK 或函式庫的型別系統,需要處理多種變體的訊息或回傳值,想要兼顧型別安全和執行效率。

給 AI 的指令

請幫我設計一套 Python 型別系統,處理 [你的領域] 中的多種訊息/回傳值變體。參考 Claude Agent SDK 的 types.py 混合策略:
1. 需要實例化的物件用 @dataclass 定義(而非 Pydantic),保持輕量
2. 純字典格式的協定訊息用 TypedDict 定義,搭配 NotRequired 處理可選欄位
3. 多變體類型用 Union type 組合,每個變體用 Literal 欄位作為鑑別器,實現 discriminated union
4. 權限控制等回傳值設計:不只是布林值,而是用 PermissionResultAllow / PermissionResultDeny 這樣的 union,允許在「允許」時附帶修改後的輸入

我的變體類型有:[列出你需要區分的訊息或回傳值類型]。

效果:AI 會產出一套混合使用 dataclass、TypedDict 和 Union type 的型別定義,比全用 Pydantic 更輕量,比全用 dict 更安全,IDE 自動補全和靜態檢查都能正常運作。

延伸閱讀

0

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *