Skip to main content

5.4 Claude Agent SDK for Python 架构设计

目录

  1. 执行摘要
  2. 系统概述
  3. 系统架构
  4. 核心组件详解
  5. 数据流分析
  6. 类型系统
  7. 使用示例

执行摘要

Claude Agent SDK for Python 是一个用于与 Claude Code 交互的高级 Python 软件开发工具包。该 SDK 提供了两种主要交互模式:

  • 一次性查询模式 (query() 函数):适用于简单的、无状态的问答场景
  • 交互式会话模式 (ClaudeSDKClient 类):适用于需要双向通信、状态管理和实时交互的场景

关键特性

  • MCP (Model Context Protocol) 服务器支持:支持进程内和外部 MCP 服务器
  • 钩子 (Hooks) 系统:提供 11 种不同的钩子事件用于生命周期管理
  • 子代理 (Subagents) 支持:可定义和使用具有特定工具集的自定义代理
  • 权限控制:灵活的工具权限管理机制
  • 错误恢复:完善的错误处理和重试机制

技术栈

  • 异步框架:anyio (支持 asyncio 和 trio)
  • 子进程管理:通过 Claude Code CLI 进行通信
  • 类型安全:完整的类型注解和数据类定义
  • MCP 集成:兼容 MCP (Model Context Protocol) 规范

系统概述

系统定位

Claude Agent SDK 作为 Python 应用与 Claude Code CLI 之间的桥梁,提供了高层抽象接口,使开发者能够轻松集成 Claude 的 AI 能力到自己的应用中。

核心设计理念

  1. 分层抽象:提供从简单到复杂的多层次 API
  2. 类型安全:完整的类型注解支持
  3. 异步优先:所有 I/O 操作都是异步的
  4. 可扩展性:通过钩子和 MCP 服务器支持自定义扩展

系统架构

整体架构图

包结构

src/claude_agent_sdk/
├── __init__.py # 公共 API 导出
├── _cli_version.py # CLI 版本信息
├── _errors.py # 错误类定义
├── _version.py # SDK 版本信息
├── client.py # ClaudeSDKClient 类
├── query.py # query() 函数
├── types.py # 类型定义
└── _internal/
├── __init__.py
├── client.py # InternalClient 实现
├── message_parser.py # 消息解析器
├── query.py # Query 控制协议处理
├── session_mutations.py # 会话变更操作
├── sessions.py # 会话列表和查询
└── transport/
├── __init__.py
└── subprocess_cli.py # 子进程 CLI 传输实现

组件关系图


核心组件详解

1. 公共 API 层

1.1 query() 函数 - 一次性查询

位置: src/claude_agent_sdk/query.py

用途: 用于简单的、无状态的一次性查询场景。

特点:

  • 单向通信:发送所有输入,接收所有输出
  • 自动管理连接生命周期
  • 适合批量处理和自动化脚本

工作流程:

1.2 ClaudeSDKClient 类 - 交互式会话

位置: src/claude_agent_sdk/client.py

用途: 用于需要双向通信、状态管理和实时交互的场景。

核心方法:

方法用途
connect()建立连接并初始化会话
query()发送新的查询
receive_messages()接收所有消息
receive_response()接收直到 ResultMessage
interrupt()中断当前执行
set_permission_mode()动态更改权限模式
set_model()动态更改模型
get_mcp_status()获取 MCP 服务器状态
get_context_usage()获取上下文使用情况
disconnect()关闭连接

生命周期:

2. 内部实现层

2.1 Query 类 - 控制协议处理

位置: src/claude_agent_sdk/_internal/query.py

职责:

  • 处理与 CLI 的控制协议通信
  • 管理初始化流程
  • 处理钩子回调
  • 路由 SDK MCP 服务器消息

控制协议消息类型:

2.2 MessageParser - 消息解析

位置: src/claude_agent_sdk/_internal/message_parser.py

职责: 将 CLI 的原始 JSON 输出解析为类型安全的 Python 对象。

支持的消息类型:

  • UserMessage - 用户消息
  • AssistantMessage - 助手消息(包含内容块)
  • SystemMessage - 系统消息
  • ResultMessage - 结果消息(包含成本和使用统计)
  • StreamEvent - 流事件
  • RateLimitEvent - 速率限制事件

