# Chat Completions 与 Responses API：差别不只是字段名

> 从请求体、输出 Item、会话状态、工具调用和流式事件理解 Chat Completions 与 Responses API，并用可验收的步骤迁移。

- Source: https://www.aifreeapi.com/zh/posts/chat-completions-vs-responses-api
- Language: zh
- Published: 2026-08-25
- Updated: 2026-08-25
- Publisher: AI Free API (https://www.aifreeapi.com)

先给结论：**新项目优先采用 Responses API；只做单轮文本、已经稳定运行的 Chat Completions 可以继续维护，再按功能收益分阶段迁移。** OpenAI 当前的[官方迁移指南](https://developers.openai.com/api/docs/guides/migrate-to-responses)明确写着，Responses 是 Chat Completions 的演进，也是新项目的推荐接口；同一页也明确说明 Chat Completions 仍受支持。它不是“明天就不能用”的旧接口。

真正的区别也不是把 `/v1/chat/completions` 改成 `/v1/responses`。前者把一次生成组织成消息列表和候选答案，后者把模型消息、推理、工具调用和工具结果都建模为不同类型的 **Item**。只改 URL，最容易在输出解析、上下文、工具回传或流式 UI 上留下静默错误。

![Chat Completions 与 Responses API 的协议合同、工具调用往返和迁移验收总览](https://www.aifreeapi.com/posts/zh/chat-completions-vs-responses-api/img/cover.webp)

## 两套接口的最小心智模型

Chat Completions 的中心对象是 `messages`。应用把历史消息重新发送给模型，再从 `choices[0].message` 读取答案：

```js
const completion = await client.chat.completions.create({
  model: "gpt-5.6",
  messages: [
    { role: "developer", content: "回答要简洁。" },
    { role: "user", content: "给这个错误日志归类。" },
  ],
});

const text = completion.choices[0].message.content;
```

Responses 的中心对象是 `input` 和类型化的 `output` Item。纯文本场景可以使用 SDK 的 `output_text` 便捷属性，但工具调用、引用、状态或其他输出类型仍要遍历 `output`：

```js
const response = await client.responses.create({
  model: "gpt-5.6",
  instructions: "回答要简洁。",
  input: "给这个错误日志归类。",
});

const text = response.output_text;
for (const item of response.output) {
  console.log(item.type, item.status);
}
```

这一区别会产生几项直接后果：

| 合同 | Chat Completions | Responses API | 迁移时要改什么 |
|---|---|---|---|
| 端点 | `/v1/chat/completions` | `/v1/responses` | SDK 调用或 HTTP URL |
| 输入 | `messages` | 字符串或 Item 列表 `input`，另有 `instructions` | 请求构造与类型定义 |
| 输出 | `choices[].message` | `output[]` 类型化 Item | 解析器、日志与错误分支 |
| 多个候选 | 可用 `n` 返回多个 choice | 一次只产生一个 generation | 依赖多候选的业务逻辑 |
| 会话状态 | 通常由应用重发消息 | 可用 `previous_response_id`、Conversations 或手动重放 Item | 状态存储与恢复策略 |
| 结构化输出 | `response_format` | `text.format` | schema 配置位置 |
| 流式返回 | chunk 与 `choices[].delta` | 有类型的语义事件 | 事件分发器和完成条件 |

因此，“能复用简单的 role/content 消息数组”只说明最小文本请求容易搬迁，不代表两套协议可以互换。

## 状态便利性和数据存储是两件事

Chat Completions 的多轮对话通常由应用维护：保存历史，把需要的消息重新放进下一次 `messages`。Responses 可以让下一轮带上 `previous_response_id`：

```js
const first = await client.responses.create({
  model: "gpt-5.6",
  input: "把这份告警归纳成三类。",
});

const next = await client.responses.create({
  model: "gpt-5.6",
  previous_response_id: first.id,
  instructions: "现在只给出需要立即处理的一类。",
  input: "继续。",
});
```

这能减少应用手动拼接上下文的工作，但有两个容易忽略的边界。

第一，[Responses API reference](https://developers.openai.com/api/reference/resources/responses/methods/create)说明，上一轮的 `instructions` 不会因为传了 `previous_response_id` 就自动沿用；需要持续生效的开发者指令，下一轮仍要明确提供。

第二，状态续接不等于数据不落盘。OpenAI 的[数据控制说明](https://developers.openai.com/api/docs/guides/your-data)对 Responses、`store`、Zero Data Retention、background mode、缓存和托管工具分别规定了边界。当前文档说明，Responses 在默认存储或 `store: true` 时可能保留至少 30 天的应用状态，但组织级设置和例外会改变行为。若你的业务有留存要求，不应只记住一个 `store: false`，而要根据组织配置和实际利用面重新核对。

## 工具调用为什么不能只改响应字段

![Chat Completions 和 Responses API 的工具调用信封差异及类型化 streaming 事件对比](https://www.aifreeapi.com/posts/zh/chat-completions-vs-responses-api/img/tool-and-streaming-protocol.webp)

两套接口都能调用自定义函数，但往返信封不同。

在 Chat Completions 中，模型的工具请求位于 assistant message 的 `tool_calls`；应用执行函数后，追加一个 `role: "tool"` 的消息，并用 `tool_call_id` 对应原调用。

在 Responses 中，模型返回独立的 `function_call` Item；应用回传 `function_call_output` Item，并用 `call_id` 建立对应关系：

```js
const calls = response.output.filter(
  (item) => item.type === "function_call"
);

const toolOutputs = calls.map((call) => ({
  type: "function_call_output",
  call_id: call.call_id,
  output: JSON.stringify(runTool(call.name, call.arguments)),
}));

const final = await client.responses.create({
  model: "gpt-5.6",
  previous_response_id: response.id,
  input: toolOutputs,
});
```

这里必须遍历全部调用，而不是假设永远只有第一个。还要分别验证：参数 JSON 解析失败怎么办、工具超时如何回传、并行调用如何关联、工具结果之后是否还有下一轮工具调用。OpenAI 的[函数调用指南](https://developers.openai.com/api/docs/guides/function-calling)给出了两套接口的完整往返示例。

如果你使用的是 web search、file search、code interpreter 或 remote MCP 等托管工具，Responses 的优势更明显：同一请求可以包含多个类型化的工具 Item。但具体模型是否支持某个工具，仍要看当前模型页；“接口支持”不能推出“任何模型都支持”。

## 流式 UI 要重写事件分发，而不是换一个取文本表达式

Chat Completions 流通常循环读取 chunk，从 `chunk.choices[0].delta.content` 追加文本。Responses 使用有明确类型的语义事件，例如文本增量、输出 Item 完成和整个 response 完成事件。官方[流式响应指南](https://developers.openai.com/api/docs/guides/streaming-responses)建议按事件类型处理。

这会改变前端和可观测性代码：

- 文本增量只更新正在显示的文本；
- 工具调用参数增量进入工具状态，而不是聊天文本；
- Item 完成用于收口单个输出；
- response 的 `completed`、`failed` 或 `incomplete` 决定整次请求最终状态；
- 用户取消、网络断线和模型生成不完整必须拥有不同的 UI 与重试策略。

仅以 HTTP 200 或“收到过文本增量”作为成功条件并不够。Responses 对象本身拥有状态生命周期，迁移后的日志至少应记录 response ID、最终状态、输出 Item 类型、usage 和错误/不完整原因。

## 新项目与旧系统应采用不同决策

对于新项目，Responses 通常是更稳的默认选择，尤其是以下任一条件成立时：需要推理模型的连续上下文、托管工具、多步函数调用、原生多模态输入、长任务状态或未来扩展到代理工作流。

对于稳定的旧系统，继续使用 Chat Completions 也可能是正确决定，特别是业务只是单轮文本或简单结构化输出，现有解析、监控和供应商抽象都围绕 `messages`/`choices`，而 Responses 暂时没有带来能覆盖迁移风险的收益。支持仍在，不需要为了“接口更新”制造一次大爆炸式重写。

第三方标注“OpenAI compatible”时更要谨慎。兼容 Chat Completions 不自动等于兼容 Responses；即使端点存在，也要分别核对 Item 类型、流式事件、工具信封、状态续接和错误对象。OpenAI 官方文档只能证明 OpenAI 自身的合同，不能替第三方供应商背书。

## 用一次影子迁移把风险变成可验收结果

![从输出解析到存储合规的六组迁移验收合同与常见静默错误](https://www.aifreeapi.com/posts/zh/chat-completions-vs-responses-api/img/migration-acceptance-contracts.webp)

一个可控迁移可以按以下顺序推进：

1. 先封装内部中间结果，不让业务代码直接读取 `choices[0]` 或任意 `output[0]`。
2. 用同一批代表性请求同时调用两条路径，对比最终文本、结构化字段、拒绝和截断行为，而不是只比字符串。
3. 为多轮场景明确选择 `previous_response_id`、Conversation 或手动重放，验证进程重启后的恢复方式。
4. 迁移全部工具往返，覆盖零次、一次、并行多次、失败与重试。
5. 重写 streaming 事件分发和完成条件，验证取消、断线、`incomplete` 与 `failed`。
6. 最后检查存储、日志脱敏、usage 计量和第三方兼容性，再逐步放量。

切换完成的标准不是“返回了一段正确文字”，而是旧接口承担的每一项可观察合同都有了新实现：状态能恢复、工具调用不丢、结构化输出能解析、流式 UI 能收口、失败能定位。做到这一步，迁移才从字段替换变成了受控的协议升级。
