Responses API 与 Chat Completions:搞懂 Agent 时代的接口分岔

2026 年 2 月起,Codex CLI 彻底不再接受 Chat Completions 格式的请求。后果比表面看起来大:一大批号称"OpenAI 兼容"的第三方模型端点,配上 base_url 之后直接连不上——因为兼容这件事,第一次变成了单向的

过去的规则是"只要实现 Chat Completions,就能接入任何 OpenAI 生态工具"。现在反过来了:OpenAI 自己的 Agent 客户端只认 Responses API,模型厂商不去适配,就进不了这个生态。2026 年 7 月 31 日和 8 月 13 日,DeepSeek 分两步把 Responses API 补上,正是为了跨过这道门槛。

这篇文章解决一件事:搞懂 Responses API 和 Chat Completions 到底差在哪,以及为什么 Agent 时代非换不可。 覆盖接口协议的演进脉络、新 API 的动机、四个维度的核心区别、DeepSeek 适配背后的真实含义,以及一份可执行的迁移判断与操作清单。

本文事实截止时间:2026 年 9 月 16 日。 API 兼容性、模型名、价格变动频繁,尤其"支持 / 不支持"这类字段几乎每个季度都在改,落地前请以官方文档为准。


一、先厘清:接口的"形状"为什么会限制能力上限

先说结论:API 不是一层薄薄的传输壳,它的数据结构会反过来决定你能表达什么。 当模型的输出从"一段文本"变成"一段文本 + 若干次工具调用 + 若干段推理过程",老的数据结构就装不下了。

1.1 一个引子:改一行 base_url 为什么不够

很多人对"OpenAI 兼容"的理解是:只要对方实现了 /v1/chat/completions,把 base_url 一换就能用。

这个理解在 2025 年之前基本成立。但它成立的前提是——调用方和你用的是同一种协议

Codex 不是。Codex 发出的请求体长这样:

{
  "model": "gpt-5-codex",
  "input": [
    { "type": "message", "role": "user", "content": "帮我修一下这个 bug" },
    { "type": "function_call", "call_id": "fc_1", "name": "shell", "arguments": "{\"cmd\":\"ls\"}" },
    { "type": "function_call_output", "call_id": "fc_1", "output": "main.py" }
  ],
  "instructions": "You are a coding agent."
}

它没有 messages 数组,而是一个 input 列表;列表里的元素不是一个"消息",而是一条消息、一次工具调用、一份工具返回结果——各自独立,类型不同。

一个只实现了 Chat Completions 的端点收到这个请求体,看到的是:没有 messages 字段。它无法回应。

1.2 "OpenAI 兼容"到底兼容了什么

把"兼容"这个词拆开,它至少有四层含义,我们平时说的通常只是第一层:

层次兼容的是什么举例
传输层HTTP 端点、鉴权头、状态码Authorization: Bearer sk-xxx
协议层请求 / 响应的数据结构messages[] vs input[]
语义层字段含义、工具调用约定apply_patch 工具的参数格式
行为层状态管理、内置工具、流式事件previous_response_idweb_search

📌 关键点:绝大多数"OpenAI 兼容"端点只做到了前两层。 而 Agent 客户端对协议层和语义层有硬要求,这就解释了为什么"兼容的端点"接不上"兼容的客户端"。

1.3 三代 API 的演进

OpenAI 的推理接口经历过三次形态变化,每一代的出现都对应一类新能力:

端点输入 / 输出回答的问题
第一代/v1/completionsprompttext续写
第二代/v1/chat/completionsmessages[]message对话
第三代/v1/responsesinputoutput[]推理 + 行动

第一代只能"把你写了一半的话补完";第二代引入了 system / user / assistant 三种角色,让模型能带着指令对话;第三代的重点不是"能多聊几句",而是一次请求里可以发生多轮"思考—调用工具—看结果—再思考"

/v1/chat/completions 是 2023 年那个周末临时加急做出来的——它的设计目标是"让 ChatGPT 那套交互可以被 API 复现",跟三年后要跑的 Agent 工作负载完全是两码事。这个出身决定了它后来的三个结构性局限(见第二章)。

1.4 关键时间线

真正把这件事从"OpenAI 的内部选择"变成"整个行业的硬约束"的,是 Codex 移除 Chat Completions 那一步:

