你是否遇到过这样的困境:

  • 场景 1:想让 Claude 查询实时数据库中的用户信息,但它只能描述"应该怎么查",无法真正执行
  • 场景 2:希望 Claude 调用多个 API(天气、股票、新闻),但每个 API 都要手动集成
  • 场景 3:想构建自动化 Agent 让它自主决定何时调用什么工具,但不知道从何下手

这些问题的根源在于:Claude 是"思考者"而非"行动者"。它能理解、分析、规划,但没有工具调用能力就无法执行计划。

工具调用(Tool Use) 是解决这个问题的关键。它让 Claude 不仅能思考,还能行动——通过明确定义工具,Claude 可以自主决定何时调用哪个工具,执行具体操作,然后基于结果继续推理。这是构建真正智能 Agent 的基础。

本文从实现角度系统讲解 Claude API 工具调用的机制、设计方法、常见错误与实战应用,帮助你快速上手。


第一部分:工具调用的工作原理

1.1 核心概念:模型决策 + 应用执行

工具调用的本质很简单:模型不执行工具,而是决定调用哪个工具

完整流程如下:

用户提问("帮我查张三的账户余额")
         ↓
    Claude 思考
         ↓
  "这需要查询数据库"
         ↓
生成结构化工具调用请求(包含工具名和参数)
         ↓
你的应用接收请求
         ↓
   执行真实数据库查询
         ↓
   返回查询结果给 Claude
         ↓
  Claude 基于结果生成回复
         ↓
用户看到最终答案

这种分工的优势:

优势 说明
安全性 Claude 无法直接修改数据库,你可以在执行前进行权限检查
可控性 你知道 Claude 想调用什么,可以决定是否允许
灵活性 同一个 Claude 模型可配合不同工具集,适应多种场景
可审计性 每次工具调用都可被记录和审计

1.2 工具调用 vs 其他方案的选择标准

很多人困惑于什么时候用工具调用、什么时候用 RAG、什么时候用 Prompt 工程。这里有个决策树:

需求 推荐方案 原因
需要调用实时 API、数据库 工具调用 模型无法直接访问外部系统
需要明确控制执行时机和方式 工具调用 可在应用层进行权限检查、错误处理
需要集成多个异构系统 工具调用 统一的接口管理工具
从已有知识库检索信息 RAG 工具调用的开销更大
需要 Claude 理解复杂业务规则 Prompt 工程 规则不需要实时执行
纯文本生成和分析 Prompt 工程 最简单、最快速

简单判断法:如果需求涉及"执行"(查询、修改、调用),用工具调用;如果只涉及"理解和生成",用 Prompt 工程或 RAG。

1.3 模型能力对比

当前 Claude API 支持的主流模型都具备工具调用能力,性能有差异:

模型 工具调用准确率 速度 成本 适用场景
claude-opus-4-8 最高 较慢 最高 复杂多工具、高准确率要求
claude-sonnet-5 中等 日常应用、大多数生产场景
claude-haiku-4-5-20251001 中等 最快 最低 简单工具调用、成本敏感场景

推荐:大多数场景下 claude-sonnet-5 是最佳平衡点。如果工具调用失败率高或工具链复杂,升级到 claude-opus-4-8。


第二部分:5 分钟快速上手

2.1 最小化可运行示例

假设你想让 Claude 能查询天气。以下是完整的工作代码:

import anthropic
import json

client = anthropic.Anthropic(api_key="your-api-key")

# 第一步:定义工具
tools = [
    {
        "name": "get_weather",
        "description": "获取指定城市的当前天气信息",
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {
                    "type": "string",
                    "description": "城市名称,例如:北京、上海"
                }
            },
            "required": ["city"]
        }
    }
]

# 第二步:用户提问
messages = [
    {"role": "user", "content": "北京今天天气怎么样?"}
]

# 第三步:发送消息并请求工具调用
response = client.messages.create(
    model="claude-sonnet-5",
    max_tokens=1024,
    tools=tools,
    messages=messages
)

# 第四步:检查 Claude 是否要调用工具
if response.stop_reason == "tool_use":
    # 找到工具调用块
    tool_use_block = next(
        (block for block in response.content if block.type == "tool_use"),
        None
    )
    
    if tool_use_block:
        tool_name = tool_use_block.name
        tool_input = tool_use_block.input
        
        print(f"Claude 要调用工具: {tool_name}")
        print(f"参数: {tool_input}")
        
        # 第五步:模拟工具执行(这里是你的应用代码)
        if tool_name == "get_weather":
            tool_result = f"{tool_input['city']}今天晴天,气温 25°C"
        else:
            tool_result = "工具不存在"
        
        # 第六步:将工具结果反馈给 Claude
        messages.append({"role": "assistant", "content": response.content})
        messages.append({
            "role": "user",
            "content": [
                {
                    "type": "tool_result",
                    "tool_use_id": tool_use_block.id,
                    "content": tool_result
                }
            ]
        })
        
        # 第七步:再次调用,获取最终回复
        final_response = client.messages.create(
            model="claude-sonnet-5",
            max_tokens=1024,
            tools=tools,
            messages=messages
        )
        
        # 提取最终文本
        final_text = next(
            (block.text for block in final_response.content if hasattr(block, "text")),
            None
        )
        print(f"\nClaude 的回复: {final_text}")
