登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  科技周边 >  人工智能

Claude 的 tool_use 为什么不是最终答案:tool_result 回传与循环边界

来源:17golang原创

时间:2026-07-22 14:31:58 368浏览 收藏

用Go开发后台服务对接Anthropic Messages API做工具调用的时候,不少开发者刚上手第一反应会直接把 tool_use 当成模型返回的最终答案。实际上它只是Claude给到对接侧的明确信号:请运行这个指定的工具,再根据对应的ID把运行结果回传回来。要是漏掉 stop_reason、错配 tool_use_id,或是直接把工具抛出的错误当成普通文本处理,整个调用链路就会出现空回复、重复查询相同内容甚至无限循环的异常问题。

把一次完整的工具调用拆成两段消息往返理解:模型先返回 tool_use,Go 服务执行预配置的白名单工具,再用同一个 tool_use_id 发送 tool_result;每一轮交互都要检查模型的停止标识,同时提前配置好最大轮数限制。

要点速览

  • stop_reason=tool_use 代表应用需要主动运行指定工具,绝对不能直接把这一轮返回的内容当成最终回复展示给用户。
  • tool_use_id 是工具请求和对应结果的关联标识,回传的时候必须原样匹配,不能自行修改生成。
  • 工具结果要放在下一条 user 角色消息的 tool_result 内容块里回传,无论执行成功还是失败都要带上明确的状态标识。
  • 工具白名单、参数校验、接口超时控制、最大循环轮数限制,是服务上线前必须完成的基础边界配置。

先认清 Messages API 的两次交互停顿

一个“查询北京实时天气”的用户请求,至少包含两次模型交互过程。第一次模型根据提前传入的工具描述生成 tool_use 内容块;应用侧执行完 get_weather 后,把工具运行的结果追加到整体消息历史中;第二次调用模型,才会拿到模型整理好的面向普通用户的自然语言回复。

type messageResponse struct {
    StopReason string        `json:"stop_reason"`
    Content    []contentPart `json:"content"`
}

type contentPart struct {
    Type  string                 `json:"type"`
    ID    string                 `json:"id,omitempty"`
    Name  string                 `json:"name,omitempty"`
    Input map[string]interface{} `json:"input,omitempty"`
    Text  string                 `json:"text,omitempty"`
}

解析响应的时候不要只取 content[0].text 直接展示。模型返回的内容可能同时包含工具请求和补充文本说明;真正决定后续流程走向的是 stop_reason。常见的 end_turn 取值代表模型已经结束本轮生成,tool_use 出现则代表当前流程要切换给应用层接管,执行一次工具调用。

Go 解析 Anthropic Messages API:tool_use 停顿点、工具名与输入参数进入本地执行器

工具描述要精简,应用执行器的校验规则要更严格

工具定义里的 input_schema 能让模型明确知道生成参数的格式规范,但这不代表应用侧要放开全部授权。比如天气查询工具只允许查询有限的城市和日期范围,Go 服务侧仍然要主动校验传入的城市字符串合法性、日期格式和调用频次,绝对不能把模型生成的参数直接拼接到任意外部接口地址中。

type toolSpec struct {
    Name        string                 `json:"name"`
    Description string                 `json:"description"`
    InputSchema map[string]interface{} `json:"input_schema"`
}

var weatherTool = toolSpec{
    Name:        "get_weather",
    Description: "查询一个城市当天的天气摘要",
    InputSchema: map[string]interface{}{
        "type": "object",
        "properties": map[string]interface{}{
            "city": map[string]string{"type": "string"},
        },
        "required": []string{"city"},
    },
}

真正执行工具请求之前要再做一次白名单判断,例如只允许 北京上海广州 三类操作,同时给对接的外部天气服务设置较短的超时时间。工具描述解决的是“模型该怎么正确发起工具请求”的问题,执行器层的规则解决的是“服务本身允许做什么操作”的问题,两者不能混为一谈。

