自学教程

Claude Agent SDK 工具开发实战

简介

本实战基于 Claude Agent SDK,开发自定义工具(Tool),让 Claude Agent 主动调用你编写的函数,实现读取本地文件、查询数据库、调用第三方接口等能力。

说明:Claude Agent SDK 的自定义工具机制,和 Claude Code 的 MCP 目标一致。MCP 是独立协议服务;SDK Tools 是代码内直接定义工具,适合后端程序内快速集成。

实战目标

开发一个文件信息查询工具:Agent 收到请求后,自动调用工具获取文件大小、修改时间,再把结果整理成自然语言回答。

前置准备

  1. Python 3.10+ / Node.js 18+
  2. 安装 SDK:pip install anthropic 或者 npm install @anthropic-ai/sdk
  3. Anthropic API Key(环境变量配置,禁止硬编码)
  4. 基础了解:工具定义 JSON Schema、Agent 工具调用回调机制

核心原理

  1. 在代码中定义工具描述(名称、说明、入参 JSON Schema)
  2. 将工具数组传入 SDK messages.create()
  3. Agent 判断是否需要调用工具,返回 tool_use 块
  4. 你的程序接收工具参数,执行自定义函数
  5. 将工具执行结果包装为 tool_result,再次发给 Agent
  6. Agent 基于工具返回内容继续生成回答

实战1:Python 完整示例(文件查询工具)

import os
import time
from anthropic import Anthropic

# 初始化客户端,从环境变量读取 API Key
client = Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))

# ========== 1. 定义工具描述给 Agent ==========
tools = [
    {
        "name": "get_file_info",
        "description": "获取指定路径文件的大小、最后修改时间,仅用于本地文件查询",
        "input_schema": {
            "type": "object",
            "properties": {
                "file_path": {
                    "type": "string",
                    "description": "文件的本地路径"
                }
            },
            "required": ["file_path"]
        }
    }
]

# ========== 2. 实现工具业务函数 ==========
def get_file_info(file_path: str):
    try:
        stat = os.stat(file_path)
        return {
            "size_bytes": stat.st_size,
            "modify_time": time.ctime(stat.st_mtime),
            "exists": True
        }
    except FileNotFoundError:
        return {"exists": False, "msg": "文件不存在"}

# ========== 3. 主会话逻辑 ==========
messages = [
    {"role": "user", "content": "查看当前目录 README.md 文件信息"}
]

response = client.messages.create(
    model="claude-3-5-sonnet-latest",
    max_tokens=1024,
    tools=tools,
    messages=messages
)

# 处理 Agent 返回内容
for block in response.content:
    if block.type == "tool_use":
        tool_name = block.name
        tool_input = block.input
        tool_use_id = block.id

        # 执行对应工具
        if tool_name == "get_file_info":
            result = get_file_info(**tool_input)

        # 将工具调用结果追加进消息历史
        messages.append(response.content[0].to_dict())
        messages.append({
            "role": "user",
            "content": [
                {
                    "type": "tool_result",
                    "tool_use_id": tool_use_id,
                    "content": str(result)
                }
            ]
        })

        # 二次请求,让Agent整合工具结果输出回答
        final_resp = client.messages.create(
            model="claude-3-5-sonnet-latest",
            max_tokens=1024,
            messages=messages,
            tools=tools
        )
        print(final_resp.content[0].text)

实战2:TypeScript / Node.js 版本示例

import Anthropic from "@anthropic-ai/sdk";
import fs from "fs/promises";
import path from "path";

const anthropic = new Anthropic({
  apiKey: process.env.ANTHROPIC_API_KEY,
});

// 工具定义
const tools = [
  {
    name: "get_file_info",
    description: "获取本地文件大小与修改时间",
    input_schema: {
      type: "object",
      properties: {
        file_path: { type: "string", description: "文件路径" },
      },
      required: ["file_path"],
    },
  },
];

