资讯详情

从Chat Completions到Responses:OpenAI接口演进

📅 2026/10/31 4:42:26 | 华诺云谱 👁 阅读
从Chat Completions到Responses:OpenAI接口演进
最近圈子里有个有意思的现象一边是 OpenAI 放出风声要把gpt-5系列逐步收敛到全新的Responses接口上另一边国内外的开源推理框架还在疯狂兼容/v1/chat/completions。你去翻 vLLM、llama.cpp 的文档铺天盖地全是 Chat Completions 的示例再看看 OpenAI 官方 SDK 的 ChangelogResponses 的权重已经明显压过了老接口。这中间隔的不只是几个端点 URL 的差别而是整个接口设计哲学在变。这篇文章我想从自己的实际体验出发聊聊 Completions 到 Responses 的演进逻辑、技术细节以及开源生态所谓“兼容 OpenAI”到底兼容的是哪一代规范希望能帮还在观望的人看明白方向。1. 接口演进背后的三次范式转移1.1 第一代 Completions文本补全时代的单次请求2019 年到 2020 年那会儿OpenAI 对外暴露的接口还叫Completions核心能力就是“给一段文本模型接着往下写”。当时的请求体非常朴素一个prompt字段加上max_tokens、temperature之类的采样参数模型返回的也就是纯文本字符串。很多老开发者应该还记得davinci、curie、babbage这些系列它们本质上是语言模型做概率续写。这个时代的接口设计有几个典型特征。第一无状态每次调用就是一次独立的续写你不需要维护对话历史所有上下文都得自己拼进prompt里。第二没有内置工具概念函数调用、检索之类的机制在最早的版本里根本不存在你想让模型用外部工具得自己在应用层做各种 hack。第三返回结构极其简单choices[0].text就是你要的唯一结果。站在当时的角度看这种设计其实没什么毛病。一个续写模型最自然的交互方式就是给 prompt 拿补全。但它有两个隐患一是“聊天”这种应用形态需要反复拼历史消息业务代码里全是字符串拼接逻辑二是模型能力往上走之后单轮续写已经不能满足 Agent 这类需要多步推理、动态调用工具的场景了。1.2 第二代 Chat Completions对话范式的开源事实标准2022 年底 GPT-3.5 Turbo 发布时OpenAI 带来了Chat Completions端点变成/v1/chat/completions请求体从裸字符串变成了消息数组。system、user、assistant三种角色第一次被正式引入多轮对话的维护成本大幅下降。紧接着 2023 年 6 月OpenAI 增加了function calling后来演进为 tools模型可以在返回内容里显式声明要不要调用某个函数、参数结构是什么样的。这波操作直接定义了今天绝大多数 LLM 应用的交互协议。为什么说它是“事实标准”因为几乎所有的开源推理框架、中间件、国产模型服务都以兼容chat/completions作为第一优先级的对外协议。DeepSeek、Qwen 这些模型的 API 文档里写的都是“兼容 OpenAI”展开之后基本都是 Chat Completions 那套字段。本地部署的 vLLM 启动之后默认暴露的也是/v1/chat/completions。可以说过去两年里的 LLM 应用开发本质上就是在跟这套协议打交道。Chat Completions 的成功之处在于它把“对话状态”从一个应用层问题变成了协议层问题。但它的短板也很明显——这套接口仍然是围绕着“多轮文本对话”建模的一旦场景变成 Agent 式的工作流需要模型连续调用多个工具、维护内部状态、甚至跑代码、查文件开发者就只能在外围堆各种补充机制。OpenAI 后来推出的 Assistants API 试图补这个位结果引入了另外一套复杂的状态管理模型不少团队用过之后都直呼太重。1.3 第三代 ResponsesAgent 原生协议的正式登场2025 年OpenAI 正式推出了Responses API端点变为/v1/responses。它最核心的变化不是字段改名而是把“一次交互”从单轮文本对话升级为多步 Agent 任务。这么说吧Chat Completions 里的messages数组只是对话历史而 Responses 里的input可以是一系列事件、推理步骤、工具调用结果的集合更关键的是你可以传previous_response_id把多次请求串成一个有状态的会话服务器端帮你记着上下文不再需要你在每次请求里把几千 token 的历史重新塞进去。除此之外Responses 把之前分散在各种 API 里的能力做了统一。Web Search、File Search、Code Interpreter 这些原本需要单独配置的工具现在直接作为参数传进去就行。这个设计明显是冲着智能体Agent场景去的——做任务而不是做聊天。早期试用的时候比如o3系列刚出来那阵给我的感觉就是OpenAI 想把“Agent 运行时”变成一种平台能力而 Responses 就是这个运行时的入口门面。2. 核心细节解析与实操要点2.1 端点与请求体对比到底改了什么先说最表面的差异。老接口是POST /v1/chat/completions新接口是POST /v1/responses。名字变了内部的数据结构也完全不同不是简单换个 URL 就行。Chat Completions 的请求体是这样的{ model: gpt-4o, messages: [ {role: system, content: 你是一个助手}, {role: user, content: 帮我查一下明天的天气} ], tools: [ { type: function, function: { name: get_weather, parameters: {type: object} } } ] }Responses 的请求体变成了这样{ model: gpt-5, input: [ {role: system, content: 你是一个助手}, {role: user, content: 帮我查一下明天的天气并规划出行} ], tools: [ {type: web_search}, {type: code_interpreter} ], reasoning: {effort: medium} }对比一下就能发现三个关键变化。一是字段变简洁了messages改成input模型内部会把普通消息自动转成带状态的对象二是工具调度更高级不需要再定义一堆 function schema内置工具直接按类型启用三是多了reasoning控制项可以显式设置推理强度这个在老接口里是没有的部分模型支持了但很晚才补上。再讲一个容易踩的坑gpt-5系列在 Responses 接口里推理输出默认是关闭的。也就是说你不显式设置reasoning.effort模型可能就直接给你结论不给你思考过程很多从 Chat Completions 迁移过来的人第一反应是“这模型怎么变笨了”其实是参数没设对。2.2 响应结构的变化从 choices 到 output老接口的返回结构核心就是choices{ choices: [ { message: { role: assistant, content: 明天晴12℃, tool_calls: [] }, finish_reason: stop } ] }Responses 的返回结构从choices变成了output而且是一个事件数组{ output: [ { type: message, role: assistant, content: [ {type: output_text, text: 明天晴12℃} ] }, { type: function_call, name: get_weather, arguments: {\city\: \北京\} } ] }注意两点。第一output里是不同type的对象可能是文本消息也可能是函数调用还可能是推理摘要。第二文本内容被包在content[].text里不再像之前那样直接给你一个字符串。做流式输出的时候事件类型也换了名字——老接口是choices[].delta.content新接口是response.output_text.delta。所有基于老接口写的流式解析代码迁移过来都得重写一遍。2.3 状态管理的隐性成本差异这个点可能是最影响架构决策的。Chat Completions 本质是无状态的每次请求你都要把完整上下文带过去服务端不记你上一轮干了什么。Responses 默认是有状态的只要传previous_response_id服务端会自动把之前的结果拼接到当前位置你不需要再维护一份完整历史副本。听起来很棒但状态这东西是双刃剑。好处很明显长对话场景省 token因为你不用把几千 token 的历史反复捎上坏处也很明显上下文变成了服务端资源而 OpenAI 的状态也有自己的过期策略一旦长时间没继续状态就断了。这就要求业务架构里必须有“随时重建上下文”的兜底逻辑——你不能假设previous_response_id永远有效。我见过有的团队把所有对话流程全押在 Responses 状态上结果隔一天再来继续对话状态丢了整个会话链全崩。所以正确姿势是状态用于短周期任务几分钟到几小时长周期场景依然要自己持久化完整上下文。3. 开源兼容的真相大家都在兼容哪一代3.1 开源社区普遍跟进的是 2023 年的协议现在你随便挑一个主流的开源推理框架来看vLLM、llama.cpp、SGLang、Ollama甚至很多国产的模型服务平台。它们标注的“OpenAI 兼容”到底兼容的是什么答案基本都是chat/completions而且是某一特定版本通常是 tools 机制稳定之后的那版。你去 vLLM 的文档里翻 API 部分OpenAI-Compatible Server 对应的路径就是/v1/chat/completions、/v1/completions、/v1/embeddings根本没有/v1/responses。为什么会出现这种情况一方面是因为 Chat Completions 这套协议足够简单、足够通用它只关心“消息进、消息出”本地推理服务很容易做到另一方面开发者生态已经在这套协议上面沉淀了太多代码——LangChain、LlamaIndex、各类 Agent 框架的默认调用层全是 Chat Completions 格式。你改成 Responses所有上游框架都得跟着变这不是一朝一夕的事。这里有个很容易被忽视的“兼容”陷阱有的网关产品或者中转服务说自己“兼容 OpenAI API”你一看文档功能点全对齐的是 Chat CompletionsResponses 压根没影。团队如果因为“兼容 OpenAI”选了某个中间件结果把gpt-5接进去才发现不支持新协议那整个项目就得推倒重来。所以选型之前一定要把“兼容哪个版本的 OpenAI API”问清楚。3.2 为什么 Responses 很难在开源生态快速普及Responses 之所以迟迟没能成为开源主流核心原因在于它把太多的“云服务能力”揉进了协议里。Web Search、File Search、Code Interpreter 这些内置工具背后都是 OpenAI 自己的基础设施在支撑。你让一个本地推理框架去实现这些能力等于要在服务端同时托管搜索引擎、向量数据库和代码沙箱——这已经不是一个推理引擎该做的事了更像是一个完整的 AI 应用平台。从开源社区的视角看Responses目前最大的价值不是“协议本身”而是它背后定义的一套 Agent 循环标准任务上下文如何组织、工具结果如何回填、推理过程如何追踪。这些设计思路其实挺值得学习但开源框架没法直接照搬只能各自用自己的方式去实现相近的能力。比如 vLLM 在做 Tool Calling 的标准化llama.cpp 也在逐步加强对工具调用的支持但它们走的还是“消息级工具调度”的路线也就是 chat completions 那套。我的判断是未来相当长一段时间里开源生态的事实标准依然是 chat/completionsResponses 会更多出现在商业 API 领域尤其是需要绑定 OpenAI 全家桶能力的场景。如果你的项目是本地部署开源模型选 Chat Completions 准没错如果你是要深度用 OpenAI 的 Agent 能力那就尽快迁移到 Responses。两条腿走路短期是这个行业的主旋律。3.3 协议兼容层的迁移成本有多大不少团队会想到用兼容层来解决两套协议并存的问题——写一个适配器把 Responses 请求转换成 Chat Completions 请求再转发给本地模型。看起来是个很优雅的方案但实际操作下来槽点非常多。首先是工具调用的语义错位。Responses 里内置工具的type是web_search、code_interpreter而 Chat Completions 里你只能定义自定义 function。你把web_search转成 function call那就需要自己在应用层实现一个搜索函数这不叫“兼容”这叫“把活揽到自己身上”。其次是状态管理无法对齐本地模型服务没有previous_response_id这个概念你在兼容层里只能自己维护一个映射表手动把历史展开成完整上下文这等于把省 token 的红利又给吃回去了。最后是流式协议不一致事件类型、事件命名全是两套兼容层要做一层完整的协议翻译开发量不是一个小数目。所以我对“自建兼容层”这件事的建议是能不做就不做。如果你是应用开发者直接在 SDK 层面做一次适配不同模型用不同的客户端比在协议层做转译要可靠得多。如果你一定要统一那就统一在“业务抽象层”别统一在“HTTP 协议层”。4. 实操迁移指南从 Chat Completions 到 Responses4.1 一个最小可用的迁移示例我以一个简单的天气助手为例演示怎么从旧接口切到新接口。老写法用 Python SDK 的openai.openai_object时代风格现在 SDK 已经改了但思想不变# 旧协议chat/completions client OpenAI() resp client.chat.completions.create( modelgpt-4o, messages[ {role: system, content: 你是天气助手}, {role: user, content: 北京明天天气} ], tools[{ type: function, function: { name: get_weather, parameters: {type: object, properties: {city: {type: string}}} } }], tool_choiceauto ) tool_call resp.choices[0].message.tool_calls[0] print(tool_call.function.arguments)新协议用 Responses# 新协议responses from openai import OpenAI client OpenAI() response client.responses.create( modelgpt-5, input[ {role: system, content: 你是天气助手}, {role: user, content: 北京明天天气} ], tools[ {type: function, name: get_weather, parameters: {...}}, {type: web_search} ], reasoning{effort: medium} # 显式开启推理 )注意这里两个细节一是tools的写法变了function 类型的工具不再用function字段包裹而是直接平铺name和parameters二是内置工具和自定义函数工具可以混着传这在老接口里没法做到。拿到函数调用结果的路径也变了老接口是choices[0].message.tool_calls新接口是response.output里type function_call的对象或者直接用response.get_function_call()这类便捷方法不同版本 SDK 略有差异。4.2 流式输出的适配重点流式是迁移过程中最容易出事的地方。老接口流式事件长这样{choices: [{delta: {content: 北京}, finish_reason: null}]}新接口流式事件长这样{type: response.output_text.delta, delta: 北京}事件名称、数据结构、终止判断全都不一样。老接口判断结束靠finish_reason新接口要看response.completed事件或者output数组里是否出现了finish_reason。如果你在框架里已经写死了对choices[0].delta的解析那迁移到新接口后必须把解析器整体重写。我的建议是给项目做一个统一的流式事件解析层把两套协议的差异收敛在同一个函数里上层业务逻辑只处理“增量文本”和“最终结果”两种抽象事件。这样即便后续模型切换改的也只是解析层的几十行代码而不是所有业务逻辑。4.3 按场景选择协议别为了新而新迁移之前先想清楚你的业务到底是什么形态。如果你的应用是典型的对话式产品——客服问答、知识库检索、多轮闲聊——那么 Chat Completions 完全够用而且生态成熟度更高所有框架、网关、监控工具都支持。迁移到 Responses 不但没有额外收益反而要付出重写客户端和排查工具的代价。如果你的应用是Agent 形态——自主规划、多步工具调用、代码执行、联网搜索——那么建议尽快上 Responses尤其是重度依赖 OpenAI 官方模型gpt-5系列、o3系列的场景。因为 OpenAI 已经明确表态新模型在老接口上的支持是有限的长期来看这些模型的 Agent 能力只会在 Responses 上完整暴露。还有一类是混合场景我的做法比较务实框架层抽象一层 client能走 Responses 就走 Responses不能走比如用的是本地开源模型就回退到 Chat Completions。在代码里预留 switch切换成本控制在半天以内。5. 常见问题与排查技巧实录5.1 报错一模型不支持 Responses 端点的调用这个问题在接开源模型或者国产模型时特别常见。你在代码里把client.chat.completions.create换成client.responses.create然后把模型名改成deepseek-chat或者本地模型名结果直接报 404 或者 “Endpoint not found”。原因其实很简单对方服务只实现了 chat/completions 这一个端点根本没实现/v1/responses。排查思路是先用 curl 打一下对方的/v1/models或者直接测一个最简请求确认对方是否支持新协议。解决办法要么是换回 chat/completions 调用要么在两套协议之间做一次轻量适配。切忌在一个统一 client 里硬切协议然后拿开源模型去跑白折腾一晚上。5.2 报错二gpt-5 系列输出“变笨”了前面已经提到过Responses 接口下gpt-5的推理默认是不开启的。不少人在迁移后第一轮测试就发现模型回答质量明显下降甚至直接给出错误的结论。这个问题的根源是reasoning参数没设置。response client.responses.create( modelgpt-5, input[{role: user, content: ...}], reasoning{effort: high} # 或者 low/medium/high )这里的effort控制的是推理算力的投入程度值越高模型在内部会做越多的推演结果质量越高但响应延迟和 token 消耗也越大。到底怎么选取决于你的业务对延迟和成本的容忍度。我的经验是简单问答用low需要分析的用medium复杂推理或代码用high。别一上来全设high账单会教你做人。5.3 报错三流式事件解析全部报错代码从旧协议切到新协议之后最常见的就是流式解析器崩掉。因为两套协议的事件类型数量都不一样——旧协议就那么几个事件message_start、delta、finish新的流式协议事件非常细碎比如response.created、response.reasoning_summary_text.delta、response.output_text.delta、response.completed。你按旧的choices[0].delta去取文本拿到的全是。排查思路是先把流式事件的原始 JSON 打出来看一遍了解每个事件在自己模型或场景下真实会触发哪些类型。然后写一个白名单解析器——只关心response.output_text.delta和response.completed其他事件一律忽略。千万别做黑名单过滤因为你永远猜不到 OpenAI 下一个版本会加什么事件类型。5.4 避坑速查两代协议关键差异对比项Chat CompletionsResponses端点/v1/chat/completions/v1/responses上下文表示messages数组input对象/数组是否服务端有状态否无状态是支持previous_response_id内置工具无只能自定义 function支持web_search、file_search、code_interpreter推理控制早期无后期部分模型支持reasoning.effort为第一公民返回结构choices[0].messageoutput[]事件数组流式事件choices[0].delta.contentresponse.output_text.delta开源兼容性全面事实标准几乎没有适合场景对话、简单工具调用Agent 多步任务、内置工具这个表基本就是迁移时最需要关心的差异点。平时排查问题的时候我建议先定位“你踩的是哪一代协议”的坑很多时候问题不在你的业务代码而在协议选型本身。说实话从 Completions 到 Chat Completions再到 Responses这几年我最大的体会是OpenAI 的接口演变史本质上就是 LLM 应用从“文本处理工具”走向“智能体运行平台”的缩影。Completions 时代我们在处理字符串Chat Completions 时代我们在组织对话Responses 时代我们在编排任务。每一代协议都精确匹配了当时模型能力的边界。最后分享一个我自己一直在用的判断方法看模型的路数定协议的选型。凡是强调“深度推理”“自主规划”的新模型优先上 Responses凡是强调“低成本”“大规模部署”“开源可控”的场景继续扎根 Chat Completions 完全没毛病。两套协议在未来很长一段时间会共存而真正决定你项目上限的往往不是你选哪套协议而是你有没有把“状态管理”“工具编排”“流式解析”这些底层细节打磨干净。踩过几次坑之后你就会明白接口只是入口工程能力才是底气。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