# Chat Completions 迁移 Responses API：函数调用完整循环与验收

> 迁移时保留业务函数，重写它外面的协议循环：遍历 output、校验并执行调用、用原 call_id 回传结果、继续请求。只有工具执行与最终回答都完成，才算迁移成功。

- URL: https://blog.laozhang.ai/zh/posts/chat-completions-to-responses-api-function-calling-migration
- Published: 2026-07-28
- Updated: 2026-10-08
- Author: LaoZhang AI Team (https://blog.laozhang.ai/zh/about)
- Topic: API 接入指南
- Tags: OpenAI API, Responses API, Function Calling, JavaScript

---
把 Chat Completions 的函数调用迁到 Responses API，需要改的是**一整条请求与执行循环**：模型返回 `function_call` 输出项，应用校验参数和权限、执行函数，把结果作为 `function_call_output` 带上原 `call_id` 回传，再请求模型给出最终回答。换了端点、拿到 HTTP 200，甚至看到“已经查好了”，都不能证明业务函数执行过。

已有业务函数可以继续用；首先迁移非流式、只读路径，再分别验证状态、并行和 streaming。下面用订单 `CN-7042` 的发货查询给出完整 JavaScript 程序，并说明哪些失败应返回工具错误，哪些情况必须停止。示例查询的是本地演示数据；本次只做离线程序与合成事件检查，没有请求模型，也没有验证 SDK 加载、服务器接受或真实订单系统。

OpenAI 当前仍支持 Chat Completions，并建议新项目使用 Responses。是否迁移现有系统，还要结合模型和工具能力；不能把“推荐”理解为所有旧应用今天都会失效。[OpenAI 迁移指南](https://developers.openai.com/api/docs/guides/migrate-to-responses)

## 先确认供应商入口和模型能否保留

记录当前 base URL、认证方式、模型或部署名、有效推理强度、状态策略和业务函数版本。它们共同决定新请求该发到哪里，以及出现问题时能恢复到哪条旧路径。

| 你实际使用的服务 | 迁移时先核对什么 | 不可直接照搬的部分 |
|---|---|---|
| OpenAI 官方 API | OpenAI 文档、项目权限和当前模型参数 | 第三方网关的响应保存期限、认证和模型别名 |
| Azure OpenAI | Azure 资源入口、认证和部署名；官方迁移文档中的 `/openai/v1` 属于 Azure | OpenAI 的 API key、base URL 及账户设置 |
| 阿里云百炼或兼容网关 | 服务自己的区域或工作空间入口、模型支持、事件和状态规则 | 不能仅凭 OpenAI-compatible 推出整个工具循环和保留策略一致 |

[Microsoft 的迁移说明](https://learn.microsoft.com/es-es/azure/developer/ai/how-to/azure-openai-to-responses)和[百炼 Responses 兼容说明](https://help.aliyun.com/zh/model-studio/compatibility-with-openai-responses-api)分别约束各自服务。例如百炼文档中的七天响应 ID 有效期，不能作为 OpenAI 官方响应 ID 的通用规则。

模型先保持原选择，推理参数按端点改写：Chat 的 `reasoning_effort` 对应 Responses 的 `reasoning.effort`。截至 2026 年 10 月 7 日，GPT-6 Astra、GPT-6.1 Sol 的工具调用需要 Responses；GPT-6 Sol/Luna 在 Chat 中调用函数须使用 `reasoning_effort: "none"`。Astra 和 6.1 Sol 不支持 `none`，因此也不能把它们的 Responses 工具请求原样切回 Chat。非 `none` 推理还有采样、logprob 参数限制，迁移前按[当前模型兼容说明](https://developers.openai.com/api/docs/guides/latest-model#gpt-6-astra-update-api-and-model-parameters)核对，不顺带把模型升级当成已验证的改进。

## 把旧协议拆成六个迁移动作

业务查询函数不变，修改它外面的适配层：

| Chat Completions 旧写法 | Responses 对应写法 | 为什么要改 |
|---|---|---|
| `client.chat.completions.create()` | `client.responses.create()` | 端点和响应对象改变 |
| `messages` | `input` | 输入可以包含消息、调用和结果等不同类型的项 |
| `tools[].function.name` 等嵌套字段 | `tools[].name`、`parameters` 等扁平字段 | 自定义函数定义结构改变 |
| `choices[0].message.tool_calls` | 遍历 `response.output` 中的 `function_call` | 输出数组还可能含消息、推理等其他项 |
| `role: "tool"`、`tool_call_id` | `type: "function_call_output"`、`call_id` | 结果须配对原始函数调用 |
| 从 assistant 文本重建历史 | 保留全部相关输出项并转成下一轮输入 | 不能丢掉调用、推理信息及消息 `phase` |

这里的关联键是 `function_call.call_id`，不是输出项自身的 `id`，也不是数组下标。每个接受的调用都须返回一个对应结果，然后继续请求；模型可以再次调用函数，不能假设第二次请求一定给最终文本。[函数调用指南](https://developers.openai.com/api/docs/guides/function-calling)

![订单查询中，模型调用与函数结果通过原 call_id 关联，应用在读取演示数据前校验参数和订单权限的概念示意](https://blog.laozhang.ai/posts/zh/chat-completions-to-responses-api-function-calling-migration/img/call-id-association.webp)

函数工具显式选择 `strict: true` 或 `strict: false`。严格模式要求每层对象有 `additionalProperties: false`，且 `properties` 中全部字段都在 `required` 中；可选值可用允许 `null` 的类型表示，仍须注意其他约束是否也允许 `null`。Responses 省略 `strict` 时会尝试严格归一化，无法兼容时可回退到尽力调用，并非无条件严格，也并非必然报 400。明确的 `strict: true` 配上不兼容 schema 则会被拒绝。[严格模式规则](https://developers.openai.com/api/docs/guides/function-calling#strict-mode)

Schema 管参数形状，应用仍须验证订单格式、访问范围和可执行函数。如果你实际只想让最终答案符合 JSON Schema，而不需要查询或修改业务系统，先看 [Structured Outputs 与 Function Calling 的选择](https://blog.laozhang.ai/zh/posts/structured-outputs-vs-function-calling)。

## JavaScript 完整循环：查询订单发货状态

保存为 `responses-fulfillment.mjs`。运行前需要 Node.js、与你安装版本相符的 OpenAI JavaScript SDK、后端环境变量 `OPENAI_API_KEY` 和已确认支持函数调用的 `OPENAI_MODEL`。此程序使用当前官方文档中的 `toResponseInputItems` 导入路径；本次没有安装或加载 SDK，实际项目须核对版本是否提供该导出。密钥和模型设置可按 [API Key 与首次请求指南](https://blog.laozhang.ai/zh/posts/openai-api-key-free-trial)完成。

下面的 `authorizedOrders` 是后端已批准的访问范围演示。生产中应来自登录身份与权限系统，不能由模型或用户参数自报；`fulfillment` 则是只读假数据，替换它时保留相同的权限检查和结果合同。

```javascript
import OpenAI from "openai";
import { toResponseInputItems } from "openai/lib/responses/ResponseInputItems";

const toolName = "lookup_fulfillment_status";
const tools = [{
  type: "function", name: toolName,
  description: "查询已获授权订单的发货和运输状态，只读",
  strict: true,
  parameters: {
    type: "object",
    properties: { order_ref: { type: "string" } },
    required: ["order_ref"], additionalProperties: false,
  },
}];
const fulfillment = new Map([["CN-7042", {
  warehouse_status: "shipped", carrier_status: "in_transit",
  last_scan: "杭州转运中心",
}]]);

export async function queryFulfillment({
  create, toInputItems, model, authorizedOrders,
  maxRounds = 6, budgetMs = 30000,
}) {
  if (!model || !(authorizedOrders instanceof Set)) {
    throw new Error("缺少模型或后端授权范围");
  }
  const input = [{ role: "user", content: "订单 CN-7042 发货了吗？到哪里了？" }];
  const instructions = "先查询订单，再根据工具结果回答；查询失败时不可猜测物流。";
  const deadline = Date.now() + budgetMs;
  const ledger = new Map();
  const audit = [];
  const checkBudget = () => {
    if (Date.now() >= deadline) throw new Error("应用处理时间已用尽");
  };

  async function dispatch(call) {
    try {
      if (call.name !== toolName) throw new Error("未知函数");
      const args = JSON.parse(call.arguments);
      if (!args || Array.isArray(args) || typeof args !== "object" ||
          Object.keys(args).length !== 1 ||
          typeof args.order_ref !== "string" || !/^CN-\d{4}$/.test(args.order_ref)) {
        throw new Error("参数必须只含合法 order_ref");
      }
      if (!authorizedOrders.has(args.order_ref)) throw new Error("无权查询此订单");
      const data = fulfillment.get(args.order_ref);
      if (!data) return { ok: false, code: "not_found" };
      // 这里只读本地假数据；业务接口应在同一授权检查之后调用。
      return { ok: true, data };
    } catch {
      return { ok: false, code: "tool_rejected" };
    }
  }

  for (let round = 0; round < maxRounds; round += 1) {
    checkBudget();
    const response = await create({
      model, instructions, input: [...input], tools,
      tool_choice: round === 0 ? { type: "function", name: toolName } : "auto",
      parallel_tool_calls: false, store: false,
      include: ["reasoning.encrypted_content"],
    });
    checkBudget();
    if (response.status !== "completed") {
      throw new Error(`响应未完成: ${response.status}; ${JSON.stringify(
        response.incomplete_details ?? response.error ?? null
      )}`);
    }
    if (typeof response.id !== "string" || !Array.isArray(response.output)) {
      throw new Error("响应结构缺失");
    }
    const calls = new Map();
    for (const item of response.output) {
      if (!["message", "reasoning", "function_call"].includes(item.type)) {
        throw new Error(`此示例未实现输出类型: ${item.type}`);
      }
      if (item.type !== "function_call") continue;
      if (typeof item.call_id !== "string" || !item.call_id ||
          typeof item.name !== "string" || typeof item.arguments !== "string" ||
          (item.status && item.status !== "completed")) {
        throw new Error("函数调用缺少有效身份或未完成");
      }
      const signature = JSON.stringify([item.name, item.arguments]);
      const prior = calls.get(item.call_id);
      if (prior && prior.signature !== signature) throw new Error("同一 call_id 内容冲突");
      calls.set(item.call_id, { item, signature });
    }

    if (calls.size === 0) {
      const parts = [];
      for (const item of response.output) {
        if (item.type !== "message") continue;
        for (const part of item.content ?? []) {
          if (part.type === "refusal") throw new Error("模型拒绝，未交付查询回答");
          if (part.type === "output_text" && item.role === "assistant" &&
              (!item.phase || item.phase === "final_answer")) parts.push(part.text);
        }
      }
      const text = parts.join("\n").trim();
      if (!text) throw new Error("没有完整最终文本");
      if (!audit.some((entry) => entry.ok)) throw new Error("没有成功查询，不能验收业务成功");
      return { text, audit };
    }

    // 官方 helper 转换所有相关输出，保留推理项和消息 phase。
    input.push(...toInputItems(response.output));
    for (const { item: call, signature } of calls.values()) {
      checkBudget();
      const key = JSON.stringify([response.id, call.call_id]);
      let entry = ledger.get(key);
      if (entry && entry.signature !== signature) throw new Error("重放调用内容冲突");
      if (!entry) {
        // 先登记待执行 Promise；dispatch 在微任务中执行，避免并发重入窗口。
        entry = { signature, pending: Promise.resolve().then(() => dispatch(call)) };
        ledger.set(key, entry);
      }
      const result = await entry.pending;
      audit.push({ response_id: response.id, call_id: call.call_id, ok: result.ok });
      input.push({ type: "function_call_output", call_id: call.call_id,
        output: JSON.stringify(result) });
    }
  }
  throw new Error("调用轮数已用尽，未取得最终回答");
}

const model = process.env.OPENAI_MODEL;
if (!model) throw new Error("请设置已验证的 OPENAI_MODEL");
const client = new OpenAI();
const result = await queryFulfillment({
  model, authorizedOrders: new Set(["CN-7042"]),
  create: (request) => client.responses.create(request),
  toInputItems: toResponseInputItems,
});
console.log(result.text);
console.log(JSON.stringify(result.audit));
```

安装 SDK 并运行会向你配置的模型发起真实请求，可能计费；以下命令供你在已授权的测试环境执行，本次离线检查没有运行它们：

```bash
npm install openai
node responses-fulfillment.mjs
```

程序保留了全部输出项参与重放，没有仅凭 `response.output_text` 续接历史。`output_text` 是 SDK 聚合文本的便利属性，不能当作完整状态，也不能假定每个原始 REST 响应都有这个顶层字段。[状态管理示例](https://developers.openai.com/api/docs/guides/conversation-state)

首轮强制查询，后续允许模型继续调用或作答；`parallel_tool_calls: false` 限制一轮零个或一个调用，不保证调用成功。解析器仍扫描整个数组，异常 fixture 中的多个不同调用也各自配对；这不等于开启了并行执行。未知类型明确停止，示例没有实现托管工具、异步工具或多代理编排。

六轮、30 秒是演示的应用限制。时间检查发生在请求和函数之间，不会强制中断已经发出的网络请求；生产接口还应设置网络超时、取消信号和下游超时。`max_output_tokens` 包含推理与可见输出；`max_tool_calls` 限制内置工具调用，不能替代自定义函数的轮数、并发、时间或费用控制。[Responses 请求参数](https://developers.openai.com/api/reference/resources/responses/methods/create)

## 多调用与幂等：先确定业务语义

每个模型调用都用自己的原 `call_id` 关联结果。应用可以限制独立只读查询的并发；有顺序依赖或会产生副作用的动作，应按业务规则排队、授权和核验，不让 Promise 的完成顺序决定结果身份。

上面的 Map 只防止同一次程序执行中、同一 `response.id + call_id` 的重复处理。相同 ID 内容冲突会停止，而不是复用另一组参数的结果；同一输出数组里的完全相同调用只回传一次。这是本地去重设计，进程重启会丢失记录，新一轮模型请求也可能产生新 ID，**不能保证业务动作只执行一次**。

若把查询改为“重新发货”，另设由服务端批准的业务操作键，例如订单、动作和已批准请求 ID 的组合。不要只用订单号，否则会误拦住以后合法的新操作。恢复流程至少要区分：

| 持久记录 | 下一动作 |
|---|---|
| 已有成功回执 | 复用结果，继续回传和生成答案 |
| 明确失败且尚未执行动作 | 根据失败类别决定是否重试 |
| `pending` 或超时，动作结果未知 | 向业务系统查询回执或人工对账，确认前不重做 |
| 没有记录 | 原子地登记操作键，再执行已批准动作 |

唯一约束、事务、下游幂等键和对账属于应用工程设计。只在数据库插入 `pending`，无法消除“下游动作完成、写回账本前进程崩溃”的窗口。模型请求重试与业务动作重试必须分开；前者拿到新调用，不代表后者获准再执行。

## 状态不是 SDK 帮你自动选的

选择一种明确的续接方式，然后对它验证恢复和保留策略：

| 方式 | 下一轮带什么 | 不能漏掉的条件 |
|---|---|---|
| 手动重放 | 历史输入、相关完整输出项、新函数结果 | 保留 reasoning、调用和 assistant `phase`；示例采用此方式 |
| `previous_response_id` | 上一响应 ID 与新增结果或输入 | 每轮重发顶层 `instructions`；不要再重复附上同一完整历史 |
| Conversation | 会话标识与新增输入 | 完成后自动追加输入和输出；须定义访问、删除与恢复策略 |

`previous_response_id` 与 `conversation` 不能在同一请求中使用。响应 ID 串联也适用于 HTTP，不限于 WebSocket；第三方网关若报不支持，应先核对它的实际实现。串联减少请求载荷，但历史输入 token 仍计费，不是免费上下文。[会话状态指南](https://developers.openai.com/api/docs/guides/conversation-state)

当前指南说明 Responses 默认返回加密推理项；`include: ["reasoning.encrypted_content"]` 仍是显式请求此信息的可用写法。不要因为内容加密就丢掉它，也不要把它当成能公开阅读的业务执行日志。支持 `phase` 的 assistant 消息也要保留原值，不能把中途说明重建成最终答案。

`store: false` 关闭普通 Response 存储，不等于项目已获 Zero Data Retention，也不会删除应用或网关日志。默认存储的 Responses 应用状态有 30 天保留规则及例外，Conversation 项没有同样的 30 天 TTL；滥用监控、经批准的 MAM/ZDR、后台模式及托管工具另有规定。要满足保留要求，核对项目、服务和启用功能的具体政策。[OpenAI 数据使用与保留说明](https://developers.openai.com/api/docs/guides/your-data)

## Streaming 的安全边界

Responses HTTP streaming 使用带类型的 SSE 事件，旧 Chat 文本 chunk 累加器不能直接承担函数调度。参数“拼完了”和整次 Response “成功完成了”是两件事：此处采用保守设计，等完整 Response 成功后才交给上面的执行循环。

| 事件 | 应用保存或判断什么 | 能否开始执行函数 |
|---|---|---|
| `response.output_item.added` | 输出项自身的 `id`、类型及原 `call_id` | 不能 |
| `response.function_call_arguments.delta` | 按 `item_id` 或 `output_index` 累积参数片段 | 不能解析半截 JSON 后执行 |
| `response.function_call_arguments.done` | 完整 `arguments`，关联原 item | 这个事件不证明整次响应完成 |
| `response.output_item.done` | 完整 `item`，其中函数项带原 `call_id` | 先暂存，等待响应终态 |
| `response.completed` | 完整 Response 状态与输出 | 此设计仅此时交给校验和执行 |
| `response.failed`、`response.incomplete`、取消或提前 EOF | 错误、部分输出与已完成动作记录 | 停止本轮，不能把 EOF 当成功 |

![流式调用的参数和输出项先暂存，等 response.completed 后校验执行，失败或提前结束则停止调度的应用策略示意](https://blog.laozhang.ai/posts/zh/chat-completions-to-responses-api-function-calling-migration/img/stream-completion-gate.webp)

特别注意：当前 `response.function_call_arguments.done` schema 有 `arguments`、`item_id`、`output_index`、`sequence_number`、`type`，没有完整 `item`，也没有 `call_id`。应从真正的函数输出项取得 `call_id`，不要从事件序号编造。[参数完成事件 schema](https://developers.openai.com/api/reference/resources/responses/streaming-events#response.function_call_arguments.done)、[输出项完成事件 schema](https://developers.openai.com/api/reference/resources/responses/streaming-events#response.output_item.done)

下面是可独立保存的 HTTP 事件收集函数。`events` 必须是传输层已解析 JSON 的异步事件迭代器；它不含 SSE 网络解析，也不启动业务函数。返回的 Response 可以交给相同的非流式校验流程，`sequence_number` 去重只覆盖这条事件流，不承担业务幂等。

```javascript
export async function collectCompletedResponse(events) {
  const seen = new Map();
  const items = new Map();
  const argumentsByItem = new Map();
  let completed;
  for await (const event of events) {
    if (!Number.isInteger(event.sequence_number)) throw new Error("事件缺少序号");
    const fingerprint = JSON.stringify(event);
    if (seen.has(event.sequence_number)) {
      if (seen.get(event.sequence_number) !== fingerprint) throw new Error("事件序号冲突");
      continue;
    }
    seen.set(event.sequence_number, fingerprint);
    if (["response.failed", "response.incomplete", "response.cancelled"].includes(event.type)) {
      throw new Error(`流式响应失败: ${event.type}`);
    }
    if (event.type === "error") throw new Error("流式传输返回错误");
    if (event.type === "response.output_item.added" ||
        event.type === "response.output_item.done") {
      items.set(event.output_index, event.item);
    } else if (event.type === "response.function_call_arguments.delta") {
      argumentsByItem.set(event.item_id,
        (argumentsByItem.get(event.item_id) ?? "") + event.delta);
    } else if (event.type === "response.function_call_arguments.done") {
      argumentsByItem.set(event.item_id, event.arguments);
    } else if (event.type === "response.completed") {
      completed = event.response;
      if (completed.status !== "completed" || !Array.isArray(completed.output)) {
        throw new Error("completed 事件缺少完整 Response");
      }
      for (const [index, item] of items) {
        if (item.type !== "function_call") continue;
        const finalItem = completed.output[index];
        if (!finalItem || finalItem.id !== item.id || finalItem.call_id !== item.call_id) {
          throw new Error("函数输出项身份不匹配");
        }
        const args = argumentsByItem.get(item.id);
        if (args !== undefined && args !== finalItem.arguments) {
          throw new Error("函数参数完成值不匹配");
        }
      }
      return completed;
    }
  }
  throw new Error("事件流提前结束，未收到 response.completed");
}
```

此收集器的释放策略是应用选择，不是服务端“工具恰好执行一次”的保证。合成事件可以验证断线前不会释放调用、重复事件不会重复累计参数；真实连接的重连、取消和代理缓冲仍需在你的环境验收。[Streaming Responses 指南](https://developers.openai.com/api/docs/guides/streaming-responses)

## 怎样判断失败，怎样恢复到旧路径

遇到 `failed`、`incomplete`、`cancelled` 或仍在 `queued/in_progress` 的响应，停止本轮业务调度。`incomplete_details` 可能指向输出预算、内容过滤等原因；先保存状态与已获得结果，再决定扩大预算、调整输入还是交给人工处理，不能把部分文本当最终成功。[失败事件](https://developers.openai.com/api/reference/resources/responses/streaming-events#response.failed)、[不完整事件](https://developers.openai.com/api/reference/resources/responses/streaming-events#response.incomplete)

未知函数、坏 JSON、数组或额外字段、越权订单在示例中返回结构化工具错误。缺失 `call_id`、相同 ID 参数冲突、未实现的输出类型、空最终答案及拒绝则明确停止。错误结果可以帮助模型说明失败，但不应让“说明得很通顺”升级为业务成功；程序还要求至少有一次成功查询。这个条件只证明应用读到了演示结果，最终文本是否忠实表达订单状态仍要对照业务结果核验。

发布前以同一份脱敏测试数据比较旧、新适配层：

- 正常查询：新旧得到同样的发货状态；新路径能留下原 `call_id`、工具结果及完整答案。
- 多调用和重复：各不同调用分别配对；同 ID 同参数不重复处理，冲突参数明确停止。
- 输入与权限：坏 JSON、`null`、额外字段、未知函数及非授权订单不会读取业务数据。
- 响应失败：`incomplete`、`failed`、拒绝、空回答、轮数耗尽各有预期失败结果。
- 状态恢复：手动重放保持推理项和 `phase`；串联重新发送指令；恢复时复用已成功业务回执。
- 流式中断：参数 `.done` 后但 Response 完成前断线仍不执行；成功动作之后文本中断，恢复只补回传和回答。
- 回滚：切换开关指向已验证的模型、端点、参数与状态适配器；保留操作账本，不重做已完成动作。

本次离线检查覆盖程序语法、假响应下的调用与失败分支、历史项保存，以及合成流事件的完成与断线处理。它们不证明 SDK helper 可加载、当前账户能访问模型、接口接受所有字段或生产权限有效。真实上线验收仍需你的授权环境、去敏执行记录与业务回执；先开放内部只读流量，再逐项增加并行、streaming 或写工具。

## 常见问题

### 只改成 `responses.create()` 为什么还没有执行函数？

模型返回的是调用请求。你的应用必须遍历 `output`，把允许的 `function_call` 交给已实现的函数，再用同一 `call_id` 回传 `function_call_output` 并请求下一轮。只显示模型文本或只保存调用 JSON，都没有完成业务动作。[函数调用流程](https://developers.openai.com/api/docs/guides/function-calling)

### `call_id` 能作为发货操作的幂等键吗？

它能关联一次协议调用，不能单独保证同一发货动作跨请求、跨进程只发生一次。模型重试可能产生新 ID；写操作须使用后端批准的业务键、持久回执和结果未知时的对账流程。`parallel_tool_calls: false` 也不替代这些机制。

### 关掉 `store` 就能保证零保留吗？

不能。`store: false` 影响普通 Response 存储，ZDR 资格、滥用监控、应用与网关日志以及额外功能有各自规则。若使用 Conversation，还须单独处理没有 30 天 TTL 的会话项。[数据保留政策](https://developers.openai.com/api/docs/guides/your-data)

### 出错时能直接切回 Chat Completions 吗？

只有旧路径的模型、工具、参数和状态转换仍可用时才行。GPT-6 Astra 和 GPT-6.1 Sol 的工具调用需要 Responses，不能以它们的 Chat 工具请求充当回滚方案。恢复时还须沿用已完成业务动作的回执，避免重做。[当前 API 与模型兼容要求](https://developers.openai.com/api/docs/guides/latest-model#gpt-6-astra-update-api-and-model-parameters)

## 参考来源

本文引用的外部页面，按正文出现顺序排列。最后更新于 2026-10-08。

- [OpenAI 迁移指南](https://developers.openai.com/api/docs/guides/migrate-to-responses) (developers.openai.com)
- [Microsoft 的迁移说明](https://learn.microsoft.com/es-es/azure/developer/ai/how-to/azure-openai-to-responses) (learn.microsoft.com)
- [百炼 Responses 兼容说明](https://help.aliyun.com/zh/model-studio/compatibility-with-openai-responses-api) (help.aliyun.com)
- [当前模型兼容说明](https://developers.openai.com/api/docs/guides/latest-model) (developers.openai.com)
- [函数调用指南](https://developers.openai.com/api/docs/guides/function-calling) (developers.openai.com)
- [状态管理示例](https://developers.openai.com/api/docs/guides/conversation-state) (developers.openai.com)
- [Responses 请求参数](https://developers.openai.com/api/reference/resources/responses/methods/create) (developers.openai.com)
- [OpenAI 数据使用与保留说明](https://developers.openai.com/api/docs/guides/your-data) (developers.openai.com)
- [参数完成事件 schema](https://developers.openai.com/api/reference/resources/responses/streaming-events) (developers.openai.com)
- [Streaming Responses 指南](https://developers.openai.com/api/docs/guides/streaming-responses) (developers.openai.com)
