Go 接入 OpenAI Responses API 流式输出:SSE 事件拼接、断线重试与取消
Go 服务把 Responses API 当成普通 JSON 接口调用时,短文本场景基本不会出问题;一旦切换到流式输出模式,真正难处理的是事件边界问题。一段完整的回复可能被拆成多次增量文本分片,连接中断后又不能把已经展示给用户的片段无脑重复拼接,用户主动点取消时也必须第一时间释放背后的长连接资源。
- 流式响应要按事件类型单独处理,增量文本统一追加到同一个字符串缓冲区中。
- 收到官方定义的完成事件后再提交最终结果,连接异常时保留已收到的partial状态,不能把半句话当成最终返回内容。
- 发起重试前必须先判断是否已经向用户输出过内容,已经输出内容的请求不适合做静默重放。
- 用 context.WithTimeout 和 context.WithCancel 控制等待时长上限、用户取消逻辑,保证资源正常回收。
先把 Responses API 流式调用拆成四个状态
流式接口不是读到一段字符串就直接返回给前端。在实际的客服回复类场景里,Go 服务至少要区分 idle、streaming、completed 和 partial 四个状态:只有收到完成信号,才把缓冲区里的完整内容提交给下游服务;网络断开、超时或用户主动取消,都应该停留在未完成状态。
官方 openai-go 仓库把 Responses API 和流式读取逻辑封装在同一套 SDK 里。后续版本迭代后事件类型和模型常量可能发生变化,下面的代码重点是生命周期处理逻辑,实际落地的时候大家要以本地锁定版本的类型定义为准。

最小可用写法:增量事件只负责拼接
把「读取事件」和「提交业务结果」两个逻辑拆分开,代码后续会更容易测试。事件循环里只做三件事:识别增量文本、记录完成状态、把异常直接抛给上层处理,不要在每个分片到达的时候就直接写数据库。
type StreamResult struct {
Text string
Completed bool
}
func readAnswer(ctx context.Context, stream *responses.ResponseStream) (StreamResult, error) {
var result StreamResult
var buf strings.Builder
for stream.Next() {
event := stream.Current()
switch event.Type {
case "response.output_text.delta":
buf.WriteString(event.Delta)
case "response.completed":
result.Completed = true
}
}
if err := stream.Err(); err != nil {
return StreamResult{Text: buf.String()}, fmt.Errorf("stream interrupted: %w", err)
}
if !result.Completed {
return StreamResult{Text: buf.String()}, errors.New("stream ended without completed event")
}
result.Text = buf.String()
return result, nil
}
不同 SDK 版本的流对象和事件访问器可能略有差异,不要把示例里用到的类型名当成永久不变的API定义。核心处理逻辑是通用的:循环结束后要检查流返回的错误,完成事件缺失的情况下不提交结果,已经收到的分片内容只作为排查日志或者前端临时展示使用。
SSE 事件为什么不能按“每行一个答案”处理
SSE 在传输层是以事件帧为单位发送,事件数据可能跨越底层的网络读取边界,单次网络读取操作不一定刚好拿到一个完整事件。SDK 负责把字节流整理成独立事件后,业务代码还要按事件的具体类型做分流处理。遇到没见过的未知事件不要直接当成正文追加,否则后续官方新增的元数据字段可能混到最终回答里。
| 事件或状态 | 业务动作 | 是否可提交 |
|---|---|---|
| output_text.delta | 追加增量文本,推送临时展示 | 否 |
| completed | 记录正常结束 | 是 |
| error / stream.Err | 记录原因,进入重试或人工路径 | 否 |
| context canceled | 停止读取并释放资源 | 否 |
这条规则也能避免重复拼接的问题:重连后发起的新请求要当成一次全新的调用,不能把新请求返回的完整回答拼到旧请求的半成品后面。如果产品层需要做断点续传,应该由服务层设计明确的幂等键和版本号来实现,不能依赖大模型两次输出的内容刚好一致。

