过去几年,几乎所有大模型应用都建立在 /v1/chat/completions 之上:一个 messages 数组进、一个 choices 数组出,简单、通用、被无数 SDK 和工具兼容。后来 OpenAI 推出了 /v1/responses,并把它定位为面向推理模型和 Agent 场景的新一代接口,Codex 等官方工具也逐步迁移到了 Responses。
那么问题来了:两者到底差在哪?现有项目要不要迁移?迁移时哪些地方最容易踩坑?通过 Zivv 这样的网关调用时又有什么需要注意?这篇逐项讲清楚。如果你还在把项目从官方迁到兼容网关,可以先读 OpenAI 兼容 API 迁移指南。
一句话结论
- Chat Completions:无状态、结构简单、生态最广,适合大多数对话和文本处理场景,短期内不会消失
- Responses API:以「输入项 / 输出项」为核心,原生表达推理、工具调用、多段输出,流式事件更语义化,更适合 Agent 和推理模型
新项目、尤其是重度使用工具调用和推理模型的 Agent 项目,优先考虑 Responses;存量项目跑得好好的,不必为迁移而迁移。
请求结构差异
先看同一个需求在两种接口下的写法。Chat Completions:
from openai import OpenAI
client = OpenAI(api_key="sk-your-zivv-key", base_url="https://zivv.pro/v1")
resp = client.chat.completions.create(
model="gpt-5.5",
messages=[
{"role": "system", "content": "你是简洁的技术助手"},
{"role": "user", "content": "用三句话解释 HTTP/2 多路复用"},
],
max_tokens=300,
)
print(resp.choices[0].message.content)Responses API:
resp = client.responses.create(
model="gpt-5.5",
instructions="你是简洁的技术助手",
input="用三句话解释 HTTP/2 多路复用",
max_output_tokens=300,
)
print(resp.output_text)核心字段的对应关系如下:
| 概念 | Chat Completions | Responses API |
|---|---|---|
| 端点 | /v1/chat/completions | /v1/responses |
| 输入 | messages 数组 | input(字符串或输入项数组) |
| 系统提示 | system / developer 消息 | instructions 字段(也可用 developer 输入项) |
| 输出 | choices[0].message | output 数组(消息、推理、工具调用等多种项) |
| 输出上限 | max_tokens / max_completion_tokens | max_output_tokens |
| 结构化输出 | response_format | text.format |
| 推理强度 | reasoning_effort | reasoning.effort |
| 用量字段 | prompt_tokens / completion_tokens | input_tokens / output_tokens |
| 状态 | 无状态,每次传完整历史 | 可选服务端存储与 previous_response_id |
最需要转变的思维是「输出不再只有一条消息」。一次 Responses 调用的 output 里可能先有一个推理项、再有一个或多个工具调用项、最后才是消息项。SDK 提供的 output_text 只是把所有文本拼起来的便捷属性,做 Agent 时要遍历 output 逐项处理。
状态管理:previous_response_id
Chat Completions 是纯无状态的:第十轮对话,你要把前九轮全部再发一遍。Responses API 提供了两种选择:
- 服务端状态:请求默认可以被存储(
store参数控制),下一轮只需传previous_response_id加新输入,服务端自动拼接历史 - 客户端状态:设置
store: false,自己把上一轮的输出项追加到 input 里再发出去,和 Chat Completions 的心智模型一致
first = client.responses.create(model="gpt-5.5", input="记住:项目代号是 Atlas")
second = client.responses.create(
model="gpt-5.5",
previous_response_id=first.id,
input="项目代号是什么?",
)需要提醒的是:previous_response_id 在不同调用方式下的可靠性不一样。走 Zivv 的 WebSocket 模式(Codex 长对话用的就是它)时,网关会在同一条连接内记住上一轮的完整输入和输出,命中 previous_response_id 就自动补成全量上下文再发上游,即使 store: false 也能用;连接断开重连后找不到上一轮,会返回官方同款的 previous_response_not_found,客户端改发全量即可。走普通 HTTP 请求时,它依赖上游保存的会话数据,跨账号调度时不保证能找回,这种场景更稳妥的做法是客户端自行维护上下文。另外要注意,即使使用服务端状态,历史 Token 依然会计入每次请求的输入用量,它省的是带宽和代码,不是 Token 费用(省 Token 请看 提示词缓存详解)。
工具调用格式差异
函数调用是迁移时改动最多的部分:
| 环节 | Chat Completions | Responses API |
|---|---|---|
| 工具定义 | {type: "function", function: {name, parameters}} | {type: "function", name, parameters}(扁平) |
| 模型发起调用 | message.tool_calls 数组 | output 中的 function_call 项 |
| 回传结果 | role 为 tool 的消息 + tool_call_id | function_call_output 输入项 + call_id |
Responses 回传工具结果的写法:
input_items = [{"role": "user", "content": "北京今天天气怎么样?"}]
resp = client.responses.create(model="gpt-5.5", input=input_items, tools=tools, store=False)
for item in resp.output:
if item.type == "function_call":
result = get_weather(item.arguments)
input_items.append(item)
input_items.append({
"type": "function_call_output",
"call_id": item.call_id,
"output": result,
})
final = client.responses.create(model="gpt-5.5", input=input_items, tools=tools, store=False)
print(final.output_text)官方 Responses 还提供网页搜索、文件检索等内置工具,这类能力依赖上游托管服务,通过网关是否可用以 接口文档 为准;自定义函数工具则不受影响。
流式事件差异
Chat Completions 的流式是一串结构相同的 chunk,你从 choices[0].delta.content 里取增量文本,最后以 data: [DONE] 结束。
Responses API 的流式是语义化事件,每个事件都带 type:
response.created:响应开始response.output_item.added:新增一个输出项(消息、推理、工具调用)response.output_text.delta:文本增量response.function_call_arguments.delta:工具参数增量response.completed:响应结束,携带完整用量
stream = client.responses.create(model="gpt-5.5", input="写一首关于网关的短诗", stream=True)
for event in stream:
if event.type == "response.output_text.delta":
print(event.delta, end="", flush=True)
elif event.type == "response.completed":
print("\n用量:", event.response.usage)迁移时最常见的 bug 是还在找 choices 字段,或者等 [DONE] 来判断结束。改成按事件类型分发即可,前端代码也会因此清晰很多。
Codex 为什么用 Responses + WebSocket
Codex 是 Responses API 的典型用户。编码 Agent 的一个任务往往包含几十轮「模型思考 → 调工具 → 读结果 → 再思考」,Responses 的输入项 / 输出项模型天然适合表达这种循环,推理内容也能在轮次间延续。
在此基础上,新版 Codex 还支持以 WebSocket 方式连接 /v1/responses:在一条长连接上连续发起多轮请求,省去每轮重新建立 HTTP 连接的开销,事件内容与 HTTP SSE 流式基本一致。对动辄几十轮工具调用的任务,延迟改善很明显。
Zivv 的 /v1/responses 同时支持 HTTP SSE 和 WebSocket 两种模式,Codex 配置 https://zivv.pro/v1 后无需额外设置。具体步骤见 Codex 接入 OpenAI 兼容中转 和 Codex 文档。
通过 Zivv 调用两种接口
两种接口都使用同一个 Base URL 和同一个 Key,用 curl 可以快速验证:
curl https://zivv.pro/v1/responses \
-H "Authorization: Bearer sk-your-zivv-key" \
-H "Content-Type: application/json" \
-d '{"model":"gpt-5.5","input":"ping","max_output_tokens":32}'返回 JSON 中有 output 数组和 usage 即说明链路正常。模型 ID 请以 模型广场 或 GET /v1/models 为准;不同模型对两种接口的支持可能不同,GPT 系列推荐优先走 Responses,需要 Claude 原生能力时走 Anthropic 协议的 /v1/messages。计费与官方口径一致:分组倍率 × 官方模型价格,按实际 Token 计费,充值汇率 ¥1 = $1。
该不该迁移:按场景判断
迁移不是目的,降低复杂度、拿到新能力才是。可以按下面的场景对号入座:
| 场景 | 建议 | 理由 |
|---|---|---|
| 简单问答、翻译、摘要 | 保持 Chat Completions | 无状态、单轮输出,迁移收益很小 |
| 已接入大量第三方工具链 | 保持 Chat Completions | Cursor、Cline 等工具和各类框架对它的兼容最成熟 |
| 多轮工具调用的 Agent | 迁移到 Responses | 输入项模型天然表达工具循环,代码更直观 |
| 重度使用推理模型 | 迁移到 Responses | 推理项可在轮次间延续,推理强度参数更规整 |
| 前端需要细粒度流式展示 | 迁移到 Responses | 语义事件能区分文本、工具参数和推理过程 |
| 自研编码助手、对标 Codex | 迁移到 Responses | 可复用 Codex 验证过的交互模式,包括 WebSocket |
一个务实的策略是「新旧并存」:存量功能继续用 Chat Completions,新开发的 Agent 模块直接上 Responses。两种接口用同一个 Key、同一套计费,在同一个项目里混用完全没有问题。等新模块稳定后,再评估是否把老功能逐步迁过去。
迁移过程中还有两个容易忽略的细节。第一是错误处理:两种接口的错误响应结构基本一致,但 Responses 在流式过程中可能通过事件返回失败状态(例如响应未完成),不能只靠 HTTP 状态码判断成功,要检查最终事件里的状态。第二是日志与计量:如果你的监控系统按 prompt_tokens 和 completion_tokens 统计成本,迁移后要同步改成 input_tokens 和 output_tokens,否则报表会出现断档。Zivv 控制台按 Key、模型和成员统计用量,两种接口的消耗会合并到同一张账单,团队场景可以参考 团队 Key 与预算最佳实践。
迁移清单
- 端点从
/v1/chat/completions换成/v1/responses - messages 改为 input,system 消息移到 instructions
- max_tokens 改为 max_output_tokens,response_format 改为 text.format
- 工具定义改为扁平结构,工具结果改为 function_call_output 输入项
- 解析逻辑从 choices 改为遍历 output,文本可用 output_text
- 流式改为按事件类型处理,以 response.completed 判断结束
- 用量统计字段改为 input_tokens / output_tokens
- 决定状态策略:长对话用 WebSocket 模式 + previous_response_id,HTTP 调用由客户端维护上下文
常见问题 FAQ
Q:Chat Completions 会被废弃吗? A:OpenAI 表示会继续支持 Chat Completions,它仍是生态兼容性最好的接口。新功能会优先在 Responses 上推出,所以新项目更推荐 Responses。
Q:Responses API 比 Chat Completions 更贵吗? A:计费都按输入输出 Token 计算,同一模型单价相同。差异来自用法:推理模型的推理 Token 计入输出,服务端状态模式下历史 Token 依然计入输入。
Q:通过 Zivv 能用 previous_response_id 吗? A:能。WebSocket 模式下由网关在连接内兑现:命中上一轮就自动补全上下文,store: false 也可用,重连后会返回 previous_response_not_found,改发全量即可,Codex 已内置这套回退。普通 HTTP 请求则依赖上游存储,不保证跨请求可用,建议客户端自己维护上下文。
Q:Responses 流式和 Chat Completions 流式能混用解析代码吗? A:不能。前者是带 type 的语义事件,后者是统一结构的 chunk,需要分别实现解析逻辑。
Q:Codex 报错找不到接口或模型怎么办? A:先用上面的 curl 验证 /v1/responses 是否可用,再核对 Base URL 是否带 /v1、模型名是否精确。错误码含义见 404 模型不存在排查。
Q:Zivv 支持 Embeddings 吗? A:Zivv 目前不提供 embeddings 接口,向量化需求请使用其他服务商,对话、推理与工具调用继续走 Zivv。
写在最后
Chat Completions 是「对话接口」,Responses 是「Agent 接口」。日常对话类功能两者都能胜任,但只要涉及多轮工具调用和推理模型,Responses 的输入项模型、语义化流式事件和 WebSocket 模式都会让代码更简单、体验更流畅。用同一个 Zivv Key 就能同时调用两种接口,注册 Zivv 后用一条 curl 就能开始对比。