## 背景介绍
开发过程中,我们经常需要和 GPT 模型打交道。网页版 ChatGPT 那种内容逐字冒出来的体验确实很爽,但用 API 调用时,默认是等模型生成完整个回复才给你。这就有问题了——等一个几千字的长回答,那段时间只能盯着空白屏幕干等。
流式输出(Server-Sent Events,简称 SSE)能解决这个问题。服务器边生成边发,客户端不用傻等,可以第一时间看到内容慢慢出现。这不仅是快,交互体验也更接近真人对聊。
本文用 Go 语言调用 OpenAI API,实现流式输出,顺手做个命令行工具出来。
## 问题描述
用 OpenAI API 调用 GPT 模型,默认行为是等完整响应生成完才返回。这会造成几个麻烦:
**体验差。** 发个复杂问题出去,模型要生成几千字的内容。流式输出时内容一段段出来,好歹能早点看到有用信息。阻塞模式下,用户只能坐着干等。
**没有实时感。** 流式输出有个好处是能显示进度,响应一部分就展示一部分。构建交互式工具时这个能力很关键。
**超时问题。** 响应内容太长的话,超时时间得设很长。流式输出不存在这个问题,边收边处理就行。
所以流式输出是个很实际的需求。下面来看看 Go 怎么实现。
## 详细步骤
### 1. 环境准备
你需要:
– Go 1.21 或更高版本
– OpenAI API Key(去 https://platform.openai.com 申请)
– 能正常访问 OpenAI API 的网络
### 2. 创建项目
建个新项目:
“`bash
mkdir gpt-stream-cli && cd gpt-stream-cli
go mod init gpt-stream-cli
“`
### 3. 安装依赖
OpenAI 官方提供了 Go 客户端库,装一下:
“`bash
go get github.com/openai/openai-go
“`
### 4. 编写代码
新建 main.go,把流式输出逻辑放进去:
“`go
package main
import (
“context”
“fmt”
“io”
“log”
“os”
“github.com/openai/openai-go”
“github.com/openai/openai-go/option”
)
func main() {
apiKey := os.Getenv(“OPENAI_API_KEY”)
if apiKey == “” {
log.Fatal(“请设置 OPENAI_API_KEY 环境变量”)
}
client := openai.NewClient(
option.WithAPIKey(apiKey),
)
ctx := context.Background()
// 没有参数时进入交互模式
if len(os.Args) < 2 {
runInteractive(ctx, client)
return
}
// 处理单次查询
prompt := os.Args[1]
streamResponse(ctx, client, prompt)
}
func runInteractive(ctx context.Context, client *openai.Client) {
fmt.Println("ChatGPT CLI 工具 (流式输出版)")
fmt.Println("输入你的问题,按回车发送,输入 exit 退出")
fmt.Println("---")
for {
fmt.Print("\n你: ")
var input string
fmt.Scanln(&input)
if input == "exit" || input == "quit" {
fmt.Println("再见!")
break
}
if input == "" {
continue
}
fmt.Print("\nAI: ")
streamResponse(ctx, client, input)
}
}
func streamResponse(ctx context.Context, client *openai.Client, prompt string) {
stream, err := client.Chat.Completions.NewStreaming(ctx, openai.ChatCompletionNewParams{
Model: openai.F(openai.GPT4oMini),
Messages: openai.F([]openai.ChatCompletionMessageParamUnion{
openai.UserMessage(prompt),
}),
Temperature: openai.F(0.7),
})
if err != nil {
log.Printf("请求错误: %v", err)
return
}
defer stream.Close()
// 流式读取响应
for {
choice, err := stream.Recv()
if err != nil {
if err == io.EOF {
break
}
log.Printf("读取错误: %v", err)
break
}
if len(choice.Choices) > 0 && choice.Choices[0].Delta.Content != “” {
fmt.Print(choice.Choices[0].Delta.Content)
}
}
fmt.Println()
}
“`
### 5. 运行程序
先设置 API Key:
“`bash
export OPENAI_API_KEY=”your-api-key-here”
“`
跑起来:
“`bash
go run main.go “用 Go 语言实现快速排序”
“`
或者进交互模式:
“`bash
go run main.go
“`
### 6. 打包成可执行文件
“`bash
go build -o gpt-cli main.go
“`
之后直接 `./gpt-cli` 就能用,不用每次都现编译。
## 运行结果
跑起来后,输入问题,你会看到类似这样的输出:
“`
你: 解释一下什么是流式输出
AI: 流式输出(Streaming Output)是一种数据传输技术,允许服务器在生成完整响应之前就开始向客户端发送数据…
(内容一个字一个字地冒出来,不用等全部生成完)
“`
交互模式下的完整流程:
“`
ChatGPT CLI 工具 (流式输出版)
输入你的问题,按回车发送,输入 exit 退出
—
你: 什么是 Go 语言的 defer 关键字?
AI: defer 是 Go 语言的一个关键字,用于延迟函数的执行。当你在一个函数中使用 defer 时,被 defer 修饰的函数会在该函数返回之前执行…
你: 它有什么实际用途?
AI: defer 的常见用途包括:
1. 资源清理 – 关闭文件、网络连接等
2. 释放锁 – 在解锁之前确保锁被释放
3. 打印调试信息 – 在函数退出时打印执行时间
你: exit
再见!
“`
内容是一段段显示的,用户能立刻看到 AI 的回复,不用等完整响应生成完。这体验和网页版 ChatGPT 挺像的。
## 总结
本文展示了用 Go 调用 OpenAI API 实现流式输出的方法,顺便做了个能用的命令行工具。流式输出让用户能早点看到 AI 的回复,体验比干等好太多。
核心就这几个要点:
1. 调用 `client.Chat.Completions.NewStreaming` 而非 `New`,创建流式请求
2. 循环调用 `stream.Recv()` 逐块接收内容
3. 每收到一块就立刻打印,实现逐字显示的效果
4. 处理 `io.EOF`——这是流结束的正常信号,别当错误处理
这是个基础版本,你可以接着扩展:
– 支持切换不同模型
– 加系统提示词
– 流式输出 JSON 数据
– 错误处理做得更完善
– 加上下文记忆,能记住对话历史
流式输出这技术很实用,Web 应用、移动端都能用,用户体验提升很明显。希望这篇文章对你有帮助!