RH
RouterHub AI API 中转站导航与平台推荐
博客

OpenAI 函数工具补上异步标记

`async: true` 让模型先继续独立工作,但后台执行、结果保存和后续回传仍由应用负责,SDK 这次只补齐了 Agents 类型支持。

muchacha 2026-11-12 02:38:23

正文

先看结论

OpenAI 在 Node SDK v7.55.0 中为 Agents 的函数工具补上了可选的 async 类型字段。这个变化的实际意义不是“把函数丢给 OpenAI 后台执行”,而是让应用可以明确告诉模型:调用这个函数后,不必暂停当前回合等待结果。模型可以先回答不依赖该结果的部分;你的应用仍要启动任务、保存任务状态,等任务完成后再用原来的 call_id 发起后续 Responses 请求,把真实结果交回模型。

这是一项很小、但容易被误读的 API 能力变化。升级到 openai v7.55.0 后,TypeScript 代码可以在 Agents、Sessions 和持久化 Agent 的函数工具定义中直接写 async?: boolean,并让返回对象也带有这个字段。真正的异步工作流仍然需要由业务代码实现:任务队列、超时、重试、幂等、结果存储和会话续接都不在 SDK 这次改动的范围内。

如果你的系统只是调用普通 Responses API 函数工具,这次升级不要求你立刻改代码;默认值仍是 false。如果你正在使用 Agents API 的 Sessions 或持久化 Agent,并且有耗时的外部查询、构建任务或审批等待,那么这个字段可以成为模型并行推进对话的一种明确协议。接入前可以先通过平台目录确认调用入口,再把异步任务的状态和权限放在自己的服务里管理。

这次 SDK 具体补了什么

GitHub 的 openai-node v7.55.0 发布说明把唯一的功能项写成“为 typed async function tools 增加支持”。对应的合并请求说明,改动为 Sessions 和 persisted agents 的 function-tool 请求与响应类型增加可选的布尔属性。源码中的四组类型都能看到同一语义:请求侧的 async?: boolean 默认是 false,响应侧的 async?: boolean 表示 Agent 是否会在函数结果返回前继续执行。

这里要区分三层东西。

第一层是 TypeScript 类型。过去服务端协议已经存在这个字段时,SDK 的类型如果没有跟上,调用方要么无法通过类型检查,要么只能手写扩展类型。v7.55.0 把字段放入 Agents 资源生成的公开接口中,因此创建或更新 Agent、创建 Session 时,函数工具配置可以直接表达异步意图。

第二层是请求协议。函数工具的定义大致可以写成下面这样:

const tools = [
  {
    type: "function" as const,
    name: "lookup_release_notes",
    description: "查询指定版本的发布说明。",
    async: true,
    parameters: {
      type: "object",
      properties: {
        version: { type: "string" },
      },
      required: ["version"],
      additionalProperties: false,
    },
  },
];

第三层才是运行时行为。官方文档明确说明,普通函数调用会让模型暂停,等待应用返回工具结果;标记为异步后,模型可以在应用执行函数期间继续处理不依赖该结果的任务。模型输出里的对应 call item 也会带有 async: true,应用可以据此决定采用异步分支,而不是靠工具名称猜测。

因此,升级 SDK 本身不会让已有工具突然并发执行。没有在请求里设置 async: true 的工具仍按同步路径处理;即使设置了它,应用也必须真正把函数放进自己的后台执行机制,否则模型只是拿到了一个“可以先继续”的声明,却不会自动得到结果。

正确的调用链是两次请求

异步工具最重要的实现细节是:第一次 Responses 请求只产生工具调用,第二次请求才提交工具结果。应用不能把第一次响应当作完整答案,也不能用新的自然语言消息替代原始调用标识。

以官方文档的流程为准,服务端收到第一轮响应后,需要完成以下动作:

  1. 从 response.output 找到函数调用,并读取 call_id、函数名和参数。
  2. 立即启动应用自己的任务,例如查询外部系统、运行编译、等待人工审批或访问内部队列。
  3. 保存 response.id、call_id、解析后的参数、任务 ID 和当前状态。至少要保证重试时不会重复执行不可逆操作。
  4. 继续处理与工具结果无关的工作,或者把第一轮输出展示给用户。
  5. 任务完成后,用最新的 previous_response_id 发起后续请求,并在输入中提交 function_call_output。

最小的续接形态如下:

let response = await client.responses.create({
  model: "gpt-6-astra",
  tools,
  input: "查询版本说明,同时先列出升级检查项。",
});

const call = response.output.find((item) => item.type === "function_call");
if (!call || call.name !== "lookup_release_notes") {
  throw new Error("未收到预期的函数调用");
}

const args = JSON.parse(call.arguments) as { version: string };
const task = queueReleaseLookup(args.version); // 应用自己的队列或任务系统

