# Go 调用 OpenAI API 流式响应实战指南
很多 AI 应用都需要实时展示大模型的输出。如果等完整结果出来了再一次性显示,用户要对着空白屏幕等很久,体验很糟糕。流式响应能逐字显示 AI 的回复,像打字机一样,体验好很多。
Go 语言本身处理并发和网络请求很稳定,拿来做 AI 后端很合适。这篇文章就来说说怎么用 Go 调用 OpenAI 的流式接口。
## 碰到什么问题
我之前做一个命令行对话工具,遇到了这些麻烦:
1. 用户问一个问题,要等好几秒才能看到回复
2. AI 生成一段长文章时,更是得等半天
3. 想要打字机那种效果,但不知道怎么实现
同步调用 API 的话,服务器要等 AI 把答案全部生成完才返回数据。流式响应则不同,AI 生成一点就返回一点,客户端可以实时渲染。
实现流式响应需要处理几件事:HTTP 长连接要保持住、SSE 协议要会解析、数据要实时显示、错误处理要做好。
## 具体怎么做
### 准备环境
先检查 Go 是否装好了:
“`bash
go version
“`
没装的话去 Go 官网下载。
### 创建项目
“`bash
mkdir openai-streaming-demo
cd openai-streaming-demo
go mod init github.com/yourname/openai-streaming-demo
“`
### 写代码
整个项目就一个 main.go,用标准库就够,不需要额外依赖。
“`go
package main
import (
“bufio”
“bytes”
“encoding/json”
“fmt”
“io”
“net/http”
“os”
“strings”
“time”
)
const (
apiURL = “https://api.openai.com/v1/chat/completions”
apiKey = os.Getenv(“OPENAI_API_KEY”)
modelName = “gpt-4o-mini”
maxTokens = 1000
)
type ChatRequest struct {
Model string `json:”model”`
Messages []Message `json:”messages”`
Stream bool `json:”stream”`
MaxTokens int `json:”max_tokens,omitempty”`
Temperature float64 `json:”temperature,omitempty”`
}
type Message struct {
Role string `json:”role”`
Content string `json:”content”`
}
type ChatResponse struct {
ID string `json:”id”`
Choices []Choice `json:”choices”`
Usage Usage `json:”usage”`
}
type Choice struct {
Index int `json:”index”`
Message Message `json:”message”`
FinishReason string `json:”finish_reason”`
Delta StreamDelta `json:”delta”`
}
type StreamDelta struct {
Role string `json:”role”`
Content string `json:”content”`
}
type Usage struct {
PromptTokens int `json:”prompt_tokens”`
CompletionTokens int `json:”completion_tokens”`
TotalTokens int `json:”total_tokens”`
}
func sendStreamRequest(prompt string) error {
requestBody := ChatRequest{
Model: modelName,
Messages: []Message{
{
Role: “user”,
Content: prompt,
},
},
Stream: true,
MaxTokens: maxTokens,
Temperature: 0.7,
}
jsonData, err := json.Marshal(requestBody)
if err != nil {
return fmt.Errorf(“JSON 序列化失败: %v”, err)
}
req, err := http.NewRequest(“POST”, apiURL, bytes.NewBuffer(jsonData))
if err != nil {
return fmt.Errorf(“创建请求失败: %v”, err)
}
req.Header.Set(“Content-Type”, “application/json”)
req.Header.Set(“Authorization”, “Bearer “+apiKey)
client := &http.Client{
Timeout: 120 * time.Second,
}
fmt.Println(“正在连接 AI…”)
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf(“发送请求失败: %v”, err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
bodyBytes, _ := io.ReadAll(resp.Body)
return fmt.Errorf(“API 返回错误状态: %d, 响应: %s”, resp.StatusCode, string(bodyBytes))
}
reader := bufio.NewReader(resp.Body)
fmt.Println(“\nAI 回复: “)
var fullContent strings.Builder
for {
line, err := reader.ReadString(“\n”)
if err != nil {
if err == io.EOF {
break
}
return fmt.Errorf(“读取响应失败: %v”, err)
}
line = strings.TrimSpace(line)
if !strings.HasPrefix(line, “data:”) {
continue
}
if strings.HasPrefix(line, “data: [DONE]”) {
break
}
jsonStr := strings.TrimPrefix(line, “data: “)
if jsonStr == “” {
continue
}
var response ChatResponse
if err := json.Unmarshal([]byte(jsonStr), &response); err != nil {
continue
}
if len(response.Choices) > 0 {
content := response.Choices[0].Delta.Content
if content != “” {
fmt.Print(content)
fullContent.WriteString(content)
}
}
}
fmt.Println(“\n”)
fmt.Printf(“完整回答长度: %d 字符\n”, fullContent.Len())
return nil
}
func chatLoop() {
fmt.Println(“=== Go OpenAI 流式响应演示 ===”)
fmt.Println(“输入你的问题,输入 quit 或 exit 退出\n”)
reader := bufio.NewReader(os.Stdin)
for {
fmt.Print(“你: “)
input, err := reader.ReadString(“\n”)
if err != nil {
fmt.Println(“读取输入失败:”, err)
continue
}
input = strings.TrimSpace(input)
if input == “” {
continue
}
if input == “quit” || input == “exit” {
fmt.Println(“再见!”)
break
}
if err := sendStreamRequest(input); err != nil {
fmt.Println(“错误:”, err)
}
fmt.Println()
}
}
func main() {
if apiKey == “” {
fmt.Println(“请设置 OPENAI_API_KEY 环境变量”)
fmt.Println(“例如: export OPENAI_API_KEY=your-api-key”)
os.Exit(1)
}
chatLoop()
}
“`
## 运行效果
先设置 API Key:
“`bash
export OPENAI_API_KEY=sk-your-api-key-here
“`
然后运行:
“`bash
go run main.go
“`
交互大概是这样:
“`
=== Go OpenAI 流式响应演示 ===
输入你的问题,输入 quit 或 exit 退出
你: 解释一下 Go 的 goroutine
正在连接 AI…
AI 回复: goroutine 是 Go 语言里的轻量级线程,由 Go 运行时管理。创建成本很低,只需要几千字节的栈空间。
和传统线程相比,一个 Go 程序可以轻松跑好几万个 goroutine。启动方式也很简单:
go func() {
// 并发执行的代码
}()
goroutine 之间通过 channel 通信,不需要锁就能安全共享数据。这套并发模型让 Go 写并行程序变得很简单。
完整回答长度: 215 字符
你: quit
再见!
“`
可以看到 AI 的回复是一个字一个字蹦出来的,这就是流式响应的效果。
## 回顾一下
这篇文章演示了 Go 调用 OpenAI 流式接口的核心要点:
1. **HTTP Client 要设置长一点的 Timeout**:大模型生成内容需要时间,120 秒比较稳妥。
2. **SSE 格式要会解析**:OpenAI 返回的是 data: {…} 格式,遇到 data: [DONE] 就该结束了。
3. **逐字打印而不是等完整响应**:用 fmt.Print 而不是 fmt.Println,这样字符会直接显示在屏幕上。
4. **环境变量管理 API Key**:不要把密钥写死在代码里,用环境变量更安全。
这个方案可以直接用到实际项目里。智能客服、聊天机器人、内容生成工具这些场景都适用。Go 处理并发很在行,拿来做 AI 后端是个不错的选择。