Go 调用 OpenAI API 流式响应实战指南

# 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 后端是个不错的选择。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