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

作者:袖梨 2026-07-01

完整链路大致如下:

用户提问↓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_useClaude 返回的工具调用请求
tool_result后端执行工具后返回给 Claude 的结果
tool_choice控制是否调用工具,以及调用哪个工具

理解这几个字段,基本就能掌握 Claude Tool Use 的主流程。

3. 工具调用完整流程

3.1 定义工具:tools

定义工具并不是把真实函数上传给 Claude,而是给 Claude 一份「工具说明书」。

Claude 会根据以下信息判断是否使用工具:

  • name:工具名称
  • description:工具能力和使用边界
  • input_schema:工具需要的参数格式

示例:

{ "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。

伪代码结构如下:

{ "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 判断需要调用工具,响应中会出现类似结构:

{ "type": "tool_use", "id": "toolu_01ABC", "name": "get_order_status", "input": {"order_id": "OD20240601001"}}

此时常见的 stop_reason 是:

"tool_use"

它表示:Claude 暂停生成最终回答,等待你执行工具并回传结果。

3.4 后端执行真实函数

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

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

伪代码示例:

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

示例:

{ "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 后,会基于真实工具结果生成自然语言回答。

例如:

你的订单 OD20240601001 已发货,预计将在 2024-06-05 送达。

所以用户最终看到的回答,并不是工具直接返回的原始数据,而是 Claude 基于工具结果组织后的表达。

4. 核心配置项怎么写?

4.1 tools.name:短、明确、方便路由

推荐使用英文小写 + 下划线:

get_order_statussearch_productscancel_ordercreate_ticket

不推荐:

get_dataquerydo_actionprocess

原因很简单:工具名不仅给 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

示例:

{"type": "object","properties": {"order_id": {"type": "string","description": "订单编号,例如 OD20240601001"},"status_type": {"type": "string","enum": ["payment", "shipping", "refund"],"description": "要查询的状态类型"}},"required": ["order_id", "status_type"]}

如果字段只有固定取值,建议使用 enum

否则模型可能生成各种近义值:

物流状态发货情况deliveryshipping_status

这些对人类来说差别不大,但对后端来说可能完全匹配不上。

4.4 tool_choice:控制是否调用工具

tool_choice 用来控制 Claude 的工具调用策略。

常见模式有三种:

自动选择

让 Claude 自己判断是否调用工具。

适合大多数对话式场景。

{"tool_choice": {"type": "auto"}}

强制调用某个工具

适合必须走结构化流程的场景,比如表单抽取、订单查询、工单创建。

{"tool_choice": {"type": "tool","name": "get_order_status"}}

禁止调用工具

如果是纯文本生成、总结、改写,不需要传 tools 即可。

如果 SDK 或 API 版本支持显式禁用,也可以按官方最新文档配置。Anthropic API 字段可能随版本演进,生产环境建议以官方文档为准。

4.5 temperaturemax_tokens

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

{"temperature": 0,"max_tokens": 1024}

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

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

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

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

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

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

示例:

[{"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_requestprocess_businessquery_data

这种设计短期省事,长期会带来三个问题:

  • Claude 不知道什么时候该用
  • 后端逻辑难以维护
  • 参数 schema 会越来越臃肿

6. 常见避坑点

6.1 不要把工具描述写得太宽泛

错误示例:

用于查询业务信息。

这类描述会导致 Claude 在很多无关问题上也尝试调用工具。

建议写成:

仅当用户明确提供订单 ID,并询问订单支付、发货、物流或退款状态时使用。

6.2 不要依赖模型做权限判断

不要让 Claude 自己判断用户有没有权限取消订单、退款或查看数据。

权限判断必须放在后端:

用户身份校验↓权限校验↓参数校验↓执行工具

模型可以帮你理解意图,但不能替代权限系统。

6.3 tool_result 不要塞太多无关数据

工具结果越大,Claude 消耗的 token 越多,也越容易抓不住重点。

推荐返回精简结构:

{"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:

{"error": true,"message": "未查询到该订单,请确认订单编号是否正确。"}

这样 Claude 可以基于错误信息给用户一个可读的回复,而不是直接中断流程。

7. 一个简化版接入流程

可以把 Claude Tool Use 接入理解成下面这段伪代码:

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 就可以稳定连接业务系统,把大模型从「只会回答」扩展到「能参与真实业务流程」。

相关文章

精彩推荐