消息类型:

内容块类型:

3. 传输层

3.1 Transport 抽象接口

位置: src/claude_agent_sdk/_internal/transport/__init__.py

定义了传输层必须实现的核心方法:

  • connect() - 建立连接
  • write(data) - 写入数据
  • read_messages() - 读取消息流
  • close() - 关闭连接

3.2 SubprocessCLITransport - 子进程传输实现

位置: src/claude_agent_sdk/_internal/transport/subprocess_cli.py

核心职责:

  • 管理 Claude Code CLI 子进程的生命周期
  • 处理 stdin/stdout/stderr 流
  • 缓冲和解析 JSON 消息
  • 版本检查和验证

CLI 查找策略:

CLI 查找位置 (按优先级):

  1. 捆绑的 CLI (_bundled/ 目录)
  2. shutil.which("claude")
  3. ~/.npm-global/bin/claude
  4. /usr/local/bin/claude
  5. ~/.local/bin/claude
  6. ~/node_modules/.bin/claude
  7. ~/.yarn/bin/claude
  8. ~/.claude/local/claude

数据流分析

1. 一次性查询数据流

2. 交互式会话数据流

3. MCP 工具调用数据流

4. 钩子执行数据流


类型系统

1. ClaudeAgentOptions - 配置选项

位置: src/claude_agent_sdk/types.py (第 1175 行)

这是 SDK 的核心配置类,包含所有可配置选项。

@dataclass
class ClaudeAgentOptions:
# 工具配置
tools: list[str] | ToolsPreset | None = None
allowed_tools: list[str] = field(default_factory=list)
disallowed_tools: list[str] = field(default_factory=list)

# 系统提示
system_prompt: str | SystemPromptPreset | SystemPromptFile | None = None

# MCP 服务器
mcp_servers: dict[str, McpServerConfig] | str | Path = field(default_factory=dict)

# 权限控制
permission_mode: PermissionMode | None = None
can_use_tool: CanUseTool | None = None

# 会话管理
continue_conversation: bool = False
resume: str | None = None
session_id: str | None = None
fork_session: bool = False

# 限制配置
max_turns: int | None = None
max_budget_usd: float | None = None
task_budget: TaskBudget | None = None

# 模型配置
model: str | None = None
fallback_model: str | None = None
betas: list[SdkBeta] = field(default_factory=list)

# 钩子配置
hooks: dict[HookEvent, list[HookMatcher]] | None = None

# 代理配置
agents: dict[str, AgentDefinition] | None = None

# 环境配置
cwd: str | Path | None = None
cli_path: str | Path | None = None
env: dict[str, str] = field(default_factory=dict)

# 高级特性
thinking: ThinkingConfig | None = None
effort: Literal["low", "medium", "high", "max"] | None = None
output_format: dict[str, Any] | None = None
enable_file_checkpointing: bool = False
sandbox: SandboxSettings | None = None

2. 权限模式 (PermissionMode)

3. 钩子事件类型

事件名称触发时机用途
PreToolUse工具使用前权限控制、参数验证、修改输入
PostToolUse工具使用后结果审核、添加上下文
PostToolUseFailure工具使用失败后错误处理、重试逻辑
UserPromptSubmit用户提示提交时添加上下文、内容过滤
Stop会话停止时清理资源
SubagentStart子代理启动时子代理初始化
SubagentStop子代理停止时子代理清理
PreCompact上下文压缩前自定义压缩逻辑
Notification通知事件状态通知处理
PermissionRequest权限请求时动态权限决策

4. 钩子回调签名

HookCallback = Callable[
[HookInput, str | None, HookContext],
Awaitable[HookJSONOutput]
]

HookJSONOutput 类型:

5. MCP 服务器配置类型


使用示例

1. 基本使用

1.1 使用 query() 进行一次性查询

import anyio
from claude_agent_sdk import (
query,
ClaudeAgentOptions,
AssistantMessage,
TextBlock,
ResultMessage
)

async def basic_example():
"""简单的一次性查询示例"""