用同一个 tool_use_id 把结果送回去

收到模型返回的工具请求后,应用要完整保留上一条 assistant 角色的全部内容,再追加一条 user 角色的消息,里面放入 tool_result 内容块。关联标识必须直接复用模型返回的 tool_use_id,不能自己生成一个新的ID来回传。

type toolResultPart struct {
    Type      string `json:"type"`
    ToolUseID string `json:"tool_use_id"`
    Content   string `json:"content"`
    IsError   bool   `json:"is_error,omitempty"`
}

func makeToolResult(id string, value string, failed bool) map[string]interface{} {
    return map[string]interface{}{
        "type":        "tool_result",
        "tool_use_id": id,
        "content":     value,
        "is_error":    failed,
    }
}

工具执行成功时,content 可以是结构轻量化的JSON或者格式化的稳定文本;工具执行失败时设置 is_error,把可以对外展示的安全错误原因交给模型处理。不要把数据库连接串、内部服务地址和完整调用堆栈这类敏感信息塞进工具结果里。

Go 使用 tool_use_id 回传 tool_result:工具成功、工具错误和最大循环轮数核对

把完整往返封装成有限循环

工具调用流程通常需要一个轻量循环:发起模型请求、判断当前停止标识、运行对应工具、追加新的消息片段,再次发起模型请求。循环必须设置硬上限阈值,一方面防止模型反复请求同一个工具,另一方面也避免外部服务异常导致的请求一直占用长连接资源。

func askWithTools(ctx context.Context, messages []map[string]interface{}) (string, error) {
    for round := 0; round 

示例里的四轮只是常规保护阈值,不是API本身的固定限制。如果业务逻辑里单个请求只需要一次天气查询,工具结果回传之后仍然收到第二次完全相同的工具调用请求,就应该直接记录上下文信息进入排查流程,而不是盲目调高循环上限。

工具失败要返回可理解的错误,不要伪造成功

外部天气服务超时、传入参数不合法或是请求的城市不存在时,应用有两种可选处理路径:把错误作为 is_error=true 的工具结果直接回传给模型,让模型生成友好提示告知用户当前无法查询;或是直接结束本次业务请求,交给上层重试机制处理。无论选择哪种方案,都不要把错误的JSON强行伪装成正常的天气结果返回。

  • 参数错误:不做重试,直接返回可供模型生成校正提示的信息。
  • 短暂网络故障:配置有限次重试次数,多次重试仍然失败就回传工具错误。
  • 权限或配额错误:记录告警信息,避免循环重试耗尽配额。
  • 工具返回结果过大:优先裁剪字段,只保留模型完成回答必须的摘要内容。

常见问题

tool_use 返回后可以直接展示给用户吗?

通常不可以。这部分内容是生成给应用执行器处理的工具请求,最终展示给用户的回复,一般要等工具结果回传之后由模型生成。

tool_use_id 不匹配会怎样?

模型无法把回传的结果和之前的原始请求做关联,整轮消息可能被直接拒绝,或是返回完全不符合预期的内容。要把ID当成不透明的关联标识,原样保存原样回传就好。

工具调用为什么会出现无限循环?

常见诱因包括工具结果格式不符合模型预期、执行侧抛出的错误没有做失败标记,或是应用侧无上限放开循环次数限制。出现这类问题先记录stop_reason、调用的工具名和当前循环轮数,再依次排查执行器逻辑和结果回传逻辑。

工具调用的稳定性来自边界,而不是更长的提示词

用Go接入Messages API的tool_use能力时,先把 stop_reason 当成状态机的入口节点,再用 tool_use_id 串联起assistant的请求和user侧的结果;执行器层做好白名单校验和超时控制,循环层做好最大轮数限制,错误场景做好显式回传。这样哪怕工具临时出现故障,整个系统也能停在一个可解释的状态,不会把空答案或是错误结果直接传递给终端用户。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>