当前位置:首页 > 文章列表 > 科技周边 > 人工智能 > Anthropic Claude tool use怎样把tool_use和tool_result配成一轮可继续的对话

Anthropic Claude tool use怎样把tool_use和tool_result配成一轮可继续的对话

来源:17golang原创 2026-11-10 02:58:21 0浏览 收藏

我第一次把 Claude 接到内部查询工具时,用户看到的症状不是模型完全没有回答,而是对话停在一段 tool_use 上,或者下一次请求返回消息顺序错误。复盘后发现,问题集中在同一轮的三个动作没有被当成一份契约:客户端没有保存完整的 assistant 内容、重新生成了关联 ID,或把工具结果放到了错误的消息里。

官方地址:https://platform.claude.com/docs/en/agents-and-tools/tool-use/handle-tool-calls

客户端工具调用的最小闭环是:Claude 返回一个或多个 tool_use,客户端执行受控函数,再把每个结果作为下一条 user 消息中的 tool_result 回填,并原样保留对应的 tool_use_id。这样下一轮才有明确的配对关系。

影响面:为什么对话会停在 tool_use

这类故障通常发生在第一次接入工具时。普通文本响应可以直接展示,但带工具调用的响应包含结构化 block,且 stop_reason 会是 tool_use。如果代码只取文本、看到非空响应就结束,用户会看到半截回答;如果客户端只发送一段普通字符串,Claude 也无法判断这段结果对应哪一次工具调用。

另一个容易被忽略的影响是批量调用:同一轮可能出现多个 tool_use。它们不能共用一个新 ID,也不应拆成夹杂其他消息的多次回填;每个调用都需要自己的 tool_result,并放在同一条结果消息的内容数组中。

时间线:真正的触发条件在消息边界

把一次失败请求按时间拆开,顺序通常是这样的:

  1. 客户端在 tools 参数中声明工具名、描述和 input_schema。
  2. Claude 返回完整的 assistant 内容,其中可能同时有文本 block 和 tool_use block,停止原因是 tool_use。
  3. 客户端执行与工具名匹配的受控函数,得到成功结果或可公开的错误结果。
  4. 客户端先追加完整 assistant 消息,再紧接着追加一条 role 为 user 的消息;这条消息的内容先放 tool_result,再放可选文本。
  5. Claude 根据回填结果继续生成;没有新的工具调用时,才把最终内容交给用户。

所以触发条件不是“模型没有继续生成”,而是客户端在 assistant 的工具调用消息和 user 的工具结果消息之间插入了错误结构,或者让两者的 ID 失配。

根因:tool_use 是请求对象,不是普通文本

工具契约要在请求阶段写清楚。Anthropic 当前文档把用户自定义客户端工具拆成 name、详细的 description 和 JSON Schema 形式的 input_schema;描述应说明用途、参数含义和限制,但它不能代替客户端的权限判断。

tools 工具定义、参数 schema、assistant content 与 tool_use_id 的静态关系说明图
图1:静态结构说明图,展示工具定义、assistant tool_use 和关联 ID 的边界。

我会把模型可见的契约和真正的执行器分开:模型只能提出结构化调用意图,执行器只从白名单中选择函数,并再次检查参数类型、格式和访问权限。这样即使模型给出未知工具名或额外字段,也不会被动态分发成任意函数调用。

修复动作:完整保存 assistant,再按原 ID 回填

下面的最小示例只演示客户端工具调用的消息循环。它没有启动真实工具,也不代表本地已经执行过查询;关键是保留消息结构和错误边界。

import json
import os
from typing import Any

from anthropic import Anthropic


# SDK 从环境变量读取凭据,模型名也由部署配置决定。
client = Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])
MODEL = os.environ["CLAUDE_MODEL"]

# 工具描述帮助模型形成参数,真正权限仍由客户端执行器控制。
TOOLS = [
    {
        "name": "lookup_order",
        "description": "按订单号查询当前用户可见的订单状态,不执行修改操作。",
        "input_schema": {
            "type": "object",
            "properties": {
                "order_id": {
                    "type": "string",
                    "description": "形如 ORD-1001 的订单号",
                }
            },
            "required": ["order_id"],
        },
    }
]