# 基本查询
async for message in query(prompt="2 + 2 等于多少?"):
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(f"Claude: {block.text}")
elif isinstance(message, ResultMessage):
print(f"成本: ${message.total_cost_usd:.4f}")

async def with_options_example():
"""带自定义选项的查询"""

options = ClaudeAgentOptions(
system_prompt="你是一个乐于助人的助手,用简单的方式解释事情。",
max_turns=1,
model="claude-sonnet-4-5-20250929"
)

async for message in query(
prompt="用一句话解释什么是 Python。",
options=options
):
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(f"Claude: {block.text}")

async def with_tools_example():
"""使用特定工具的查询"""

options = ClaudeAgentOptions(
allowed_tools=["Read", "Write"],
system_prompt="你是一个乐于助人的文件助手。",
permission_mode="acceptEdits" # 自动接受文件编辑
)

async for message in query(
prompt="创建一个叫 hello.txt 的文件,内容是 'Hello, World!'",
options=options
):
# 处理消息...
pass

if __name__ == "__main__":
anyio.run(basic_example)

1.2 使用 ClaudeSDKClient 进行交互式会话

import asyncio
from claude_agent_sdk import (
ClaudeSDKClient,
ClaudeAgentOptions,
AssistantMessage,
TextBlock,
ResultMessage
)

async def interactive_session():
"""交互式会话示例"""

options = ClaudeAgentOptions(
system_prompt="你是一个编程助手。",
allowed_tools=["Read", "Grep", "Bash"]
)

# 使用 async with 自动管理连接
async with ClaudeSDKClient(options=options) as client:
# 第一个问题
print("用户: 帮我分析这个目录的结构")
await client.query("帮我分析当前目录的结构")

async for msg in client.receive_response():
if isinstance(msg, AssistantMessage):
for block in msg.content:
if isinstance(block, TextBlock):
print(f"Claude: {block.text}")
elif isinstance(msg, ResultMessage):
print("--- 第一轮结束 ---")

# 第二个问题(基于上下文)
print("\n用户: 这里有 Python 代码吗?")
await client.query("这里有 Python 代码吗?分析一下主要文件")

async for msg in client.receive_response():
if isinstance(msg, AssistantMessage):
for block in msg.content:
if isinstance(block, TextBlock):
print(f"Claude: {block.text}")

async def dynamic_configuration():
"""动态配置示例"""

async with ClaudeSDKClient() as client:
# 发送第一个查询
await client.query("用默认模型分析...")
async for _ in client.receive_response():
pass

# 动态切换模型
await client.set_model("claude-opus-4-7")

# 用新模型继续
await client.query("现在用 Opus 进行更深入的分析...")
async for _ in client.receive_response():
pass

# 动态切换权限模式
await client.set_permission_mode("acceptEdits")

# 获取 MCP 状态
mcp_status = await client.get_mcp_status()
for server in mcp_status["mcpServers"]:
print(f"{server['name']}: {server['status']}")

# 获取上下文使用情况
usage = await client.get_context_usage()
print(f"上下文使用: {usage['percentage']:.1f}%")

if __name__ == "__main__":
asyncio.run(interactive_session)

2. MCP (Model Context Protocol) 使用

2.1 创建 SDK MCP 服务器(进程内)

import asyncio
from typing import Any
from claude_agent_sdk import (
ClaudeSDKClient,
ClaudeAgentOptions,
create_sdk_mcp_server,
tool,
AssistantMessage,
TextBlock
)

# 使用 @tool 装饰器定义工具
@tool("add", "将两个数字相加", {"a": float, "b": float})
async def add_numbers(args: dict[str, Any]) -> dict[str, Any]:
"""加法工具"""
result = args["a"] + args["b"]
return {
"content": [{"type": "text", "text": f"{args['a']} + {args['b']} = {result}"}]
}

@tool("multiply", "将两个数字相乘", {"a": float, "b": float})
async def multiply_numbers(args: dict[str, Any]) -> dict[str, Any]:
"""乘法工具"""
result = args["a"] * args["b"]
return {
"content": [{"type": "text", "text": f"{args['a']} × {args['b']} = {result}"}]
}

