Claude 不会直接执行你的函数,它只会生成一段结构化的工具调用请求。真正执行函数、访问数据库、请求外部 API 的动作,必须由你的后端完成。

完整链路大致如下:

复制代码
用户提问
  ↓
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 后端执行真实函数

接下来进入你的服务端逻辑:

  1. 读取 tool_use.name
  2. 根据工具名路由到对应函数
  3. 校验 tool_use.input
  4. 执行业务逻辑
  5. 组装工具结果

伪代码示例:

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 至少应该包含:

  • type
  • properties
  • required
  • 字段级 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 temperaturemax_tokens

工具调用场景下,建议降低随机性:

yaml 复制代码
{
  "temperature": 0,
  "max_tokens": 1024
}

尤其是参数抽取、查询类任务,temperature 不宜过高。否则可能出现:

  • 参数值不稳定
  • 工具选择不稳定
  • 返回格式变化
  • 多工具场景下路由混乱

max_tokens 也要留足空间,否则可能出现工具调用内容被截断的问题。


5. 多工具场景如何设计?

当业务中存在多个工具时,建议遵循两个原则:

  1. 工具职责单一
  2. 工具边界明确

示例:

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,可以按这个顺序推进:

  1. 先定义少量高价值工具,不要一开始挂十几个工具
  2. 每个工具只做一件事
  3. description 写清楚使用条件和边界
  4. input_schema 尽量严格,能用 enum 就用 enum
  5. 后端必须做参数校验和权限校验
  6. 工具返回结果保持精简
  7. 记录完整日志,方便排查误调用
  8. 对高风险操作增加二次确认

尤其是涉及写操作时,不建议让模型一步完成。

例如取消订单可以拆成两步:

复制代码
第一步:Claude 识别用户想取消订单,调用查询工具确认订单状态
第二步:向用户确认是否取消
第三步:用户确认后,后端再执行取消动作

这样可以降低误操作风险。


总结

Claude API 的 Function Calling,本质上是 Anthropic 的 Tool Use 机制。

它的核心不是「让模型执行函数」,而是:

复制代码
让模型判断调用哪个工具、生成结构化参数;
让后端执行真实逻辑、返回工具结果;
再让模型基于结果生成最终回答。

接入时重点关注五件事:

  • 工具定义是否清晰
  • 参数 schema 是否严格
  • 工具选择是否可控
  • 后端校验是否完整
  • 异常和权限是否兜底

只要把这些边界处理好,Claude Tool Use 就可以稳定连接业务系统,把大模型从「只会回答」扩展到「能参与真实业务流程」。

相关推荐
不加辣椒1 小时前
第14章 Prompt 编排与优化技术
人工智能
Bolt1 小时前
读懂 Claude Code `/loop` 与编码 Agent 的循环革命
人工智能·程序员·agent
用户208046804561 小时前
文本分块策略与最佳实践实战指南
人工智能
用户208046804563 小时前
文档解析实战:PDF、Word 与 HTML 的清洗提取指南
人工智能
得物技术3 小时前
从狂野代码到按目标生产:得物推荐 AI Harness 的工程化实践|AICon 演讲整理
人工智能·算法·架构
HokKeung3 小时前
飞书 lark-cli 如何存储 tenant_access_token 和 user_access_token
人工智能·go
Ralph_Salar3 小时前
从0到1搭建AI智能支付风控助手Stage3-Function Calling — 让AI能动起来
人工智能
Ralph_Salar3 小时前
从0到1搭建AI智能支付风控助手Stage4-Agent编排 — 让AI自己思考、决策、行动
人工智能
smallyoung3 小时前
Spring AI 2.0 VectorStore实战:从原理到RAG落地
人工智能·后端