Responses API 与 Chat Completions 的区别:一条迁移指南(2026)
Responses API 与 Chat Completions 的区别:一条迁移指南(2026)
OpenAI 推荐所有新项目使用 Responses API,同时明确表示 Chat Completions 会继续受支持。两个端点长期共存,但字段结构、状态管理和工具体系都不一样。这篇讲清楚四个维度的差异、迁移要改哪些代码,以及什么场景该选哪个——文中的示例在 EXAIV 的两个端点上都可以直接跑通。
定义: Responses API 是 OpenAI 在 Chat Completions 之后推出的统一接口:输入用 input 数组、输出为带类型的 output 项列表,默认由服务端保存会话状态(store),并内置网页搜索、文件搜索、代码解释器等托管工具。Chat Completions 则是无状态协议:每次请求携带完整 messages 历史,由客户端自己管理上下文。
一张表看核心区别
| 维度 | Chat Completions | Responses API |
|---|---|---|
| 端点 | /v1/chat/completions | /v1/responses |
| 输入字段 | messages 数组 | input(字符串或消息数组) |
| 输出结构 | choices[0].message.content | output 项列表,取 output_text |
| 状态管理 | 无状态,客户端重发全部历史 | 默认 store: true,可用 previous_response_id 引用上一轮 |
| 内置工具 | 无托管工具体系 | 网页搜索、文件搜索、代码解释器、计算机使用、远程 MCP |
| 函数定义格式 | 旧版 tools 结构 | 新版结构,与旧版相似但字段不同 |
| 多候选输出 | 支持 n > 1 | 已移除,仅单个结果 |
官方定位
OpenAI 的说法是:"Chat Completions 仍然受支持,推荐所有新项目使用 Responses"。没有强制迁移期限,但新能力(托管工具、推理状态延续等)只在 Responses 侧增加。另一个参照:Assistants API 已于 2026 年 8 月 26 日停用,新特性收敛到 Responses 是明确方向。
代码对比:同样的任务,两种写法
Chat Completions(EXAIV 端点 /api/ai/v1/chat/completions):
curl https://exaiv.com/api/ai/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"model": "gpt-6-sol",
"messages": [
{ "role": "user", "content": "用一句话解释什么是 AI 网关" }
]
}'
Responses API(EXAIV 端点 /api/ai/v1/responses):
curl https://exaiv.com/api/ai/v1/responses \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"model": "gpt-6-sol",
"input": "用一句话解释什么是 AI 网关"
}'
SDK 层面对应关系:client.chat.completions.create(...) 换成 client.responses.create(...);取结果从 completion.choices[0].message.content 换成 response.output_text。完整参数说明见Chat Completions 参考与Responses 参考。
迁移四步法
messages→input:纯文本请求直接传字符串;带历史的请求把messages数组原样改为input数组,role/content 结构保留。- 取结果改路径:
choices[0].message.content→ 遍历output过滤type === "message",或直接用 SDK 的output_text便捷字段。 - 系统提示移到
instructions:Chat 里的role: "system"消息,在 Responses 中有独立的顶层参数。 - 工具定义重写:两个版本的 function 定义结构相似但不相同,逐个核对字段;Responses 的内置工具(web_search 等)直接传工具名即可启用,不需要自己写编排循环。
流式事件不同
两个接口都用 SSE 流式输出,但事件类型完全不同(Chat 是 delta 增量,Responses 是 response.output_text.delta 等具名事件)。迁移时 SSE 解析器要单独改。
怎么选:按场景决策
| 场景 | 推荐 | 原因 |
|---|---|---|
| 已有代码正常工作 | 留在 Chat Completions | 没有废弃时间表,按流程逐个迁移即可 |
| 新项目、多步 Agent、要内置工具 | Responses | 托管工具 + 服务端状态,少写一层编排代码 |
| 代码要跑在多家模型服务上 | Chat Completions | 它是行业事实标准,兼容面最广 |
| 需要多候选(n > 1) | Chat Completions | Responses 已移除该参数 |
EXAIV 两个端点同时提供、同一个 API Key 通用,所以不必"二选一站到底":存量流量走 Chat Completions,新功能用 Responses,通过模型页切换底层模型即可。
为什么会有两个接口
理解演进背景有助于做决策。Chat Completions 是 OpenAI 早期确立的接口,因为结构简单、跨厂商通用,逐渐成为整个行业模仿的事实标准——今天几乎所有模型服务商都提供 messages 风格的兼容端点。Responses API 则是 OpenAI 为 Agent 时代重新设计的接口:把"模型可能要做多件事"(调工具、查资料、执行代码)作为一等公民内建到接口层,把状态管理和工具执行放回服务端。两个接口的目标场景不同,这也是它们长期共存的原因。
参数对照速查表
从 Chat Completions 迁移时,最常见的参数对应关系:
| Chat Completions | Responses API | 说明 |
|---|---|---|
messages | input | 纯文本可直接传字符串 |
role: "system" | instructions | 系统提示有独立参数 |
choices[0].message.content | output_text / 遍历 output | 结果取值路径不同 |
max_tokens | max_output_tokens | 名字不同,语义相近 |
response_format | text.format | 结构化输出,字段结构有变化 |
tools + tool_choice | 新版 tools 格式 | 定义结构相似,字段需逐个核对 |
n(多候选) | 已移除 | 需要多候选请留在 Chat Completions |
| — | store / previous_response_id | Responses 独有的状态能力 |
精确字段以两边的官方参考文档为准,迁移时逐个核对。
状态管理深入:store 与 previous_response_id
这是两代接口最本质的哲学差异。Chat Completions 假设"服务器不记得任何事":每一轮你都要把全部历史重新发送,上下文窗口管理和成本控制都在客户端手里。Responses API 默认由服务端保存会话(store: true),下一轮只需引用上一轮的 previous_response_id,服务端会带着完整的对话与工具上下文继续推理。
对开发者的实际影响:
- 省代码:自己维护 messages 数组、裁剪历史、压缩上下文的模块可以删掉
- 省 token:服务端能复用已有上下文,重复发送的开销消失
- 注意合规:对话数据存在服务端,有数据驻留要求的团队可用
store: false关闭存储,代价是回到"每轮全量发送"的模式
常见迁移坑(五个高频问题)
- 结果为空:取值路径没改,还在读
choices[0].message.content,而 Responses 的内容在output里 - 工具调用失效:tools 定义结构两代不同,字段照搬会静默失败,需要逐个核对
- 流式解析崩坏:SSE 事件类型完全不同,
delta逻辑套不到response.output_text.delta上 - 多候选报错:
n参数在 Responses 中已移除,依赖多候选的逻辑要重写或留在 Chat - 状态泄漏:
store默认开启,测试数据会被保存——对数据敏感的场景记得显式关闭
FAQ
工具执行的两种心智模型
把差异说得最透的方式,是看"模型调工具"这件事在两代接口里分别长什么样。
Chat Completions:回合制。 模型说"我要调 A 工具"→ 你执行 → 把结果作为新消息塞回去 → 再次请求 → 模型可能又要调 B 工具 → 你再执行再请求。一个复杂的 Agent 任务被拆成 N 次独立的 HTTP 往返,每一次你都要把越来越长的历史重新发送。它的好处是每一步都在你眼皮底下、完全可控;代价是编排逻辑全在你这边。
Responses:连续剧。 一次请求就是一个完整的生命周期:模型在服务端循环调用工具、读取结果、继续推理,直到产出最终答案。你看到的是一串带类型的输出项——文本、function_call、工具结果交错排列。它更像"委托"而不是"对话":你描述目标,服务端推进过程。
两种模型没有绝对优劣:需要逐步审核每个工具调用的场景,回合制反而更合适;需要长链路自动化完成的场景,连续剧模型省掉大量胶水代码。理解了这一点,"要不要迁移"就不再是立场问题,而是按流程逐个判断的问题。