@tool("divide", "将两个数字相除", {"a": float, "b": float})
async def divide_numbers(args: dict[str, Any]) -> dict[str, Any]:
"""除法工具,带错误处理"""
if args["b"] == 0:
return {
"content": [{"type": "text", "text": "错误:不能除以零"}],
"is_error": True
}
result = args["a"] / args["b"]
return {
"content": [{"type": "text", "text": f"{args['a']} ÷ {args['b']} = {result}"}]
}

# 使用 TypedDict 定义更复杂的输入模式
from typing import TypedDict, Annotated

class WeatherArgs(TypedDict):
city: Annotated[str, "城市名称"]
unit: Annotated[str, "温度单位: celsius 或 fahrenheit"]

@tool("get_weather", "获取城市天气", WeatherArgs)
async def get_weather(args: dict[str, Any]) -> dict[str, Any]:
"""获取天气的工具"""
city = args["city"]
unit = args.get("unit", "celsius")

# 模拟天气 API 调用
temp = 22 if unit == "celsius" else 72
return {
"content": [{
"type": "text",
"text": f"{city} 的天气:晴朗,{temp}°{unit[0].upper()}"
}]
}

async def mcp_example():
"""MCP 服务器使用示例"""

# 创建 SDK MCP 服务器
calculator_server = create_sdk_mcp_server(
name="calculator",
version="1.0.0",
tools=[add_numbers, multiply_numbers, divide_numbers]
)

weather_server = create_sdk_mcp_server(
name="weather",
version="1.0.0",
tools=[get_weather]
)

# 配置使用这些服务器
options = ClaudeAgentOptions(
mcp_servers={
"calc": calculator_server,
"weather": weather_server
},
# 预批准这些工具,避免权限提示
allowed_tools=[
"mcp__calc__add",
"mcp__calc__multiply",
"mcp__calc__divide",
"mcp__weather__get_weather"
]
)

# 使用客户端
async with ClaudeSDKClient(options=options) as client:
# 使用计算器
await client.query("计算 15 + 27 × 2")
async for msg in client.receive_response():
if isinstance(msg, AssistantMessage):
for block in msg.content:
if isinstance(block, TextBlock):
print(f"Claude: {block.text}")

# 使用天气工具
print("\n---\n")
await client.query("北京的天气怎么样?用摄氏度")
async for msg in client.receive_response():
if isinstance(msg, AssistantMessage):
for block in msg.content:
if isinstance(block, TextBlock):
print(f"Claude: {block.text}")

# 获取 MCP 状态
status = await client.get_mcp_status()
for server in status["mcpServers"]:
print(f"\n服务器: {server['name']}")
print(f"状态: {server['status']}")
if server.get("tools"):
print(f"工具: {[t['name'] for t in server['tools']]}")

if __name__ == "__main__":
asyncio.run(mcp_example())

2.2 使用外部 MCP 服务器

from pathlib import Path
from claude_agent_sdk import ClaudeAgentOptions, query

async def external_mcp_example():
"""使用外部 MCP 服务器示例"""

# 方式 1: 使用字典配置
options1 = ClaudeAgentOptions(
mcp_servers={
"filesystem": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/allow"]
},
"github": {
"type": "sse",
"url": "http://localhost:8080/sse",
"headers": {"Authorization": "Bearer xxx"}
}
}
)

# 方式 2: 使用 JSON 配置文件
options2 = ClaudeAgentOptions(
mcp_servers=Path("mcp-config.json")
)

# 方式 3: 使用 JSON 字符串
mcp_config_json = '''
{
"mcpServers": {
"my-server": {
"command": "my-mcp-server",
"args": []
}
}
}
'''
options3 = ClaudeAgentOptions(mcp_servers=mcp_config_json)

# 使用配置
async for message in query(prompt="列出文件", options=options1):
# 处理消息...
pass

3. 钩子 (Hooks) 使用

3.1 PreToolUse - 工具使用前钩子

import asyncio
import logging
from claude_agent_sdk import (
ClaudeSDKClient,
ClaudeAgentOptions,
HookMatcher,
HookInput,
HookContext,
HookJSONOutput
)

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

async def security_check_hook(
input_data: HookInput,
tool_use_id: str | None,
context: HookContext
) -> HookJSONOutput:
"""安全检查钩子 - 阻止危险操作"""