时间事件
2025-03-11OpenAI 发布 Responses API 与 Agents SDK,内置 web search / file search / computer use
2025-05-21扩展:远程 MCP、原生图像生成、Code Interpreter、后台模式、加密推理项
2025-08-26Assistants API 宣布弃用,定于 2026-08-26 下线
2025-12-09Codex 团队宣布弃用 chat/completions,2026 年 2 月初完全移除
2026-02Codex 硬移除完成,wire_api 只剩 "responses"
2026-04-24DeepSeek V4(Pro / Flash)发布,走 Chat Completions 与 Anthropic 两套接口
2026-07-31DeepSeek-V4-Flash 正式版原生支持 Responses API,明确标注"为 Codex 适配"
2026-08-13DeepSeek-V4-Pro GA,官方 changelog 写明"原生支持 Responses API"
2026-09-10V4.1-Flash 发布,模型名更新为 deepseek-flash

🔴 重点:OpenAI 一边说"Chat Completions 不会被弃用",一边让自己的旗舰 Agent 客户端先把它删了。 这两句话不矛盾——官方口径是"不弃用,但新项目推荐 Responses";而 Codex 作为内部第一个吃螃蟹的产品,已经没有义务替老协议兜底。

一句话总结:"OpenAI 兼容"平时只指端点长得像,而 Agent 客户端要求的是协议层与语义层的对齐;Codex 移除 Chat Completions 之后,这个差距从"体验问题"变成了"能不能用"的问题。


二、Agent 时代为什么需要一套新 API

先说结论:因为 Chat Completions 的三个设计假设,在 Agent 场景下全部不成立。 这三个假设不是 bug,是它诞生时的时代局限。

2.1 先搞懂:什么是 Agent loop

在动手比 API 之前,得先明确 Agent 到底在做什么。一句话:

Agent 就是"模型自己决定要不要调工具、调哪个、拿到结果后再决定下一步"的循环。

拆成步骤:

1. 收到任务
2. 模型推理 → 判断需要调用工具 A
3. 执行工具 A → 拿到结果
4. 把结果塞回上下文 → 模型再推理 → 判断需要调用工具 B
5. ……(循环 N 次)
6. 判断任务完成 → 输出最终答案

这个循环里有两个东西是"每转一圈都在增长"的:

  • 推理过程(reasoning state):模型在第 2 步想了什么、在第 4 步又想了什么;
  • 工具轨迹(tool trace):调了哪些工具、参数是什么、返回了什么。

Chat Completions 对这两样东西都没有原生的位置。

2.2 概念前置:多轮状态到底该由谁保存

这是理解两套 API 分歧的根子,必须讲清。

LLM API 本质上是无状态的——每次请求都是一次独立的函数调用,模型不记得上次说过什么。所谓"多轮对话",靠的是把历史重新发一遍

那么问题来了:这份历史,由谁保存、由谁拼装?

方案谁保存历史每次请求要发什么
无状态(Chat Completions 默认)客户端完整的历史消息数组
有状态(Responses 默认)服务端只发新增的那一轮 + previous_response_id

无状态方案的好处是简单、可控、易调试——出问题把整个 messages 打印出来就能复现。坏处是:

  • 上下文越长,每次请求要发的 token 越多(虽然可以靠缓存缓解,见[推理篇 03]);
  • 推理过程无处安放。推理模型的思考内容如果每轮都被丢掉,模型等于"每走一步就忘掉自己刚才是怎么想的"。

OpenAI 官方博客里那个侦探的类比很贴切:Responses API 让侦探把笔记本开着,上一轮的推理能带进下一轮;Chat Completions 则是侦探每离开一个房间就把线索忘了。

📌 这不是玄学,有可测的差异。官方公布的内部评测:同样 prompt 与设置下,用 Responses 跑推理模型在 SWE-bench 上有 3% 的提升,TAUBench 上有 5% 的提升;缓存利用率提升 40% 到 80%

2.3 Chat Completions 的三个结构性局限

把上面的分析收拢,具体是这三条:

① 一个 message 想装下所有东西,装不下

Chat Completions 的返回是 choices[0].message,里面把 contenttool_calls 粘在同一个对象里。于是立即出现一个无法回答的问题:

模型说"我准备调用 get_weather 工具"这句话,和那个 tool_calls 字段——到底哪个先发生?

这个顺序在调试和审计时很要命,但数据结构本身没有表达它的能力。Responses 的 output 是一个按发生顺序排列的 Items 数组messagereasoningfunction_call 各自是独立元素:

