完整链路大致如下:
用户提问
↓
Claude 判断是否需要工具
↓
返回 tool_use
↓
后端解析并执行真实函数
↓
回传 tool_result
↓
Claude 基于结果生成最终回答
也就是说,Claude 负责「决策和参数生成」,你的服务端负责「执行和安全控制」。
1. Claude Tool Use 适合什么场景?
工具调用并不是所有场景都需要。它更适合处理模型无法仅靠自身知识稳定完成的任务。
典型适用场景:
- 查询实时数据:天气、库存、物流、订单、股票价格
- 访问内部系统:CRM、ERP、客服工单、支付系统
- 结构化参数提取:表单解析、意图识别、规则判断
- 多步骤任务编排:搜索、计算、查询、写入等 Agent 类流程
不太适合的场景:
- 普通知识问答
- 文案生成
- 总结改写
- 固定 FAQ
- 不依赖外部数据的对话
如果一个任务 Claude 直接回答即可,强行挂工具只会带来额外请求轮次、更高 token 成本和更复杂的工程逻辑。
2. Claude 工具调用涉及的核心字段
在 Claude Messages API 中,和工具调用相关的字段主要有:
| 字段 | 作用 |
|---|---|
tools |
声明当前有哪些工具可用 |
input_schema |
定义工具参数结构和约束 |
tool_use |
Claude 返回的工具调用请求 |
tool_result |
后端执行工具后返回给 Claude 的结果 |
tool_choice |
控制是否调用工具,以及调用哪个工具 |
理解这几个字段,基本就能掌握 Claude Tool Use 的主流程。
3. 工具调用完整流程
3.1 定义工具:tools
定义工具并不是把真实函数上传给 Claude,而是给 Claude 一份「工具说明书」。
Claude 会根据以下信息判断是否使用工具:
name:工具名称description:工具能力和使用边界input_schema:工具需要的参数格式
示例:
json
{
"name": "get_order_status",
"description": "根据订单 ID 查询订单的支付状态、发货状态和预计送达时间。只有当用户明确询问订单状态、物流进度或订单是否发货时才使用。",
"input_schema": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "订单编号,例如 OD20240601001"
}
},
"required": ["order_id"]
}
}
这里的 get_order_status 只是一个声明。真正的 get_order_status 函数,需要你在后端自己实现。
3.2 发送用户问题和工具列表
首次请求时,需要把用户消息和 tools 一起传给 Claude。
伪代码结构如下:
json
{
"model": "claude-3-5-sonnet-latest",
"max_tokens": 1024,
"tools": [
{
"name": "get_order_status",
"description": "根据订单 ID 查询订单的支付状态、发货状态和预计送达时间。只有当用户明确询问订单状态、物流进度或订单是否发货时才使用。",
"input_schema": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "订单编号"
}
},
"required": ["order_id"]
}
}
],
"messages": [
{
"role": "user",
"content": "帮我查一下订单 OD20240601001 发货了吗?"
}
]
}
Claude 会根据用户问题自行判断是否需要调用工具。
3.3 解析 Claude 返回的 tool_use
如果 Claude 判断需要调用工具,响应中会出现类似结构:
json
{
"type": "tool_use",
"id": "toolu_01ABC",
"name": "get_order_status",
"input": {
"order_id": "OD20240601001"
}
}
此时常见的 stop_reason 是:
arduino
"tool_use"
它表示:Claude 暂停生成最终回答,等待你执行工具并回传结果。
3.4 后端执行真实函数
接下来进入你的服务端逻辑:
- 读取
tool_use.name - 根据工具名路由到对应函数
- 校验
tool_use.input - 执行业务逻辑
- 组装工具结果
伪代码示例:
javascript
async function handleToolUse(toolUse) {
const { name, input } = toolUse;
if (name === "get_order_status") {
if (!input.order_id) {
throw new Error("order_id is required");
}
return await getOrderStatus(input.order_id);
}
throw new Error(`Unknown tool: ${name}`);
}
重点:不要信任模型生成的参数。
即使你在 input_schema 中定义了参数格式,后端也必须再次校验,尤其是以下高风险操作:
- 数据库写入
- 支付操作
- 文件系统操作
- 权限变更
- 邮件 / 短信发送
- 调用内部敏感接口
模型负责生成参数,不负责保证参数绝对安全。
3.5 使用 tool_result 回传结果
工具执行完成后,需要把结果包装成 tool_result 再发回 Claude。
关键点:tool_use_id 必须等于上一轮 Claude 返回的 tool_use.id。
示例:
lua
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01ABC",
"content": "{"status":"shipped","estimated_delivery":"2024-06-05"}"
}
]
}
如果 tool_use_id 对不上,Claude 就无法知道这个结果对应哪一次工具调用。
3.6 获取最终回答
Claude 收到 tool_result 后,会基于真实工具结果生成自然语言回答。
例如:
yaml
你的订单 OD20240601001 已发货,预计将在 2024-06-05 送达。
所以用户最终看到的回答,并不是工具直接返回的原始数据,而是 Claude 基于工具结果组织后的表达。

