AI Agents & Skills

Claude Agent SDK (Python): tự build agent trên chính “bộ máy” của Claude Code

Tóm tắt

pip install claude-agent-sdk (Python 3.10+, đã kèm Claude Code CLI). Dùng query() cho tác vụ một chiều, ClaudeSDKClient cho hội thoại hai chiều, custom tool và hook. Tool tự viết chạy in-process qua create_sdk_mcp_server, không cần tiến trình MCP riêng.

Claude Code mạnh vì nó có sẵn một “bộ máy” agent hoàn chỉnh: vòng lặp suy nghĩ–hành động, bộ tool đọc/sửa file, chạy lệnh, quản lý context, hook, quyền. Claude Agent SDK cho phép bạn dùng chính bộ máy đó trong code của mình — để xây agent riêng cho team, nhúng vào backend, hay tự động hoá những quy trình mà claude -p chưa đủ linh hoạt. Bài này tập trung vào bản Python.

Cài đặt

pip install claude-agent-sdk     # Python 3.10+

Gói đã kèm sẵn Claude Code CLI, không cần cài riêng. Muốn dùng một phiên bản CLI cụ thể, truyền ClaudeAgentOptions(cli_path="/duong/dan/claude"). Xác thực bằng ANTHROPIC_API_KEY (hoặc thông tin của Bedrock / Google Cloud / Foundry). Lưu ý: khi xây sản phẩm kinh doanh trên SDK, phải dùng API key — không dùng token thuê bao.

Hai cách gọi

query()ClaudeSDKClient
KiểuHàm async, trả về iterator các messageClient hội thoại hai chiều
Hợp vớiTác vụ một chiều: giao việc → nhận kết quảHội thoại nhiều lượt, custom tool, hook

Tối giản với query()

import anyio
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, TextBlock

async def main():
    opts = ClaudeAgentOptions(
        system_prompt="Bạn là reviewer, chỉ đọc code, không sửa.",
        allowed_tools=["Read", "Grep", "Glob"],
        cwd="./my-repo",
        max_turns=5,
    )
    async for msg in query(prompt="Tìm chỗ xử lý lỗi mà không ghi log", options=opts):
        if isinstance(msg, AssistantMessage):
            for block in msg.content:
                if isinstance(block, TextBlock):
                    print(block.text)

anyio.run(main)

Các tuỳ chọn quan trọng

Tuỳ chọnÝ nghĩa
system_promptVai trò và quy tắc của agent
max_turnsGiới hạn số lượt — chặn vòng lặp tốn kém
allowed_toolsTool được tự động duyệt — lưu ý: không phải danh sách tool khả dụng; các tool khác vẫn tồn tại
disallowed_toolsChặn hẳn tool (muốn agent chỉ đọc, hãy chặn Write/Edit/Bash ở đây)
permission_modeVí dụ "acceptEdits" để tự nhận sửa file
cwdThư mục làm việc
mcp_serversMCP server — cả loại chạy trong tiến trình (SDK) lẫn bên ngoài
hooksHàm Python chạy tại các sự kiện như PreToolUse

Mặc định agent có toàn bộ bộ tool của Claude Code (Read, Write, Edit, Bash…) — nên luôn cấu hình quyền rõ ràng.

Custom tool bằng Python — không cần MCP server riêng

Đây là điểm mạnh nhất của SDK: viết tool bằng hàm Python, chạy ngay trong tiến trình của bạn.

from claude_agent_sdk import tool, create_sdk_mcp_server, ClaudeAgentOptions, ClaudeSDKClient

@tool("get_sprint", "Lấy thông tin sprint hiện tại của một team", {"team": str})
async def get_sprint(args):
    data = await jira_client.current_sprint(args["team"])   # code của bạn
    return {"content": [{"type": "text", "text": str(data)}]}

server = create_sdk_mcp_server(name="pm", version="1.0.0", tools=[get_sprint])

opts = ClaudeAgentOptions(
    mcp_servers={"pm": server},
    allowed_tools=["mcp__pm__get_sprint"],
)

async with ClaudeSDKClient(options=opts) as client:
    await client.query("Sprint của team Mobile còn bao nhiêu story chưa xong?")
    async for msg in client.receive_response():
        print(msg)

So với MCP server bên ngoài: không phải quản lý tiến trình con, không tốn chi phí giao tiếp giữa tiến trình, triển khai và debug đơn giản hơn, có kiểu dữ liệu Python rõ ràng. Vẫn có thể trộn server SDK với server bên ngoài trong cùng mcp_servers.

Hook bằng code: luật cứng cho agent

from claude_agent_sdk import ClaudeAgentOptions, HookMatcher

async def block_prod_db(input_data, tool_use_id, context):
    if input_data["tool_name"] != "Bash":
        return {}
    cmd = input_data["tool_input"].get("command", "")
    if "prod-db" in cmd:
        return {"hookSpecificOutput": {
            "hookEventName": "PreToolUse",
            "permissionDecision": "deny",
            "permissionDecisionReason": "Không được chạm vào database production",
        }}
    return {}

opts = ClaudeAgentOptions(
    allowed_tools=["Bash"],
    hooks={"PreToolUse": [HookMatcher(matcher="Bash", hooks=[block_prod_db])]},
)

Hook do ứng dụng gọi theo quy tắc cố định, không phụ thuộc việc model có “nhớ” quy tắc hay không.

Xử lý lỗi

from claude_agent_sdk import CLINotFoundError, ProcessError, CLIJSONDecodeError

try:
    async for m in query(prompt="Xin chào"):
        pass
except CLINotFoundError:
    print("Chưa cài Claude Code CLI")
except ProcessError as e:
    print("Tiến trình lỗi, exit code:", e.exit_code)
except CLIJSONDecodeError as e:
    print("Không parse được phản hồi:", e)

Nâng cấp từ gói cũ claude-code-sdk (trước 0.1.0): đổi ClaudeCodeOptions thành ClaudeAgentOptions và xem CHANGELOG cho các thay đổi khác.

Khi nào dùng SDK thay vì claude -p?

Nhu cầuChọn
Script ngắn, cron, CI đơn giảnclaude -p
Custom tool gọi hệ thống nội bộ (Jira, DB, API công ty)SDK
Xử lý message có cấu trúc, hiển thị tiến trình trong UI riêngSDK
Hook/quyền viết bằng logic phức tạpSDK
Nhúng agent vào backend/dịch vụSDK

Áp dụng thế nào?

  • Dev: Bắt đầu với một agent chỉ đọc (Read/Grep/Glob) trả lời câu hỏi về codebase cho team mới — 30 dòng code.
  • Tech Lead: Gói các thao tác nội bộ (tra sprint, tra log, tạo ticket) thành custom tool; agent dùng được ngay, không cần dựng MCP server riêng.
  • PM: SDK là nền để làm “trợ lý PM” nội bộ: đọc Jira, tổng hợp tiến độ, soạn báo cáo — với quyền chỉ đọc và người duyệt trước khi gửi.

Nguồn & tham khảo

  1. anthropics/claude-agent-sdk-python — GitHub
  2. Agent SDK reference — Python — Claude Docs