ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

从Chat Completions到Responses:OpenAI接口演进与迁移实战

从Chat Completions到Responses:OpenAI接口演进与迁移实战 从去年开始帮几个团队做AI应用落地大家问得最多的问题越来越集中OpenAI的接口到底换成Responses了没有我之前接的Chat Completions代码会不会报废为什么开源框架里翻来覆去还是推荐chat/completions说实话这些疑问背后确实是同一件事——Completions、Chat Completions、Responses三代接口并存加上开源生态各说各话很多人被绕晕了。这篇文章就把接口规范演进的前因后果、Responses和Completions的核心差异、以及开源项目里“兼容”的真实现状一次说清楚顺便把我迁移过程中踩过的坑和验证过的方案都写出来。不管你是在写Agent、做RAG、还是只想把OpenAI SDK接进现有项目这篇文章都值得你花10分钟通读一遍。1. 三重接口背后的演进逻辑为什么会有 Completions、Chat Completions 和 Responses1.1 从补丁式接口到统一入口一个 API 演进史先捋一条时间线。OpenAI最早的API是2020年前后的Completions接口那时候的用法特别原始你给一段文本模型帮你续写所有指令都挤在一个字符串里。你写“请把下面这段话翻译成英文你好”模型就真的把引号里的内容当prompt处理了。当时没有system角色、没有多轮结构化的概念一次请求就是一次独立的续写整个接口可以说是一个“文本补全器”。到了2023年GPT-3.5 Turbo带出了Chat Completions/v1/chat/completions这才是真正意义上被大众熟知的大模型接口。它的核心变化是把原先的一串文本拆成了messages数组每条消息有rolesystem/user/assistant从此有了“对话历史”的意识。但那个时期接口演进非常快速tools、tool_choice、response_format、parallel_tool_calls、logprobs……几十个参数一层一层地往上叠。用过的人都知道一个看似简单的多轮对话加上工具调用前端的组装代码能写到两百行服务端还一点忙帮不上。Responses API/v1/responses是2024年伴随GPT-4o系列推出的新一代统一接口。官方给的定义是“用于构建智能体的新接口”它把原先散落在Chat Completions里的工具调用、结构化输出、多轮记忆、文件处理等能力封装进了一个入口同时引入了previous_response_id这种服务端状态引用机制。一句话总结Chat Completions是你每次把上下文全量发给服务器Responses是服务器替你把状态记住了你只需要告诉它“接着上次的继续干”。1.2 为什么需要“有状态”的接口我刚开始接触Responses时也困惑多轮对话在Chat Completions里明明也能做无非是messages数组越攒越长为什么要推翻重来这里关键在于Chat Completions虽然支持多轮但消息状态是保存在客户端侧的服务器每次收到的都是海量重复上下文。一个5轮对话、每轮携带2千token上下文你发出去的数据是累加的而不是增量的token消耗和延迟都在同比例上涨。更重要的问题出现在Agent场景。如果你想做一个能调用搜索、能读文件、能跑代码的智能体用Chat Completions写出来的编排代码非常痛苦。每一次工具返回结果后你得手动把tool message插进messages再重新发起一次补全请求。中间任何一个环节出问题整个上下文就得重发。Responses的思路是服务端维护了一个“会话状态对象”每次响应中的output条目都有独立的item_id你可以把它作为下一次请求的previous_response_id直接引用也可以单独引用某个具体的输出Item让它作为新的input的一部分参与后续推理。这个设计对Agent的暂停、恢复、分支续写特别有用。打个比方Chat Completions像发消息全靠你手动粘贴聊天记录Responses则像你雇了一个私人助理你跟他说“接着昨天那件事推进”他自己知道文件在哪、进度到哪你只需要下指令。2. 核心机制对比Responses 到底比 Completions 强在哪2.1 上下文管理从消息数组到 previous_response_id在Chat Completions中每一轮请求的上下文由客户端拼装messages里要放完整的历史对话resp client.chat.completions.create( modelgpt-4o, messages[ {role: system, content: 你是数据分析助手}, {role: user, content: 帮我看看这份销售数据}, {role: assistant, content: 好的请上传文件}, {role: user, content: 文件在/tmp/sales.csv分析一下季度趋势} ] )而在Responses里第一次请求后拿到response后续请求直接引用它的id# 第一次请求 resp client.responses.create( modelgpt-4o, input[ {role: system, content: 你是数据分析助手}, {role: user, content: 帮我看看这份销售数据} ] ) # 拿到 resp.id传给下一次请求 resp2 client.responses.create( modelgpt-4o, previous_response_idresp.id, input[{role: user, content: 文件在/tmp/sales.csv分析一下季度趋势}] )这个差异带来的好处不只是代码变短。Responses服务端会在状态对象里保存已经处理过的输入和输出后续调用你可以用input直接塞入新的用户消息配合previous_response_id引用完整历史客户端就不再需要维护一份随时可能超长的消息数组。稍微提醒一下Responses的存储不是永久的官方虽然没给特别硬性的公开承诺但实践中建议短时间内连续引用即可别指望几天前的会话还能原样恢复。2.2 内置工具能力搜索、文件处理、代码执行的差距Chat Completions想调用外部能力连OpenAI自家的工具都得自己“体外”实现文件搜索要自己搭向量库和检索逻辑网页搜索要自己接搜索服务商API代码执行要自己准备沙箱环境。Responses把这些做成了服务端内置能力你只要在tools参数里声明resp client.responses.create( modelgpt-4o, input查一下今天AI领域的大新闻并总结, tools[ {type: web_search_preview}, {type: file_search} ], tool_choiceauto )注意web_search_preview、file_search这类工具在底层是OpenAI平台托管的搜索与检索基础设施它不在开源协议范围内。这也是后文要聊的开源兼容分歧的根源——Responses不只是一套接口格式它还捆绑了服务端能力。如果你用Chat Completions你可以把同样的消息格式发给本地部署的vLLM、OllamaResponses可没那么容易替换Server端的状态管理和内置工具不是开源的。对大多数开发者来说Responses更像是一个云服务能力包而不是一个可以随意复现的协议。2.3 结构化输出与推理参数少写一半校验代码做数据提取和业务集成时结构化输出是刚需。Chat Completions也有json_schema支持但write起来比较繁琐response_format里要嵌套json_schema还需要在请求里反复确认strict模式返回结果偶尔会带着解释性的富文本。Responses这边text.format直接就是json_schema配合strict: true返回的就是严格符合schema的JSON对象。实测下来用Responses做结构化抽取解析异常率比Chat Completions低很多。还有一个细节容易被忽略reasoning能力在Responses里是显式的参数reasoning: {effort: medium}你可以让模型在做复杂推理时输出reasoning items解析事件流时只要过滤event type为reasoning_summary_text_delta即可。Chat Completions时代只能通过换模型变体来控制推理强度在Responses里同一个模型就能按要求开关和分级。对写复杂Agent的人来说这是很实用的增强不需要为“轻度思考”和“深度思考”分别部署模型实例。2.4 参数映射对照表迁移时最省力的做法是先做参数映射。下面是我整理的一份高频率参数对照表基本覆盖了90%日常使用场景Chat Completions 参数Responses 参数备注messagesinputinput可以是消息列表、文本、或item_id引用modelmodel参数名一致max_tokensmax_output_tokens旧参数在Responses上直接报错temperature / top_ptemperature / top_p语义一致toolstools但工具内type值有差异如function→functiontool_choicetool_choice语义一致auto/required/noneresponse_formattext.formatjson_schema写法位置变了streamstream一致但事件流结构完全不同stop无直接对应旧接口的stop序列在新接口暂不支持logprobs暂不支持需要采样级日志时只能用Chat Completionsseed暂无法直接使用语义类似但未对标我在实际迁移中最大的感受是别写一个“万能转换层”就指望所有参数都能透传。有几个参数比如stop序列、logprobs在Responses里目前没有对应实现如果你的业务依赖这些特性建议保留部分调用继续走Chat Completions等官方补齐再全量切。3. 开源兼容的真相Responses 为什么在开源社区“水土不服”3.1 本地推理服务的事实标准仍是 Chat Completions现在打开任意一个开源推理项目的文档看兼容性vLLM、SGLang、llama.cpp、Ollama写的都是“OpenAI-Compatible API”点进去一看基本全是/v1/chat/completions。这个现象不是巧合。本地部署场景天然要求无状态因为推理服务要水平扩展多副本后把“会话状态”放到某一台实例里本身就是灾难Chat Completions那种每次请求把完整消息带上的设计反而恰好符合无状态网关的扩展需求。另一个原因更实际实现成本。Chat Completions的协议层很简单一个messages数组加若干采样参数任何推理框架都能低成本地兼容Responses需要服务端维护状态、实现文件搜索、网页搜索、沙箱执行等能力本地要让每个能力都能跑起来工程量差一个量级。开源社区衡量过性价比之后用脚投票选出了事实标准。3.2 “OpenAI 兼容”到底兼容的是什么聊“开源兼容”之前得先把概念说破目前开源的是OpenAI的客户端SDK、Agent SDK和一批框架库Responses的服务端规范并没有以开源形式完整公开。也就是说你能从GitHub上clone到用Responses API编排Agent的Python/TS源码但clone不到那个处理previous_response_id的状态服务也clone不到file_search背后的检索系统。开源生态对Responses的“兼容”只停留在SDK层调用不涉及服务端能力的平替。所以你在LangChain、LlamaIndex里看到Responses相关的封装基本逻辑是走OpenAI官方API时用Responses一旦把基座换成本地模型或第三方API就自动回退到Chat Completions格式。这导致了一个很微妙的结果——同一个框架里Responses支持看起来“有”但深层兼容能力其实很有限。3.3 开源网关与代理侧的实际选择很多企业和个人开发者会用OneAPI、LiteLLM这类网关把各家模型统一成OpenAI格式方便业务侧一套代码接所有模型。这类网关对Chat Completions已经做到非常成熟但对Responses的适配普遍还处于早期。做网关的人都知道适配Responses不只是把URL路径换掉还得处理状态对象、item_id、内置工具事件流这些在普通模型提供方那里根本不存在。所以现实情况是如果你想通过自建网关使用Responses的完整能力绝大多数情况下会失败最稳的组合依然是——生产环境直接调用OpenAI官方API时用Responses其他需要走网关或本地模型的业务继续沿用Chat Completions。这不算妥协而是基于兼容性的理性决策。4. 迁移实操从 Chat Completions 到 Responses 的落地指南4.1 最小迁移示例一个单轮对话请求的两种写法最简单的迁移只需要改调用路径和参数名。新版openai-python SDK建议1.40以上同时支持两类接口单轮对话的Chat写法resp client.chat.completions.create( modelgpt-4o, messages[ {role: system, content: 你是简洁的助手}, {role: user, content: 用一句话介绍Responses API} ], max_tokens200, temperature0.7 ) print(resp.choices[0].message.content)对应的Responses写法resp client.responses.create( modelgpt-4o, input[ {role: system, content: 你是简洁的助手}, {role: user, content: 用一句话介绍Responses API} ], max_output_tokens200, temperature0.7 ) print(resp.output_text)注意几个差异max_tokens在Responses里是非法参数直接用会报ValidationError取文本时Chat是resp.choices[0].message.contentResponses直接有output_text聚合字段省一层结构。单轮对话迁移大概10分钟就能完成。4.2 多轮对话与工具调用的迁移要点多轮对话是第一个容易翻车的地方。Chat写法里每次请求要把所有历史消息重发一遍Responses写法里第一次请求后取resp.id第二次请求用previous_response_idresp.id传入只需要追加新消息即可。看起来简单但要注意状态对象里保存的是“处理过的输出”如果你在业务里经常修改历史消息比如用户编辑了上一轮输入那么记忆引用机制会引发一些反直觉行为——引用已存储的会话时旧消息更新不会自动生效。工具调用这块Chat写法是让模型返回tool_calls然后你手动把tool结果拼进messages再请求一次Responses写法里工具结果在事件流里以function_call_output的形式出现你可以边接收边继续追加input更接近流式Agent的形态。但有一点要提醒不是所有开源框架都对Responses的function_call输出解析做过充分测试如果你在LangChain里用Responses做复杂工具编排建议先写一个最小验证用例跑通再全量接。4.3 流式输出的差异SSE 事件结构对比流式输出是迁移时代码改动最大的环节。Chat Completions的流式结构是stream client.chat.completions.create(modelgpt-4o, messages[...], streamTrue) for chunk in stream: delta chunk.choices[0].delta.content if delta: print(delta, end)Responses流式事件是按event type区分的stream client.responses.create(modelgpt-4o, input讲个笑话, streamTrue) for event in stream: if event.type response.output_text.delta: print(event.delta, end)如果你开启了reasoning还会看到response.reasoning_summary_text.delta事件这也是很多人在Responses流式解析里踩坑的地方——官方文档对事件类型列表列得很长不少人图省事只监听output_text.delta结果发现思维链内容没体现出来排错半天。我建议迁移时全部按event.type做白名单过滤不要假定只有一种事件类型。5. 常见问题与踩坑实录5.1 npm 安装 openai 时的 optional dependency 报错很多人在Node.js项目里执行npm install openai时会遇到一段疑似报错的输出关键词是missing optional dependency openai/codex-win32-x64后面还跟着reinstall codex: npm in之类的提示。这个问题的根因是openai npm包把codex相关二进制放进了optionalDependencies在Windows平台或者部分npm版本下optional依赖缺失会被打印成类似error的warning。注意它本质上是非阻断性的openai主包能正常安装代码也能正常跑。如果你不想看到这类信息可以改用npm install --omitoptional或者把npm版本升到较新稳定版再装一次。5.2 旧参数失效与新参数不生效迁移后最常撞上的两个参数坑一个是max_tokens在Responses里报错另一个是Chat里常用的logprobs在Responses里静默无效。我在帮同事排查一个生成结果异常变短的问题时发现他把max_tokens直接带进了client.responses.createSDK倒是没拦但OpenAI服务端直接拒绝了请求。另外Responses的output_text聚合字段默认是全部输出文本拼接如果你需要区分多个消息项的文本内容还是要遍历output数组逐个item解析。5.3 上下文长度与 token 统计口径不一致Responses通过previous_response_id引用历史状态确实减少了重复文本传输但要注意服务端状态对象处理的token仍然会计入你的usage账单。也就是说传输上省流量不代表计费上省token深层上下文累积到一定程度费用照样可观。我的建议是仍然要在业务上控制会话轮数别因为Responses写法轻巧就无限续聊必要时做一个会话窗口裁剪策略。5.4 API Key 权限与模型可见性问题Responses接口对API Key权限的要求更严格。你如果用一个只有基础模型权限的项目Key去调用web_search_preview或file_search很可能会收到permission denied或模型不存在的报错。排查这类问题先别怀疑代码去平台后台检查Key的权限范围以及organization里有没有开通对应模型。实践中的另一个坑是同样的Key在Chat Completions里能看到全部模型列表切到Responses接口后就看不见了——这是因为Responses侧的模型清单跟平台授权是另一个口径需要单独核对。迁移之后再说两句我个人的体会是Completions到Responses的迁移本质上不是“改代码”而是“改架构认知”。如果业务只是简单多轮问答继续用Chat Completions完全没问题毕竟它生态成熟、兼容性最好如果业务是重Agent——多工具联动、文件检索、需要跨轮次的状态恢复——Responses带来的收益很直观值得你为它重构一套状态管理方案。最后分享一个实测很稳的迁移策略先在线上灰度跑一段时间新旧接口同时开放把两种流量打上不同日志标签对比一段时间的成功率、延迟和费用再决定全量切换的时间点。任何技术选型都别拍脑袋用数据说话最稳。
返回列表