OpenAI Responses API 迁移实战:从 messages 到 input 的最小改造与回归检查
来源:17golang原创
时间:2026-07-27 12:58:41 446浏览 收藏
把已经上线的Chat Completions调用迁移到OpenAI Responses API,最容易踩的坑是以为把 messages 改成 input 就全部改完了。实际迁移至少会碰到输入结构、文本读取、工具调用结果和回归用例四个边界问题。下面用一个Go客户端的最小改造演示每一处该改什么、为什么改,以及怎么校验旧逻辑没有偷偷变更业务行为。
要点速览
- 请求入口从Chat Completions的消息数组切换为Responses API的
input。 - 文本读取优先走SDK暴露的
OutputText,不要再假定固定的choices下标。 - 工具调用要把“模型请求工具”和“工具结果回传”拆成两个可记录的状态。
- 迁移验收至少覆盖纯文本、空结果、工具调用和超时四条路径。
先确认迁移范围:四个边界会同时变化
如果旧代码只负责单次文本生成,迁移范围可以控制在客户端适配层;如果代码还读取函数调用、保存会话状态或统计token,就不能只改请求体。OpenAI官方快速入门现在以Responses API的 client.responses.create 为示例,并从响应对象读取 output_text。这意味着调用方最好依赖自己封装的结果结构,不要把供应商返回的原始响应直接散落在业务代码各处。
| 检查点 | 旧习惯 | 迁移后的判断 |
|---|---|---|
| 输入 | 拼装 messages | 通过 input 传递提示内容或输入项 |
| 文本 | 读取 choices[0].message.content | 读取SDK提供的聚合文本字段 |
| 工具 | 把函数调用和文本混在一条判断里 | 分别记录工具请求、工具结果、最终文本 |
| 测试 | 只比较一次成功字符串 | 增加空结果、超时和工具分支 |
先整理这张表的原因很简单:迁移时最难发现的不是编译错误,而是代码能正常跑、日志也有输出,但工具结果没有传回模型,或者空响应被判定成了调用成功。

输入和输出先改成自己的适配层
下面的示例刻意把供应商响应包在 GenerateResult 里。这样上层逻辑只关心文本和请求编号,后续再换模型或升级SDK时,改动不会扩散到订单摘要、客服回复等核心业务包。
type GenerateResult struct {
RequestID string
Text string
}
func (c *Client) Generate(ctx context.Context, prompt string) (GenerateResult, error) {
resp, err := c.responses.Create(ctx, responses.CreateParams{
Model: "gpt-5",
Input: prompt,
})
if err != nil {
return GenerateResult{}, err
}
text := strings.TrimSpace(resp.OutputText)
if text == "" {
return GenerateResult{}, errors.New("model returned empty text")
}
return GenerateResult{
RequestID: resp.ID,
Text: text,
}, nil
}
这里有三个迁移检查点。第一,Input 只是请求入口,提示词仍然应该由业务层明确拼装;第二,OutputText 为空时要返回可定位的错误,不能把空字符串直接写入下游;第三,保留响应ID,线上遇到“相同提示偶发不同结果”的问题时,日志才能和供应商侧的请求对应上。
如果项目用的是HTTP原生封装而不是Go SDK,适配原则完全一致:把响应JSON解码到适配层自定义结构,在这一层完成文本聚合与空值判断,不要让控制器直接索引原始JSON字段。
旧代码里最隐蔽的风险:工具调用被当成最终答案
带工具的请求不能再用“有响应就是成功”的判断逻辑。一次完整链路通常是:模型提出工具请求,应用校验参数并调用本地函数,再把工具结果作为下一轮输入交回模型,最后才得到可展示给用户的文本。迁移时建议把这三个阶段分别打日志,日志里只保留必要的参数摘要,避免把用户隐私明文落盘。
type ToolState string
const (
ToolRequested ToolState = "requested"
ToolReturned ToolState = "returned"
TextReady ToolState = "text_ready"
)
func recordToolState(reqID string, state ToolState, name string) {
log.Printf("ai_request=%s tool_state=%s tool=%s", reqID, state, name)
}
这段记录不是为了增加无效日志量,而是为了区分两类完全不同的故障:模型没有提出工具请求,和应用拿到工具请求却没有把结果回传。前者要排查提示词与工具声明,后者要排查参数校验、权限配置和超时逻辑。