else:
    # Claude 没有调用工具,直接返回文本
    print("Claude 的回复:", response.content[0].text)

运行结果

Claude 要调用工具: get_weather
参数: {'city': '北京'}

Claude 的回复: 根据天气工具的查询结果,北京今天晴天,气温 25°C。这是个很好的天气,适合户外活动。

2.2 代码执行流程详解

步骤 操作 关键点
1 定义工具 name(唯一标识)、description(Claude 通过它判断是否调用)、input_schema(参数定义)
2 用户提问 包装成标准的消息格式
3 首次调用 API 传入 tools 参数,告诉 Claude 有哪些工具可用
4 检查响应 stop_reason == "tool_use" 表示 Claude 决定调用工具
5 执行工具 你的应用代码真正执行查询、API 调用等操作
6 反馈结果 tool_result 消息类型将结果返回给 Claude
7 再次调用 API Claude 基于工具结果继续推理并生成最终回复

2.3 Claude 的工具调用决策逻辑

Claude 通过什么判断是否调用工具?答案是 工具的 description

例如:

  • 好的描述:"获取指定城市的当前天气信息"(明确说明功能和输入)
  • 不好的描述:"天气工具"(太模糊,Claude 不知道什么时候用)

同时,Claude 也会评估问题是否真的需要工具:

  • 用户问"天气通常怎么形成?" → Claude 不调用天气工具,直接回答
  • 用户问"北京今天天气怎么样?" → Claude 调用天气工具

这种"智能决策"是 Claude 的核心优势,无需手动指定何时调用工具。


第三部分:工具设计的关键规则

3.1 工具定义的 5 个黄金规则

规则 1:一个工具做一件事

反面示例

{
    "name": "manage_user",
    "description": "管理用户,可以创建、删除、修改、查询用户",
    "input_schema": {
        "type": "object",
        "properties": {
            "action": {"type": "string", "enum": ["create", "delete", "update", "query"]},
            "user_id": {"type": "string"},
            "name": {"type": "string"},
            "email": {"type": "string"}
            # ... 还有很多字段
        }
    }
}

问题:参数过多,Claude 容易调用错参数。

正确做法:拆分成多个工具

tools = [
    {
        "name": "query_user",
        "description": "根据用户 ID 查询用户信息",
        "input_schema": {
            "type": "object",
            "properties": {
                "user_id": {"type": "string", "description": "用户的唯一标识"}
            },
            "required": ["user_id"]
        }
    },
    {
        "name": "create_user",
        "description": "创建一个新用户",
        "input_schema": {
            "type": "object",
            "properties": {
                "name": {"type": "string"},
                "email": {"type": "string"}
            },
            "required": ["name", "email"]
        }
    }
]
规则 2:描述清晰且包含使用场景

反面示例

"description": "查询数据"

正确做法

"description": "从用户数据库中查询指定用户的信息。适用场景:需要获取用户的名字、邮箱、注册时间等基本信息。"
规则 3:参数数量不超过 5 个

如果需要超过 5 个参数,说明工具设计有问题。参数过多时 Claude 容易出错。

最佳实践

  • 保持参数数量在 3-5 个
  • required 明确标记必需参数
  • 可选参数提供默认值
规则 4:参数类型与约束明确

反面示例

"properties": {
    "date": {"type": "string"}  # 什么格式?
}

正确做法

"properties": {
    "date": {
        "type": "string",
        "description": "日期,格式为 YYYY-MM-DD,例如 2024-01-15"
    }
}

或使用 enum 限制可选值:

"properties": {
    "status": {
        "type": "string",
        "enum": ["pending", "approved", "rejected"],
        "description": "订单状态"
    }
}
规则 5:提供示例与默认值
"properties": {
    "limit": {
        "type": "integer",
        "description": "返回的最大记录数,默认 10",
        "default": 10
    },
    "city": {
        "type": "string",
        "description": "城市名称,例如:北京、上海、广州"
    }
}

3.2 工具执行结果的反馈规范

工具调用后,返回结果的格式很重要。Claude 能否正确理解结果,直接影响最终回复质量。

成功情况的好格式

