OpenAI接口从Completions到Responses的演进与开源兼容性解析

发布时间:2026/11/2 14:12:25
OpenAI接口从Completions到Responses的演进与开源兼容性解析 1. 接口换代这件事为什么值得单独聊一次如果你最近半年翻过 OpenAI 的官方文档大概率会有一种走错片场的感觉以前闭着眼睛都能写的chat.completions.create现在文档首页推的却是responses.create。更让人犯嘀咕的是社区里一边有人说Completions 要废弃了赶紧迁移一边又有人说Responses 根本跑不通还是老接口香。信息互相打架最后的结果就是——很多人干脆两个都不敢动项目里那套老代码继续跑着心里却一直悬着。这篇东西就是想把这件事讲透。核心话题是OpenAI 接口规范从 Completions 到 Responses 的演进逻辑以及围绕它衍生出来的一堆开源兼容问题。我会从接口设计的底层动机讲起拆解 Responses 到底改了什么、为什么这么改然后重点聊一个大家最关心也最容易被带偏的问题那些号称兼容 OpenAI的开源实现、第三方网关、本地推理服务到底兼容到了什么程度哪些能无缝切换哪些一碰就碎。适合谁看三类人。第一类是把 OpenAI 接口当成基础设施在用的应用开发者你需要判断要不要迁移、什么时候迁移、迁移成本多大。第二类是在做 AI 网关、模型路由、多模型聚合的工程师你需要搞清楚兼容层的边界在哪。第三类是对接口设计本身感兴趣的技术人想理解一个被全球开发者依赖的 API 是怎么一步步演化的。不管你是哪一类读完应该都能对该用哪个接口、怎么用、坑在哪有个清晰的判断。先说结论性的判断免得你看到一半才发现方向不对Completions 不会明天就消失但新能力基本只会往 Responses 上堆开源兼容层对 Completions 的兼容度远高于 Responses所以短期内双接口并存是常态。下面展开讲为什么。2. Completions 接口的设计基因与历史包袱2.1 从补全一段文本到对话的语义漂移要理解 Responses 为什么出现得先回到 Completions 的出身。最早的/v1/completions接口本质是一个文本补全接口你给它一段 prompt它还你一段续写。这个设计非常纯粹输入是字符串输出是字符串模型干的事就是接着往下写。那时候大家用它写代码、续写文章、做翻译都是这个逻辑。后来 ChatGPT 火了对话成了主流用法于是有了/v1/chat/completions。注意这个名字——它是在completions前面加了个chat而不是重新设计一个接口。这个命名本身就暴露了它的历史包袱它是在文本补全的骨架上硬套了一层消息数组的皮。你传进去的不再是字符串而是一个messages数组每个元素有role和content。模型内部把这些消息拼成一个特殊格式的 prompt再走一遍补全逻辑。这个套皮设计在早期够用但随着能力越来越复杂问题就藏不住了。最典型的就是多轮对话的状态管理。在 Completions 里模型是无状态的每一轮你都得把完整的历史消息重新传一遍。对话越长请求体越大token 消耗越多而且你还得自己维护这个历史数组稍不注意就丢上下文或者重复发送。2.2 那些年被套皮设计坑过的场景我踩过最典型的一个坑是函数调用Function Calling。Completions 时代的工具调用流程是这样的你传tools参数模型返回一个tool_calls你执行完工具再把结果以role: tool的消息塞回messages数组重新发一次请求。整个链路里工具调用的中间状态全靠你自己在客户端维护。问题来了如果模型一次要调三个工具其中两个成功一个失败你怎么把失败信息优雅地传回去如果工具调用过程中你想插入一段系统提示怎么保证不破坏消息顺序这些在 Completions 里都没有原生支持全靠开发者自己拼消息数组。我见过不少项目光是为了处理工具调用的状态机就写了上千行胶水代码。再比如多模态输入。Completions 后来支持了图片但格式是content数组里塞image_url对象。这个设计能用但很别扭——文本和图片混在一个数组里顺序敏感解析起来容易出错。而且音频、文件这些新模态加进来的时候接口结构越来越臃肿。还有一个更隐蔽的问题推理过程不可见。Completions 返回的就是最终答案模型想了什么你完全看不到。对于需要调试、需要审计、需要展示推理链的场景这是个硬伤。虽然后来有reasoning相关的字段陆续加进来但都是在原有结构上打补丁不够系统。2.3 为什么不能直接在 Completions 上继续改有人会问既然问题这么多为什么不直接在 Completions 上迭代非要搞个新接口答案在于兼容性诅咒。Completions 已经被全球无数应用依赖任何破坏性改动都会引发灾难。你没法把messages数组改成别的结构没法把工具调用的返回格式换掉没法重新定义多模态的输入方式——因为一改所有现存代码全挂。这就导致 Completions 只能做加法加字段、加参数、加能力但底层结构动不了。加法的结果是接口越来越复杂语义越来越混乱。同一个功能可能有多种实现方式文档越写越厚新用户上手成本越来越高。这时候与其在旧地基上盖危楼不如另起炉灶。Responses 就是那个另起炉灶的产物。3. Responses 接口到底重构了什么3.1 从消息数组到事件流的思维转变Responses 最根本的变化是把交互模型从**请求-响应改成了请求-事件流。在 Completions 里你发一个请求等一个完整响应中间过程是黑盒。在 Responses 里整个交互过程被拆成了一串事件events**每个事件代表状态的一次变化。这个转变听起来抽象但影响非常实际。举个例子在 Completions 里做流式输出你拿到的是一个个delta每个 delta 里有一小段文本。你只能看到文本在往外蹦但不知道模型是在思考、在调工具、还是在生成最终答案。而在 Responses 里事件类型是明确的有response.created、有response.output_text.delta、有response.function_call_arguments.delta、有response.completed。你能精确知道当前处于哪个阶段。这个设计的好处是状态可观测。对于需要展示AI 正在思考、需要区分推理内容和最终答案、需要在工具调用时给用户反馈的场景事件流提供了原生支持。你不用再靠猜或者靠 hack 来实现这些效果。3.2 有状态会话把历史管理从客户端搬到服务端Responses 另一个重量级变化是有状态会话。前面说过Completions 是无状态的每轮对话你都得重传历史。Responses 引入了previous_response_id这个概念你第一次请求拿到一个 response id下一轮请求带上这个 id服务端自动帮你把历史接上。这个改动直接解决了好几个痛点。第一请求体变小了。不用每次重传几十轮对话省 token 也省带宽。第二历史管理交给服务端客户端逻辑简化。第三多轮一致性更好不会因为客户端拼错历史导致模型行为异常。但这里有个坑必须提醒有状态意味着服务端要存东西。如果你用的是第三方兼容实现它可能根本不支持previous_response_id或者支持得很粗糙。我实测过几个开源网关有的直接忽略这个参数有的虽然接受了但内部还是无状态处理结果就是你以为接上了历史实际上模型啥都不记得。这个后面会详细讲。3.3 工具调用与多模态的原生整合Responses 在工具调用上的设计比 Completions 干净太多。在 Completions 里工具调用是模型返回 tool_calls你执行再塞回去的三段式。在 Responses 里工具调用是事件流的一部分模型决定调工具时会发出response.function_call_arguments.done事件你执行完把结果作为新的 input 传回去继续同一个 response。更关键的是Responses 支持内置工具。比如网页搜索、文件搜索、代码执行这些在 Completions 里你得自己接第三方或者自己实现在 Responses 里可以直接声明tools: [{type: web_search}]之类的服务端帮你搞定。这个能力对做 Agent 的团队来说是质变——以前要搭一整套工具编排框架现在接口层面就支持了。多模态方面Responses 把输入统一成了input数组里面可以放文本、图片、文件结构比 Completions 的content数组更清晰。而且输出侧也支持多模态模型可以直接返回图片、文件不用你再单独调别的接口。3.4 一个对比表看清两代接口的差异维度CompletionsResponses交互模型请求-响应请求-事件流状态管理客户端维护历史服务端维护previous_response_id工具调用三段式客户端编排事件流原生支持含内置工具多模态输入content 数组混排input 数组结构更清晰推理可见性有限靠字段补丁事件流区分推理与输出流式输出delta 文本流类型化事件流兼容性事实标准广泛兼容新接口兼容层支持有限这张表建议存下来后面判断某个开源实现能不能用的时候对着它逐项检查就行。4. 开源兼容层的真相兼容的是哪一层4.1 兼容 OpenAI这句话的水分有多大现在市面上大量开源项目、推理框架、API 网关都标榜OpenAI Compatible。但这句话的信息量其实极低因为它没说兼容的是哪个 OpenAI 接口。是/v1/completions/v1/chat/completions还是/v1/responses三者差别巨大。我做过一轮实测把常见的兼容实现分成三档第一档只兼容/v1/completions。这是最老的接口实现最简单很多轻量级推理服务只做到这一层。你用它做文本补全没问题但对话、工具调用基本别想。第二档兼容/v1/chat/completions。这是目前的主流绝大多数开源网关、本地推理服务都停在这一层。对话、流式、基础工具调用能跑但细节参差不齐。第三档兼容/v1/responses。这一档目前很少而且大多只实现了子集事件流、有状态会话、内置工具这些核心特性往往缺失。所以当你看到OpenAI Compatible时第一件事就是问兼容到哪一档如果对方文档里只提 chat completions那 Responses 相关的能力你就别指望了。4.2 兼容层最容易形似神不似的几个点即便都声称兼容 chat completions实际表现也差很多。我踩过的坑主要集中在下面几个地方。第一个是流式格式。OpenAI 的流式返回是 SSEServer-Sent Events每个 chunk 是data: {...}格式最后以data: [DONE]结束。有些兼容实现虽然也返回 SSE但 chunk 结构不对比如缺少choices[0].delta的某些字段或者finish_reason的时机不对。你的客户端如果严格按 OpenAI 的格式解析就会出错。第二个是工具调用的参数拼接。流式工具调用时OpenAI 会把函数参数分多个 chunk 传每个 chunk 是参数 JSON 的一个片段你需要自己拼接。有些兼容实现直接一次性把完整参数传过来或者拼接逻辑有 bug导致你拿到的 JSON 不完整。这个坑很隐蔽因为非流式下看不出来一开流式就炸。第三个是 token 计数和截断。OpenAI 的max_tokens、finish_reason: length这些行为兼容实现经常对不齐。有的把max_tokens当摆设有的截断后不返回正确的 finish_reason导致你的重试逻辑失效。第四个是错误码和错误结构。OpenAI 的错误返回有固定结构包含error.type、error.code、error.message。兼容实现经常直接抛一个通用错误你的错误处理代码就抓瞎了。4.3 为什么 Responses 的兼容这么难做Responses 兼容难根本原因在于它的事件流模型。Completions 的兼容本质是把请求转成内部格式把内部输出转成 OpenAI 格式是个相对简单的适配层。而 Responses 的事件流要求兼容实现真正理解并维护一套状态机什么时候发response.created什么时候发output_item.added工具调用和文本输出怎么交错previous_response_id怎么映射到内部会话……这套东西不是简单转格式能搞定的需要兼容层自己实现一套完整的事件编排逻辑。对于大多数开源项目来说投入产出比太低——毕竟用 Responses 的人还不多做深了也没多少人用。所以现状就是Responses 的兼容层大多停留在能返回个结果的水平事件流的精髓基本丢失。5. 迁移决策什么时候该动什么时候该等5.1 判断你的项目属于哪一类要不要迁移到 Responses不能一刀切得看你的项目类型。我把它分成三类。第一类纯文本对话应用。比如客服机器人、写作助手、问答系统。这类应用 Completions 完全够用迁移到 Responses 的收益主要是历史管理简化和推理可见性但如果你现在的历史管理没出问题迁移的紧迫性不高。可以等 Responses 生态更成熟再说。第二类Agent / 工具调用密集型应用。比如自动化工作流、多工具编排、需要内置搜索或代码执行的场景。这类应用强烈建议尽早评估 Responses。因为工具调用的原生支持、内置工具、事件流的可观测性对这类应用是实打实的效率提升。你在 Completions 上写的那些工具编排胶水代码在 Responses 上可能直接省掉。第三类多模型 / 多供应商架构。如果你的系统要同时对接 OpenAI、开源模型、其他厂商那迁移决策要更谨慎。因为 Responses 的兼容性差你一旦深度绑定切换成本会很高。这种情况下保持 Completions 作为统一抽象层可能更稳妥把 Responses 的特性作为可选增强。5.2 迁移成本的真实构成很多人低估了迁移成本以为改个接口名、调下参数就完事。实际成本主要在三个地方。一是数据结构重构。Completions 的messages数组和 Responses 的input数组结构不一样。你的消息构造、历史管理、工具结果回传这些逻辑都要重写。如果代码里到处散落着messages.push(...)那改动量不小。二是流式处理重写。前面说过Responses 是类型化事件流你的流式解析逻辑要从解析 delta 文本改成解析事件类型并分发处理。这是个架构级改动不是改几行能搞定的。三是测试和回归。接口行为变了你的测试用例、mock、断言都要跟着改。而且 Responses 的一些行为比如事件顺序可能和 Completions 不完全一致需要重新验证。我的建议是先做一个小范围试点挑一个非核心功能迁移到 Responses跑通全链路把坑摸清楚再决定要不要全面铺开。别一上来就全量迁移风险太大。5.3 双接口并存的过渡策略现实一点讲未来一两年内双接口并存会是大多数项目的常态。你需要一套策略来管理这种并存。我的做法是在应用层做一层抽象把对话、工具调用、流式输出这些概念抽象成内部接口底层可以切换 Completions 或 Responses 实现。这样迁移的时候只改底层适配器上层业务代码不动。这层抽象会增加一些前期成本但长期看非常值——尤其是当你还要对接多个供应商的时候。具体实现上可以定义一个LLMClient接口包含chat()、stream()、call_tool()这些方法然后写CompletionsClient和ResponsesClient两个实现。业务代码只依赖LLMClient。这样 Responses 成熟了切过去就行某个供应商只支持 Completions也能兼容。6. 实操中那些文档不会告诉你的细节6.1 事件流解析的正确姿势Responses 的事件流解析有几个细节文档讲得不清楚但实操中很关键。第一事件类型要穷举处理但要有兜底。Responses 的事件类型会随版本增加你不能只处理已知类型遇到未知类型要能优雅忽略而不是崩溃。我的做法是维护一个已知类型集合未知类型记日志但不中断。第二response.completed不一定是你以为的那个时机。在工具调用场景下模型可能先发一个response.completed然后你回传工具结果又触发新一轮。所以不能简单地把response.completed当成整个交互结束要结合上下文判断。第三文本 delta 和推理 delta 要分开处理。如果你把推理内容也当最终答案展示给用户体验会很怪。要区分output_text.delta和推理相关的事件只把前者展示给用户。6.2 previous_response_id 的隐藏限制previous_response_id用起来很爽但有几个限制要知道。一是有效期。服务端存的历史不是永久的超过一定时间或一定轮数可能就失效了。你不能假设一个 response id 永远有效要有降级方案——比如失效时回退到手动传历史。二是跨会话隔离。不同用户的 response id 不能混用否则会串上下文。这个在多人应用里要特别注意做好 id 和用户的绑定。三是兼容层可能不支持。前面提过很多兼容实现忽略这个参数。如果你依赖它做历史管理换到兼容层就会出问题。所以要么确认兼容层支持要么自己维护历史作为兜底。6.3 工具调用结果回传的格式陷阱Responses 里回传工具结果格式和 Completions 不一样。Completions 是往messages里塞role: tool的消息Responses 是往input里塞特定类型的 item。这个格式如果搞错模型会看不懂工具结果表现为重复调用工具或者胡言乱语。我踩过一次坑把工具结果按 Completions 的格式塞进 Responses 的 input结果模型完全无视了工具返回的内容继续按自己的想象回答。排查了半天才发现是格式问题。所以迁移时工具结果回传这块一定要对照文档仔细改别想当然。7. 兼容性测试怎么验证一个实现到底行不行7.1 一套可复用的测试清单与其听别人说兼容不如自己测。我整理了一套测试清单覆盖从基础到进阶的能力你可以拿它去验证任何声称兼容 OpenAI 的实现。测试项验证内容通过标准基础对话单轮文本问答返回结构正确内容合理多轮对话历史传递模型能引用前文流式输出SSE 格式chunk 结构正确以 [DONE] 结束工具调用非流式tools 参数返回 tool_calls结构正确工具调用流式参数分片拼接拼接后 JSON 完整可解析多模态输入图片理解能正确描述图片内容错误处理非法参数返回标准错误结构截断行为max_tokensfinish_reason 正确有状态会话previous_response_id历史正确接续事件流类型Responses 事件事件类型齐全顺序合理这套清单跑一遍一个实现的真实兼容度就清楚了。别嫌麻烦踩坑的成本比测试高得多。7.2 遇到不兼容时的降级方案测试发现不兼容怎么办不是所有情况都要换实现有些可以降级处理。流式格式不对如果只是 chunk 结构小差异可以在客户端做一层适配把对方的格式转成标准格式。这个适配层写一次到处能用。工具调用不支持如果兼容层不支持工具调用可以在应用层用 prompt 工程模拟——让模型输出特定格式的文本你解析后执行。虽然不如原生优雅但能用。有状态会话不支持这个最简单回退到客户端维护历史每次请求带上完整历史。功能上没损失只是费点 token。事件流不完整如果兼容层只返回最终结果不返回事件流那就当非流式用或者用轮询模拟进度。体验差一些但功能能保证。降级方案的核心思路是把 Responses 的高级特性当成增强而不是必需。这样即使兼容层拉胯你的应用也能跑。8. 我对这套接口演进的一点个人判断聊了这么多技术细节最后说点我自己的观察。接口这东西从来不是纯技术问题。Completions 能成为事实标准不只是因为它设计得好更因为它出现得早、生态大、大家都用。Responses 设计上确实更先进但它要面对的是如何让已经用惯 Completions 的人愿意迁移这个非技术难题。从目前的开源兼容现状看这个迁移会比我最初预想的慢。我的实际做法是新项目直接用 Responses老项目保持 Completions 但做好抽象层。新项目没有历史包袱直接用新接口能吃到工具调用、事件流这些红利。老项目迁移成本高没必要为了迁移而迁移等 Responses 生态更成熟、兼容层更完善了再动。还有一个判断未来一两年兼容 OpenAI这个标签会从兼容 chat completions逐渐转向兼容 responses。现在做兼容层的团队如果还没开始支持 Responses建议尽早排期。因为一旦 Responses 成为主流兼容层的竞争点就会从能不能对话变成事件流完不完整、有状态会话支不支持。这个窗口期不会太长。至于那些还在纠结要不要学 Responses的开发者我的建议很简单先跑通一个最小 demo。不用管迁移就写个几十行的脚本调一次 Responses看看事件流长什么样工具调用怎么回传。跑通了你自然就知道它适不适合你的场景了。光看文档是看不出门道的动手跑一遍比什么都强。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询