tool_name = input_data["tool_name"]
tool_input = input_data["tool_input"]

# 阻止删除重要文件
if tool_name == "DeleteFile":
file_path = tool_input.get("file_path", "")
if "important" in file_path.lower() or "secret" in file_path.lower():
logger.warning(f"阻止删除文件: {file_path}")
return {
"systemMessage": "🚫 安全策略阻止了此操作",
"reason": "删除重要文件被安全策略禁止",
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "安全策略:不能删除重要文件"
}
}

# 阻止危险的 bash 命令
if tool_name == "Bash":
command = tool_input.get("command", "")
dangerous_patterns = ["rm -rf", "mkfs", "dd if=", ":(){ :|:& };:", "chmod 777"]

for pattern in dangerous_patterns:
if pattern in command:
logger.warning(f"阻止危险命令: {command}")
return {
"systemMessage": "⚠️ 检测到危险命令",
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": f"命令包含危险模式: {pattern}"
}
}

# 允许其他操作
return {
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow"
}
}

async def modify_input_hook(
input_data: HookInput,
tool_use_id: str | None,
context: HookContext
) -> HookJSONOutput:
"""修改工具输入的钩子"""

tool_name = input_data["tool_name"]
tool_input = input_data["tool_input"]

# 在 Write 操作中添加头部注释
if tool_name == "Write":
file_path = tool_input.get("file_path", "")
if file_path.endswith(".py"):
content = tool_input.get("content", "")
header = "# 自动生成的文件\n# 请审查后使用\n\n"
updated_input = {
**tool_input,
"content": header + content
}
return {
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"updatedInput": updated_input
}
}

return {}

async def pre_tool_use_example():
"""PreToolUse 钩子示例"""

options = ClaudeAgentOptions(
allowed_tools=["Write", "Bash", "DeleteFile"],
hooks={
"PreToolUse": [
# 匹配所有工具,进行安全检查
HookMatcher(matcher=None, hooks=[security_check_hook]),
# 只匹配 Write 工具
HookMatcher(matcher="Write", hooks=[modify_input_hook]),
# 匹配多个工具
HookMatcher(matcher="Bash|DeleteFile", hooks=[logging_hook])
]
}
)

async with ClaudeSDKClient(options=options) as client:
await client.query("尝试删除 important_file.txt")
async for msg in client.receive_response():
# 处理消息...
pass

3.2 PostToolUse - 工具使用后钩子

async def audit_log_hook(
input_data: HookInput,
tool_use_id: str | None,
context: HookContext
) -> HookJSONOutput:
"""审计日志钩子 - 记录所有工具使用"""

tool_name = input_data["tool_name"]
tool_input = input_data["tool_input"]
tool_response = input_data.get("tool_response")

logger.info(f"工具使用审计: {tool_name}")
logger.info(f"输入: {tool_input}")
logger.info(f"输出: {tool_response}")

return {}

async def validate_output_hook(
input_data: HookInput,
tool_use_id: str | None,
context: HookContext
) -> HookJSONOutput:
"""验证工具输出并添加上下文"""

tool_name = input_data["tool_name"]
tool_response = input_data.get("tool_response", "")

# 检测到错误时添加警告
if "error" in str(tool_response).lower():
return {
"systemMessage": "⚠️ 工具执行产生了错误",
"reason": "检测到工具执行错误,请检查输出",
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "上一个工具执行产生了错误,考虑使用其他方法。"
}
}

return {}

async def post_tool_use_example():
"""PostToolUse 钩子示例"""

options = ClaudeAgentOptions(
allowed_tools=["Read", "Write", "Bash"],
hooks={
"PostToolUse": [
HookMatcher(matcher=None, hooks=[audit_log_hook]),
HookMatcher(matcher="Bash", hooks=[validate_output_hook])
]
}
)

3.3 其他钩子事件

async def user_prompt_submit_hook(
input_data: HookInput,
tool_use_id: str | None,
context: HookContext
) -> HookJSONOutput:
"""用户提示提交钩子 - 添加上下文"""

prompt = input_data["prompt"]