{
  "output": [
    { "type": "reasoning", "id": "rs_1", "content": [], "summary": [] },
    { "type": "message", "role": "assistant", "content": [
        { "type": "output_text", "text": "我准备用 get_weather 工具查天气。" } ] },
    { "type": "function_call", "id": "fc_1", "call_id": "call_1",
      "name": "get_weather", "arguments": "{\"location\":\"Beijing\"}" }
  ]
}

结构即顺序,不用猜。

② 推理模型的思考过程被丢掉

上一节已经讲过。Chat Completions 每轮都要客户端把历史重发一遍,而推理模型的思考内容没有标准位置可以放回 messages 里,等于被丢弃。

③ 内置工具没有容身之处

Chat Completions 的 tools 字段只接受你自己定义的 JSON schema 函数。想联网搜索?自己写一个函数、自己调搜索引擎、自己把结果塞回去。想跑代码?同上。

Responses 把这些做成了服务端内置工具web_searchfile_searchcode_interpretercomputer_use、远程 mcpimage_generation。一次请求里模型可以连续调用多个内置工具,编排循环由服务端负责。

# Responses:内置工具是一个字段,编排在服务端
response = client.responses.create(
    model="gpt-5",
    tools=[{"type": "web_search"}],
    input="查一下 OpenAI 最新发布的模型,并给出来源链接",
)

2.4 顺带一提:为什么这件事非在 2026 年爆发

三个条件在同一时期凑齐了:

  1. 推理模型成为主力——思考过程变成了必须保留的资产;
  2. 工具调用从 demo 走向生产——一个任务动辄十几次工具调用,客户端手写编排循环不现实;
  3. Agent 客户端成为入口——Codex 这类产品把协议牢牢钉死在 Responses 上。

一句话总结:Chat Completions 假定"一轮 = 一条消息",而 Agent 的一轮里要装下推理、多次工具调用和它们的返回——数据结构装不下,于是状态管理、工具编排、推理保留这三件事全部落到了客户端头上,这正是新 API 要收回的部分。


三、四大核心区别:格式、状态、工具、定位

先说结论:格式和工具是"看得见"的差异,状态和定位是"看不见但更致命"的差异。 前两者迁移时改代码就行,后两者会改变整个应用的架构。

3.1 格式:messages[]items

Chat CompletionsResponses
请求字段messagesinput
系统指令messages 里的 system 角色独立的 instructions 字段
支持裸字符串input="你好"
响应字段choices[0].messageoutput(Items 数组)
一次生成几个n 参数可并行多个已移除 n,只出一份
取文本completion.choices[0].message.contentresponse.output_text
结构化输出response_formattext.format
流式事件通用 delta带类型的事件(见下)

Items 是一个"联合类型"——message(消息)、reasoning(思考)、function_call(工具调用)、function_call_output(工具返回)、web_search_call(联网搜索)……Message 只是 Item 的一种。

同一件事,两套写法对照:

from openai import OpenAI
client = OpenAI()

# Chat Completions:消息数组 in / 消息 out
completion = client.chat.completions.create(
    model="gpt-5",
    messages=[{"role": "user", "content": "用一句话讲个睡前故事"}],
)
print(completion.choices[0].message.content)

# Responses:input in / items out
response = client.responses.create(
    model="gpt-5",
    input="用一句话讲个睡前故事",
)
print(response.output_text)

流式事件的差别值得单独说。Chat Completions 推的是一串 delta,你得自己判断这段增量是正文还是工具参数。Responses 的事件自带类型标签,而且带自增的 sequence_number

stream = client.responses.create(model="gpt-5", input="你好", stream=True)
for event in stream:
    if event.type == "response.output_text.delta":
        print(event.delta, end="")           # 正文增量
    elif event.type == "response.reasoning_text.delta":
        ...                                  # 思考过程增量,可单独渲染
    elif event.type == "response.function_call_arguments.delta":
        ...                                  # 工具参数增量

⚠️ 一个容易踩的坑:Responses 的流没有 data: [DONE] 结束符,它以 response.completed / response.incomplete / response.failed 收尾。照搬老代码里"等 [DONE]"的判断逻辑,会等不到结束。

3.2 状态管理:客户端拼历史 vs 服务端持有

这是两套 API 最本质的分歧。

