从 Chat Completions 迁到 Responses API,发布前至少做这 7 个验收
很多项目迁移 OpenAI 接口时,会先改一行代码:
messages -> input这一步只能说明你开始迁移了,不能说明你已经可以发布。Chat Completions 和 Responses API 的差异不只在字段名,还会影响资源路径、输出读取、流式事件、工具调用、会话状态、网关兼容和回滚方式。
如果你维护的是 SDK wrapper、内部 API 网关、AI 编程工具配置,或者给客户提供 OpenAI-compatible endpoint,发布前至少要做一张迁移验收表。否则很容易出现这种情况:最小文本请求能 200,真实业务一开流式、一接工具、一走中转,就开始 404、解析失败或前端不更新。
先明确:迁移目标是什么
不要把“迁移 Responses API”写成抽象目标。先选一个具体范围:
范围 A:只迁移非流式纯文本 范围 B:纯文本 + 流式 范围 C:加工具调用 范围 D:保留多轮状态 范围 E:通过内部中转网关暴露给多个客户端如果今天只做范围 A,就不要在发布说明里写“已完整支持 Responses API”。读者、同事或客户会按你写的边界使用。
验收 1:资源路径不能混
Chat Completions 的典型资源是:
POST /v1/chat/completionsResponses API 的典型资源是:
POST /v1/responses迁移时先记录最终请求路径。不要只看配置文件里的base_url,因为 SDK 会在基地址后面追加资源路径。如果你把资源路径也写进base_url,最终可能出现这种重复:
/v1/responses/responses发布前验收标准:
chat path = /v1/chat/completions responses path = /v1/responses bad path = blocked or returns expected 404验收 2:输入形状要分层
Chat Completions 常见输入是messages:
{"model":"your-model","messages":[{"role":"user","content":"hello"}]}Responses API 使用input,并且可以配合instructions、工具、状态等字段:
{"model":"your-model","input":"hello"}如果你的业务代码里有统一 wrapper,不要在一个函数里偷偷兼容所有形状。更稳的方式是显式传入协议:
protocol=openai-chat protocol=openai-responses然后进入各自的请求构造器。这样日志里能直接看出哪一层出了问题。
验收 3:输出读取不能继续读choices
旧代码经常读:
completion.choices[0].message.content迁到 Responses 后,纯文本可以优先读 SDK 或响应对象提供的文本聚合字段;需要处理工具或 reasoning 时,则应遍历类型化输出项。发布前不要只测“HTTP 200”,还要测应用真正读取到文本。
验收标准:
HTTP_STATUS=200 TEXT_READ=ok WRONG_READER=blocked by test如果错误读取仍然静默返回空字符串,前端就可能显示“生成中”或空结果,但日志里只有 200。这类问题比 404 更难排查。
验收 4:流式事件要单独测
Chat Completions 流式和 Responses 流式不是同一套事件形状。迁移前端或 SSE 解析器时,至少验证三件事:
- 首个事件到达后 UI 是否进入输出状态。
- 文本增量事件是否能追加到同一个消息。
- 完成事件是否能关闭 loading,并写入最终 usage / request id。
不要只用非流式请求证明流式已经可用。非流式 200 只能证明路由和基本请求体没坏。
验收 5:工具调用不是普通文本
如果你的业务用函数调用、文件检索、代码执行或自定义工具,迁移时要把工具调用当成独立输出类型处理,而不是拼进文本。
一个最小验收表可以这样写:
tool_call_detected=yes tool_name=get_weather tool_args_json_valid=yes tool_result_roundtrip=not_tested / passed今天没有测工具结果回传,就不要写“工具调用完整支持”。最多写“已能识别 tool_call item,工具结果回传待测”。
验收 6:多轮状态要决定谁保存
旧的 Chat Completions 代码通常由应用自己累积messages。Responses API 的状态使用方式会让你重新选择:
- 继续由应用保存完整上下文。
- 使用响应 ID 或会话能力承接上一轮。
- 混合方式:业务关键消息自己保存,临时推理状态交给 API。
这不是代码洁癖问题,而是数据边界问题。发布前要写清楚:用户隐私、审计日志、失败重试和回滚时,到底以哪份状态为准。
验收 7:中转网关要按能力矩阵放行
如果你通过中转服务暴露 OpenAI-compatible endpoint,不要因为/v1/chat/completions成功,就默认/v1/responses、工具调用、图像、流式和状态都成功。
建议维护一张能力矩阵:
| 能力 | 验收状态 | 证据 |
|---|---|---|
| Chat Completions 非流式 | passed | 最小请求 200,文本读取成功 |
| Responses 非流式 | passed / pending | /v1/responses最小请求 |
| Responses 流式 | pending | 事件解析截图或日志 |
| 工具调用 | pending | tool_call item 和结果回传 |
| 状态延续 | pending | previous response / conversation 读写 |
| 计费与用量 | pending | usage readback |
| 回滚到 Chat | passed | feature flag 或路由开关 |
这张表比一句“兼容 OpenAI”更有价值。它能告诉调用方今天能放心用什么,哪些只是下一阶段。
本地实测:用夹具跑一遍验收表
为了避免把线上服务能力写成未经验证的结论,我用标准库写了一个只监听127.0.0.1的本地夹具。它模拟 7 个检查项:Chat 路径、Responses 路径、错误路径、文本读取、流式事件、工具调用 item、状态字段。
执行:
python3 06-evidence/probe_responses_migration_checklist.py本次输出:
PYTHON_VERSION=... CHAT_ENDPOINT=200 TEXT=chat fixture RESPONSES_ENDPOINT=200 TEXT=responses fixture BAD_RESPONSES_BASE=404 PATH=/v1/responses/responses STREAM_EVENTS=3 FINAL=completed TOOL_ITEM=present NAME=lookup_config STATEFUL_FIELD=previous_response_id ONLINE_PROVIDER_REQUEST=NO SUMMARY=pass checks=7/7本地实测图:Responses 迁移验收夹具结果已生成,平台草稿保存后可按需要再上传正文图。
这组输出证明的是迁移验收思路:每一项都能被单独检查。它不证明任何线上中转服务已经完整支持 Responses API,也不证明某个模型在生产环境可用。
发布前建议保留一个回滚开关
迁移最容易忽略的是回滚。你可以在配置里显式保留:
OPENAI_PROTOCOL=chat OPENAI_PROTOCOL=responses或者在网关路由里为不同客户端保留协议模式。上线后如果发现流式事件、工具结果或状态延续有问题,可以把部分客户端回到 Chat Completions,而不是在生产上临时改代码。
回滚不是退步,它是迁移验收的一部分。没有回滚路径的迁移,本质上还没准备好进入真实用户流量。
CodeLink 相关的正确写法
如果你使用 CODELINK API 中转服务测试这类迁移,建议把它放在能力矩阵里写清楚:今天测了哪个 endpoint、哪个模型、是否流式、是否有 usage readback、是否真实计费。没有这些证据时,就只写“可作为 OpenAI 兼容调用场景之一”,不要写“完整支持 Responses API”。
在 CSDN 文章里,CodeLink 更适合通过审核通过的官方网站卡承接下一步,而不是在正文里放注册链接或充值入口。读者读完这篇文章后,真正需要的是一份可复制的迁移验收脚本和技术落地页。
总结
从 Chat Completions 迁到 Responses API,不是把messages改成input就结束。发布前至少验收:
资源路径 输入形状 输出读取 流式事件 工具调用 状态延续 网关能力矩阵 回滚路径每一项都要有自己的成功信号。这样你才能区分:到底是 SDK 配置错、端点没实现、前端解析错、工具调用没接上,还是中转网关还没有放行某个能力。
迁移最好的状态不是“全部一次性改完”,而是每一层都能独立证明、独立回滚、独立记录证据。