# 可以在这里添加自定义上下文
additional_context = "用户当前时间: 2024-01-01 12:00:00"

return {
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"additionalContext": additional_context
}
}

async def subagent_start_hook(
input_data: HookInput,
tool_use_id: str | None,
context: HookContext
) -> HookJSONOutput:
"""子代理启动钩子"""

agent_id = input_data["agent_id"]
agent_type = input_data["agent_type"]

logger.info(f"子代理启动: {agent_id} ({agent_type})")

return {
"hookSpecificOutput": {
"hookEventName": "SubagentStart",
"additionalContext": "子代理已启动,将在独立上下文中运行。"
}
}

async def notification_hook(
input_data: HookInput,
tool_use_id: str | None,
context: HookContext
) -> HookJSONOutput:
"""通知钩子 - 处理系统通知"""

title = input_data.get("title", "")
message = input_data["message"]
notification_type = input_data["notification_type"]

logger.info(f"通知 [{notification_type}]: {title} - {message}")

return {}

async def permission_request_hook(
input_data: HookInput,
tool_use_id: str | None,
context: HookContext
) -> HookJSONOutput:
"""权限请求钩子 - 动态权限决策"""

tool_name = input_data["tool_name"]
tool_input = input_data["tool_input"]

# 基于业务逻辑做决策
if tool_name == "Write":
file_path = tool_input.get("file_path", "")
if file_path.startswith("/safe/"):
# 自动允许安全目录的写入
return {
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {"behavior": "allow"}
}
}

# 其他情况让 CLI 提示用户
return {}

async def complete_hooks_example():
"""完整的钩子配置示例"""

options = ClaudeAgentOptions(
hooks={
"PreToolUse": [
HookMatcher(matcher=None, hooks=[security_check_hook])
],
"PostToolUse": [
HookMatcher(matcher=None, hooks=[audit_log_hook])
],
"PostToolUseFailure": [
HookMatcher(matcher=None, hooks=[error_recovery_hook])
],
"UserPromptSubmit": [
HookMatcher(matcher=None, hooks=[user_prompt_submit_hook])
],
"SubagentStart": [
HookMatcher(matcher=None, hooks=[subagent_start_hook])
],
"SubagentStop": [
HookMatcher(matcher=None, hooks=[subagent_stop_hook])
],
"Notification": [
HookMatcher(matcher=None, hooks=[notification_hook])
],
"PermissionRequest": [
HookMatcher(matcher=None, hooks=[permission_request_hook])
],
"PreCompact": [
HookMatcher(matcher=None, hooks=[pre_compact_hook])
],
"Stop": [
HookMatcher(matcher=None, hooks=[stop_hook])
]
},
# 钩子超时设置(秒)
hooks={
"PreToolUse": [
HookMatcher(matcher=None, hooks=[slow_hook], timeout=30.0)
]
}
)

4. 子代理 (Subagents) 使用

import anyio
from claude_agent_sdk import (
AgentDefinition,
ClaudeAgentOptions,
query,
ClaudeSDKClient,
AssistantMessage,
TextBlock
)

async def code_reviewer_agent_example():
"""代码审查代理示例"""

options = ClaudeAgentOptions(
agents={
"code-reviewer": AgentDefinition(
description="专业的代码审查员,检查代码质量和潜在问题",
prompt="""你是一位资深的代码审查专家。请仔细分析代码,关注:
1. 潜在的 bug 和逻辑错误
2. 性能问题
3. 安全漏洞
4. 代码风格和最佳实践
5. 可维护性问题

提供具体、建设性的反馈。""",
tools=["Read", "Grep", "Glob"], # 代理可用的工具
disallowedTools=["Write", "Edit", "Bash"], # 明确禁止的工具
model="sonnet", # 代理使用的模型
skills=None, # 代理使用的技能
memory="project", # 记忆范围
maxTurns=10, # 最大回合数
permissionMode="plan" # 代理的权限模式
)
}
)

# 使用代理
async for message in query(
prompt="使用 code-reviewer 代理审查 src/claude_agent_sdk/client.py",
options=options
):
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(f"Claude: {block.text}")

async def multiple_agents_example():
"""多代理协作示例"""

