如何构建AI智能体:用 Python 和 Claude Agent SDK 一步步实现
如何构建AI智能体,让它自己读代码、修 bug?本文一步步安装 Claude Agent SDK,编写第一个智能体,再加上自定义工具和 MCP。
本文的其他语言版本:العربيةDeutschEnglishEspañolFrançaisItaliano日本語Polski
本文内容
如何构建AI智能体,又不必从零写整个循环?我知道的最短路径是 Claude Agent SDK。这是一个面向 Python 和 TypeScript 的库,提供的工具、智能体循环和上下文管理,和 Claude Code 底层用的完全一样。我们只写十几行代码,智能体就能自己读文件、修 bug、运行命令。
在这篇教程里,我们用 Python 搭建第一个智能体,配置它的权限,再给它加上自定义工具和 MCP 服务器。所有命令和代码片段都来自 Agent SDK 官方快速入门,以及文中链接的文档页面。
从开发者的角度看,AI 智能体是什么?
按照文档的说法,智能体是一种应用:它自己规划接下来的步骤并调用工具来完成任务,比如读文件、运行命令或修改代码。不涉及代码的整体介绍,我在什么是 AI 智能体里写过。
对开发者来说,最关键的是循环:模型思考,调用工具,查看结果,再决定下一步。Agent SDK 接管了这个循环,以及工具执行、上下文管理和重试,我们只需要读取消息流。
文档把使用 Claude 的方式分成四种:
- Agent SDK:在我们自己的应用里运行 Claude Code 智能体的库(运行在我们自己管理的进程中),
- Claude Code CLI:用于日常交互式工作的终端(详见我的 Claude Code 教程),
- Client SDK:直接访问 Claude API,工具循环要自己写(或者交给测试版的“tool runner”),
- Managed Agents:由 Anthropic 托管、通过 API 配置的智能体。
本教程讲的是第一种。
第 1 步:如何安装 Claude Agent SDK?
需要 Python 3.10+(TypeScript 则需要 Node.js 18.14+)和一个 Claude Console 账号。先创建项目文件夹:
mkdir my-agent
cd my-agent
默认情况下,智能体可以访问这个目录及其子目录中的文件。文档推荐使用包管理器 uv:
uv init
uv add claude-agent-sdk
用普通的 pip(macOS 和 Linux)是这样:
python3 -m venv .venv
source .venv/bin/activate
pip install claude-agent-sdk
有意思的是,通常不需要单独安装 Claude Code:两个 SDK 都自带 Claude Code 的原生二进制文件。例外是没有预编译包的平台,比如 Windows ARM64 (这时手动安装 Claude Code,SDK 会在 PATH 里找到它)。
第 2 步:如何设置 API 密钥?
在 Claude Console 创建密钥,然后在运行智能体的终端里把它设为环境变量:
export ANTHROPIC_API_KEY=your-api-key
SDK 不会自动加载 .env 文件,所以如果把密钥放在 .env 里,需要自己加载(比如用 dotenv 包)。除了 Anthropic 密钥,也可以通过设置相应的变量来使用 Amazon Bedrock、Claude Platform on AWS、Google Cloud 或 Microsoft Foundry,例如 CLAUDE_CODE_USE_BEDROCK=1。
文档里有一条限制:未经事先批准,Anthropic 不允许基于 Agent SDK 构建的产品提供 claude.ai 账号登录,也不允许使用订阅额度。智能体靠 API 密钥运行,按 token 付费(怎么追踪用量,我在 Claude API 成本一文中写过)。
第 3 步:如何编写第一个 AI 智能体?
快速入门里的例子是一个查找并修复代码 bug 的智能体。先创建一个故意带两个 bug 的 utils.py:
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
),
):
# Print human-readable output
if isinstance(message, AssistantMessage):
for block in message.content:
if hasattr(block, "text"):
print(block.text) # Claude's reasoning
elif hasattr(block, "name"):
print(f"Tool: {block.name}") # Tool being called
elif isinstance(message, ResultMessage):
print(f"Done: {message.subtype}") # Final result
asyncio.run(main())
这段代码有三部分:
query创建智能体循环,返回一个异步迭代器,所以用async for实时读取消息,prompt是任务。用哪些工具由 Claude 自己决定,options是配置:这里allowed_tools预先批准了Read、Edit和Glob,permission_mode="acceptEdits"会自动接受文件修改。
运行:
uv run agent.py
(或在已激活的 venv 中运行 python agent.py)。智能体会输出它的推理过程和每次调用的工具,最后打印 Done: success。完成后,utils.py 里已经处理了空列表和用户缺失的情况。智能体自己读了文件、找到了边界情况并修好了代码,而我们没写一行处理工具的函数。
如果看到 Not logged in 或 Invalid API key,几乎可以肯定是这个终端里没有设置 ANTHROPIC_API_KEY 变量。
第 4 步:如何设定智能体能自己做什么?
智能体能做什么,由工具决定。文档给出了三种典型组合:
Read、Glob、Grep:只做分析,不修改文件,Read、Edit、Glob:分析加修改代码,Read、Edit、Bash、Glob、Grep:带终端命令的完全自动化。
比如想让智能体搜索网页或运行测试,只需加一个工具并修改提示词:
options = ClaudeAgentOptions(
allowed_tools=["Read", "Edit", "Glob", "Bash"], permission_mode="acceptEdits"
)
启用 Bash 后,快速入门建议的任务是“Write unit tests for utils.py, run them, and fix any failures”。还可以通过 system_prompt 给智能体设定角色,比如一位遵守 PEP 8 的资深 Python 开发者。
第二个杠杆是权限模式(permission_mode):
"default"把每个未批准的调用交给我们的canUseTool函数,没有这个函数就拒绝,"acceptEdits"接受文件修改,以及工作目录中mkdir、mv之类的简单命令,"plan"让智能体先研究代码、制定计划,"dontAsk"从不询问:只运行预先批准的内容,其余一律拒绝(适合没人守在键盘前的智能体),"auto"让每个操作都经过一个分类器,由它逐一审查并决定拦截或放行,"bypassPermissions"不经询问执行一切,按文档的说法只适用于隔离环境,比如 CI 或容器。
文档里的黄金法则:开发机上用 "acceptEdits","bypassPermissions" 只用在智能体无法破坏任何重要东西的地方。为什么这很重要,可以看我写的 AI 智能体安全指南。
最好一开始就设好上限:max_turns 限制轮数,max_budget_usd 在估算成本超出时停止循环。没有它们,智能体会一直运行到它自己认为完成为止,碰上“改进这个项目”这类开放式任务,可能要跑很久 ;)
第 5 步:如何给智能体加上自定义工具?
内置工具只是开始。自定义工具用装饰器 @tool 定义(名称、描述、输入模式和函数),然后封装进一个在同一进程中运行的 MCP 服务器。文档的例子从 Open-Meteo API 获取气温,所以先添加 httpx 库:
uv add httpx
工具定义:
from typing import Annotated, Any
import httpx
from claude_agent_sdk import tool, create_sdk_mcp_server
# Define a tool: name, description, input schema, handler
@tool(
"get_temperature",
"Get the current temperature at a location",
{
"latitude": Annotated[float, "Latitude coordinate"],
"longitude": Annotated[float, "Longitude coordinate"],
},
)
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 a content array - Claude sees this as the tool result
return {
"content": [
{
"type": "text",
"text": f"Temperature: {data['current']['temperature_2m']}°F",
}
]
}
# Wrap the tool in an in-process MCP server
weather_server = create_sdk_mcp_server(
name="weather",
version="1.0.0",
tools=[get_temperature],
)
在同一个文件里,把服务器传给智能体。工具的完整名称格式是 mcp__{服务器}__{工具},把它写进 allowed_tools,工具就能在不请求许可的情况下运行:
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
async def main():
options = ClaudeAgentOptions(
mcp_servers={"weather": weather_server},
allowed_tools=["mcp__weather__get_temperature"],
)
async for message in query(
prompt="What's the temperature in San Francisco?",
options=options,
):
# ResultMessage is the final message after all tool calls complete
if isinstance(message, ResultMessage) and message.subtype == "success":
print(message.result)
asyncio.run(main())
智能体会调用我们的工具,并根据结果作答。把天气换成任何东西都行:我们的数据库、内部 API 或某个脚本。
第 6 步:如何把智能体连接到 MCP 服务器?
现成的 MCP 服务器(协议本身我在什么是 MCP 里介绍过)在 mcp_servers 选项中配置。文档里的例子通过 HTTP 把智能体连到 Claude Code 的远程文档服务器:
options = ClaudeAgentOptions(
mcp_servers={
"claude-code-docs": {
"type": "http",
"url": "https://code.claude.com/docs/mcp",
}
},
allowed_tools=["mcp__claude-code-docs__*"],
)
allowed_tools 中的星号会批准该服务器的所有工具。给出提示词“Use the docs MCP server to explain what hooks are in Claude Code”,智能体就会检索文档并返回答案。
第一个智能体之后做什么?
Agent SDK 还具备 Claude Code 的其他功能:
- hooks:在工具调用之前或之后运行的自定义代码,
- 子智能体,用于拆分出来的子任务,
- 会话:用
ResultMessage.session_id中的 ID 可以带着完整上下文恢复对话,或者分叉出新对话, - 技能、命令和记忆,和 Claude Code 一样从
.claude/目录加载。
现成的示例应用(包括邮件助手和研究型智能体)在 claude-agent-sdk-demos 仓库里。
需要注意什么?
第一,成本。上下文每一轮都在增长,一个大文件或输出很多的命令一下子就能吃掉几千个 token。固定部分(系统提示词、工具定义)会自动缓存,但还是建议从第一天就设置 max_budget_usd (上限可能会被超出一次回复的成本,所以留点余量)。
第二,权限。在普通笔记本电脑上给智能体 Bash 加 bypassPermissions,等于自找麻烦,完全自动化还是留给容器吧。
第三,品牌。按照文档,基于 SDK 构建的产品可以叫“Claude Agent”或“
常见问题
如何构建AI智能体,而不用从零写循环?
最简单的办法是用一个已经内置循环的库,比如 Claude Agent SDK。安装 claude-agent-sdk,设置 ANTHROPIC_API_KEY,再用提示词和允许的工具列表调用 query()。剩下的工作,也就是调用工具和管理上下文,都由 SDK 完成。
Claude Agent SDK 免费吗?
库本身不收费,但智能体使用 Claude API,所以要为消耗的 token 付费。未经 Anthropic 批准,不能把它接到 claude.ai 订阅上。
Agent SDK 和 Claude API 有什么区别?
用普通的 Client SDK(Claude API)时,工具循环要自己写,或者使用测试版的 tool runner。Agent SDK 自带 Claude Code 的内置工具(读写文件、执行命令、网页搜索),以及权限、会话和 hooks。
能用 TypeScript 写智能体吗?
可以。包名是 @anthropic-ai/claude-agent-sdk,需要 Node.js 18.14+。在文档的示例中,选项名称和 Python 一样,只是改用 camelCase(allowedTools、permissionMode)。