Chat Completions:无状态。 每一轮你都要把完整历史拼好发过去。

messages = [{"role": "system", "content": "你是助手"}]
messages.append({"role": "user", "content": "北京天气怎么样"})
r1 = client.chat.completions.create(model="gpt-5", messages=messages)
messages.append(r1.choices[0].message)          # ← 手动拼回去
messages.append({"role": "user", "content": "那上海呢"})
r2 = client.chat.completions.create(model="gpt-5", messages=messages)

Responses:默认有状态。 服务端存下 response 对象,下一轮只发新的一句:

r1 = client.responses.create(model="gpt-5", input="北京天气怎么样")
r2 = client.responses.create(
    model="gpt-5",
    previous_response_id=r1.id,                  # ← 服务端接上下文
    input="那上海呢",
)

差异一览:

维度Chat CompletionsResponses
状态归属客户端服务端(默认开启)
多轮方式重发完整 messagesprevious_response_id
关闭存储新账号默认存储,可 store: false默认存储,可 store: false
推理状态轮次之间丢弃跨轮保留
线程容器Conversations API(跨会话、跨设备的容器)
后台任务不支持background: true + webhook 回调
加密推理可选加密推理项,既能跨轮又不落明文

🔴 重点:"服务端有状态"的好处不只是少发几个 token。 它让推理过程、工具轨迹、上下文压缩这些事有了正式的归属方。Conversations 这个容器在多个 Agent 协作同一个对话、或者对话需要跨设备延续时才会显出价值;单 Agent 场景下直接用 previous_response_id 串起来就够了。

⚠️ 副作用也要清楚:默认存储意味着数据留在服务端。 官方明确说明不会用业务数据训练模型,但合规敏感场景仍应显式设 store: false,或者改用加密推理项——加密推理让你"既保留推理能力,又不把明文状态留在服务端"。

3.3 工具:自带 schemas vs 内置工具集

Chat CompletionsResponses
自定义函数tools + JSON schema同样支持
联网搜索❌ 自建web_search
文件检索❌ 自建file_search
代码执行❌ 自建code_interpreter
操作电脑❌ 自建computer_use
远程 MCPmcp
图像生成image_generation
一次请求内多工具连续调用客户端循环服务端负责编排

差别不在于"能不能做",而在于谁来做。自建联网搜索意味着你要处理:搜索引擎选型、结果清洗、引用标注、失败重试、超长结果截断……这些都是与业务无关但必须做对的基础设施。内置工具把这段工程接了过去,代价是把控制权交出去一部分。

3.4 定位:通用对话 vs 推理模型优先

这一条最容易被忽略,但它决定了官方对两套 API 的长期投入方向。

定位维度Chat CompletionsResponses
设计面向对话式交互Agent 循环
官方态度保持支持,不弃用新项目推荐
面向模型通用推理模型优先
与模型能力的耦合

⚠️ 注意第四行。官方迁移文档里已经出现这样的表述:从某个推理模型版本开始,Chat Completions 在特定推理配置下不再支持工具调用。 这是一个信号——Chat Completions 正在从"通用接口"滑向"兼容接口"。

也就是说,两套 API 的差距不会固定,而会随着新模型发布持续拉大。 新能力(更好的推理、更复杂的工具编排、多模态)会优先甚至只在 Responses 上落地。

3.5 汇总对比表

维度Chat CompletionsResponses
端点POST /v1/chat/completionsPOST /v1/responses
请求 / 响应messages[]messageinputoutput[](Items)
系统指令role: systeminstructions
状态无状态,客户端拼历史有状态,store / previous_response_id
推理状态轮次间丢弃跨轮保留
内置工具web search / file search / code interpreter / computer use / MCP / image generation
服务端编排循环
流式通用 delta[DONE] 收尾带类型事件 + sequence_numberresponse.completed 收尾
后台任务不支持background: true
官方推荐保持支持新项目首选

3.6 两个必须澄清的误读

❌ 误读一:Chat Completions 要被弃用了

不对。官方 2025 年 3 月的原话是"Chat Completions 仍是我们采用最广的 API,我们全力支持它继续获得新模型和新能力";迁移文档也写着"Chat Completions 保持支持,但所有新项目推荐 Responses"。被弃用的是 Assistants API(2025-08-26 宣布,2026-08-26 下线),不是 Chat Completions。

两套端点大概率会像 /v1/completions/v1/chat/completions 那样长期共存多年。"能用"和"是未来的方向"是两件事。