{
    "success": true,
    "data": {
        "user_id": "123",
        "name": "张三",
        "email": "zhangsan@example.com",
        "created_at": "2024-01-01"
    },
    "message": "用户查询成功"
}

失败情况的好格式

{
    "success": false,
    "error": "USER_NOT_FOUND",
    "message": "用户 ID 456 不存在"
}

不好的格式

"查询失败"  // 纯文本,Claude 难以解析

最佳实践

  1. 总是包含 success 字段(布尔值)
  2. 成功时返回 data,失败时返回 errormessage
  3. 如果结果过长(>1000 字符),进行摘要或分页
  4. 包含敏感信息时,返回前进行脱敏

第四部分:多工具场景与工具链

4.1 tool_choice 参数控制工具调用行为

当有多个工具时,用 tool_choice 参数控制 Claude 的调用策略。

模式 1:"auto"(推荐,默认值)

response = client.messages.create(
    model="claude-sonnet-5",
    max_tokens=1024,
    tools=tools,
    tool_choice="auto",  # Claude 自主决定是否调用工具
    messages=messages
)

Claude 会评估问题,自主决定是否调用工具、调用哪个工具,或者不调用任何工具直接回答。

适用场景:大多数情况,让 Claude 自主判断

模式 2:"any"

tool_choice="any"  # 强制 Claude 必须调用至少一个工具

Claude 必须调用工具,即使不需要也要调用。

适用场景:流程化场景,确保一定会触发工具调用

模式 3:指定工具名

tool_choice={"type": "tool", "name": "get_weather"}  # 强制调用特定工具

Claude 必须调用指定的工具。

适用场景:流程化场景,如"用户选择了查询天气,就必须调用天气工具"

4.2 工具链:工具 A 的输出 → 工具 B 的输入

实际应用中,常需多个工具协作。例如:

  1. 先调用"获取用户信息"工具
  2. 基于用户信息,调用"获取用户订单"工具
  3. 基于订单信息,调用"计算优惠"工具

Claude 会自动处理这个流程。完整示例:

tools = [
    {
        "name": "get_user_info",
        "description": "获取用户基本信息",
        "input_schema": {
            "type": "object",
            "properties": {
                "user_id": {"type": "string"}
            },
            "required": ["user_id"]
        }
    },
    {
        "name": "get_user_orders",
        "description": "获取用户的订单列表",
        "input_schema": {
            "type": "object",
            "properties": {
                "user_id": {"type": "string"}
            },
            "required": ["user_id"]
        }
    },
    {
        "name": "calculate_discount",
        "description": "根据用户等级计算优惠",
        "input_schema": {
            "type": "object",
            "properties": {
                "user_level": {"type": "string", "enum": ["vip", "normal", "new"]},
                "order_amount": {"type": "number"}
            },
            "required": ["user_level", "order_amount"]
        }
    }
]

messages = [
    {"role": "user", "content": "用户 123 能得到多少折扣?"}
]

# 循环处理工具调用
while True:
    response = client.messages.create(
        model="claude-sonnet-5",
        max_tokens=1024,
        tools=tools,
        messages=messages
    )
    
    if response.stop_reason == "tool_use":
        tool_use_block = next(
            (block for block in response.content if block.type == "tool_use"),
            None
        )
        
        if tool_use_block:
            # 根据工具名执行相应操作
            if tool_use_block.name == "get_user_info":
                tool_result = json.dumps({"user_level": "vip", "name": "张三"})
            elif tool_use_block.name == "get_user_orders":
                tool_result = json.dumps({"orders": [100, 200, 300], "total": 600})
            elif tool_use_block.name == "calculate_discount":
                tool_result = json.dumps({"discount": 0.2, "amount": 120})
            else:
                tool_result = json.dumps({"error": "Unknown tool"})
            
            # 反馈结果
            messages.append({"role": "assistant", "content": response.content})
            messages.append({
                "role": "user",
                "content": [
                    {
                        "type": "tool_result",
                        "tool_use_id": tool_use_block.id,
                        "content": tool_result
                    }
                ]
            })
    else:
        # Claude 完成推理,返回最终回复
        final_text = next(
            (block.text for block in response.content if hasattr(block, "text")),
            None
        )
        print(f"最终回复: {final_text}")
        break

第五部分:错误处理与调试

5.1 工具调用失败的常见原因与恢复

原因 1:参数校验失败

症状:Claude 传入的参数不符合 schema 定义

恢复策略

required_fields = ["user_id"]  # 根据工具定义
for field in required_fields:
    if field not in tool_input:
        return {
            "success": False,
            "error": "MISSING_PARAMETER",
            "message": f"缺少必需参数:{field}"
        }
原因 2:工具执行超时