options = ClaudeAgentOptions(
agents={
"analyzer": AgentDefinition(
description="代码结构分析员",
prompt="分析代码结构和架构模式。",
tools=["Read", "Grep", "Glob"],
model="sonnet"
),
"tester": AgentDefinition(
description="测试专家",
prompt="编写和运行测试,确保代码质量。",
tools=["Read", "Write", "Bash", "Edit"],
model="sonnet",
permissionMode="acceptEdits"
),
"documenter": AgentDefinition(
description="文档编写专家",
prompt="为代码编写清晰、全面的文档。",
tools=["Read", "Write", "Edit"],
model="sonnet"
)
},
# 从哪些来源加载设置
setting_sources=["user", "project", "local"]
)

async with ClaudeSDKClient(options=options) as client:
# 使用分析代理
await client.query("让 analyzer 代理分析项目结构")
async for msg in client.receive_response():
# 处理响应...
pass

# 使用测试代理
await client.query("让 tester 代理为核心模块编写测试")
async for msg in client.receive_response():
# 处理响应...
pass

async def background_agent_example():
"""后台代理示例"""

options = ClaudeAgentOptions(
agents={
"monitor": AgentDefinition(
description="后台监控代理",
prompt="持续监控系统状态并报告问题。",
tools=["Read", "Grep"],
background=True, # 后台运行
effort="low" # 低努力级别
)
}
)

5. 错误处理

5.1 错误类型层级

from claude_agent_sdk import (
ClaudeSDKError,
CLIConnectionError,
CLINotFoundError,
CLIJSONDecodeError,
ProcessError
)

# 错误层级:
# ClaudeSDKError (基类)
# ├── CLIConnectionError - 连接错误
# │ ├── CLINotFoundError - CLI 未找到
# │ └── CLIJSONDecodeError - JSON 解析错误
# └── ProcessError - 进程执行错误

5.2 常见错误处理示例

import anyio
from claude_agent_sdk import (
query,
ClaudeSDKClient,
ClaudeAgentOptions,
CLINotFoundError,
CLIConnectionError,
ProcessError,
CLIJSONDecodeError,
ClaudeSDKError
)

async def error_handling_example():
"""完整的错误处理示例"""

# 示例 1: 处理 CLI 未找到错误
try:
options = ClaudeAgentOptions(
cli_path="/invalid/path/to/claude"
)
async for message in query(prompt="你好", options=options):
pass
except CLINotFoundError as e:
print(f"未找到 Claude Code CLI: {e}")
print("请安装: npm install -g @anthropic-ai/claude-code")
print("或通过 ClaudeAgentOptions(cli_path=...) 指定路径")

# 示例 2: 处理连接错误
try:
async with ClaudeSDKClient() as client:
await client.connect()
except CLIConnectionError as e:
print(f"连接失败: {e}")
if hasattr(e, '__cause__'):
print(f"原因: {e.__cause__}")

# 示例 3: 处理进程错误
try:
options = ClaudeAgentOptions(
cwd="/nonexistent/directory"
)
async for message in query(prompt="你好", options=options):
pass
except ProcessError as e:
print(f"进程执行失败: {e}")
print(f"退出代码: {e.exit_code}")
print(f"错误输出: {e.stderr}")

# 示例 4: 处理 JSON 解析错误
try:
async with ClaudeSDKClient() as client:
await client.connect()
# 解析错误可能在消息接收时发生
async for msg in client.receive_messages():
pass
except CLIJSONDecodeError as e:
print(f"JSON 解析失败: {e}")
print(f"原始错误: {e.__cause__}")

# 示例 5: 捕获所有 SDK 错误
try:
async for message in query(prompt="你好"):
pass
except ClaudeSDKError as e:
print(f"SDK 错误: {e}")
# 根据错误类型进行不同处理
if isinstance(e, CLINotFoundError):
print("需要安装 CLI")
elif isinstance(e, ProcessError):
print(f"进程失败,退出代码: {e.exit_code}")
else:
print("其他错误")

async def retry_pattern_example():
"""重试模式示例"""

max_retries = 3
retry_delay = 1.0 # 秒