// 这里可以先处理 response.output 中与查询无关的内容。
const result = await task;

response = await client.responses.create({
  model: "gpt-6-astra",
  tools,
  previous_response_id: response.id,
  input: [
    {
      type: "function_call_output",
      call_id: call.call_id,
      output: JSON.stringify(result),
    },
  ],
});

示例中的 queueReleaseLookup 只是业务边界,不是 SDK 提供的函数。生产实现还应记录任务状态,并让结果提交接口具备幂等性。因为网络可能在工具已经完成后、第二次 Responses 请求之前中断,重试逻辑必须能够判断“任务是否已经完成”和“结果是否已经提交”,不能简单地再次执行原动作。

它和 Background mode 不是一回事

官方文档专门把异步工具调用与 Background mode 分开。异步工具调用解决的是“模型调用一个由应用执行的工具后,是否必须等待工具结果”;Background mode 解决的是“响应生成本身是否在后台运行”。前者不会替你托管任务,也不会改变应用的队列和存储责任。

这一区分会影响网关设计。如果网关只看到一个长请求就把连接挂起,仍然没有获得异步工具的价值;正确做法是让第一轮模型请求尽快返回,应用把工具任务写入可恢复的状态,然后在结果可用时续接 Responses。网关需要保留 response.id 和 call_id 的关联,同时把任务身份、租户权限和重试次数放在自己的持久化记录里。

对于中转或多供应商调用层,不能只把 async 字段透传后就宣称兼容。应至少检查三件事:上游是否接受该字段、返回的函数调用是否保留 async 和 call_id、续接请求是否原样支持 function_call_output。如果某个兼容层只实现了“返回文本”,它就不能承接这条异步工具链。需要比较不同 API 入口的协议能力时,可以参考Responses API 浏览器驱动实践,但不要把浏览器工具的托管执行能力和应用函数工具混为一谈。

三个明确的边界

只适用于应用执行的函数和自定义工具。 官方文档明确写出,异步执行不适用于 hosted built-in tools。文件搜索、代码解释器、图片生成等由平台托管的工具,不能因为应用把某个配置写成异步就获得同样的生命周期控制。

不能用于 programmatic tool calling。 如果工具是在模型生成的代码里调用,而不是通过直接的工具调用项交给应用,官方文档要求不要配置 async tools。两种执行路径的结果回传协议不同,混用会让应用无法判断哪个调用需要在后续 Responses 请求中补结果。

Multi-agent mode 不要和 parallel tool calls 组合。 文档给出的兼容性限制要求,在 Multi-agent mode 中不要把异步工具和并行工具调用一起使用。原因不是“异步一定不能并行”,而是多 Agent、并行调用和延迟回传叠加后,调用顺序、权限边界和结果归属会变得难以确定。需要并发时,应先在一个简单的单 Agent 流程中验证任务 ID、call_id、结果存储和失败恢复,再逐步增加并发维度。

模型支持也不能省略。官方文档写明,Async tool calling 支持 GPT-6 Astra 及后续模型。应用应把模型能力作为配置或启动检查的一部分,而不是把 async: true 当成所有模型都能接受的通用字段。对于不支持该能力的模型,网关应明确拒绝、降级到同步工具,或者选择不暴露这类工具。

升级时应验证什么

如果你只需要类型补齐,升级 openai 到 v7.55.0 后可以先运行现有类型检查和 Agents 资源测试。官方仓库的测试样例已经在 Agent 创建、Session 创建和更新请求中加入 async: true,说明字段会进入这些请求形状;这不代表测试替你的业务验证了后台任务。

如果要真正启用异步行为,建议按下面的顺序验收:

  1. 用一个可重复、无副作用的查询工具验证第一轮响应确实先返回,并检查工具调用中的 call_id 与 async。
  2. 故意让后台任务延迟,确认模型可以处理不依赖该结果的独立部分,而不是等待 HTTP 连接保持打开。
  3. 用原始 call_id 提交结果,确认第二轮响应能引用真实结果;同时测试任务失败时提交结构化错误,而不是伪造成功数据。
  4. 在第二次请求前模拟进程重启,确认任务记录和 previous_response_id 能恢复。
  5. 对不可逆工具增加幂等键、超时、取消和权限检查,并把每次工具执行与租户、用户和 Agent 会话关联起来。
  6. 在不支持 async 的模型、托管内置工具、programmatic tool calling 和 Multi-agent 并行路径上分别验证拒绝或降级行为。

这次版本更新的价值在于把协议能力和 Node 类型定义对齐,而不是替应用省掉异步系统。理解这一点后,开发者可以把“模型继续推理”和“工具任务后台运行”拆成两个可测试的层:前者由 Responses 协议和模型决定,后者由应用的队列、存储和权限系统负责。这样既能减少长工具调用对当前回合的阻塞,也不会把一个可选字段误当成托管任务平台。

参考来源