断线重试的边界:没有展示内容时重试会更安全
请求刚建立就失败,或者还没有向用户展示任何正文内容时,可以按指数退避策略做一次有限次数的重试。已经显示了「正在生成」之外的正文后再做静默重放,很可能造成前端展示重复回答,甚至误触发重复的工具调用和重复计费请求。
func runWithOneRetry(parent context.Context, ask func(context.Context) (StreamResult, error)) (StreamResult, error) {
ctx, cancel := context.WithTimeout(parent, 45*time.Second)
defer cancel()
first, err := ask(ctx)
if err == nil {
return first, nil
}
if first.Text != "" || errors.Is(ctx.Err(), context.Canceled) {
return first, err
}
retryCtx, retryCancel := context.WithTimeout(parent, 45*time.Second)
defer retryCancel()
return ask(retryCtx)
}
示例只演示策略逻辑,不包含完整的网络重放代码。生产环境里还要额外记录尝试次数、请求唯一标识、首个分片到达时间和最终状态,方便后续排查问题属于服务端错误、客户端超时还是用户主动取消。
context 取消要贯穿到流和上层请求
浏览器断开连接、用户点击停止生成、网关到达超时阈值,最终都应该触发同一个 context 的取消逻辑。只在外层设置定时器却不把 context 传给底层SDK,常见后果就是前端页面已经结束请求,后台和大模型服务的连接还在默默消耗资源读数据。
处理取消逻辑时不要把它误记成服务故障。监控里可以单独统计 context canceled、超时、远端错误和正常完成这几类指标,几类指标对应的优化方向完全不同。完成事件正常到达后再关闭流,异常分支里要保证资源清理逻辑一定会执行。
常见问题:Go 流式接入 Responses API 的几个误区
每收到一段 delta 就写一次数据库,行不行?
不建议。增量写入会放大数据库的压力,也让断线后的半成品内容很难回滚。更稳妥的做法是前端先临时展示,等收到完成事件后一次性提交写入数据库。
连接断开后能不能总是自动重试?
不能。没有展示正文且错误属于短暂网络问题时可做有限重试;已经展示过内容、用户主动取消或鉴权失败时,要直接停止重放。
收到 EOF 就代表回答完成了吗?
不一定。需要同时拿到 SDK 识别的完成事件,并确认流本身没有返回错误,否则只能把当前内容标记为 partial 状态。
上线前的流式检查表
- 锁定
openai-go版本,编译验证当前事件类型和流对象所有可用方法。 - 覆盖正常完成、空输出、远端错误、网络中断、超时和用户取消等所有分支场景。
- 记录尝试次数与完成状态,禁止 partial 结果直接覆盖数据库里的最终答案。
- 重试最多设置有限次数,并且依据「是否已展示正文」判断当前请求能不能重放。
Go slices.SortFunc 怎么选比较器:等值排序、稳定性与三种排序边界
- 上一篇
- Go slices.SortFunc 怎么选比较器:等值排序、稳定性与三种排序边界
- 下一篇
- Linux DynamicUser 配合 StateDirectory:服务降权后的持久目录怎么迁移
-
- 科技周边 · 人工智能 | 13小时前 | go · 人工智能 · ollama · Go 健康检查 模型管理 Ollama API
- Go 接 Ollama API 做模型健康检查:版本、模型存在性与超时处理
- 216浏览 收藏
-
- 科技周边 · 人工智能 | 2星期前 |
- Go 处理 Responses API 图片输入:MIME 预检、Base64 预算与失败回退
- 303浏览 收藏
-
- 科技周边 · 人工智能 | 2星期前 | go · 人工智能 · 流式响应 · 接口排错 · Go context 流式输出 Server-Sent Events Responses API
- Go 接入 Responses API 流式输出中断怎么排查:从事件序列到 Context 取消
- 326浏览 收藏
-
- 科技周边 · 人工智能 | 2星期前 |
- OpenAI Responses API 迁移实战:从 messages 到 input 的最小改造与回归检查
- 446浏览 收藏
-
- 科技周边 · 人工智能 | 2星期前 | 人工智能 · sse · 流式输出 · 接口稳定性 · 重试 · SSE 断线重连 Responses API AI流式输出 sequence_number 重复片段
- AI 流式输出断线后怎么处理:SSE 事件序号、重放与重复片段去重
- 217浏览 收藏
-
- 科技周边 · 人工智能 | 2星期前 | go · openai · AI接口 · Responses API · Go OpenAI Responses API background mode 异步轮询 大模型接口
- Go 调用 OpenAI Responses API 后台模式:从超时请求迁移到可轮询任务
- 388浏览 收藏
-
- 科技周边 · 人工智能 | 2星期前 | go语言 · 异步任务 · 人工智能 · openai · API工程化 · Go 异步任务 轮询 数据保留 OpenAI Responses API background mode
- Go 接 Responses API background mode:轮询异步任务、状态与数据保留边界
- 183浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- ljg-skills
- ljg-skills 是李继刚开源的 AI 技能与提示词集合,面向大模型使用者整理了一批可复用的 prompt、角色设定和任务技能模板,适合用于学习提示词设计、搭建个人 AI 工作流和沉淀团队常用智能体能力。
- 4795次使用
-
- MELO音乐
- MELO音乐是一站式AI视频与音乐制作助手,对标suno, udio的高品质体验。提供伴奏生成、原创写词、无损导出、哼唱识曲、混音变声等全套音频与短视频编辑工具。无论是流行Kpop、电音说唱、民谣古风、摇滚儿歌还是商用轻音乐,MELO为你免费谱曲,轻松做同款!
- 4385次使用
-
- UniScribe
- UniScribe 是一款 AI 音视频转文字与内容整理工具,支持上传音频、视频文件或粘贴 YouTube 链接,自动生成转写文本、摘要、思维导图和关键问题,并支持多格式导出,适合会议记录、课程学习、访谈整理和内容创作复盘。
- 4330次使用
-
- 剧云
- 剧云是专业中文剧本创作平台,安全稳定运行十余年,集成AI编剧、剧本医生审核、人物小传、剧情关系图、大纲编写、多人协作、Word导入导出、版权管控功能,数据安全防护,轻松高效创作剧本。
- 4569次使用
-
- 万象有声
- 万象有声,一个专为有声创作者打造的新一代智能有声内容创作平台。平台提供专业的智能拆章、智能画本编辑、AI配音、AI生成音效、后期制作、智能对轨、智能审听等有声创作全流程工具,可以帮助创作者高效、低成本创作出引人入胜的有声作品。立即体验,让有声书制作更简单!
- 4512次使用
-
- Go map 并发写 panic 怎么办:从共享 map 到可控写入路径
- 2026-06-30 123浏览
-
- go语言中的defer关键字
- 2023-02-17 150浏览
-
- Golang中Interface接口的三个特性
- 2023-01-07 394浏览
-
- go语言中函数与方法介绍
- 2023-01-07 297浏览
-
- go语言数据类型之字符串string
- 2022-12-30 321浏览