4. 核心配置项怎么写?
4.1 tools.name:短、明确、方便路由
推荐使用英文小写 + 下划线:
get_order_status
search_products
cancel_order
create_ticket
不推荐:
arduino
get_data
query
do_action
process
原因很简单:工具名不仅给 Claude 看,后端通常也会根据它做分发。名字越抽象,越容易误调用,也越难排查。
4.2 tools.description:重点写清楚「什么时候用」
很多人只写工具能做什么,但这还不够。对 Claude 来说,更重要的是判断「什么时候该用」。
不推荐:
查询订单。
推荐:
根据订单 ID 查询订单的支付状态、发货状态和预计送达时间。只有当用户明确询问订单状态、物流进度或订单是否发货时才使用。
如果多个工具能力接近,一定要在 description 里写清楚边界。
例如:
search_products:当用户想查找可购买商品、价格、库存时使用。
get_order_status:当用户已经提供订单 ID,并询问订单支付、发货、物流状态时使用。
否则模型很容易把「查商品」和「查订单」混在一起。
4.3 input_schema:用 JSON Schema 约束参数
一个合格的 input_schema 至少应该包含:
typepropertiesrequired- 字段级
description
示例:
json
{
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "订单编号,例如 OD20240601001"
},
"status_type": {
"type": "string",
"enum": ["payment", "shipping", "refund"],
"description": "要查询的状态类型"
}
},
"required": ["order_id", "status_type"]
}
如果字段只有固定取值,建议使用 enum。
否则模型可能生成各种近义值:
物流状态
发货情况
delivery
shipping_status
这些对人类来说差别不大,但对后端来说可能完全匹配不上。
4.4 tool_choice:控制是否调用工具
tool_choice 用来控制 Claude 的工具调用策略。
常见模式有三种:
自动选择
让 Claude 自己判断是否调用工具。
适合大多数对话式场景。
json
{
"tool_choice": {
"type": "auto"
}
}
强制调用某个工具
适合必须走结构化流程的场景,比如表单抽取、订单查询、工单创建。
json
{
"tool_choice": {
"type": "tool",
"name": "get_order_status"
}
}
禁止调用工具
如果是纯文本生成、总结、改写,不需要传 tools 即可。
如果 SDK 或 API 版本支持显式禁用,也可以按官方最新文档配置。Anthropic API 字段可能随版本演进,生产环境建议以官方文档为准。
4.5 temperature 和 max_tokens
工具调用场景下,建议降低随机性:
yaml
{
"temperature": 0,
"max_tokens": 1024
}
尤其是参数抽取、查询类任务,temperature 不宜过高。否则可能出现:
- 参数值不稳定
- 工具选择不稳定
- 返回格式变化
- 多工具场景下路由混乱
max_tokens 也要留足空间,否则可能出现工具调用内容被截断的问题。
5. 多工具场景如何设计?
当业务中存在多个工具时,建议遵循两个原则:
- 工具职责单一
- 工具边界明确
示例:
css
[ { "name": "search_products", "description": "根据关键词搜索商品信息,包括商品名称、价格和库存。仅当用户想查找或比较商品时使用。", "input_schema": { "type": "object", "properties": { "keyword": { "type": "string", "description": "商品搜索关键词" } }, "required": ["keyword"]
}
},
{
"name": "get_order_status",
"description": "根据订单 ID 查询订单状态。仅当用户提供订单 ID 并询问支付、发货、物流或退款状态时使用。",
"input_schema": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "订单编号"
}
},
"required": ["order_id"]
}
}
]
不要把一个工具设计成万能入口:
handle_user_request
process_business
query_data
这种设计短期省事,长期会带来三个问题:
- Claude 不知道什么时候该用
- 后端逻辑难以维护
- 参数 schema 会越来越臃肿
6. 常见避坑点
6.1 不要把工具描述写得太宽泛
错误示例:
用于查询业务信息。
这类描述会导致 Claude 在很多无关问题上也尝试调用工具。
建议写成:
仅当用户明确提供订单 ID,并询问订单支付、发货、物流或退款状态时使用。
6.2 不要依赖模型做权限判断
不要让 Claude 自己判断用户有没有权限取消订单、退款或查看数据。
权限判断必须放在后端:
用户身份校验
↓
权限校验
↓
参数校验
↓
执行工具
模型可以帮你理解意图,但不能替代权限系统。
6.3 tool_result 不要塞太多无关数据
工具结果越大,Claude 消耗的 token 越多,也越容易抓不住重点。
推荐返回精简结构:
json
{
"order_id": "OD20240601001",
"shipping_status": "shipped",
"estimated_delivery": "2024-06-05"
}
不推荐直接返回完整数据库记录,尤其是包含内部字段、日志、用户隐私信息的结果。
6.4 注意工具调用可能不止一次
在复杂任务中,Claude 可能连续发起多次工具调用。
例如:
用户:帮我查一下这款手机有没有货,如果有货就告诉我最近的门店。
可能流程是:
search_products
↓
check_inventory
↓
find_nearest_store
↓
最终回答
因此后端代码不要假设「每次对话只会有一次 tool_use」。
6.5 处理未知工具和异常参数
生产环境必须处理异常情况:
- Claude 返回了未知工具名
- 参数缺失
- 参数类型错误
- 工具执行失败
- 外部 API 超时
- 结果为空
建议统一封装错误结果,再返回给 Claude:
json
{
"error": true,
"message": "未查询到该订单,请确认订单编号是否正确。"
}
这样 Claude 可以基于错误信息给用户一个可读的回复,而不是直接中断流程。
7. 一个简化版接入流程
可以把 Claude Tool Use 接入理解成下面这段伪代码:
php
async function chatWithTools(userMessage) {
const firstResponse = await claude.messages.create({
model: "claude-3-5-sonnet-latest",
max_tokens: 1024,
temperature: 0,
tools,
messages: [
{
role: "user",
content: userMessage
}
]
});
const toolUse = firstResponse.content.find(
item => item.type === "tool_use"
);
if (!toolUse) {
return firstResponse.content;
}
const toolResult = await handleToolUse(toolUse);
const finalResponse = await claude.messages.create({
model: "claude-3-5-sonnet-latest",
max_tokens: 1024,
messages: [
{
role: "user",
content: userMessage
},
{
role: "assistant",
content: firstResponse.content
},
{
role: "user",
content: [
{
type: "tool_result",
tool_use_id: toolUse.id,
content: JSON.stringify(toolResult)
}
]
}
]
});
return finalResponse.content;
}
真实项目中还需要补充:
- 多轮上下文管理
- 多个
tool_use的并发或串行处理 - 参数校验
- 权限校验
- 日志追踪
- 超时重试
- 敏感信息过滤
8. 实践建议
如果你准备在业务中接入 Claude Tool Use,可以按这个顺序推进:
- 先定义少量高价值工具,不要一开始挂十几个工具
- 每个工具只做一件事
description写清楚使用条件和边界input_schema尽量严格,能用enum就用enum- 后端必须做参数校验和权限校验
- 工具返回结果保持精简
- 记录完整日志,方便排查误调用
- 对高风险操作增加二次确认
尤其是涉及写操作时,不建议让模型一步完成。
例如取消订单可以拆成两步:
第一步:Claude 识别用户想取消订单,调用查询工具确认订单状态
第二步:向用户确认是否取消
第三步:用户确认后,后端再执行取消动作
这样可以降低误操作风险。
总结
Claude API 的 Function Calling,本质上是 Anthropic 的 Tool Use 机制。
它的核心不是「让模型执行函数」,而是:
让模型判断调用哪个工具、生成结构化参数;
让后端执行真实逻辑、返回工具结果;
再让模型基于结果生成最终回答。
接入时重点关注五件事:
- 工具定义是否清晰
- 参数 schema 是否严格
- 工具选择是否可控
- 后端校验是否完整
- 异常和权限是否兜底
只要把这些边界处理好,Claude Tool Use 就可以稳定连接业务系统,把大模型从「只会回答」扩展到「能参与真实业务流程」。