症状:工具执行时间过长,导致请求超时

恢复策略

import signal

def timeout_handler(signum, frame):
    raise TimeoutError("工具执行超时")

signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(5)  # 5 秒超时

try:
    tool_result = execute_tool(tool_name, tool_input)
except TimeoutError:
    tool_result = {
        "success": False,
        "error": "TIMEOUT",
        "message": "工具执行超时,请稍后重试"
    }
finally:
    signal.alarm(0)
原因 3:权限拒绝

症状:用户没有权限调用某个工具

恢复策略

if not user_has_permission(user_id, tool_name):
    return {
        "success": False,
        "error": "PERMISSION_DENIED",
        "message": "你没有权限调用此工具"
    }

5.2 调试与可观测性

为了快速定位问题,需要记录工具调用的每个环节:

import logging

logger = logging.getLogger(__name__)

def execute_tool_with_logging(tool_name, tool_input, user_id):
    logger.info(f"[TOOL_CALL] User: {user_id}, Tool: {tool_name}, Input: {tool_input}")
    
    try:
        result = execute_tool(tool_name, tool_input)
        logger.info(f"[TOOL_SUCCESS] Tool: {tool_name}, Result: {result}")
        return result
    except Exception as e:
        logger.error(f"[TOOL_ERROR] Tool: {tool_name}, Error: {str(e)}")
        return {
            "success": False,
            "error": "EXECUTION_ERROR",
            "message": str(e)
        }

5.3 常见问题速查

Q:模型为什么不调用工具?

A:检查以下几点:

  • 工具描述是否清晰?(太模糊会导致 Claude 不调用)
  • 用户的问题是否真的需要工具?(问"天气怎么形成"不需要天气工具)
  • 工具定义的 JSON Schema 格式是否正确?

Q:怎么强制模型调用工具?

A:使用 tool_choice="any" 或指定工具名:

tool_choice={"type": "tool", "name": "tool_name"}

Q:工具调用失败怎么办?

A:返回清晰的错误信息,包含错误代码和描述,Claude 会基于错误信息重新尝试。

Q:怎么监控工具调用的质量?

A:记录每次工具调用的请求、响应和结果,分析失败率和错误类型,定期审视工具设计。


第六部分:实战案例

案例 1:数据库查询 Agent

场景:用户想查询数据库中的用户信息

def query_user_from_db(user_id):
    # 模拟数据库查询
    if user_id == "123":
        return {
            "success": True,
            "data": {
                "id": "123",
                "name": "张三",
                "email": "zhangsan@example.com"
            }
        }
    else:
        return {
            "success": False,
            "error": "USER_NOT_FOUND",
            "message": f"用户 {user_id} 不存在"
        }

tools = [
    {
        "name": "query_user",
        "description": "从数据库查询用户信息。使用场景:用户想了解某个用户的基本信息。",
        "input_schema": {
            "type": "object",
            "properties": {
                "user_id": {
                    "type": "string",
                    "description": "用户的唯一标识,例如:123、456"
                }
            },
            "required": ["user_id"]
        }
    }
]

messages = [
    {"role": "user", "content": "帮我查一下用户 123 的信息"}
]

# 发送请求...

常见坑点

  • 忘记处理"用户不存在"的情况
  • 返回的数据包含敏感信息(如密码哈希)
  • 没有权限检查,任何用户都能查询任何人的信息

案例 2:多 API 集成(天气 + 股票)

tools = [
    {
        "name": "get_weather",
        "description": "获取指定城市的天气",
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {"type": "string"}
            },
            "required": ["city"]
        }
    },
    {
        "name": "get_stock_price",
        "description": "获取指定股票的当前价格",
        "input_schema": {
            "type": "object",
            "properties": {
                "stock_code": {
                    "type": "string",
                    "description": "股票代码,如:AAPL、TSLA"
                }
            },
            "required": ["stock_code"]
        }
    }
]

messages = [
    {"role": "user", "content": "北京天气怎么样?苹果股票现在多少钱?"}
]

# Claude 会自动调用两个工具,然后综合结果回答

总结与要点

Claude API 的工具调用能力是构建智能 Agent 的基础。关键要点:

  1. 工具调用的本质:让 Claude 决定调用什么工具,由你的应用真正执行
  2. 工具设计很关键:清晰的描述、合理的参数、明确的约束是高成功率的前提
  3. 多工具协作:Claude 能自动处理工具链,依次调用多个工具
  4. 错误处理必不可少:工具调用失败很正常,关键是如何恢复
  5. 性能与成本权衡:工具调用会增加 Token 消耗和延迟,需要合理设计

从简单的单工具开始,逐步构建复杂的 Agent,是最稳妥的学习路径。


推荐资源

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