打开项目一搜chat.completions.create还在到处冒头——这不是少数团队的问题而是过去两年几乎所有接 OpenAI 兼容接口的项目的共同遗留。麻烦在于今天这行代码已经不只是“还能跑就先别动”的级别了。OpenAI 已经把开发重心明确切到 Responses APICodex 侧甚至已经移除了chat/completions支持阿里百炼也在 6 月更新了 OpenAI 兼容的 Responses API 路线。你现在面对的不是一个“新接口要不要试试”的问题而是你的集成还要不要继续押注旧范式。我最近把几套已经在线的调用链重新过了一遍结论很明确Responses API 值得迁但它绝对不是“把/chat/completions改成/responses”这么简单。它改的是模型交互的抽象层——从 message-centered 变成 item-centered从“你发一组 messages我给你一条 message”变成“你提交一段输入序列我返回一组类型化的行为结果”。这意味着收益是真的成本也是真的。如果你用的是 OpenAI 的推理模型官方给过一组很硬的内部评估数据同样的 prompt 和 setup 下Responses API 在 SWE-bench 上比 Chat Completions 高 3%同时缓存利用率在内部测试里比 Chat Completions 提升了 40% 到 80%对应的是更低成本。这不是文案修辞是 OpenAI 自己写进迁移指南里的数字。但另一方面只要你的项目里有多轮对话、函数调用、结构化输出、流式响应、网关转发迁移就不再是“一小时收工”。你得改 schema得改状态管理很多第三方兼容服务还得额外踩坑。这篇文章我不讲空话只回答三个现实问题你到底该不该迁真迁的话代码要改到什么程度如果你走的是第三方模型或网关层应该怎么落地。Chat Completions vs Responses不是改个 URL 就行很多人第一次看 Responses API会下意识把它理解成“新版本的 completions”。这个理解太轻了。两者最本质的区别不在字段名字而在交互模型。Chat Completions 的核心单位是message。你维护一个messages[]数组用户消息、系统提示、工具结果全都塞进这个线性历史里。模型返回的核心结果通常是choices[0].message流式时你再去拼delta。Responses API 不一样。根据 OpenAI 官方迁移文档和社区拆解它是item-centered的输入不再强制要求 message 列表输出也不是“一条 message”而是一组带类型的output[] items。文本、工具调用、推理痕迹、状态引用都以更明确的 item 形式存在。这个变化听起来像设计洁癖实际上影响非常大。你以前把一切都看成“聊天记录”现在你得把模型交互理解成“一个由多种 typed items 组成的行为序列”。对于只发一句话拿一句回答的 demo它没区别。对真实系统它会渗透到解析逻辑、状态存储、工具编排、网关协议适配的每一层。先看一个最直观的对比表维度Chat CompletionsResponses API迁移影响核心抽象message-centereditem-centered需要重想输入输出结构输入字段messages[]inputstring 或 items简单场景可平移复杂场景要重构输出字段choices[].messageoutput[]读取结果方式必须改多轮状态客户端自己拼历史可用previous_response_id引用服务端状态对话管理逻辑可能大改Function calling外层function包裹内层直接声明type/name/parametersschema 不兼容必须改Structured Outputsresponse_formattext.format必须改流式delta片段流命名 SSE events消费流的代码必须改推理模型收益无官方迁移收益说明OpenAI 声称 SWE-bench 3%推理场景更值得迁缓存利用率基线内测提升 40%-80%高并发下有真实成本收益如果你只想知道“最少能不能跑起来”答案是可以。基础调用确实不难。下面直接看真实代码差异。Python最小可用迁移 diff先看最常见的 Python 场景。Chat Completions 版本fromopenaiimportOpenAI# 初始化客户端clientOpenAI(api_keyYOUR_API_KEY)# 调用 Chat Completionsrespclient.chat.completions.create(modelgpt-4.1-mini,messages[{role:system,content:你是一个严谨的技术助手},{role:user,content:用一句话解释 Responses API}],temperature0.2,)# 读取文本结果textresp.choices[0].message.contentprint(text)Responses API 版本fromopenaiimportOpenAI# 初始化客户端clientOpenAI(api_keyYOUR_API_KEY)# 调用 Responses APIrespclient.responses.create(modelgpt-4.1-mini,instructions你是一个严谨的技术助手,input用一句话解释 Responses API,temperature0.2,)# SDK 一般会提供聚合后的输出文本print(resp.output_text)对应的 diff 长这样- resp client.chat.completions.create( resp client.responses.create( modelgpt-4.1-mini, - messages[ - {role: system, content: 你是一个严谨的技术助手}, - {role: user, content: 用一句话解释 Responses API} - ], instructions你是一个严谨的技术助手, input用一句话解释 Responses API, temperature0.2, ) - text resp.choices[0].message.content text resp.output_text如果你的调用停留在这个复杂度那我可以很负责任地说迁移成本低得惊人。改 SDK 入口、改输入字段、改输出读取方式——5 分钟级别。问题是大多数线上项目都不只这点复杂度。Node.js同样能跑但别被表面简单骗了Node.js 侧也是一样。Chat Completions:importOpenAIfromopenai;// 初始化客户端constclientnewOpenAI({apiKey:process.env.OPENAI_API_KEY});asyncfunctionmain(){constrespawaitclient.chat.completions.create({model:gpt-4.1-mini,messages:[{role:system,content:你是一个后端架构顾问},{role:user,content:解释一下 item-centered 是什么意思}],temperature:0.2});console.log(resp.choices[0].message.content);}main().catch(console.error);Responses API:importOpenAIfromopenai;// 初始化客户端constclientnewOpenAI({apiKey:process.env.OPENAI_API_KEY});asyncfunctionmain(){constrespawaitclient.responses.create({model:gpt-4.1-mini,instructions:你是一个后端架构顾问,input:解释一下 item-centered 是什么意思,temperature:0.2});console.log(resp.output_text);}main().catch(console.error);这个阶段的迁移甚至可以批量机械替换一部分代码。但你要是因此得出“Responses API 基本零成本”那后面一定要吃亏。因为真正麻烦的地方几乎都在“上下文”和“行为”上而不在“单轮文本生成”上。迁移成本实测到底要改多少代码我建议把迁移成本拆成 5 档而不是笼统地问“难不难”。1. 基础调用便宜如果你只是让模型总结、改写、翻译、抽取且没有工具调用、没有复杂会话、没有结构化 JSON 输出那么 Responses API 基本可以视为一次低风险升级。你需要做的事通常只有三件client.chat.completions.create改成client.responses.createmessages改成instructions input或者改成input itemschoices[0].message.content改成output_text或解析output[]这部分没什么玄学也不值得拖。2. 多轮对话贵而且经常是最容易低估的那部分Chat Completions 的常规做法是你自己在客户端维护整段对话历史messages[{role:system,content:你是客服助手},{role:user,content:我昨天买的耳机没声音},{role:assistant,content:先确认蓝牙是否正常连接。},{role:user,content:已经连上了还是没声音},]然后每一轮都把整段历史重新发给模型。Responses API 给了你另一条路直接引用上一次响应的previous_response_id。这是一个非常实用的状态管理机制因为它把“部分会话状态”挪到了服务端引用层。对于长对话、重复上下文、高频请求价值不小。但它不是白送的。你原来那套把消息数组存在 Redis、数据库、浏览器 session 里的逻辑可能要重新设计。尤其是当你做审计、回放、脱敏、缓存命中分析时服务端状态引用虽然方便却会让你的系统变成“本地历史 远端状态 id”的双轨模型。阿里百炼在兼容文档里还明确写了一个细节previous_response_id必须传上一轮响应里的顶层 id也就是resp_xxx这类 UUID 格式 id不是output数组内部消息项的 id。这个地方非常容易踩错。它还给了有效期约束——previous_response_id的有效期是 7 天。这就意味着如果你的对话链路本来就会跨天、跨周或者你会做历史重放不能想当然地把它当永久 session key 用。3. Function calling必须认真改不能靠正则糊过去很多团队真正会被迁移卡住的地方其实不是多轮而是工具调用。OpenAI 官方迁移指南已经点明function calling 的 API shape 在两个接口之间不一样。社区拆解则讲得更直白——Chat Completions 的 function 定义是externally taggedResponses 是internally tagged。说人话就是字段长得像一回事但结构层级不一样。先看 Chat Completions 常见写法{type:function,function:{name:get_weather,description:查询天气,parameters:{type:object,properties:{city:{type:string,description:城市名}},required:[city]}}}Responses API 里则更接近{type:function,name:get_weather,description:查询天气,parameters:{type:object,properties:{city:{type:string,description:城市名}},required:[city]}}看着只是少了一层function包裹但如果你的系统里有工具 schema 校验器tool registry请求转发网关工具调用日志归档Chat-compatible provider 兼容层那这就不是“删掉一层对象”那么简单。你得把上下游都统一。下面给一个可运行的 Python 适配示例把 Chat 风格工具定义转成 Responses 风格fromcopyimportdeepcopy# 将 Chat Completions 风格的 tools 转成 Responses API 风格# 输入和输出都保持为 Python list[dict]defconvert_chat_tools_to_responses(chat_tools):result[]fortoolinchat_tools:tooldeepcopy(tool)iftool.get(type)!function:result.append(tool)continuefntool.get(function,{})result.append({type:function,name:fn.get(name),description:fn.get(description,),parameters:fn.get(parameters,{type:object,properties:{}}),})returnresultif__name____main__:chat_tools[{type:function,function:{name:get_weather,description:查询天气,parameters:{type:object,properties:{city:{type:string,description:城市名}},required:[city],},},}]convertedconvert_chat_tools_to_responses(chat_tools)print(converted)这段代码不复杂但它反映了一个现实你需要明确做协议转换而不是假设 SDK 会帮你全包。4. Structured Outputs看上去边角实际上常在生产环境炸很多做抽取、分类、工作流编排的项目早就依赖response_format输出严格 JSON 了。Responses API 在这里也不是字段平移。OpenAI 的迁移文档写得很清楚Structured Outputs 从 Chat Completions 的response_format迁到了 Responses API 的text.format。这意味着你的请求构造器要改你的 schema 注入逻辑要改你的测试样例要重跑你的网关透传字段白名单也要改。如果你们线上有一层“只允许某些字段透传到模型”的安全网关迁移这一步经常会漏。结果就是本地跑得通线上死活不出结构化结果。5. 流式最烦因为 UI 和服务端都要跟着动Chat Completions 的流式消费模型很多前端和后端都很熟读choices[].delta一点点往屏幕上 append。Responses API 则不是这个心智模型。根据社区拆解它的流式更接近“命名事件驱动”不是单纯把一条文本 message 切成 delta。也就是说你消费的是一串有语义的 SSE named events而不是一个纯文本流。如果你的系统做过下面这些事打字机效果输出工具调用中途插入状态提示流中断恢复服务端边流边落库前端按 token / chunk 做统计那这块代码大概率得重写一部分而不是改两行解析器。阿里百炼 Responses API 兼容性能用但有坑国内开发者真正关心的通常不是 OpenAI 官方而是“我现在走的兼容服务能不能跟上”。阿里百炼这波更新是个很明显的信号Responses API 不再只是 OpenAI 自己玩的新接口国内兼容层已经开始接这个协议。根据阿里百炼文档它已经支持 OpenAI 兼容的 Responses API 调用并且明确给出迁移指引。这对已经用 OpenAI SDK、但底层接的是百炼模型的项目来说意义非常直接你可以不重写整套 client只要改兼容 endpoint 和参数结构就能试水。但文档里有几个坑我建议你不要跳过。坑 1旧版 URL 已经在倒计时百炼明确写了旧版路径/api/v2/apps/protocols/compatible-mode/v1/responses即将停止维护应该尽快迁到新版/compatible-mode/v1/responses。这个信息很关键因为很多团队的网关配置、环境变量、SDK base URL 封装都写死在配置中心里一旦你只看代码不看 endpoint后面出问题会非常隐蔽。最稳妥的做法是把 base URL 从“模型平台配置”里抽出来变成显式可切换的协议配置项。坑 2previous_response_id不只是字段名对上就行前面提过百炼要求这里传的是顶层resp_xxxid而且有效期是 7 天。这两个约束叠在一起意味着你的 session 管理要回答两个问题你存的是“完整消息历史”还是“最近一个 response id”当previous_response_id过期时你能不能自动降级回本地历史重放如果答不上来别急着大面积切生产。坑 3兼容不等于完全等价阿里百炼说的是“OpenAI 兼容 Responses API 调用”不是“你写什么 OpenAI 新能力都无脑等价支持”。这是两个概念。兼容通常意味着主路径可用但你一旦碰到推理参数、工具行为、流式事件、特殊 schema 字段最好都做一轮验证。尤其是推理相关参数例如reasoning.effort一类别看文档标题就默认所有模型都支持同一套行为。下面给一个基于 OpenAI Python SDK 指向百炼兼容地址的示例实际项目里很好用fromopenaiimportOpenAI# 使用 OpenAI SDK 直连阿里百炼兼容的 Responses APIclientOpenAI(api_keyYOUR_DASHSCOPE_API_KEY,base_urlhttps://dashscope.aliyuncs.com/compatible-mode/v1,)respclient.responses.create(modelqwen-plus,instructions你是一个代码迁移顾问,input把 chat.completions.create 迁移到 responses.create 需要注意什么,)print(resp.output_text)print(resp.id)# 顶层响应 id可用于下一轮 previous_response_id如果你的目标只是“在现有 OpenAI SDK 上复用百炼兼容层”这条路线比你自己重新造一套 provider client 稳得多。网关路由层怎么适配真正有技术债的团队大多不会直接让业务代码碰模型厂商 endpoint而是会加一层网关。到这一步Responses API 的迁移难度会再上一个台阶。因为网关不是只负责转发 URL它要承担协议整形。你需要同时面对两套世界现实情况通常是这样的上游业务已经开始想用 Responses API下游部分模型提供方还只有 Chat Completions另一部分提供方已经支持 Responses但字段细节又不完全一致。这就逼着网关同时理解两种协议。你要么规定“全站先别迁等供应商统一”要么做一层桥接把 Responses 请求翻译成 Chat再把 Chat 响应重新拼成 Responses 风格输出。社区已经有类似实践。OpenAI Codex 的讨论里就有人提到本地做了一个 API Bridge让 Codex 继续使用wire_api responses而上游 provider 只暴露 OpenAI-compatible Chat Completions。这个信号很明确桥接不是理论方案而是已经有人在生产里这么干。桥接最难的不是文本而是语义保真如果只是单轮文本生成Chat ↔ Responses 的互转还算容易。难点在于tool 定义层级不同tool call 返回结构不同reasoning / typed items 并不是 Chat 的原生概念流式事件模型不同。换句话说你能“转出一个能跑的结果”不代表你能“转出一个语义等价的结果”。下面给一个非常简化的 Node.js 桥接思路演示如何把基础 Responses 请求降级成 Chat 请求。它不覆盖工具调用和流式只展示最小骨架// 将最简单的 Responses 请求降级转换为 Chat Completions 请求functionresponsesToChatRequest(req){constmessages[];if(req.instructions){messages.push({role:system,content:req.instructions});}if(typeofreq.inputstring){messages.push({role:user,content:req.input});}elseif(Array.isArray(req.input)){for(constitemofreq.input){if(item.typemessageitem.roleitem.content){messages.push({role:item.role,content:item.content});}}}return{model:req.model,messages,temperature:req.temperature,tools:req.tools?convertResponsesToolsToChat(req.tools):undefined,};}// 将 Responses 风格 tools 转回 Chat 风格 toolsfunctionconvertResponsesToolsToChat(tools){returntools.map((tool){if(tool.type!function)returntool;return{type:function,function:{name:tool.name,description:tool.description||,parameters:tool.parameters||{type:object,properties:{}}}};});}你会发现哪怕是这个简化版核心工作也不是“透传字段”而是“恢复消息语义”。一旦你涉及工具结果回放、链式推理、结构化 item 输出这层映射会迅速复杂起来。所以我对网关层的建议一直很明确把协议适配做成独立模块不要散落在 controller 里。把 Chat → Responses 和 Responses → Chat 当两件事分别设计。在测试里固定几组金样本纯文本、多轮、function calling、structured output、streaming。不要承诺“完全兼容”除非你真的把 typed items 语义都补齐了。如果你们现在已经有一层统一 AI 网关这时候最怕的是“假兼容”——接口打通了但工具调用字段、流式事件、状态引用语义全悄悄变了最后是业务层承担代价。什么时候该迁什么时候别动我不赞成那种“新接口出来就全面梭哈”的做法。技术迁移从来不是价值观问题而是收益和代价能不能对上。下面这个决策矩阵基本就是我对大多数团队的建议场景建议原因用 OpenAI 原生模型尤其推理模型立刻迁官方给出 SWE-bench 3%、缓存利用率提升 40%-80%收益明确用阿里百炼等第三方但已支持 Responses可以迁先做灰度主路径已通但要验证参数、流式、工具调用细节用第三方模型但只支持 Chat Completions暂时别动迁移成本会转化为桥接成本收益不一定覆盖业务只做简单单轮文本生成可以低优先级迁成本低但收益未必立刻显著已有大量 function calling / streaming 逻辑分阶段迁协议差异大最好先做适配层再切强依赖结构化输出和长会话状态谨慎迁但值得评估Responses 的状态与格式能力更强但改造面也更大我的判断标准其实很朴素迁移应该买到东西。如果你迁过去只是为了“看起来跟上官方路线”但你根本不用推理模型、不吃缓存收益、也不需要 typed items 和服务端状态引用那这件事的优先级没你想得那么高。反过来如果你已经在做 agent、工具调用、长上下文对话、多 provider 网关那继续困在 Chat Completions 里长期看只会越来越别扭。因为整个生态都在往 Responses 这套抽象靠。OpenAI 在迁移文档里强调了推理模型和缓存收益微软的 Azure OpenAI 文档也已经把新的 Agents 路线建立在 Responses client 上xAI 文档直接把与 Chat Completions 的迁移描述为 straightforward。这里面传递出的行业信号很一致Responses 正在变成新的公共接口语言。这不代表 Chat Completions 明天就不能用。恰恰相反OpenAI 目前并没有给出明确的 Chat Completions 下线时间表只是把它放在 Legacy 语境里并把新能力持续放到 Responses 上。所以短期内“还能用”这件事大概率不变但中长期“继续只围着它做架构”已经不划算了。常见问题Q1Chat Completions 会被下线吗现在没有看到 OpenAI 给出明确下线日期。这点要说得准确一点目前更接近“继续支持但不再是主航道”。所以如果你问我会不会明天突然不能用我认为风险不高但如果你问我未来一年还该不该围绕它继续扩展新能力我的答案是否定的。尤其是当 Codex 侧已经移除chat/completions支持时这种信号已经很强了生态上游在往 Responses 收口。Q2Responses API 会更贵吗至少从 OpenAI 官方给出的迁移信息看不应该简单理解成“更贵”。相反官方内部测试给出的说法是Responses API 的缓存利用率比 Chat Completions 提高 40% 到 80%这会带来更低成本。这里我强调一下边界这是 OpenAI 内部测试数据不是你在所有业务场景下一定能无脑拿到的统一收益。如果你的请求高度重复、上下文重用率高这个优势更可能转化出来如果你的请求每次都完全不同那缓存收益自然没那么好看。Q3迁移后流式怎么处理别想着“原来拼delta现在随便改个字段继续拼”。Responses API 的流式更接近命名 SSE 事件流不只是把文本拆块吐出来。对于只需要把最终文本显示到页面的简单产品你可以在 SDK 层找聚合后的文本事件但如果你们前端会展示工具调用过程、后端会边流边存、还要支持断线恢复那就应该把这次迁移当作一次流式协议重构而不是字段替换。最后的判断该不该迁看你想不想继续背旧抽象的债我自己的结论很明确新项目直接上 Responses API用 OpenAI 推理模型的老项目尽快迁大量依赖第三方 Chat-only 提供方的项目先别硬切先做适配层网关型系统优先建设协议桥接和测试基线再灰度切流。原因不复杂。Responses API 提供的不只是一个新 endpoint而是一套更适合未来模型能力的接口抽象typed items、服务端状态引用、更明确的工具调用结构、面向推理模型的优化路径。这些东西一旦进入你的系统你后面的能力扩展会顺很多。当然代价也不能装作看不见。Function calling schema 要改Structured Outputs 要改Streaming 要改多轮状态管理也可能要重设计。我自己用 TheRouter 做网关切到 Responses API 只改了一个 endpoint 配置。类似的网关还有 LiteLLM、OneAPI 都能做。真正不值钱的不是迁移本身而是拖着旧抽象继续叠需求。等到你后面再补工具调用、推理链、长会话状态到时候改动只会更疼。所以如果你今天刚好在评估路线我的建议很简单别把它当“新接口尝鲜”把它当一次协议升级决策。这样你看代码、看收益、看兼容性判断就会清楚很多。
Chat Completions →Responses API 迁移实战:7 个代码diff+ 兼容性踩坑+ 决策矩阵
发布时间:2026/6/7 17:20:11
打开项目一搜chat.completions.create还在到处冒头——这不是少数团队的问题而是过去两年几乎所有接 OpenAI 兼容接口的项目的共同遗留。麻烦在于今天这行代码已经不只是“还能跑就先别动”的级别了。OpenAI 已经把开发重心明确切到 Responses APICodex 侧甚至已经移除了chat/completions支持阿里百炼也在 6 月更新了 OpenAI 兼容的 Responses API 路线。你现在面对的不是一个“新接口要不要试试”的问题而是你的集成还要不要继续押注旧范式。我最近把几套已经在线的调用链重新过了一遍结论很明确Responses API 值得迁但它绝对不是“把/chat/completions改成/responses”这么简单。它改的是模型交互的抽象层——从 message-centered 变成 item-centered从“你发一组 messages我给你一条 message”变成“你提交一段输入序列我返回一组类型化的行为结果”。这意味着收益是真的成本也是真的。如果你用的是 OpenAI 的推理模型官方给过一组很硬的内部评估数据同样的 prompt 和 setup 下Responses API 在 SWE-bench 上比 Chat Completions 高 3%同时缓存利用率在内部测试里比 Chat Completions 提升了 40% 到 80%对应的是更低成本。这不是文案修辞是 OpenAI 自己写进迁移指南里的数字。但另一方面只要你的项目里有多轮对话、函数调用、结构化输出、流式响应、网关转发迁移就不再是“一小时收工”。你得改 schema得改状态管理很多第三方兼容服务还得额外踩坑。这篇文章我不讲空话只回答三个现实问题你到底该不该迁真迁的话代码要改到什么程度如果你走的是第三方模型或网关层应该怎么落地。Chat Completions vs Responses不是改个 URL 就行很多人第一次看 Responses API会下意识把它理解成“新版本的 completions”。这个理解太轻了。两者最本质的区别不在字段名字而在交互模型。Chat Completions 的核心单位是message。你维护一个messages[]数组用户消息、系统提示、工具结果全都塞进这个线性历史里。模型返回的核心结果通常是choices[0].message流式时你再去拼delta。Responses API 不一样。根据 OpenAI 官方迁移文档和社区拆解它是item-centered的输入不再强制要求 message 列表输出也不是“一条 message”而是一组带类型的output[] items。文本、工具调用、推理痕迹、状态引用都以更明确的 item 形式存在。这个变化听起来像设计洁癖实际上影响非常大。你以前把一切都看成“聊天记录”现在你得把模型交互理解成“一个由多种 typed items 组成的行为序列”。对于只发一句话拿一句回答的 demo它没区别。对真实系统它会渗透到解析逻辑、状态存储、工具编排、网关协议适配的每一层。先看一个最直观的对比表维度Chat CompletionsResponses API迁移影响核心抽象message-centereditem-centered需要重想输入输出结构输入字段messages[]inputstring 或 items简单场景可平移复杂场景要重构输出字段choices[].messageoutput[]读取结果方式必须改多轮状态客户端自己拼历史可用previous_response_id引用服务端状态对话管理逻辑可能大改Function calling外层function包裹内层直接声明type/name/parametersschema 不兼容必须改Structured Outputsresponse_formattext.format必须改流式delta片段流命名 SSE events消费流的代码必须改推理模型收益无官方迁移收益说明OpenAI 声称 SWE-bench 3%推理场景更值得迁缓存利用率基线内测提升 40%-80%高并发下有真实成本收益如果你只想知道“最少能不能跑起来”答案是可以。基础调用确实不难。下面直接看真实代码差异。Python最小可用迁移 diff先看最常见的 Python 场景。Chat Completions 版本fromopenaiimportOpenAI# 初始化客户端clientOpenAI(api_keyYOUR_API_KEY)# 调用 Chat Completionsrespclient.chat.completions.create(modelgpt-4.1-mini,messages[{role:system,content:你是一个严谨的技术助手},{role:user,content:用一句话解释 Responses API}],temperature0.2,)# 读取文本结果textresp.choices[0].message.contentprint(text)Responses API 版本fromopenaiimportOpenAI# 初始化客户端clientOpenAI(api_keyYOUR_API_KEY)# 调用 Responses APIrespclient.responses.create(modelgpt-4.1-mini,instructions你是一个严谨的技术助手,input用一句话解释 Responses API,temperature0.2,)# SDK 一般会提供聚合后的输出文本print(resp.output_text)对应的 diff 长这样- resp client.chat.completions.create( resp client.responses.create( modelgpt-4.1-mini, - messages[ - {role: system, content: 你是一个严谨的技术助手}, - {role: user, content: 用一句话解释 Responses API} - ], instructions你是一个严谨的技术助手, input用一句话解释 Responses API, temperature0.2, ) - text resp.choices[0].message.content text resp.output_text如果你的调用停留在这个复杂度那我可以很负责任地说迁移成本低得惊人。改 SDK 入口、改输入字段、改输出读取方式——5 分钟级别。问题是大多数线上项目都不只这点复杂度。Node.js同样能跑但别被表面简单骗了Node.js 侧也是一样。Chat Completions:importOpenAIfromopenai;// 初始化客户端constclientnewOpenAI({apiKey:process.env.OPENAI_API_KEY});asyncfunctionmain(){constrespawaitclient.chat.completions.create({model:gpt-4.1-mini,messages:[{role:system,content:你是一个后端架构顾问},{role:user,content:解释一下 item-centered 是什么意思}],temperature:0.2});console.log(resp.choices[0].message.content);}main().catch(console.error);Responses API:importOpenAIfromopenai;// 初始化客户端constclientnewOpenAI({apiKey:process.env.OPENAI_API_KEY});asyncfunctionmain(){constrespawaitclient.responses.create({model:gpt-4.1-mini,instructions:你是一个后端架构顾问,input:解释一下 item-centered 是什么意思,temperature:0.2});console.log(resp.output_text);}main().catch(console.error);这个阶段的迁移甚至可以批量机械替换一部分代码。但你要是因此得出“Responses API 基本零成本”那后面一定要吃亏。因为真正麻烦的地方几乎都在“上下文”和“行为”上而不在“单轮文本生成”上。迁移成本实测到底要改多少代码我建议把迁移成本拆成 5 档而不是笼统地问“难不难”。1. 基础调用便宜如果你只是让模型总结、改写、翻译、抽取且没有工具调用、没有复杂会话、没有结构化 JSON 输出那么 Responses API 基本可以视为一次低风险升级。你需要做的事通常只有三件client.chat.completions.create改成client.responses.createmessages改成instructions input或者改成input itemschoices[0].message.content改成output_text或解析output[]这部分没什么玄学也不值得拖。2. 多轮对话贵而且经常是最容易低估的那部分Chat Completions 的常规做法是你自己在客户端维护整段对话历史messages[{role:system,content:你是客服助手},{role:user,content:我昨天买的耳机没声音},{role:assistant,content:先确认蓝牙是否正常连接。},{role:user,content:已经连上了还是没声音},]然后每一轮都把整段历史重新发给模型。Responses API 给了你另一条路直接引用上一次响应的previous_response_id。这是一个非常实用的状态管理机制因为它把“部分会话状态”挪到了服务端引用层。对于长对话、重复上下文、高频请求价值不小。但它不是白送的。你原来那套把消息数组存在 Redis、数据库、浏览器 session 里的逻辑可能要重新设计。尤其是当你做审计、回放、脱敏、缓存命中分析时服务端状态引用虽然方便却会让你的系统变成“本地历史 远端状态 id”的双轨模型。阿里百炼在兼容文档里还明确写了一个细节previous_response_id必须传上一轮响应里的顶层 id也就是resp_xxx这类 UUID 格式 id不是output数组内部消息项的 id。这个地方非常容易踩错。它还给了有效期约束——previous_response_id的有效期是 7 天。这就意味着如果你的对话链路本来就会跨天、跨周或者你会做历史重放不能想当然地把它当永久 session key 用。3. Function calling必须认真改不能靠正则糊过去很多团队真正会被迁移卡住的地方其实不是多轮而是工具调用。OpenAI 官方迁移指南已经点明function calling 的 API shape 在两个接口之间不一样。社区拆解则讲得更直白——Chat Completions 的 function 定义是externally taggedResponses 是internally tagged。说人话就是字段长得像一回事但结构层级不一样。先看 Chat Completions 常见写法{type:function,function:{name:get_weather,description:查询天气,parameters:{type:object,properties:{city:{type:string,description:城市名}},required:[city]}}}Responses API 里则更接近{type:function,name:get_weather,description:查询天气,parameters:{type:object,properties:{city:{type:string,description:城市名}},required:[city]}}看着只是少了一层function包裹但如果你的系统里有工具 schema 校验器tool registry请求转发网关工具调用日志归档Chat-compatible provider 兼容层那这就不是“删掉一层对象”那么简单。你得把上下游都统一。下面给一个可运行的 Python 适配示例把 Chat 风格工具定义转成 Responses 风格fromcopyimportdeepcopy# 将 Chat Completions 风格的 tools 转成 Responses API 风格# 输入和输出都保持为 Python list[dict]defconvert_chat_tools_to_responses(chat_tools):result[]fortoolinchat_tools:tooldeepcopy(tool)iftool.get(type)!function:result.append(tool)continuefntool.get(function,{})result.append({type:function,name:fn.get(name),description:fn.get(description,),parameters:fn.get(parameters,{type:object,properties:{}}),})returnresultif__name____main__:chat_tools[{type:function,function:{name:get_weather,description:查询天气,parameters:{type:object,properties:{city:{type:string,description:城市名}},required:[city],},},}]convertedconvert_chat_tools_to_responses(chat_tools)print(converted)这段代码不复杂但它反映了一个现实你需要明确做协议转换而不是假设 SDK 会帮你全包。4. Structured Outputs看上去边角实际上常在生产环境炸很多做抽取、分类、工作流编排的项目早就依赖response_format输出严格 JSON 了。Responses API 在这里也不是字段平移。OpenAI 的迁移文档写得很清楚Structured Outputs 从 Chat Completions 的response_format迁到了 Responses API 的text.format。这意味着你的请求构造器要改你的 schema 注入逻辑要改你的测试样例要重跑你的网关透传字段白名单也要改。如果你们线上有一层“只允许某些字段透传到模型”的安全网关迁移这一步经常会漏。结果就是本地跑得通线上死活不出结构化结果。5. 流式最烦因为 UI 和服务端都要跟着动Chat Completions 的流式消费模型很多前端和后端都很熟读choices[].delta一点点往屏幕上 append。Responses API 则不是这个心智模型。根据社区拆解它的流式更接近“命名事件驱动”不是单纯把一条文本 message 切成 delta。也就是说你消费的是一串有语义的 SSE named events而不是一个纯文本流。如果你的系统做过下面这些事打字机效果输出工具调用中途插入状态提示流中断恢复服务端边流边落库前端按 token / chunk 做统计那这块代码大概率得重写一部分而不是改两行解析器。阿里百炼 Responses API 兼容性能用但有坑国内开发者真正关心的通常不是 OpenAI 官方而是“我现在走的兼容服务能不能跟上”。阿里百炼这波更新是个很明显的信号Responses API 不再只是 OpenAI 自己玩的新接口国内兼容层已经开始接这个协议。根据阿里百炼文档它已经支持 OpenAI 兼容的 Responses API 调用并且明确给出迁移指引。这对已经用 OpenAI SDK、但底层接的是百炼模型的项目来说意义非常直接你可以不重写整套 client只要改兼容 endpoint 和参数结构就能试水。但文档里有几个坑我建议你不要跳过。坑 1旧版 URL 已经在倒计时百炼明确写了旧版路径/api/v2/apps/protocols/compatible-mode/v1/responses即将停止维护应该尽快迁到新版/compatible-mode/v1/responses。这个信息很关键因为很多团队的网关配置、环境变量、SDK base URL 封装都写死在配置中心里一旦你只看代码不看 endpoint后面出问题会非常隐蔽。最稳妥的做法是把 base URL 从“模型平台配置”里抽出来变成显式可切换的协议配置项。坑 2previous_response_id不只是字段名对上就行前面提过百炼要求这里传的是顶层resp_xxxid而且有效期是 7 天。这两个约束叠在一起意味着你的 session 管理要回答两个问题你存的是“完整消息历史”还是“最近一个 response id”当previous_response_id过期时你能不能自动降级回本地历史重放如果答不上来别急着大面积切生产。坑 3兼容不等于完全等价阿里百炼说的是“OpenAI 兼容 Responses API 调用”不是“你写什么 OpenAI 新能力都无脑等价支持”。这是两个概念。兼容通常意味着主路径可用但你一旦碰到推理参数、工具行为、流式事件、特殊 schema 字段最好都做一轮验证。尤其是推理相关参数例如reasoning.effort一类别看文档标题就默认所有模型都支持同一套行为。下面给一个基于 OpenAI Python SDK 指向百炼兼容地址的示例实际项目里很好用fromopenaiimportOpenAI# 使用 OpenAI SDK 直连阿里百炼兼容的 Responses APIclientOpenAI(api_keyYOUR_DASHSCOPE_API_KEY,base_urlhttps://dashscope.aliyuncs.com/compatible-mode/v1,)respclient.responses.create(modelqwen-plus,instructions你是一个代码迁移顾问,input把 chat.completions.create 迁移到 responses.create 需要注意什么,)print(resp.output_text)print(resp.id)# 顶层响应 id可用于下一轮 previous_response_id如果你的目标只是“在现有 OpenAI SDK 上复用百炼兼容层”这条路线比你自己重新造一套 provider client 稳得多。网关路由层怎么适配真正有技术债的团队大多不会直接让业务代码碰模型厂商 endpoint而是会加一层网关。到这一步Responses API 的迁移难度会再上一个台阶。因为网关不是只负责转发 URL它要承担协议整形。你需要同时面对两套世界现实情况通常是这样的上游业务已经开始想用 Responses API下游部分模型提供方还只有 Chat Completions另一部分提供方已经支持 Responses但字段细节又不完全一致。这就逼着网关同时理解两种协议。你要么规定“全站先别迁等供应商统一”要么做一层桥接把 Responses 请求翻译成 Chat再把 Chat 响应重新拼成 Responses 风格输出。社区已经有类似实践。OpenAI Codex 的讨论里就有人提到本地做了一个 API Bridge让 Codex 继续使用wire_api responses而上游 provider 只暴露 OpenAI-compatible Chat Completions。这个信号很明确桥接不是理论方案而是已经有人在生产里这么干。桥接最难的不是文本而是语义保真如果只是单轮文本生成Chat ↔ Responses 的互转还算容易。难点在于tool 定义层级不同tool call 返回结构不同reasoning / typed items 并不是 Chat 的原生概念流式事件模型不同。换句话说你能“转出一个能跑的结果”不代表你能“转出一个语义等价的结果”。下面给一个非常简化的 Node.js 桥接思路演示如何把基础 Responses 请求降级成 Chat 请求。它不覆盖工具调用和流式只展示最小骨架// 将最简单的 Responses 请求降级转换为 Chat Completions 请求functionresponsesToChatRequest(req){constmessages[];if(req.instructions){messages.push({role:system,content:req.instructions});}if(typeofreq.inputstring){messages.push({role:user,content:req.input});}elseif(Array.isArray(req.input)){for(constitemofreq.input){if(item.typemessageitem.roleitem.content){messages.push({role:item.role,content:item.content});}}}return{model:req.model,messages,temperature:req.temperature,tools:req.tools?convertResponsesToolsToChat(req.tools):undefined,};}// 将 Responses 风格 tools 转回 Chat 风格 toolsfunctionconvertResponsesToolsToChat(tools){returntools.map((tool){if(tool.type!function)returntool;return{type:function,function:{name:tool.name,description:tool.description||,parameters:tool.parameters||{type:object,properties:{}}}};});}你会发现哪怕是这个简化版核心工作也不是“透传字段”而是“恢复消息语义”。一旦你涉及工具结果回放、链式推理、结构化 item 输出这层映射会迅速复杂起来。所以我对网关层的建议一直很明确把协议适配做成独立模块不要散落在 controller 里。把 Chat → Responses 和 Responses → Chat 当两件事分别设计。在测试里固定几组金样本纯文本、多轮、function calling、structured output、streaming。不要承诺“完全兼容”除非你真的把 typed items 语义都补齐了。如果你们现在已经有一层统一 AI 网关这时候最怕的是“假兼容”——接口打通了但工具调用字段、流式事件、状态引用语义全悄悄变了最后是业务层承担代价。什么时候该迁什么时候别动我不赞成那种“新接口出来就全面梭哈”的做法。技术迁移从来不是价值观问题而是收益和代价能不能对上。下面这个决策矩阵基本就是我对大多数团队的建议场景建议原因用 OpenAI 原生模型尤其推理模型立刻迁官方给出 SWE-bench 3%、缓存利用率提升 40%-80%收益明确用阿里百炼等第三方但已支持 Responses可以迁先做灰度主路径已通但要验证参数、流式、工具调用细节用第三方模型但只支持 Chat Completions暂时别动迁移成本会转化为桥接成本收益不一定覆盖业务只做简单单轮文本生成可以低优先级迁成本低但收益未必立刻显著已有大量 function calling / streaming 逻辑分阶段迁协议差异大最好先做适配层再切强依赖结构化输出和长会话状态谨慎迁但值得评估Responses 的状态与格式能力更强但改造面也更大我的判断标准其实很朴素迁移应该买到东西。如果你迁过去只是为了“看起来跟上官方路线”但你根本不用推理模型、不吃缓存收益、也不需要 typed items 和服务端状态引用那这件事的优先级没你想得那么高。反过来如果你已经在做 agent、工具调用、长上下文对话、多 provider 网关那继续困在 Chat Completions 里长期看只会越来越别扭。因为整个生态都在往 Responses 这套抽象靠。OpenAI 在迁移文档里强调了推理模型和缓存收益微软的 Azure OpenAI 文档也已经把新的 Agents 路线建立在 Responses client 上xAI 文档直接把与 Chat Completions 的迁移描述为 straightforward。这里面传递出的行业信号很一致Responses 正在变成新的公共接口语言。这不代表 Chat Completions 明天就不能用。恰恰相反OpenAI 目前并没有给出明确的 Chat Completions 下线时间表只是把它放在 Legacy 语境里并把新能力持续放到 Responses 上。所以短期内“还能用”这件事大概率不变但中长期“继续只围着它做架构”已经不划算了。常见问题Q1Chat Completions 会被下线吗现在没有看到 OpenAI 给出明确下线日期。这点要说得准确一点目前更接近“继续支持但不再是主航道”。所以如果你问我会不会明天突然不能用我认为风险不高但如果你问我未来一年还该不该围绕它继续扩展新能力我的答案是否定的。尤其是当 Codex 侧已经移除chat/completions支持时这种信号已经很强了生态上游在往 Responses 收口。Q2Responses API 会更贵吗至少从 OpenAI 官方给出的迁移信息看不应该简单理解成“更贵”。相反官方内部测试给出的说法是Responses API 的缓存利用率比 Chat Completions 提高 40% 到 80%这会带来更低成本。这里我强调一下边界这是 OpenAI 内部测试数据不是你在所有业务场景下一定能无脑拿到的统一收益。如果你的请求高度重复、上下文重用率高这个优势更可能转化出来如果你的请求每次都完全不同那缓存收益自然没那么好看。Q3迁移后流式怎么处理别想着“原来拼delta现在随便改个字段继续拼”。Responses API 的流式更接近命名 SSE 事件流不只是把文本拆块吐出来。对于只需要把最终文本显示到页面的简单产品你可以在 SDK 层找聚合后的文本事件但如果你们前端会展示工具调用过程、后端会边流边存、还要支持断线恢复那就应该把这次迁移当作一次流式协议重构而不是字段替换。最后的判断该不该迁看你想不想继续背旧抽象的债我自己的结论很明确新项目直接上 Responses API用 OpenAI 推理模型的老项目尽快迁大量依赖第三方 Chat-only 提供方的项目先别硬切先做适配层网关型系统优先建设协议桥接和测试基线再灰度切流。原因不复杂。Responses API 提供的不只是一个新 endpoint而是一套更适合未来模型能力的接口抽象typed items、服务端状态引用、更明确的工具调用结构、面向推理模型的优化路径。这些东西一旦进入你的系统你后面的能力扩展会顺很多。当然代价也不能装作看不见。Function calling schema 要改Structured Outputs 要改Streaming 要改多轮状态管理也可能要重设计。我自己用 TheRouter 做网关切到 Responses API 只改了一个 endpoint 配置。类似的网关还有 LiteLLM、OneAPI 都能做。真正不值钱的不是迁移本身而是拖着旧抽象继续叠需求。等到你后面再补工具调用、推理链、长会话状态到时候改动只会更疼。所以如果你今天刚好在评估路线我的建议很简单别把它当“新接口尝鲜”把它当一次协议升级决策。这样你看代码、看收益、看兼容性判断就会清楚很多。