for attempt in range(max_retries):
try:
async for message in query(prompt="执行任务"):
# 处理消息
pass
break # 成功,退出循环
except (CLIConnectionError, ProcessError) as e:
if attempt < max_retries - 1:
print(f"尝试 {attempt + 1} 失败,等待后重试...")
await anyio.sleep(retry_delay * (attempt + 1)) # 退避策略
else:
print(f"所有 {max_retries} 次尝试都失败")
raise

async def with_stderr_callback_example():
"""使用 stderr 回调进行调试"""

def stderr_handler(line: str):
"""处理 CLI 的 stderr 输出"""
print(f"[CLI stderr] {line}")
# 可以在这里记录日志或分析错误

options = ClaudeAgentOptions(
stderr=stderr_handler # 设置 stderr 回调
)

try:
async for message in query(prompt="你好", options=options):
pass
except ClaudeSDKError as e:
print(f"错误: {e}")
# stderr 回调已经输出了详细的调试信息

async def result_message_error_check():
"""检查 ResultMessage 中的错误"""

from claude_agent_sdk import ResultMessage

async for message in query(prompt="执行可能失败的任务"):
if isinstance(message, ResultMessage):
if message.is_error:
print("任务执行出错!")
if message.errors:
for error in message.errors:
print(f" - {error}")
if message.permission_denials:
print(f"权限拒绝次数: {len(message.permission_denials)}")
else:
print(f"任务成功完成")
print(f"总耗时: {message.duration_ms}ms")
if message.total_cost_usd:
print(f"成本: ${message.total_cost_usd:.4f}")

5.3 工具权限回调错误处理

from claude_agent_sdk import (
ClaudeAgentOptions,
ClaudeSDKClient,
CanUseTool,
ToolPermissionContext,
PermissionResultAllow,
PermissionResultDeny,
PermissionResult
)

async def tool_permission_callback_example():
"""工具权限回调示例"""

async def can_use_tool(
tool_name: str,
tool_input: dict,
context: ToolPermissionContext
) -> PermissionResult:
"""权限决策回调"""

try:
# 在这里进行权限检查
if tool_name == "Bash":
command = tool_input.get("command", "")
if "danger" in command:
return PermissionResultDeny(
message="不允许执行危险命令",
interrupt=True
)

# 记录权限决策建议
if context.suggestions:
print(f"CLI 建议: {context.suggestions}")

# 允许执行
return PermissionResultAllow()

except Exception as e:
# 回调出错时默认拒绝
print(f"权限回调出错: {e}")
return PermissionResultDeny(
message="权限检查失败,操作被拒绝"
)

options = ClaudeAgentOptions(
can_use_tool=can_use_tool,
# 注意: can_use_tool 需要流式模式
# 不能同时设置 permission_prompt_tool_name
)

# 使用时需要提供 AsyncIterable 作为 prompt
from collections.abc import AsyncIterable

async def prompt_stream() -> AsyncIterable[dict]:
yield {
"type": "user",
"message": {"role": "user", "content": "你好"},
"parent_tool_use_id": None,
"session_id": "default"
}

async with ClaudeSDKClient(options=options) as client:
await client.connect(prompt=prompt_stream())
async for msg in client.receive_response():
# 处理消息...
pass

总结

Claude Agent SDK for Python 提供了一个功能丰富、设计良好的接口,用于与 Claude Code 交互。通过合理使用其提供的各种功能(MCP 服务器、钩子系统、子代理等),可以构建强大的 AI 辅助应用程序。

关键要点

  1. 选择合适的 API: query() 适合简单场景,ClaudeSDKClient 适合复杂交互
  2. 利用类型系统: 完整的类型注解提供了良好的开发体验和错误检查
  3. 善用钩子系统: 11 种钩子事件可以实现精细的生命周期管理
  4. MCP 服务器: 进程内 MCP 服务器提供高性能的工具扩展能力
  5. 错误处理: 完善的错误类型和处理模式确保应用的健壮性

进一步资源

  • 查看 examples/ 目录获取更多示例代码
  • 阅读 tests/ 目录了解各组件的详细用法
  • 参考 Claude Code 官方文档了解更多 CLI 功能

文档生成时间: 2026-04-18
SDK 版本: 查看 src/claude_agent_sdk/_version.py