ARTICLE DETAIL

资讯详情

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

OpenAI接口演进:从Chat Completions到Responses API的兼容与迁移指南

OpenAI接口演进:从Chat Completions到Responses API的兼容与迁移指南 上个月排查一个 Agent 框架的日志时我被一行输出卡了半小时error unexpected endpoint or method. (POST /chat/completions). returning 2第一反应是去查是不是有人把请求拼错了路径查完才发现这根本不是网络问题而是 OpenAI 接口规范迭代留下的生态阵痛。从 Completions 到 Chat Completions再到最近的 Responses API协议换了两代而大量开源组件、本地推理引擎、网关仍然停在上一代。今天这篇想把这条演进线完整讲清楚也把“OpenAI 兼容”这四个字的真相摊开看。这个主题适合谁如果你在用 OpenAI SDK 写业务代码或者维护一个接入多家模型服务的网关又或者自己跑了一份 Ollama、vLLM、LocalAI 等着接各种上层工具那你多半已经踩过类似上面的报错。看完本文你能明白这些报错为什么会出现、两个时代协议到底差在哪、开源生态现在真实支持到什么程度以及迁移时最容易被忽视的几个坑。1. 三个接口世代为什么我们走到“换端点”这一步1.1 Completions只懂“续写”的早期形态最早的一代接口对应的是 GPT-3 时代对语言模型的理解给它一段 prompt它返回一段 completion。端点就是/v1/completions请求里最关键的两个字段是prompt和max_tokens返回结果放在choices[0].text。那个阶段还没有真正意义上的“多轮对话”。你要做聊天机器人就得自己把历史对话手工拼进 prompt前一轮的内容连同角色名一起倒进上下文里。角色区分靠的不是协议而是你写在 prompt 里的“Human:”“Assistant:”这类前缀。这套做法在模型能力并不复杂时勉强够用但很快暴露问题角色信息容易干扰模型上下文拼接逻辑散落在业务代码里工具调用更是无从谈起——你只能靠提示词诱导模型输出一段 JSON再祈祷它格式别写错。1.2 Chat Completionsmessages 数组成为行业默认2023 年Chat Completions 接口随着对话类模型的普及成为事实标准端点是/v1/chat/completions。核心变化是把“一段文本”变成了“一组消息”协议里正式定义了system、user、assistant角色后来的tool角色也在工具调用普及后加了进来。请求体长这样{ model: gpt-4o-mini, messages: [ {role: system, content: 你是运维助手}, {role: user, content: 帮我把这段日志翻译成人话} ] }这套结构简单到不能再简单但配合tools参数和响应里的tool_calls字段它几乎支撑了 2023 到 2025 年之间所有大模型应用Agent、RAG、多轮对话、函数调用全部建立在 Chat Completions 之上。为什么它能赢因为 messages 数组天然贴合对话场景模型服务端只需把消息列表渲染成内部模板就能工作上层框架也只需要处理 choices 数组和 message 对象解析成本极低。于是所有开源推理引擎和兼容层都优先实现这个端点整个生态在此形成巨大惯性。1.3 Responses API协议层开始替开发者干活当应用复杂度上来以后Chat Completions 也开始吃力。Agent 应用需要多轮工具调用、需要保留会话状态、需要判断何时调搜索、何时调文件检索而这些逻辑目前都散落在开发者自己写的循环里。OpenAI 因此推出了新一代 Responses API端点是/v1/responses。官方口径很清楚新功能只加在 Responses 上Chat Completions 进入长期维护甚至弃用倒计时。但生态迁移远比预想慢官方后来也不得不调整节奏保留旧端点更长时间。这正是我文章开头那行日志出现的土壤新旧两代端点并存服务端实现各不相同客户端请求一旦落在不支持的端点上就会出现各种“莫名其妙”的报错。2. Responses API 关键改变拆解从 messages 到 items2.1 请求结构instructions 与 inputResponses API 最大的变化是把“消息”这个概念升级成了“条目”items。请求里不再有messages取而代之的是input和instructions。{ model: gpt-5, instructions: 你是运维助手回答前先分析日志, input: 帮我把这段日志翻译成人话 }input可以是普通字符串也可以是一个数组。数组里不全是消息还有function_call_output这类工具执行结果条目。instructions则独立于对话历史存在专门用来承载系统级的约束不需要再塞进 messages 里占位置。这里有个容易误解的点input数组里的元素和旧版 messages 不是一一对应关系。你在请求里既可以放type: message的条目也可以放type: function_call_output的条目甚至可以用previous_response_id直接引用之前某次完整响应而不必每次都把全部历史重发一遍。这个设计本身就在鼓励开发者把会话状态交给服务端管理。2.2 内置工具让 function calling 从“约定”变成“一等公民”Chat Completions 时代工具调用本质上是一种协议层面的“约定”。你传tools模型在回复的tool_calls里带上function.name和function.arguments然后由你的代码执行真实函数再把结果作为role: tool的消息传回去。整个过程里服务端只负责“产生调用意图”执行、轮询、状态维护都是客户端的事。Responses API 改变了这个分工。除了function这种业务自定义工具它还支持官方托管的web_search、file_search、computer_use等工具。你在请求里声明一个web_search模型真的会去执行搜索并把结果纳入上下文你不再需要自己写一套浏览器搜索逻辑。响应结构里工具调用变成了顶级条目function_call和message平级{ output: [ {type: function_call, id: fc_1, name: get_weather, arguments: {\city\:\北京\}}, {type: message, role: assistant, content: 我来查一下天气} ] }这个转变对 Agent 开发是实质性的。过去你要写一个 while 循环不断检查有没有tool_calls、执行工具、再发回结果现在服务端把工具执行编排纳入协议客户端代码量能明显减少。2.3 会话状态、推理过程与流式事件的重构Responses API 还引入了几个 Chat Completions 没有的机制。第一是状态化会话。请求带store: true后服务端会保存完整时间线下一轮只需要传previous_response_id模型就能接着上下文继续。多轮对话的传输量会大幅下降。第二是推理过程的可控可见。reasoning参数可以控制思维链的可见性、摘要方式。这在需要审计模型“思考过程”的场景下很有价值而在 Chat Completions 里你只能拿到最终文本。第三是流式事件名完全不同。Chat Completions 的流式是一堆 delta 分片核心字段是choices[0].delta.contentResponses 的流式则是一组语义化事件比如response.output_text.delta表示正文增量response.function_call_arguments.delta表示工具参数的增量。如果你原来监听的是delta.content切到新端点后这段代码会彻底失效。我见过不少团队迁移时只改了请求体忘了改流式解析结果就是界面空白、日志静默。协议换代解析逻辑必须跟着换代。3. 那些 unexpected endpoint and method 日志是怎么来的3.1 两个端点并存带来的“鸡同鸭讲”回到开头那行日志error unexpected endpoint or method. (POST /chat/completions). returning 2注意它的格式不是标准 HTTP 404 响应体更像是某个中间层自己打印的错误提示returning 2大概率是中间层内部定义的状态码意思是“这个端点我不认识”。这个中间层只实现了 Responses API当上游客户端发来POST /chat/completions时它直接判定为未知路由。为什么会出现这样的中间层因为新协议要推广一些网关和工具链为了“跟上时代”只实现了新的 Responses 端点却忘了大量开源 SDK 和旧客户端默认还在打/chat/completions。结果就是新服务把旧请求挡在门外客户端报错服务端日志却只有这一行轻飘飘的提示。3.2 排查链路从一行日志定位到协议不匹配遇到这类报错我的排查顺序是固定的你可以直接照着做。第一步确认报错来自哪一层。终端输出、SDK 抛错、服务端日志三者含义不同。如果报错出现在自定义网关的日志里问题大概率在路由和协议支持范围。第二步看报错文本里的请求行。POST /chat/completions说明客户端发的是旧协议如果请求行是POST /responses而服务端只支持 chat/completions那就是相反方向的冲突。第三步核查客户端配置的 base_url 到底指向谁。官方地址、自建网关、本地推理引擎各自支持的端点列表完全不同。本地推理引擎大多只实现/v1/chat/completions少数实现了全部协议。第四步查组件版本。很多 Agent 框架和 CLI 工具在新版本里悄悄把默认端点切到了 Responses。你以为是代码逻辑变了其实只是底层 SDK 换了端点。第五步做最小验证。用 curl 分别打一下新旧端点看哪个有响应问题范围立刻缩小。下面这张表整理了我见过的高频组合对应处理方向也一起写清楚。日志现象可能原因处理方向unexpected endpoint ... /chat/completions中间层只实现了 Responses客户端显式改用 responses 端点或让中间层补上 chat/completions 路由SDK 报 404 且请求路径是 /chat/completionsbase_url 指向的服务没有该端点确认兼容服务支持的端点列表换到支持旧端点的服务或换用 /v1/responses工具调用结果莫名丢失兼容层在联系响应翻译时没解析 function_call升级兼容层版本或改用原生协议直连流式输出空白解析器仍在读 delta.content但响应事件已换名将流式解析切换到 response.output_text.delta 等新事件3.3 一个容易被忽略的坑SDK 版本绑架端点还有一个特别隐蔽的坑同一个请求在不同版本的官方 SDK 里走的是不同端点。旧版 openai-python 库的client.chat.completions.create和client.responses.create是并存的代码本身不复杂。但一些上游框架封装时可能在新版本里把默认路径改成了 responses而你的自定义网关只实现了 chat/completions于是一升级就翻车。这种问题排查起来最费时间因为代码看起来完全没变。我的建议是如果项目里用到了第三方 Agent 框架升级前先查一下它的 changelog重点关注“默认端点”和“协议支持”这类关键词。不要等到线上报错才去翻依赖源码。4. 开源兼容层的真相几乎都在兼容 chat/completions4.1 为什么本地推理引擎只做 chat/completions现在把目光转向开源生态。Ollama、vLLM、llama.cpp 服务端、LM Studio、LocalAI 这些项目对外基本都是“OpenAI 兼容”接口。但这句话的实际含义几乎全部指向/v1/chat/completions。原因很实际。Chat Completions 结构简单请求里的 messages 可以直接映射到模型需要的对话模板响应里的 choices 也方便上层框架消费。对所有开源推理引擎来说实现一个 chat/completions 端点就能立刻被 LangChain、Open WebUI、Dify 这些主流工具调用性价比极高。Responses API 则复杂得多它有状态化会话有不同类型的内置工具有独立的函数调用条目还有一整套新的流式事件。一个本地推理引擎要做完这些适配工作量和 chat/completions 完全不是一个量级。更关键的是绝大多数开源模型本身没有官方托管的 web_search、file_search 这类能力让本地模型实现这些内置工具协议基本是空谈。所以我常说一句话看到“OpenAI 兼容”四个字先默认它只兼容 chat/completions等真在测试里跑通了 Responses API 再下结论。这样能少踩很多坑。4.2 兼容层是怎么把两份协议互相翻译的如果你架了一个网关想同时服务两代客户端就得做协议翻译。从 chat/completions 到 responses 的翻译大致分成几步把messages映射成input数组把system角色内容抽出来放到instructions把返回的choices[0].message.content映射成output里的 message 条目把工具请求从tool_calls挪到function_call。反向翻译更麻烦。responses 的output里可能混着 message、function_call、reasoning 多种条目要合成一个单一内容的 choices 结构就得丢弃一部分信息。如果你在调试时发现工具调用参数丢了、推理摘要没了多半就是反向翻译时发生了字段丢失。说到底兼容层解决的是“能连通”的问题不等于“信息完全等价”。真正需要完整能力的场景还是建议直接用原生协议不要让中间层做太多手脚。4.3 官方开源工具 Codex CLI 里的 wire_api 选择有趣的是官方自己也在开源工具里做了兼容通道。OpenAI 的命令行编码代理 Codex CLI 默认走的是新的 Responses API这也是它内置工具调用能力和状态管理的方式。但如果你把它的 base_url 指向某个只实现了 chat/completions 的本地推理服务它会在建立会话的阶段直接失败。Codex CLI 的应对方式是在 provider 配置里提供一个wire_api字段可选值就是responses和chat。改成chat后它会用旧协议跟服务端通信[model_providers.local] name Local Engine base_url http://localhost:1234/v1 wire_api chat这个设计本身就是开源兼容现状的一个缩影官方也明白整个生态不可能一夜之间切到新协议必须给工具留一个“说旧版语言”的开关。顺带说一个 Codex CLI 在 Windows 上的安装坑。有段时间不少人遇到这样的报错missing optional dependency openai/codex-win32-x64. reinstall codex: npm install -g openai/codex这个报错是 npm 安装时可选的原生二进制依赖没装好导致的最常见的原因是 Node.js 版本过老或 npm 缓存损坏。处理办法也不复杂先卸载全局包再清掉 npm 缓存升级到当前 LTS 版本 Node 之后重新安装基本就能解决。5. 从 Completions 系迁移到 Responses 系的最小实操与密钥管理5.1 最小迁移messages 映射到 inputchoices 变成 output如果你的新项目准备直接上 Responses API最核心的映射关系就三条messages变成inputsystem提示词移到instructions返回取值从choices[0].message.content换成output_text。旧代码通常长这样from openai import OpenAI client OpenAI() resp client.chat.completions.create( modelgpt-4o-mini, messages[ {role: system, content: 你是运维助手}, {role: user, content: 帮我把这段日志翻译成人话} ], ) print(resp.choices[0].message.content)换到 Responses API 之后改动非常直接from openai import OpenAI client OpenAI() resp client.responses.create( modelgpt-4o-mini, instructions你是运维助手, input帮我把这段日志翻译成人话, ) print(resp.output_text)如果你的input还要包含多轮历史可以直接传结构化数组数组里的元素类型是message或function_call_output。注意这里不再有role数组那种写法每条消息需要明确标出type和role格式和旧的 messages 并不相同。5.2 tool_calls 到 function_call 的格式转换有工具调用的业务迁移时最费神。旧协议里工具调用藏在choices[0].message.tool_calls里每个调用有id、type、function.name和function.arguments。新协议里调用是output数组中的function_call条目字段更平展。旧解析逻辑大概是这样先读message.tool_calls逐个执行函数再把结果作为role: tool消息连同之前的messages一起重新发给模型。新协议的做法则是把函数执行结果作为function_call_output条目连同此前的function_call条目一起放进新的请求或者更简单直接使用previous_response_id续接。我建议迁移时不要只改字段名还要重新审视整个工具调用循环。旧代码里大量的“拼接历史消息”逻辑可以删除改成维护一个响应 ID 链代码会清爽很多。5.3 API Key 获取、环境变量与安全红线最后说一个老生常谈但值得重复的安全问题。API Key 的创建入口在官方平台的 API Keys 管理页创建后立刻复制保存因为关闭页面后你就再也看不到完整密钥了。日常使用建议只放到环境变量里export OPENAI_API_KEYsk-...应用代码里通过os.environ[OPENAI_API_KEY]读取不要硬编码更别提交到 Git 仓库。你可能会在群里或社交媒体上看到有人“分享 API Key”这种事千万别跟。密钥一旦泄露别人可以拿你的额度跑任何模型账单分分钟涨到你怀疑人生。发现疑似泄露第一时间去后台撤销并重新生成。另外不少第三方工具还支持用 ChatGPT 账号直接登录省去单独管理 API Key 的麻烦。比如 Codex CLI 启动后会显示欢迎信息引导用户选择“Sign in with ChatGPT”或使用 API Key。两者各有利弊API Key 适合自动化脚本和无人值守场景账号登录适合个人交互式使用但要留意账号本身的会话权限范围。最后的个人选择我自己现在接新项目的时候判断标准很简单纯文本对话、简单 RAG、只需要一个稳定的 chat 语义直接用 chat/completions因为生态支持最成熟、踩坑成本最低只要涉及多轮工具调用、需要官方内置搜索、想减少会话历史传输量就直接上 responses别再纠结旧接口。那行unexpected endpoint or method的日志以后大概率还会出现。这不是某个服务写得不好而是协议换代过程中必然的混乱期。理解了这一点再看到类似报错你就知道该去检查谁在说旧话、谁只懂新话然后对症下药。
返回列表