// 工具函数
async function getFileInfo(filePath: string) {
  try {
    const stat = await fs.stat(filePath);
    return {
      size_bytes: stat.size,
      modify_time: stat.mtime.toString(),
      exists: true,
    };
  } catch (e) {
    return { exists: false, msg: "文件不存在" };
  }
}

async function main() {
  const messages = [
    { role: "user", content: "查看 README.md 文件信息" },
  ];

  const resp = await anthropic.messages.create({
    model: "claude-3-5-sonnet-latest",
    max_tokens: 1024,
    tools,
    messages,
  });

  const block = resp.content[0];
  if (block.type === "tool_use") {
    const result = await getFileInfo(block.input.file_path);
    messages.push(resp.content[0]);
    messages.push({
      role: "user",
      content: [
        {
          type: "tool_result",
          tool_use_id: block.id,
          content: JSON.stringify(result),
        },
      ],
    });
    const finalResp = await anthropic.messages.create({
      model: "claude-3-5-sonnet-latest",
      max_tokens: 1024,
      tools,
      messages,
    });
    console.log(finalResp.content[0].text);
  }
}

main();

工具开发规范

1. 工具描述规范

  • description 写清楚工具能力、边界,告诉 Agent 什么时候调用
  • input_schema 严格 JSON Schema,类型不能乱写
  • 参数命名清晰,避免歧义;必填参数放在 required

2. 工具返回规范

  • 返回结构化 JSON,方便 Agent 解析
  • 一定要返回错误信息(文件不存在、接口超时等)
  • 不要返回超长原始文本,精简结果,降低 token

3. 多工具注册

你可以一次性传入多个工具到 tools 数组,Agent 会自动判断选择合适工具。
例如同时注册:文件查询、数据库查询、HTTP 请求工具。

流式工具调用(Stream)

生产环境推荐使用流式模式,实时接收 Agent 输出,工具调用逻辑不变:

流式只是边返回内容边接收,当检测到 tool_use 块完整后,执行工具回调。

安全要点(重点)

  1. 输入校验:工具收到参数必须校验,防止路径穿越(../)、命令注入
  2. 权限限制:文件工具限定工作目录,禁止读取系统敏感文件
  3. 超时控制:网络类工具设置超时时间,防止长时间阻塞
  4. 参数白名单:不允许直接执行 shell 命令,高危操作禁止开放
  5. 日志记录:记录工具名称、入参、返回结果,方便审计排查

调试技巧

  1. 先单独运行工具函数,确保函数本身可以正常返回结果
  2. 打印 Agent 返回的 tool_use 块,检查参数是否符合预期
  3. 减少 system prompt 干扰,最小 demo 先跑通,再加业务规则
  4. 如果 Agent 不调用工具:优化 description,明确触发条件

SDK Tools vs MCP 对比

项目SDK 自定义工具MCP
部署方式代码内直接定义函数独立进程服务
适用场景后端程序内轻量工具跨进程、IDE/Claude Code 复用工具
开发成本低,直接写代码高,需要实现 MCP 协议
复用性仅当前程序可用多个客户端共用同一个工具服务

常见问题

Q:Agent 不调用我定义的工具?

A:①工具描述写得不清楚;②用户提问没有触发工具场景;③模型版本不支持工具调用,使用 claude-3-5-sonnet-latest。

Q:工具调用循环触发,无限来回?

A:增加终止条件,或者在 system prompt 限制最大工具调用轮次。

Q:工具返回内容太长,token 消耗很高?

A:工具返回结果做摘要,只返回必要字段,不要返回完整原始数据。

小结

Claude Agent SDK 自定义工具开发流程:定义工具 Schema → 编写工具业务函数 → 处理 Agent 的 tool_use 请求 → 回传 tool_result。
轻量工具直接使用 SDK Tools;如果需要给 Claude Code、多个项目复用工具,优先选择 MCP。

0 条笔记