Claude API 工具调用完全指南:从零构建 AI Agent 执行能力导言:为什么 AI 需要工具调用
你是否遇到过这样的困境:
- 场景 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 难以解析
最佳实践:
- 总是包含
success字段(布尔值) - 成功时返回
data,失败时返回error和message - 如果结果过长(>1000 字符),进行摘要或分页
- 包含敏感信息时,返回前进行脱敏
第四部分:多工具场景与工具链
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 的输入
实际应用中,常需多个工具协作。例如:
- 先调用"获取用户信息"工具
- 基于用户信息,调用"获取用户订单"工具
- 基于订单信息,调用"计算优惠"工具
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 的基础。关键要点:
- 工具调用的本质:让 Claude 决定调用什么工具,由你的应用真正执行
- 工具设计很关键:清晰的描述、合理的参数、明确的约束是高成功率的前提
- 多工具协作:Claude 能自动处理工具链,依次调用多个工具
- 错误处理必不可少:工具调用失败很正常,关键是如何恢复
- 性能与成本权衡:工具调用会增加 Token 消耗和延迟,需要合理设计
从简单的单工具开始,逐步构建复杂的 Agent,是最稳妥的学习路径。
推荐资源

更多推荐


所有评论(0)