❌ 误读二:厂商说"支持 Responses API",就等于拿到了 Responses 的全部能力

这是本文最想强调的一条,第四章会用 DeepSeek 的实例来证明它。

🔴 重点:Responses API 是一份规范,"支持"意味着协议形状对齐,不意味着所有能力都实现了。 尤其是最值钱的那部分——服务端状态管理——恰恰是最容易在实现时省掉的。

一句话总结:格式差异(Items、类型化事件)和工具差异是迁移动机里最直观的部分,状态差异改变的是应用架构,而定位差异决定了这个差距未来只会扩大不会缩小;同时记住 Chat Completions 没有被弃用,"推荐新项目用"和"老项目必须迁"是两句话。


四、DeepSeek 支持 Responses API 的价值与意义

先说结论:这不是"某家国产厂商跟进了一个新接口"这么简单。它标志着一件事——协议的主导权从客户端侧(写兼容层)转移到了服务端侧(改协议),而前者已经走不通了。

4.1 时间线:两步走

DeepSeek 的适配不是一次性完成的,节奏很值得注意:

时间事件支持范围
2026-04-24V4-Pro / V4-Flash 发布只有 Chat Completions + Anthropic 两套接口
2026-07-31V4-Flash 正式版发布Flash 原生支持 Responses API,明确"为 Codex 适配"
2026-08-13V4-Pro GA官方 changelog 写明"原生支持 Responses API"
2026-09-10V4.1-Flash 发布模型名更新为 deepseek-flash,继续支持

中间存在一个多星期的"只有 Flash 能用 Responses"的窗口期——那段时间想在 Codex 里跑 Pro,只能靠第三方网关做协议适配。官方 changelog 对这件事的表述非常直白,Flash 那次更新的第一句就是:

To meet the demand for Codex, our API now supports the Responses API format.
(为了满足 Codex 的需求,我们的 API 现已支持 Responses API 格式。)

📌 这句话是整件事的注脚:驱动厂商改协议的不是"更先进",而是"客户端的硬性要求"。

4.2 真正的转折点:兼容的方向反了

把这件事放进更大的背景里看。

过去十年的兼容逻辑是"客户端向下兼容":新客户端要照顾老服务端,所以自己在本地做协议转换。Codex 早期的做法就是这样——它同时支持 Chat Completions 和 Responses 两套协议,由 wire_api 配置决定用哪个,遇到只支持 Chat Completions 的第三方端点就自动降级。

# Codex 旧版配置:还能选协议
[model_providers.my_provider]
wire_api = "chat"        # 走老协议,兼容第三方端点

2026 年 2 月之后,这条路被堵死了。 Codex 移除 Chat Completions 支持,wire_api 只剩 "responses"。于是:

角色过去要做什么现在要做什么
模型厂商实现 Chat Completions 即可必须实现 Responses API
客户端 / 工具自己做协议适配降级无路可退
使用者base_url看模型厂商跟不跟

这就是为什么 2026 年上半年国内出现了"降级 Codex 才能用第三方模型"这种奇怪操作——模型的 API 明明没问题,只是协议对不上。

🔴 重点:协议兼容的方向一旦反转,就变成了准入资格。 不实现 Responses API 的模型,等于自动放弃整个 Codex 生态。这不再是"体验差一点"的问题。

4.3 但它是"无状态子集"——这点必须说清楚

现在回到第三章埋的那个结论。DeepSeek 实现了 Responses API,但它没有实现 Responses 最值钱的那部分。

官方文档的兼容性表格里写得清清楚楚:

