Claude Agent SDK 使用指南:把 Claude Code 当库来跑(Python / TypeScript)
Claude Agent SDK 使用指南:把 Claude Code 当库来跑(Python / TypeScript)
先决定"谁跑这个 Agent",再谈怎么写代码——这四行对照表能省掉半天弯路。
接了 Claude Agent SDK 之后最常见的第一个困惑不是"怎么写",而是"我到底该用哪个":CLI?Client SDK?Managed Agents?还是它?官方在 Agent SDK overview 开头就用一张表把四种东西分开了,这张表值得放在最前面。
这篇是使用向的教程:从安装、第一个可运行的 Agent,到权限链、自定义工具、hooks、子 Agent、会话、流式与结构化输出,最后是成本、部署和排障。代码以 Python 为主,关键处给出 TypeScript 对应写法。
本文提纲
- 先选对工具:Agent SDK 与另外三者的区别
- 安装与第一个能跑的 Agent
- 两个入口:
query()与ClaudeSDKClient - 配置:
ClaudeAgentOptions该填什么 - 权限:六步评估链与规则的准确语义
- 自定义工具:进程内 MCP server
- Hooks:在生命周期上拦截与改写
- 子 Agent:程序化定义专家
- 会话:continue / resume / fork
- 输入输出:流式、结构化输出与用户审批
- 成本、部署与排障
先选对工具:Agent SDK 与另外三者的区别
| 你想要 | 用什么 | 你会得到 |
|---|---|---|
| 把 Claude Code 的 Agent 嵌进你自己的 Python / TypeScript 应用,进程由你掌握 | Agent SDK | 一个会启动 Claude Code 二进制的库,自带内置工具、权限、会话、hooks 等能力 |
| 在终端里交互式开发、跑一次性任务 | Claude Code CLI | 为日常交互设计的终端界面 |
| 从自己的代码里直接调 Claude API | Client SDK | 直接访问 API;工具循环要你自己写,或用 beta 的 tool runner |
| 让 Anthropic 托管这个 Agent,用 Claude API 配置 | Managed Agents | 托管 harness,会话跑在 Anthropic 沙箱或你自托管的基础设施里 |
一句话记法:Agent SDK = 库 + 你运行进程;CLI = 终端给人用;Client SDK = 裸 API;Managed Agents = 托管。
另一个常见需求是"我想用 Go/Rust 驱动同一套循环"——官方的答案是把 CLI 当子进程跑(-p 加 --output-format json),不必非得用 Python/TS。
还有一条必须提前知道的合规限制:除非事先获批,Anthropic 不允许第三方开发者在自己的产品里提供 claude.ai 登录或速率限制,要按文档用 API key 认证。
安装与第一个能跑的 Agent
前置条件是 Node.js 18+ 或 Python 3.10+,以及一个 Anthropic 账号。以 Python(uv)为例:
mkdir my-agent && cd my-agent
uv init
uv add claude-agent-sdk
用 pip 的话:
python3 -m venv .venv
source .venv/bin/activate
pip install claude-agent-sdk
TypeScript 则是 npm install @anthropic-ai/claude-agent-sdk 加 npm install --save-dev tsx(新项目建议 npm pkg set type=module)。
两个 SDK 都捆绑了 Claude Code 原生二进制,大多数情况不需要单独装。例外有两种:pip 装到了源码分发包(比如 ARM64 Windows)时不自带二进制;npm ci --omit=optional 会跳过可选依赖导致没有二进制。这两种情况都装一次原生 Claude Code,Python SDK 会从 PATH 找到它。
然后设置密钥——注意 SDK 不会自动读 .env 文件,它读的是运行进程的环境变量:
export ANTHROPIC_API_KEY=your-api-key
它同时支持第三方认证:Bedrock(CLAUDE_CODE_USE_BEDROCK=1)、Claude Platform on AWS、Google Cloud 的 Agent Platform(CLAUDE_CODE_USE_VERTEX=1)、Microsoft Foundry(CLAUDE_CODE_USE_FOUNDRY=1)。
来一个真实的例子:让它修 bug
新建 utils.py,故意留两个 bug:
def calculate_average(numbers):
total = 0
for num in numbers:
total += num
return total / len(numbers)
def get_user_name(user):
return user["name"].upper()
calculate_average([])会除零崩溃;get_user_name(None)会抛 TypeError。
然后是 agent.py:
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, ResultMessage
async def main():
# Agentic loop: streams messages as Claude works
async for message in query(
prompt="Review utils.py for bugs that would cause crashes. Fix any issues you find.",
options=ClaudeAgentOptions(
allowed_tools=["Read", "Edit", "Glob"], # Auto-approve these tools
permission_mode="acceptEdits", # Auto-approve file edits
),
):
if isinstance(message, AssistantMessage):
for block in message.content:
if hasattr(block, "text"):
print(block.text) # Claude 的推理
elif hasattr(block, "name"):
print(f"Tool: {block.name}") # 正在调用的工具
elif isinstance(message, ResultMessage):
print(f"Done: {message.subtype}")
asyncio.run(main())
运行 python agent.py 就行。TypeScript 版结构完全一样:
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({
prompt: "Review utils.py for bugs that would cause crashes. Fix any issues you find.",
options: {
allowedTools: ["Read", "Edit", "Glob"],
permissionMode: "acceptEdits"
}
})) {
if (message.type === "assistant" && message.message?.content) {
for (const block of message.message.content) {
if ("text" in block) console.log(block.text);
else if ("name" in block) console.log(`Tool: ${block.name}`);
}
} else if (message.type === "result") {
console.log(`Done: ${message.subtype}`);
}
}
这段代码里三个东西要理解:
query()是主入口,它创建 agentic loop 并返回 async iterator——所以用async for边跑边收消息;prompt是你要它做的事,用哪个工具由 Claude 自己判断;options是配置,这里用allowed_tools预批准三个工具,permission_mode="acceptEdits"自动批准文件修改。
async for 循环会一直转:Claude 思考、调工具、看结果、决定下一步。编排、工具执行、上下文管理、重试都由 SDK 负责,你只消费这条流。 循环在任务完成或出错时结束。
另外注意:循环里那些 isinstance 判断是在过滤出人类可读的部分。不过滤的话你会看到系统初始化和内部状态消息——调试时有用,平时很吵。
两个入口:query() 与 ClaudeSDKClient
query() |
ClaudeSDKClient |
|
|---|---|---|
| 形态 | 异步生成器,一次性 | 上下文管理器,可多轮 |
| 适合 | 单次任务、后台作业、CI | 交互式对话、需要中途改配置 |
| 自定义工具 / hooks | 不支持 | 支持 |
Python 的双向会话长这样:
import asyncio
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions, AssistantMessage, ResultMessage, TextBlock
async def main():
options = ClaudeAgentOptions(
system_prompt="You are a helpful assistant",
max_turns=4,
)
async with ClaudeSDKClient(options=options) as client:
await client.query("帮我看看这个项目")
async for message in client.receive_response():
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(block.text)
elif isinstance(message, ResultMessage):
print(message.result)
消息类型固定就那几个:AssistantMessage、UserMessage、SystemMessage、ResultMessage;内容块是 TextBlock、ToolUseBlock、ToolResultBlock。ResultMessage 是最后一条,携带结果、成本与错误状态。
TypeScript 没有 ClaudeSDKClient 这个概念对等的类,多轮通过 query() 的流式输入(yield 用户消息)或 continue: true 实现。
配置:ClaudeAgentOptions 该填什么
常用选项一张表:
| 选项(Python / TS) | 作用 |
|---|---|
model / model |
指定模型,如 "claude-sonnet-5" |
fallback_model / fallbackModel |
降级模型;TS 可用逗号分隔多个 |
allowed_tools / allowedTools |
自动批准清单(不是白名单,见下节) |
disallowed_tools / disallowedTools |
禁用工具或加作用域 deny 规则 |
permission_mode / permissionMode |
权限模式 |
max_turns / maxTurns |
限制循环轮数 |
cwd |
工作目录(默认能访问该目录及子目录) |
env |
传给子进程的环境变量,比如指向自建网关 ANTHROPIC_BASE_URL |
system_prompt |
系统提示词(preset 或自定义,见下) |
mcp_servers / mcpServers |
接入 MCP server(外部进程或进程内) |
agents |
程序化定义子 Agent |
hooks |
生命周期拦截 |
output_format / outputFormat |
结构化输出(JSON Schema) |
include_partial_messages / includePartialMessages |
开启逐 token 流式 |
cli_path / pathToClaudeCodeExecutable |
指定 Claude Code 二进制位置 |
一段典型的配置:
options = ClaudeAgentOptions(
model="claude-sonnet-5",
allowed_tools=["Read", "Glob", "Grep"],
max_turns=8,
cwd="/path/to/repo",
)
会话中途也能改配置(这是 ClaudeSDKClient 的独有能力):
async with ClaudeSDKClient(options=ClaudeAgentOptions(model="claude-sonnet-5")) as client:
await client.query("Reply with exactly: ready")
async for message in client.receive_response():
...
await client.set_model("claude-opus-5")
await client.set_permission_mode("acceptEdits")
await client.query("Reply with exactly: done")
async for message in client.receive_response():
...
系统提示词有个容易踩的缓存语义:默认情况下 Claude Code 在会话的第一次请求时构建系统提示词并记录,之后每个请求(包括 resume 之后)都复用它。所以你改了自定义提示词、或改了 claude_code preset 的 append 文本,在会话被压缩或新开会话之前不会生效。要每个请求都重建(比如正在反复调措辞),把 snapshot 设为 False:
options = ClaudeAgentOptions(
system_prompt={"type": "custom", "prompt": "You are a release bot.", "snapshot": False}
)
这个选项需要 Claude Code CLI 2.1.257 或更高版本。
权限:六步评估链与规则的准确语义
这块是 Agent SDK 最需要认真读的部分。每次工具请求,SDK 按固定六步评估:
- Hooks 先跑。hook 可以直接拒绝或放行往下走。注意:hook 返回 allow 并不能跳过后面的 deny / ask 规则;而且
PreToolUsehook 的 allow 也无法批准针对关键路径的rm/rmdir。 - Deny 规则(来自
disallowed_tools和settings.json)。命中即拦截,即使在bypassPermissions模式下也拦。裸名字的 deny 规则(如Bash)会在这个步骤之前就把工具从 Claude 的上下文里移除,所以这一步只检查带作用域的规则(如Bash(rm *))。 - Ask 规则(来自
settings.json)。命中就落到你的canUseTool回调去确认,同样在bypassPermissions下也生效。需要用户交互的工具(AskUserQuestion、标注了_meta["anthropic/requiresUserInteraction"]的 MCP 工具)永远走回调;在dontAsk模式下则直接拒绝,因为该模式从不提示。 - 权限模式。
bypassPermissions批准一切走到这步的调用(关键路径的rm/rmdir除外);acceptEdits批准文件编辑类操作;plan模式把文件编辑和写类 shell 工具一律送进回调,所以规划阶段不可能被自动批准。 - Allow 规则(来自
allowed_tools和settings.json)。命中即批准。有些调用不需要规则也会在这步解决,比如工作目录内的文件读取、只读的 Bash 命令。 canUseTool回调。前面都没解决就问你。dontAsk模式下这一步跳过、直接拒绝。
TypeScript 有个细节:如果你在"回调之前就会被自动批准"的配置下传了 canUseTool,SDK 会在构造 query 时打一次进程警告,代码是 CLAUDE_SDK_CAN_USE_TOOL_SHADOWED。两种配置会触发它:permissionMode: 'bypassPermissions',或者 allowedTools 里出现裸名字(如 "Read")。想让每一次工具调用都必须过你的关,正确做法是用 PreToolUse hook,而不是 canUseTool。
规则的语义差异(很容易搞错)
| 写法 | 效果 |
|---|---|
allowed_tools=["Read", "Grep"] |
这两个工具自动批准;其他工具依然存在,需要批准的调用会落到权限模式和 canUseTool |
disallowed_tools=["Bash"] |
Bash 的工具定义从请求里移除,Claude 看不到也调不了 |
disallowed_tools=["Bash(rm *)"] |
Bash 仍可用;匹配 rm * 的调用在任何模式(含 bypassPermissions)下都被拒绝,其他 Bash 调用(包括 /bin/rm)照常走流程 |
一句话:allowed_tools 是"预批准清单",不是"可用工具白名单";要禁用工具得用 disallowed_tools。 这条被无数教程写反。
自定义工具:进程内 MCP server
这是 SDK 里最实用的扩展点:自定义工具用进程内 MCP server 实现,不用起子进程。
from typing import Any
import httpx
from claude_agent_sdk import tool, create_sdk_mcp_server
# 定义工具:名字、描述、输入 schema、处理函数
@tool(
"get_temperature",
"Get the current temperature at a location",
{"latitude": float, "longitude": float},
)
async def get_temperature(args: dict[str, Any]) -> dict[str, Any]:
async with httpx.AsyncClient() as client:
response = await client.get(
"https://api.open-meteo.com/v1/forecast",
params={
"latitude": args["latitude"],
"longitude": args["longitude"],
"current": "temperature_2m",
"temperature_unit": "fahrenheit",
},
)
data = response.json()
return {"content": [{"type": "text", "text": f"Temperature: {data['current']['temperature_2m']}°F"}]}
weather_server = create_sdk_mcp_server(
name="weather",
version="1.0.0",
tools=[get_temperature],
)
挂到 Agent 上,并在 allowed_tools 里用命名空间形式预批准:
options = ClaudeAgentOptions(
mcp_servers={"weather": weather_server},
allowed_tools=["mcp__weather__get_temperature"],
)
命名规则是 mcp__<server 名>__<工具名>。TypeScript 版用 Zod 定义 schema,.describe() 写的字段描述 Claude 能看到,.optional() 让参数可省;Python 里则把不在 schema 里的参数用 args.get() 取默认值。
还有一个细节很值钱:工具注解。给只读工具标上 readOnlyHint,Claude 就可以把它和其他只读调用批量并发:
from claude_agent_sdk import tool, ToolAnnotations
@tool(
"get_temperature",
"Get the current temperature at a location",
{"latitude": float, "longitude": float},
annotations=ToolAnnotations(readOnlyHint=True),
)
async def get_temperature(args):
...
进程内 server 的好处官方列得很清楚:不用管子进程、没有 IPC 开销、单进程部署、同进程调试、类型安全;而且可以和外部 MCP server 混用——mcp_servers 里同时放进程内 server 和 stdio/HTTP server 都行。外部 MCP 的选型规则很简单:文档给的是命令就用 stdio,给的是 URL 就用 HTTP 或 SSE,自己写代码就用 SDK MCP server。
Hooks:在生命周期上拦截与改写
Hook 是由 Claude Code 这个应用(不是 Claude)在固定节点调用的回调,用于确定性地拦截和反馈。典型用法是保护敏感文件:
from claude_agent_sdk import ClaudeAgentOptions, ClaudeSDKClient, HookMatcher
async def protect_env_files(input_data, tool_use_id, context):
file_path = input_data["tool_input"].get("file_path", "")
file_name = file_path.split("/")[-1]
if file_name == ".env":
return {
"hookSpecificOutput": {
"hookEventName": input_data["hook_event_name"],
"permissionDecision": "deny",
"permissionDecisionReason": "Cannot modify .env files",
}
}
return {}
options = ClaudeAgentOptions(
hooks={"PreToolUse": [HookMatcher(matcher="Edit", hooks=[protect_env_files])]},
)
因为权限链的第一步就是 hooks,它能做规则做不到的事:基于参数内容判断,而不是基于工具名;也能改写或注入上下文。配合权限回调(canUseTool)一起用时记住分工:hook 是确定性的程序判断,回调是"要人拍板"的路径。
子 Agent:程序化定义专家
官方推荐用程序化定义(另一种是文件系统定义,把 agent 放进 .claude/agents/):
from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition
options = ClaudeAgentOptions(
allowed_tools=["Read", "Grep", "Glob", "Agent"], # 注意要放行 Agent 工具
agents={
"code-reviewer": AgentDefinition(
description="Expert code review specialist. Use for quality, security, and maintainability reviews.",
prompt="You are a code review specialist with expertise in security, performance, and best practices.",
tools=["Read", "Grep"], # 只给只读工具
model="sonnet", # 可以指定更便宜的模型
),
},
)
async for message in query(
prompt="Review the authentication module for security issues",
options=options,
):
...
三个要点:description 是给主 Agent 看的,它决定"什么时候该派这个子 Agent";prompt 定义子 Agent 的行为和专业领域;tools 和 model 可以单独收窄——只读工具 + 便宜模型做审查是典型的省钱配置。
调用方式有两种:自动(主 Agent 根据 description 自己决定)和显式——直接在 prompt 里说"Use the code-reviewer agent to check the authentication module"。
会话:continue / resume / fork
会话负责跨轮次保留历史。三种用法:
- 自动管理:Python 用
ClaudeSDKClient的多轮上下文;TypeScript 在第二次query()里传continue: true续接最近一次会话; - resume:拿到 session ID 后按 ID 恢复某次历史运行;
- fork:从某个会话分叉出新的分支——适合"在同一个上下文里试两条不同路线"。
如果要跨主机恢复,会话还可以镜像到外部存储(对象存储、KV、数据库),文档里给了专门的一页(session storage),Python 仓库里也有 Postgres / Redis / S3 的示例实现。
输入输出:流式、结构化输出与用户审批
流式输出
默认你拿到的是"一条条完整消息"。要逐 token 流,把 include_partial_messages=True 打开,然后处理 StreamEvent:
from claude_agent_sdk import query, ClaudeAgentOptions
from claude_agent_sdk.types import StreamEvent
options = ClaudeAgentOptions(
include_partial_messages=True,
allowed_tools=["Bash", "Read"],
)
async for message in query(prompt="List the files in my project", options=options):
if isinstance(message, StreamEvent):
event = message.event
if event.get("type") == "content_block_delta":
delta = event.get("delta", {})
if delta.get("type") == "text_delta":
print(delta.get("text", ""), end="", flush=True)
注意这是三层嵌套类型检查(StreamEvent → content_block_delta → text_delta),第一次写容易少一层。
另外要区分两个概念:流式输入模式(你持续把用户消息喂进去)与流式输出(SDK 持续把事件吐出来)是两件事,文档把它们分成两页,别混。
结构化输出
要让 Agent 返回可校验的 JSON,用 output_format 传 JSON Schema:
schema = {
"type": "object",
"properties": {
"company_name": {"type": "string"},
"founded_year": {"type": "number"},
"headquarters": {"type": "string"},
},
"required": ["company_name"],
}
options = ClaudeAgentOptions(output_format={"type": "json_schema", "schema": schema})
TypeScript 里可以直接用 Zod 定义再交给 SDK;Python 侧同理可用 Pydantic 模型生成 schema。这样多轮工具调用之后拿到的就是类型安全的结构化数据,而不是一段需要自己解析的自然语言。
用户审批与提问
两条路径要分清:审批(工具要不要执行)走 canUseTool 回调;提问(Claude 需要澄清)走 AskUserQuestion 工具。文档专门有一页讲怎么把这两类请求呈现给用户、再把决定送回 SDK——这是做交互式产品时绕不过去的一段。
成本、部署与排障
成本:先记住它不是账单
ResultMessage.total_cost_usd(TS 是 costUSD)是客户端估算,不是权威账单——SDK 用构建时打包的价格表在本地算,会和你实际付费产生偏差。要用它做开发期观察和粗略预算,但不要用它给终端用户计费,也不要据此触发财务决策;权威数据去查 Usage and Cost API 或 Console。
字段分工:Python 在每条 assistant 消息上有 message.usage 和 message.message_id,ResultMessage 上有 model_usage(按模型的成本)和 total_cost_usd(累计);TypeScript 对应 message.message.usage、modelUsage、costUSD。SDK 甚至建模了一条计费规则:当响应的 usage 报告 inference_geo: "us" 时,按列表价乘 1.1(数据驻留定价)。
部署:理解子进程模型
官方 hosting 页的起点是子进程模型:SDK 在本地启动 Claude Code 进程,所以状态默认落在本地磁盘。由此推出三种会话模式:
- Ephemeral(短命):每个任务一个新容器,跑完就丢,简单、隔离好;
- Long-running(长驻):容器常驻,会话在本地累积,适合交互式产品;
- Hybrid(混合):热路径留在本地,同时把会话镜像进外部存储,兼顾延迟与可恢复。
容器化还要配好:运行时依赖、资源限制、网络策略,以及多 Agent 场景下的容器拓扑。安全侧另有 secure deployment 一页讲沙箱与权限收紧。
排障:官方把常见错误列全了
文档把错误分成三类,照着查就行:
- CLI 启动失败:
CLINotFoundError(找不到 Claude Code,装一个或指定cli_path)、CLIConnectionError(拒绝执行批处理脚本 / 启动失败 / 未连接); - CLI 进程退出:
ProcessError: Command failed with exit code、进程以非零码退出、返回错误结果; - 结构化输出异常:最典型的是"result 说 success,但
structured_output是 None"。
另外从旧的 Claude Code SDK(< 0.1.0)升级要注意改名成本:ClaudeCodeOptions → ClaudeAgentOptions、系统提示词配置合并、settings 隔离与显式控制,以及新增的程序化子 Agent 与会话 fork。CHANGELOG 里有完整清单。
顺带提一句品牌合规:Anthropic 允许你用 "Claude Agent"、菜单已标注 Agents 时用 "Claude"、或 "{你的产品名} Powered by Claude";不允许用 "Claude Code" / "Claude Code Agent" 或仿 Claude Code 的 ASCII art——你的产品要保持自己的品牌。
参考链接
- Agent SDK overview — 与 CLI / Client SDK / Managed Agents 的分工、能力清单
- Quickstart — 安装、密钥、第一个找 bug 的 Agent(Python / TypeScript)
- Configure your agent — 选项对象、模型与降级、环境变量、会话中改配置
- Configure permissions — 六步评估链、权限模式、allow/deny 规则语义
- Give Claude custom tools — 进程内 MCP server、工具注解与访问控制
- Intercept and control agent behavior with hooks — 可用 hook 与配置方式
- Subagents in the SDK — 程序化与文件系统定义、调用方式
- Work with sessions / Persist sessions to external storage — continue / resume / fork 与外部存储
- Stream responses in real-time / Streaming Input — 流式输出与两种输入模式
- Get structured output from agents — JSON Schema / Zod / Pydantic
- Handle approvals and user input — 审批与澄清问题怎么接
- Connect to external tools with MCP / Scale to many tools with tool search — 传输类型、认证与工具搜索
- Track cost and usage / Observability — 用量口径与可观测性
- Hosting / Secure deployment — 子进程模型、容器与会话模式
- Troubleshoot the Agent SDK — 三类常见错误与排查
- How the agent loop works — 消息生命周期、工具执行与上下文窗口
- GitHub: claude-agent-sdk-python(8,216 stars)/ claude-agent-sdk-typescript(1,781 stars)
你打算把 Agent SDK 用在哪:嵌进现有产品,还是当一个自动化流水线的执行引擎?评论区聊聊你的用法,觉得这份指南有用就点个赞收藏。
作者: itech001
来源: 公众号:AI人工智能时代(the-ai-era)
网站: https://www.theaiera.top/
关注每日最新AI新闻和技术博客,主页有更多的文章的AI 技术参考:https://www.theaiera.top
本文首发于 AI人工智能时代,转载请注明出处。

浙公网安备 33010602011771号