def execute_tool(name: str, payload: dict[str, Any]) -> dict[str, Any]:
    # 只允许显式登记的工具,避免模型拼出任意函数名。
    if name != "lookup_order":
        raise ValueError("unsupported tool")

    order_id = payload.get("order_id")
    # 参数校验放在执行器中,不能只依赖模型遵守 schema。
    if not isinstance(order_id, str) or not order_id.startswith("ORD-"):
        raise ValueError("order_id format is invalid")

    # 示例只返回脱敏字段,生产代码还要做调用者与资源权限校验。
    return {"order_id": order_id, "status": "processing"}


messages = [{"role": "user", "content": "帮我查询订单 ORD-1001 的状态"}]

while True:
    response = client.messages.create(
        model=MODEL,
        max_tokens=800,
        tools=TOOLS,
        messages=messages,
    )

    # 必须保存完整 assistant content,不能只留下文本 block。
    messages.append({"role": "assistant", "content": response.content})
    tool_results = []

    # 只有 stop_reason 为 tool_use 时,才按工具调用分支处理。
    if response.stop_reason == "tool_use":
        for block in response.content:
            if block.type != "tool_use":
                continue

            try:
                # block.id 是配对 tool_result 的关键,不能重新生成。
                result = execute_tool(block.name, block.input)
                tool_results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": json.dumps(result, ensure_ascii=False),
                })
            except Exception as exc:
                # 失败也回填原 ID,并明确告诉模型本次执行出错。
                tool_results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "is_error": True,
                    "content": str(exc),
                })

    if not tool_results:
        # 没有待回填的工具结果时,当前响应可作为最终回答处理。
        print(response.content)
        break

    # tool_result 紧跟 assistant tool_use,且结果 block 放在 user 内容最前面。
    messages.append({"role": "user", "content": tool_results})

防复发:把执行器和消息审计一起固定下来

修复后最值得保留的不是一条提示词,而是几项可复查的记录:每次请求的 stop_reason、工具名、原始 tool_use_id、参数校验结果、结果类型和耗时。日志只记录足够定位问题的摘要,不要无条件写入令牌、授权头、完整订单内容或未脱敏的工具响应。

工具失败时不要吞掉异常,也不要伪造成功数据。把稳定、低敏感的错误信息作为同一个 ID 的 tool_result 返回,并设置 is_error;这样模型可以解释“查询失败”,客户端也能限制重试次数,避免工具故障形成无限循环。

白名单执行器、成功或错误 tool_result、原 tool_use_id 与消息审计边界的静态关系图
图2:静态关系说明图,展示 tool_result 回填、错误分支和审计字段的边界。

上线前可以固定四项复查:每个 tool_use 是否都有唯一结果;assistant 消息是否完整保留;user 结果消息是否紧随其后且结果 block 在前;执行器是否做了工具名、参数和资源权限检查。涉及删除、转账、发信或权限修改时,还要增加人工确认、幂等键和回滚设计。

相关问题

为什么不能把工具结果当普通文本发送? 普通文本没有与原 tool_use_id 的结构化配对关系,模型无法可靠判断结果属于哪一次调用。

多个工具调用要拆成多条 user 消息吗? 同一轮的多个结果通常放在同一条 user 消息的内容数组中,并分别保留各自的 tool_use_id。

工具报错后还能继续请求吗? 可以,但应把错误作为原调用的 tool_result 返回,限制重试次数,并过滤堆栈、凭据和隐私数据。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
OpenAI Responses API 工具调用工具参数校验失败时怎样保留可诊断信息怎样完成一个可复用的最小示例OpenAI Responses API 工具调用工具参数校验失败时怎样保留可诊断信息怎样完成一个可复用的最小示例
上一篇
OpenAI Responses API 工具调用工具参数校验失败时怎样保留可诊断信息怎样完成一个可复用的最小示例
OpenAI Responses API 工具调用工具参数校验失败时怎样保留可诊断信息为什么结果为空或不符合预期
下一篇
OpenAI Responses API 工具调用工具参数校验失败时怎样保留可诊断信息为什么结果为空或不符合预期
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    965次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    966次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    1077次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    1057次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    888次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码