回归检查要覆盖结果,不只覆盖请求成功
迁移完成后,先用固定提示做一组小而稳定的回归校验,不要一上来就切全量线上流量验证。建议至少保留下面四条用例:
- 纯文本:返回非空文本,并记录响应ID。
- 空结果:模拟没有可展示文本的场景,确认业务侧收到明确错误。
- 工具调用:确认工具请求、工具结果和最终文本按预期顺序出现。
- 超时:缩短上下文或人为延迟工具响应,确认请求可以正常取消且不会重复写入结果。
检查通过后再对比迁移前后的业务指标:成功率、空文本率、工具回传失败率、平均延迟和超时率。输出文字本身不适合做逐字相等的断言,更稳妥的做法是检查结构、关键实体和安全边界;需要固定格式时,再给业务层增加JSON Schema或字段校验。
迁移清单:合并代码前逐项打勾
- 把Responses API的调用封装在单独适配层,业务代码不直接依赖原始响应结构。
- 确认所有读取文本的地方都不再假定
choices结构。 - 为工具请求、工具结果和最终文本分别设置日志状态。
- 保留响应ID、超时和取消信息,设置敏感字段脱敏规则。
- 跑完四条回归用例,再观察一小段真实流量的指标变化。
如果项目暂时没有工具调用需求,仍建议先完成适配层和空结果测试。后续增加网页检索、数据库查询或业务函数调用时,迁移成本会低很多。
常见问题
Responses API 的输入一定只能是字符串吗?
不是。字符串适合最小文本请求,复杂场景还要按官方接口支持的输入项组织内容。迁移时应以当前SDK或API Reference的字段定义为准,不要把旧消息数组未经检查就直接塞进新字段。
为什么不建议继续读取 choices[0]?
因为这会把旧接口的响应形状带入新适配层。使用SDK提供的聚合文本字段,能减少输出项顺序变化对业务代码的影响。
工具调用失败时应该重试模型还是重试工具?
先按失败位置判断:参数不合法时修正或拒绝,网络临时失败时只重试幂等工具;不要在没有幂等保证时盲目重复执行写操作。
怎样确认迁移没有改变业务语义?
用固定输入跑纯文本和工具两条基线,再对比业务指标与结构化校验结果。不要只看HTTP 200状态,也不要只抽查一条自然语言回复。
最后的判断
这次迁移的核心不是替换一个接口名,而是把“输入、输出、工具状态、回归证据”四个边界收拢到适配层。先让最小文本请求跑稳,再接入工具和流式场景,最后用响应ID与指标补齐线上核对,改动会更可控。
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
科技周边 · 人工智能 | 17小时前 | 人工智能 · mcp · sampling · 协议迁移 · MRTR · 模型 API · MCP Sampling sampling/createMessage MCP 2026-07-28 MRTR SEP-2577 大模型 API213 收藏
-
科技周边 · 人工智能 | 20小时前 | oauth · 人工智能 · mcp · ai agent · OAuth MCP redirect_uri iss CIMD Client ID Metadata Documents267 收藏
-
科技周边 · 人工智能 | 23小时前 | 人工智能 · mcp · ai agent · 协议迁移 · MCP Model Context Protocol Roots roots/list 工作区边界293 收藏
-
376 收藏
-
367 收藏
-
363 收藏
-
241 收藏
-
340 收藏
-
320 收藏
-
426 收藏
-
407 收藏
-
科技周边 · 人工智能 | 2天前 | 安全 · mcp · ai agent · MCP ToolAnnotations readOnlyHint destructiveHint idempotentHint195 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习