当前位置:首页 > 文章列表 > 科技周边 > 人工智能 > OpenAI 工具调用返回多个 tool call 时如何逐个回传结果

OpenAI 工具调用返回多个 tool call 时如何逐个回传结果

来源:17golang原创 2026-09-14 13:42:30 0浏览 收藏

遇到多个工具调用时,正确做法不是取 `response.output[0]`,而是保留整组输出,筛出每个 `function_call`,解析它自己的参数,执行完成后用同一个 `call_id` 生成一个 `function_call_output`。即使工具实际并行执行,回传仍然是一项一项绑定;少回一项或串了 ID,模型就拿不到完整上下文。

官方文档:https://developers.openai.com/api/docs/guides/function-calling

要点速览
  • Responses API 的多个调用都在 `response.output` 中,不能只读第一项。
  • `call_id` 是回传结果的唯一关联键,不能用数组下标代替。
  • 先用顺序循环理清映射;确认工具无副作用后,再考虑并发执行。

先识别 response.output 中的 function_call

我排查这类问题时,第一步总是把响应当成“调用清单”,而不是一条普通文本。Responses API 的输出项里,类型为 `function_call` 的对象至少要看三个字段:`call_id` 用于关联结果,`name` 用于选择本地函数,`arguments` 是 JSON 字符串,必须解析后再传给函数。

字段用途常见误区
call_id标记这一条具体调用用数组下标或工具名替代
name路由到本地工具把展示名称当成可执行代码
arguments携带本次调用参数忘记 JSON 解析,或复用上一项参数
OpenAI Responses API response.output 中多个 function_call 及 call_id、name、arguments 的结构示意图
图1:多个 function_call 的字段映射示意图;每个调用都保留独立 call_id。

保留完整响应并逐个执行工具

处理循环要先把原始输出放进下一轮输入,再为每个调用追加结果。下面的示例故意采用顺序执行,便于看清每个 `call_id` 的去向;生产代码可以把 `dispatch_tool` 换成真实服务适配器。

import json
from openai import OpenAI

client = OpenAI()

def dispatch_tool(name, arguments):
    # 只允许白名单工具,避免把模型给出的名称直接当作函数名执行。
    if name == "get_weather":
        return {"city": arguments["city"], "temperature": 23}
    if name == "send_email":
        return {"to": arguments["to"], "status": "queued"}
    raise ValueError(f"unknown tool: {name}")

def make_tool_outputs(response):
    # 保留整组输出,后续请求需要带回模型刚刚发出的调用上下文。
    next_input = list(response.output)
    for item in response.output:
        # 文本、推理等输出项不属于本轮需要执行的工具。
        if item.type != "function_call":
            continue
        try:
            arguments = json.loads(item.arguments)
            result = dispatch_tool(item.name, arguments)
            payload = {"ok": True, "data": result}
        except (ValueError, KeyError, json.JSONDecodeError) as exc:
            # 失败也要回传,让模型知道这一项失败,不要悄悄丢弃。
            payload = {"ok": False, "error": str(exc)}
        next_input.append({
            "type": "function_call_output",
            "call_id": item.call_id,
            "output": json.dumps(payload, ensure_ascii=False),
        })
    return next_input

这里最关键的不是 `try` 的写法,而是 `next_input` 的构造顺序:先保留 `response.output`,再为每个调用增加一条结果。这样模型下一轮既能看到自己发出的调用,也能看到每一项工具反馈。

按 call_id 逐条构造 function_call_output

完成工具执行后,结果对象的外壳固定为 `function_call_output`,关联键必须来自当前项的 `item.call_id`。不要把多个结果拼成一个字符串,也不要只回传成功项;某一项失败时,返回结构化错误通常比省略它更容易让模型决定重试、改参数或向用户解释。

response = client.responses.create(
    model="gpt-5",
    tools=tools,
    input="查询北京和上海天气,并准备一封通知邮件",
)

# 每个 function_call 都得到一条独立的 function_call_output。
tool_input = make_tool_outputs(response)
follow_up = client.responses.create(
    model="gpt-5",
    tools=tools,
    input=tool_input,
)
print(follow_up.output_text)

可以把回传关系记成一张简单的表:`item.call_id` → 工具执行结果 → `function_call_output.call_id`。只有左右两边相同,模型才知道“这个结果属于哪一次调用”。

OpenAI 多工具调用通过 tool router 按 call_id 生成 function_call_output 并进入下一轮 input 的结构示意图
图2:工具结果回传队列示意图;成功和失败都通过对应 call_id 返回。

处理并行调用、重复调用和边界错误

支持并行函数调用的模型可能在一轮里给出多个调用;这不代表应用必须并发执行。顺序执行更适合发邮件、写订单、扣库存等有副作用的工具。天气查询、只读检索等独立任务可以并发,但完成后仍要把每个结果映射回原来的 `call_id`,不要按完成先后重排成匿名数组。

  • 想强制一轮最多一个调用,可在请求中设置 parallel_tool_calls: false
  • 同一个工具出现两次时,两个 call_id 仍然是两次独立调用,不能用工具名做字典唯一键。
  • 参数 JSON 解析失败、工具不存在、下游超时,都应生成可识别的错误结果并保留关联键。
  • 继续请求前统计已处理的调用数;如果应用在相同响应上反复重试,要检查幂等键和响应状态保存。

相关问题

多个工具结果必须按返回顺序提交吗?

重点是每条结果都带正确的 `call_id`,而不是依赖完成顺序。为便于日志和重放,实际项目仍建议保存原始输出顺序。

一个工具调用失败,其他成功结果还要回传吗?

要。成功项和失败项都应各自生成回传项,让模型拥有完整状态,再由它决定是否补偿或重试。

为什么结果已经执行了,模型却像没收到?

优先检查是否把原始 `response.output` 带入下一轮,以及 `function_call_output.call_id` 是否严格等于对应调用的 `call_id`。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
SkildArt AIGC营销素材生成结果怎么检查?质量、格式与权限清单SkildArt AIGC营销素材生成结果怎么检查?质量、格式与权限清单
上一篇
SkildArt AIGC营销素材生成结果怎么检查?质量、格式与权限清单
Go io.TeeReader 如何同时保存读取内容和传给下游
下一篇
Go io.TeeReader 如何同时保存读取内容和传给下游
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之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模型性能。
    21次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    125次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    49次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    18次使用
  • OpenCompass大模型评测体系详解:功能、使用指南与应用场景
    OpenCompass
    OpenCompass是上海AI实验室推出的开源大模型评测平台,提供CompassKit、CompassHub和CompassRank三大核心组件,支持LLM及多模态模型的一站式标准化评估与排行榜查询。
    71次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码