研究了 OpenAI 新 API 三天,发现 90% 的开发者都理解错了
OpenAI Responses API 是什么?
从"传纸条"到"打电话"——大模型 API 的一场底层升级,以及它背后的商业博弈
一、核心比喻:从"传纸条"到"打电话"
想象你要和一个超级聪明的助手(AI 模型)合作完成一项任务。
旧方式(Chat Completions API)= 传纸条
你每问一个问题,都要把从头到尾所有的聊天记录写在一张新纸条上递给助手:
- 第 1 次纸条:“你好。”
- 第 2 次纸条:“你好。帮我查一下天气。”
- 第 3 次纸条:“你好。帮我查天气。好的,北京晴天。那上海呢?”
麻烦在哪:
- 累——每次都要抄写所有历史,纸条越来越长
- 你自己管上下文——必须自己记住聊到哪了,确保纸条没漏
- 工具调用像接力赛——让助手"查天气再推荐衣服",你得自己编排:发查天气纸条 → 收结果 → 再发推荐衣服纸条
新方式(Responses API)= 打电话
你给助手打了个电话,建立了一条专线,对话是连续的:
- 你:“你好。”
- 助手:“你好。”
- 你:“帮我查一下北京天气。”(助手自动去查)
- 助手:“北京今天晴天。”
- 你:“那上海呢?”(助手自动理解你在接着问)
方便在哪:
- 轻松——不用每次重复历史,只需记住一个"通话 ID"(
previous_response_id) - API 自动管上下文——叫"有状态"
- 工具调用像吩咐下属——一句话"查天气再推荐衣服",助手自己内部完成整套流程,结果一次性给你
一句话总结
Chat Completions 是"无状态"的——每次请求独立,上下文全靠你维护。
Responses API 是"有状态"的——服务端帮你记住对话,你只需给个 ID。
二、关键变化对比
| 特性 | 旧方式(Chat Completions) | 新方式(Responses API) |
|---|---|---|
| 核心概念 | 消息(Messages) | 响应(Response) |
| 上下文管理 | 你自己负责,每次手动拼接历史 | API 自动负责,通过 previous_response_id 关联 |
| 工具调用 | 手动循环,你写代码处理每一步 | 内建能力,模型自主完成工具调用链 |
| 类比 | 传纸条 | 打电话 |
三、四个核心问题拆解
问题 1:数据都存在服务器上,隐私安全怎么办?
是的,但这是"临时会话缓存",不是永久存储。
- API 会在服务器上保留你的对话状态一段时间(几分钟到几小时),让你能通过 ID 继续对话
- 不等于拿去训练模型——企业级 API 通常承诺不用 API 数据改进模型
- 风险仍在——任何数据离开设备都有理论泄露风险
建议:
- 高度敏感信息(医疗记录、商业机密)→ 不要在 API 调用中明文传输
- 普通业务场景 → OpenAI 的安全措施足够
问题 2:服务端管上下文,能省 Token 吗?
能,而且是大头。
- 旧方式:第 10 次请求要把前 9 轮全部内容作为 messages 发送,Token 消耗巨大
- 新方式:只需发最新消息 +
previous_response_id,服务端自动拼接
打个比方:
- 旧方式 = 每次写信都把以前所有信复印一份一起寄
- 新方式 = 只写新信,信封上写"请接续我上次的信(编号:12345)",邮局自动把两封信放一起处理
注意:服务端推理时仍然要把完整上下文送进模型,所以服务端成本没降,降的是你的传输量和客户端拼装复杂度。
问题 3:previous_response_id 是全局唯一还是每次不同?
每次响应都不同,形成链式结构。
- 第 1 次请求(无 ID)→ 返回
resp_A - 第 2 次请求(带
resp_A)→ 返回resp_B - 第 3 次请求(带
resp_B)→ 返回resp_C
形成链条:resp_A → resp_B → resp_C → ...
它不是固定不变的"房间号",而是指向上一封回信的引用指针,每一轮都产生新的唯一指针。
问题 4:能不能修改历史?
不能直接改,但有三招"曲线救国":
方法 1:伪造分支历史(最常用)
自己本地维护消息列表,修改后不用 previous_response_id,把修改后的完整列表作为初始上下文发新请求,产生全新对话链。
- ✅ 完全控制权
- ❌ 回到手动管理,失去省 Token 优势
方法 2:系统指令覆盖
每次请求注入强力指令:“忽略我之前关于 X 的说法,现在 Y 才对。”
- ✅ 简单快捷
- ❌ 不算真修改,只是覆盖模型理解
方法 3:等官方功能
未来 API 可能提供更精细的会话编辑功能。
四、各厂商支持情况(2026 年中视角)
第一梯队:原生支持 OpenAI Responses API
| 厂商 | 端点 | 代表模型 | 状态管理能力 |
|---|---|---|---|
| OpenAI 官方 | /v1/responses |
GPT-5 系列、o3/o4-mini | 原生全量支持 |
| 阿里云百炼 | /compatible-mode/v1/responses |
Qwen3-Max/Plus/Flash | 服务端存 7 天,真支持 |
| 百度千帆 | /v2/responses |
DeepSeek-V4、GLM-5、Qwen3 | 有 store 机制 |
| 火山方舟 | /responses(新版默认) |
豆包大模型 | store 可控,部分小模型不支持 |
| 腾讯云 TokenHub | Responses 兼容模式 | GLM-5.2、Kimi K2.7、DeepSeek-V4 | 部分受限,非全量原生 |
第二梯队:协议转换层"伪支持"
- LiteLLM、Vercel AI Gateway、NovAI 等把
/v1/responses翻译成/v1/chat/completions - ❌
previous_response_id在服务端不生效 - ❌ 内置工具不会真触发
- ✅ 形状看起来像 Responses
第三梯队:自己推一套等价协议
| 厂商 | 协议 | 特点 |
|---|---|---|
| Anthropic(Claude) | Messages API + Managed Agents | 无状态 + cache_control 前缀缓存,不走 previous_response_id |
| Google(Gemini) | Interactions API | previous_interaction_id,状态留 55 天(付费)/ 1 天(免费) |
快速判断"真支持"的方法
厂商文档写"OpenAI Responses 兼容"≠ 原生支持。要看有没有明说 previous_response_id 服务端有效期——百炼写 7 天、千帆写 store 机制,才是真的。
五、为什么很多厂商不跟 Responses?
误解澄清:不是"为了省硬件"
- Responses API 比 Chat Completions 慢 2-3 倍
- 相同对话长度下 Token 账单几乎没降,服务端还要多做状态重建
- 真正省 Token 的是 Prompt Cache 命中,跟"谁管状态"无关
结论:支持 Responses 服务端成本反而上升,厂商没有"省硬件"的动机。
真正原因有三:
原因 1:Chat Completions 已是事实标准,换轨成本极高
Chat Completions 是无状态 RPC,任何有推理能力的团队周末就能对齐。Responses 要求服务端有:
- 响应对象生命周期管理(ID 生成、TTL、压缩)
- 内置工具运行时(web_search / code_interpreter / 沙箱)
- 多步 Agent Loop 编排
这不是改个 endpoint,是把"应用层 Agent 框架"下沉到基础设施。
原因 2:各家在推自己的"Responses"
| 厂商 | 自己的有状态方案 | 为什么不兼容 OpenAI |
|---|---|---|
| Anthropic | Managed Agents + cache_control | schema 私有命名,跟了 = 承认 OpenAI 是标准制定者 |
| Interactions API | 自家推理链表示法(thoughts block),硬塞会丢语义 |
原因 3:锁定与反锁定(最深层)
Responses 把三样东西绑死在服务端:
- 对话状态(
previous_response_id) - 工具执行(官方沙箱)
- 推理链(加密存服务端,客户端看不到)
用得越深,迁移成本越高。兼容 Responses = 帮 OpenAI 修护城河。
六、Open Responses:开源社区的"开放标准"反击
是什么?
2026 年 1 月正式亮相的 Open Responses,是把 OpenAI Responses 形状抽出来的开放规范:
- 发起:OpenAI 捐出 2025-03 Responses API 形状做底子
- 共建:Hugging Face 主导,OpenRouter、Vercel、LM Studio、Ollama、vLLM 等跟进
- 治理:openresponses.org + GitHub
openresponses/openresponses,带合规测试工具
和 OpenAI 闭源版的关键区别
| 维度 | OpenAI 原生 Responses | Open Responses |
|---|---|---|
| 状态管理 | 强制服务端托管 | 默认无状态,可选有状态 |
| 推理链 | 只吐 summary + 加密内容 | 三级:原始思维链 / 加密 / summary |
| 版本机制 | 隐式 | 请求头 OpenResponses-Version: latest |
| 内置工具 | 必须用官方沙箱 | 区分内部工具(沙箱)和外部工具(MCP) |
和 MCP 的关系:各管一段,不抢地盘
- MCP(Model Context Protocol)= 工具连接标准,无状态,用
handle参数化 - Open Responses = Agent 推理/多轮调度标准,可无状态可可选有状态
两者叠加:
- 本地跑 Ollama/LM Studio → 无状态 Open Responses + MCP 工具
- 云端百炼/OpenAI → 有状态 + 内置沙箱工具
进度(2026-07)
- ✅ HF 空间已路由 Kimi/Qwen 通过 Open Responses
- ✅ LM Studio 0.3.39+ 本地
/v1/responses兼容 - ✅ Ollama 近期构建支持
- ❌ DeepSeek 官方仍只给 Chat Completions
- ❌ Anthropic 不实现 Responses 形状
- ⚠️ 治理弱于 HTTP——没有 IETF 级中立组织背书
七、实战代码示例:同一段代码在三个模型间零改动切换
场景设定
假设我们要做一个"旅行助手 Agent":
- 先问用户想去哪
- 让模型自己调用天气查询工具
- 根据天气推荐穿搭
- 多轮对话持续追问
我们用 Open Responses 网关(如 Hugging Face 的 evalstate 路由),在 GPT-5.1 / Qwen3-Max / Kimi-K2 之间切换——只改一行 model 名称,其余代码完全不动。
完整 Python 示例
"""
Open Responses 跨模型零改动切换示例
====================================
只需改 model 字段,同一段代码在 GPT-5.1 / Qwen3-Max / Kimi-K2 间切换
"""
import httpx
import json
# ============================================================
# 只需改这里 —— 切换模型只换一行
# ============================================================
# MODEL = "gpt-5.1" # OpenAI 官方
# MODEL = "qwen3-max" # 阿里百炼(通过 Open Responses 网关)
MODEL = "kimi-k2" # Moonshot Kimi(通过 Open Responses 网关)
# 网关端点(统一走 Open Responses 协议)
ENDPOINT = "https://evalstate-openresponses.hf.space/v1/responses"
API_KEY = "your-api-key"
# ============================================================
# 第 1 轮:发起对话 + 定义工具
# ============================================================
request_1 = {
"model": MODEL,
"instructions": "你是一个旅行助手,需要查询天气后给用户推荐穿搭。",
"input": "我明天要去北京出差,带什么衣服合适?",
"tools": [
{
"type": "function",
"name": "get_weather",
"description": "查询指定城市天气",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "城市名"}
},
"required": ["city"]
}
}
],
# Open Responses 特有:控制推理深度
"reasoning": {"effort": "medium"},
# 版本声明(Open Responses 开放规范头)
"headers": {
"OpenResponses-Version": "latest"
}
}
resp_1 = httpx.post(
ENDPOINT,
headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
json=request_1,
timeout=60
).json()
print("=== 第 1 轮响应 ===")
print(f"Response ID: {resp_1['id']}")
print(f"输出: {json.dumps(resp_1['output'], ensure_ascii=False, indent=2)}")
# 保存 ID 用于续接
prev_id = resp_1["id"]
# ============================================================
# 第 2 轮:续接对话(只发新消息 + previous_response_id)
# ============================================================
request_2 = {
"model": MODEL,
"input": "那如果改去广州呢?",
"previous_response_id": prev_id, # ← 关键:链式续接
"tools": [{"type": "function", "name": "get_weather", ...}] # 同上
}
resp_2 = httpx.post(ENDPOINT, ...).json()
prev_id = resp_2["id"]
# ============================================================
# 第 3 轮:用户追问细节
# ============================================================
request_3 = {
"model": MODEL,
"input": "广州有什么推荐的餐厅吗?",
"previous_response_id": prev_id,
"tools": [{"type": "function", "name": "search_restaurants", ...}]
}
如果换成 Chat Completions,同样的功能要写多少?
"""
同样的功能,用 Chat Completions 要这么写(伪代码对比)
==================================================
"""
messages = [
{"role": "system", "content": "你是一个旅行助手..."},
{"role": "user", "content": "我明天要去北京出差,带什么衣服合适?"}
]
# 第 1 轮
resp = openai.chat.completions.create(model="gpt-5.1", messages=messages, tools=[...])
messages.append(resp.choices[0].message) # 手动追加助手回复
# 如果模型要调工具 → 你得自己检测 tool_call → 自己执行 → 自己把结果塞回 messages
if resp.choices[0].message.tool_calls:
for tc in resp.choices[0].message.tool_calls:
result = execute_tool(tc.function.name, tc.function.arguments) # 你自己写
messages.append({"role": "tool", "content": result, "tool_call_id": tc.id})
# 再发一次请求拿最终回复
resp = openai.chat.completions.create(model="gpt-5.1", messages=messages)
# 第 2 轮 → 你要把整个 messages 数组(含所有历史)再发一遍
messages.append({"role": "user", "content": "那如果改去广州呢?"})
resp = openai.chat.completions.create(model="gpt-5.1", messages=messages) # 全量重传!
# 第 3 轮 → 再全量重传...messages 越来越长,Token 越来越多
两种写法差距一目了然
| 维度 | Chat Completions | Open Responses |
|---|---|---|
| 第 3 轮发送的数据量 | 全量 messages(含前 2 轮全部内容) | 1 条新消息 + 1 个 ID 字符串 |
| 工具调用编排 | 你写 while 循环 + if 判断 | API 内部自动完成 |
| 切换模型 | 改 model 名 + 祈祷格式兼容 | 改 model 名,协议统一 |
| 历史修改 | 直接改 messages 数组 | 需伪造分支(见第三节方法 1) |
| 推理链可见 | 无(模型直接给结果) | 可选 reasoning.content 看思维链 |
哪些字段在闭源 Responses 里会被拒?
| 字段 | OpenAI 闭源 Responses | Open Responses |
|---|---|---|
reasoning.content(原始思维链) |
❌ 返回 encrypted_content,你解不了 |
✅ 开源模型可直接吐原始推理过程 |
vendor:xxx 扩展前缀 |
❌ 未知字段直接 400 报错 | ✅ 规范允许厂商自定义扩展 |
store: false(关闭服务端存储) |
⚠️ 部分支持,但默认强制存 | ✅ 默认无状态,store 可选开关 |
previous_response_id 跨模型续接 |
❌ 只能在同模型内续接 | ✅ 理论上可跨模型(同网关内) |
| MCP 外部工具引用 | ❌ 只认官方内置工具 | ✅ 明确区分内部/外部工具 |
最典型的踩坑场景:
# 你想看 Kimi-K2 的推理过程
request = {
"model": "kimi-k2",
"input": "9.11 和 9.9 哪个大?请逐步推理。",
"reasoning": {"content": "full"} # ← 要原始思维链
}
# 走 OpenAI 官方端点 → 400 错误:"reasoning.content is not supported"
# 走 Open Responses 网关 → 正常返回,output 里带完整推理步骤
八、与 HTTP/JS 的类比总结
你之前的比喻非常精准,下面是精确对应:
| 你的比喻 | LLM API 圈对应 |
|---|---|
| HTTP 1.1 长期不换 | Chat Completions 仍是事实标准,Open Responses 不废它,并列存在 |
| JS 原始版 → TS 是用户多了才推 | OpenAI Responses(闭源)→ Open Responses(开源扩展),用的人多了才由社区抽规范 |
| 不强制升级 | Open Responses 头里 Version: latest,老字段保留,新增字段带 vendor:xxx 前缀 |
| 微软推 TS ≠ 官方标准 | OpenAI 推闭源 Responses,HF+社区推 Open Responses 做"中立版" |
最大区别: HTTP 有 IETF 中立组织管,Open Responses 还挂在社区治理,离真正中立差一步。
九、选型决策树
你的需求是什么?
│
├─ 跨模型可移植 + 自己控状态
│ → Chat Completions + 客户端拼历史 + Prompt Cache
│
├─ 只钉 OpenAI / 百炼 Qwen3 + 要用内置工具
│ → Responses API(闭源版)
│
├─ Claude 长程 Agent
│ → Messages API + cache_control + 自建或 Managed Agents
│
├─ Gemini 多步研究
│ → Interactions API
│
└─ 开源模型本地跑 + 想要 Responses 形状
→ Open Responses(LM Studio / Ollama / vLLM)
十、核心要点速记
- Responses API = 有状态 API,用
previous_response_id链式关联对话 - 省的是传输 Token,不是推理 Token——服务端成本反而上升
- 数据存服务端是临时缓存,非永久存储,但敏感数据仍建议不明文传
- 不能直接改历史,但可伪造分支 / 指令覆盖
- 大多数厂商不跟不是省硬件,是商业博弈——拒绝被 OpenAI 锁定
- Open Responses 是社区版的开放标准,默认无状态,兼容 MCP,2026 年正在铺开
- Chat Completions 短期内不会死——它是事实标准,就像 HTTP 1.1 一直活着
- 代码层面:改一行 model 名就能跨模型切换,是 Open Responses 最实用的价值
更多推荐


所有评论(0)