特性支持状态
请求体形状(input / Items / instructions✅ 完全对齐
流式事件(类型化 + sequence_number✅ 对齐
function 工具✅ 支持
custom 工具⚠️ 仅 Codex 需要的 apply_patch
web_search / file_search / code_interpreter / computer_use / mcp❌ 忽略
previous_response_id不支持(无状态 API)
conversation❌ 不支持
store❌ 不支持,store 恒为 false
background / metadata / include / prompt❌ 不支持
truncation❌ 不支持,超上下文直接返回 400
service_tier / context_management / stream_options❌ 不支持

能用的部分里,最核心的一句是文档的这句自述:

The API is stateless: responses and conversations are not stored on the server. For multi-turn conversations, the client needs to send the full conversation history in input on each request.
(该 API 是无状态的:响应与对话不存于服务端。多轮对话时,客户端需要在每次请求的 input 中发送完整历史。)

也就是说:DeepSeek 的 Responses API 拿到了新的"形状",但没拿到新的"状态管理"。

这带来一个非常实际的后果:

# 这是 OpenAI Responses 的标准写法
r2 = client.responses.create(
    model="gpt-5",
    previous_response_id=r1.id,        # DeepSeek 上这行没意义
    input="那上海呢",
)

# 在 DeepSeek 上,你仍然要自己拼完整历史
r2 = client.responses.create(
    model="deepseek-flash",
    input=items + [{"role": "user", "content": "那上海呢"}],
)

⚠️ 但有个缓冲设计值得一提:DeepSeek 对不支持的参数是"静默忽略"而不是报错。

Unsupported parameters are silently ignored and do not cause errors, so existing Responses API clients can connect without modification.

这个选择很务实——让现有 Responses 客户端改一行 base_url 就能跑,不必为字段报错而改代码。但它同时是个坑:你写了 previous_response_id,请求成功了,响应也回来了,只是这个字段被无声地丢掉了。 如果代码逻辑依赖服务端记住上下文,你会在多轮之后才发现问题。

4.4 那为什么只做子集也够了

因为 Codex 实际需要的东西,恰好就是这个子集。 从 DeepSeek 的文档看,Codex 场景下的要求是:

  • 按 Responses 格式接收工具定义和执行结果;
  • 返回函数调用或补丁内容 —— 这就是 custom: apply_patch 存在的唯一理由;
  • 支持流式 SSE 事件;
  • 支持思考强度设置(reasoning.effort)。

Codex 作为客户端,自己就是那个保存状态的人——它的会话历史存在本地(~/.codex/),每轮本来就把完整上下文发上去。所以 DeepSeek 不支持 previous_response_id,对 Codex 的实际体验没有影响。

🔴 重点:这里藏着一个选型陷阱。 “能不能被 Codex 用"和"能不能享受 Responses 架构红利”,是两个完全不同的问题。前者只需要协议形状对齐,后者要求服务端真的持有状态。厂商的宣传语通常把这两件事合并成一句"原生支持 Responses API"。

判断方法很简单——去看兼容性表格里 previous_response_idconversationstore 三行的状态。 这三行是"形状支持"和"能力支持"的分水岭。

4.5 云厂商补位:托管版把内置工具补齐了

同一个模型,走不同的托管端点,能力可以完全不同。以阿里云百炼托管的 DeepSeek 为例,它的 OpenAI 兼容 Responses API 在 tools 里是可以挂 web_search(联网搜索)、web_extractor(网页抓取)、code_interpreter(代码解释器)这些内置工具的。

接入方式协议形状服务端状态内置工具
DeepSeek 官方 API❌ 无状态❌ 仅 function
云厂商托管(如百炼)视实现✅ 部分补齐

这说明"支持 Responses API"这件事本身也不是二值的:协议对齐是及格线,能力补齐是加分项,同一个模型在不同端点上的表现可以差出一整层。

(具体支持范围与可用地域以各厂商官方文档为准,这类字段变动很频繁。)

4.6 对行业的连锁影响

把视角拉到整个国内模型生态,这件事至少意味着三件事:

  1. Responses API 从"OpenAI 的内部选择"变成了行业事实标准。 一家国产厂商为了接入一个客户端而专门实现它,比任何官方宣传都更能说明它的地位。
  2. 协议支持成了模型竞争力的一个维度。 在 Agent 编程这个高价值场景里,"能不能被 Codex 用"直接影响开发者是否选择你。
  3. 但"协议兼容"会带来新的割裂。 每个厂商都实现一份"Responses 子集",字段支持程度各不相同。开发者仍要面对"这家支持 store,那家不支持"这种碎片化——只是碎片化从"端点形状"下移到了"能力字段"。

一句话总结:DeepSeek 适配的是"被 Codex 接纳"的资格,而不是 Responses 的状态管理红利;"原生支持"是协议形状的对齐,判断真实能力要看 previous_response_id / conversation / store 是否被实现。


五、迁移建议:面向 Agent 开发者

先说结论:不要因为"新"就迁,也不要因为"还能用"就不迁。判断标准只有一条——你的应用是否需要服务端状态和内置工具。

5.1 先判断:你属于哪一类

你的场景建议理由
纯对话、历史完全自管留在 Chat Completions无状态反而更可控
简单 RAG,索引自建留在 Chat Completions内置 file search 用不上
接 ModelScope / 第三方兼容端点留在 Chat Completions对方大概率只有这一套
多步 Agent,带工具循环迁到 Responses服务端编排省掉大量胶水代码
用推理模型,且要跨轮保留思考迁到 Responses这是 Chat Completions 结构性做不到的
接 Codex / 类似的 Responses-only 客户端必须迁(或让端点适配)没有替代方案
长时任务,不想轮询迁到 Responsesbackground: true + webhook

一句话的决策规则:

如果你的代码里有"手动把模型输出拼回 messages 再发一次"的循环,那就是该迁的信号。

5.2 实操:把 DeepSeek 接进 Codex

这是当前最典型的需求,官方给了一键脚本,也给了手写配置的方式。手写版本更值得看,因为它把机制暴露得很清楚。

第一步:准备模型目录文件 ~/.codex/models.json

这个文件声明模型的元信息(上下文窗口、支持的思考强度、工具调用格式),让 Codex 把第三方模型当作内置模型对待。

第二步:改 ~/.codex/config.toml

model = "deepseek-flash"
model_provider = "deepseek"
preferred_auth_method = "apikey"
forced_login_method = "api"
model_reasoning_effort = "high"
web_search = "disabled"
model_catalog_json = "~/.codex/models.json"

[model_providers.deepseek]
name = "deepseek"
base_url = "https://api.deepseek.com/"
wire_api = "responses"                                  # ← 关键:协议选 responses
experimental_bearer_token = "<your DeepSeek API Key>"

几个字段的作用:

字段作用
wire_api走哪套协议,"responses" 即 Responses API
model_provider指向下面的 [model_providers.deepseek]
model_reasoning_effort思考强度:low / high / max,越高越慢但越准
web_search置为 disabled,因为 DeepSeek 当前不提供该内置工具
model_catalog_json模型元信息文件路径
preferred_auth_method / forced_login_method用 API Key 鉴权,跳过 ChatGPT 账号登录

或者直接用官方一键脚本(会先备份原配置再写入,并校验语法):

# macOS / Linux —— 脚本会提示选择 deepseek-flash 或 deepseek-v4-pro
bash <(curl -fsSL https://cdn.deepseek.com/api-docs/codex-deepseek-setup-en.sh)
# Windows PowerShell
irm https://cdn.deepseek.com/api-docs/codex-deepseek-setup-en.ps1 | iex

✅ 成功标志:进入项目目录执行 codex,启动横幅里显示 model: deepseek-flash。Codex CLI、ChatGPT 桌面端、VS Code 扩展共用同一份配置,配一次三处生效。

⚠️ 一个容易误判的现象:切换供应商后,之前的会话看起来"消失了"。 这是正常行为——Codex 按登录方式分组存放会话历史,ChatGPT 订阅的会话和第三方 API 的会话分开保存,当前配置只显示对应那一组。历史没有丢,切回去就能看到。

5.3 三条避坑

① 别把 previous_response_id 当成"服务端会记住"

在无状态实现上,这个字段被静默忽略。多轮对话仍然要自己拼完整 input。写代码时最好显式包一层,把"状态由谁管"变成代码里看得见的东西:

class Conversation:
    """统一多轮状态管理:有状态服务端用 previous_response_id,无状态端点自动拼历史。"""
    def __init__(self, client, model, supports_previous_id: bool):
        self.client, self.model = client, model
        self.supports_previous_id = supports_previous_id
        self.items, self.last_id = [], None

    def ask(self, text: str) -> str:
        if self.supports_previous_id:
            resp = self.client.responses.create(
                model=self.model, input=text, previous_response_id=self.last_id,
            )
        else:
            # 无状态:显式带上全部历史
            resp = self.client.responses.create(
                model=self.model, input=self.items + [{"role": "user", "content": text}],
            )
        self.last_id = resp.id
        self.items.append({"role": "user", "content": text})
        self.items.append({"role": "assistant", "content": resp.output_text})
        return resp.output_text

② 注意 truncation 不支持,超长会直接 400

OpenAI 的 Responses 支持 truncation,超上下文时自动截断。DeepSeek 明确不支持——超出上下文窗口的请求直接返回 400 错误。如果你的 Agent 跑长任务,必须自己做上下文裁剪或摘要,不能指望服务端兜底。

③ 别依赖 [DONE] 结束符

Responses 的流式以 response.completed / response.incomplete / response.failed 收尾,没有 data: [DONE]。老代码里的等待逻辑要一起改掉,否则会挂在流上等一个永远不来的信号。

一句话总结:判断标准是"是否需要服务端状态和内置工具",不是新旧;迁移时重点验证三处——多轮状态由谁持有、超长上下文怎么处理、流式怎么判断结束。


六、总结:你真正需要记住的 9 件事

  1. API 的数据结构会决定能力上限。 当 Agent 的一轮里要装下推理、多次工具调用和返回,"一条消息"这个结构就不够用了。
  2. "OpenAI 兼容"通常只做到了端点和协议两层。 Agent 客户端要求的是语义层和行为层的对齐。
  3. Codex 在 2026 年 2 月彻底移除 Chat Completions,协议兼容的方向因此反转——从"客户端向下适配"变成"模型厂商必须支持 Responses"。
  4. Chat Completions 没有被弃用。 被弃用的是 Assistants API(2026-08-26 下线)。官方口径是"保持支持,但新项目推荐 Responses"。
  5. 四个维度的核心区别:格式(messages[] vs Items)、状态(客户端拼历史 vs 服务端持有)、工具(自建 schema vs 内置工具集)、定位(通用对话 vs 推理模型优先)。
  6. 状态差异比格式差异更致命。 它决定推理过程能否跨轮保留、工具轨迹归谁管、上下文压缩由谁做。
  7. “支持 Responses API"不等于"拿到 Responses 的能力”。 DeepSeek 的实现是无状态子集:协议形状对齐,但 previous_response_id / conversation / store 全部不支持。
  8. 判断真实能力的三个字段previous_response_idconversationstore。这三行是"形状支持"与"能力支持"的分水岭。
  9. 迁移判断只看一条:你的代码里有没有"手动把模型输出拼回 messages 再发一次"的循环。有,就该迁。

验证清单

  • 能说清 Chat Completions 的三个结构性局限,以及哪个是"结构上做不到"而非"实现不好"
  • 能解释"OpenAI 兼容"的四层含义,并判断手上的第三方端点做到了哪一层
  • 打开要接入的模型厂商文档,检查它的 Responses 兼容表里 previous_response_id / conversation / store 三行的状态
  • 确认现有代码里多轮历史是"客户端拼"还是"服务端持有",不要依赖静默忽略的字段
  • 确认流式处理逻辑没有依赖 data: [DONE] 结束符
  • 长任务链路里加上自己的上下文裁剪,不要指望 truncation
  • 若要接 Codex,核对 ~/.codex/config.tomlwire_api = "responses" 已生效,启动横幅显示正确的模型名
  • 合规敏感场景确认 store 字段的实际行为(是否被支持、是否恒为 false

参考资源

  • OpenAI《Why we built the Responses API》:https://developers.openai.com/blog/responses-api
  • OpenAI 迁移指南《Migrate to the Responses API》:https://platform.openai.com/docs/guides/responses-vs-chat-completions
  • OpenAI《New tools for building agents》(2025-03-11 发布公告):https://openai.com/index/new-tools-for-building-agents/
  • OpenAI《New tools and features in the Responses API》:https://openai.com/index/new-tools-and-features-in-the-responses-api
  • Codex 弃用 chat/completions 讨论:https://github.com/openai/codex/discussions/7782
  • DeepSeek《Using the Responses API》(含完整兼容性表格,本文 4.3 节数据来源):https://api-docs.deepseek.com/guides/responses_api/
  • DeepSeek《Integrate with Codex》(含 config.toml 字段参考与一键脚本):https://api-docs.deepseek.com/quick_start/agent_integrations/codex
  • DeepSeek API Change Log(本文时间线来源):https://api-docs.deepseek.com/updates
  • DeepSeek 模型与定价(Responses / Anthropic API 支持对照):https://api-docs.deepseek.com/quick_start/pricing
  • 缓存命中与上下文缓存机制,见本系列《推理篇 03:KV Cache 与缓存命中》
  • 显存与推理引擎优化,见本系列《推理篇 02:vLLM 部署与推理优化实战》

标签

#Responses API #Chat Completions #Codex #DeepSeek #Agent开发 #API协议 #大模型应用开发

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