OpenAI 工具调用返回多个 tool call 时如何逐个回传结果
来源:17golang原创
时间:2026-09-14 13:42:30 146浏览 收藏
遇到多个工具调用时,正确做法不是取 `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 解析,或复用上一项参数 |

保留完整响应并逐个执行工具
处理循环要先把原始输出放进下一轮输入,再为每个调用追加结果。下面的示例故意采用顺序执行,便于看清每个 `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`。只有左右两边相同,模型才知道“这个结果属于哪一次调用”。

处理并行调用、重复调用和边界错误
支持并行函数调用的模型可能在一轮里给出多个调用;这不代表应用必须并发执行。顺序执行更适合发邮件、写订单、扣库存等有副作用的工具。天气查询、只读检索等独立任务可以并发,但完成后仍要把每个结果映射回原来的 `call_id`,不要按完成先后重排成匿名数组。
- 想强制一轮最多一个调用,可在请求中设置
parallel_tool_calls: false。 - 同一个工具出现两次时,两个
call_id仍然是两次独立调用,不能用工具名做字典唯一键。 - 参数 JSON 解析失败、工具不存在、下游超时,都应生成可识别的错误结果并保留关联键。
- 继续请求前统计已处理的调用数;如果应用在相同响应上反复重试,要检查幂等键和响应状态保存。
相关问题
多个工具结果必须按返回顺序提交吗?
重点是每条结果都带正确的 `call_id`,而不是依赖完成顺序。为便于日志和重放,实际项目仍建议保存原始输出顺序。
一个工具调用失败,其他成功结果还要回传吗?
要。成功项和失败项都应各自生成回传项,让模型拥有完整状态,再由它决定是否补偿或重试。
为什么结果已经执行了,模型却像没收到?
优先检查是否把原始 `response.output` 带入下一轮,以及 `function_call_output.call_id` 是否严格等于对应调用的 `call_id`。
-
485 收藏
-
493 收藏
-
433 收藏
-
489 收藏
-
267 收藏
-
科技周边 · 人工智能 | 2小时前 | openai · json schema · Structured Outputs · OpenAI Nullable Schema JSON Schema Structured Outputs281 收藏
-
科技周边 · 人工智能 | 3小时前 | 人工智能 · openai api · 检索增强生成 · 文件搜索 · OpenAI Attributes 元数据过滤 Responses API File Search vector store426 收藏
-
科技周边 · 人工智能 | 4小时前 | 异步任务 · 人工智能 · openai api · 接口开发 · 轮询 后台任务 background true OpenAI Responses API response_id314 收藏
-
345 收藏
-
115 收藏
-
171 收藏
-
科技周边 · 人工智能 | 1天前 | 人工智能 · 性能排查 · 提示词工程 · Hugging Face · 模型推理 · KV Cache · 提示词缓存动态字段 DynamicCache KV缓存 Transformers缓存 past_key_values use_cache StaticCache397 收藏
-
科技周边 · 人工智能 | 1天前 | 人工智能 · 模型量化 · 校准数据 · ONNX Runtime · GPTQ · 推理优化 · 量化校准数据 模型量化配置 代表性校准集 ONNX静态量化 GPTQ校准数据 AI模型精度排查276 收藏
-
221 收藏
-
387 收藏
-
264 收藏
-
447 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习