# Go 语言调用 OpenAI API 完全指南:从入门到实战
## 背景介绍
人工智能飞速发展的今天,OpenAI 的 GPT 系列模型已经成为开发者不可或缺的工具。无论是构建智能聊天机器人,还是实现自动化文本处理,调用 OpenAI API 都是最直接有效的方案。
Go 语言以其高效的并发模型和简洁的部署能力,在服务端开发中占据重要地位。那么,如何在 Go 项目中优雅地调用 OpenAI API?本文将带你从零开始,构建一个完整的 Go + OpenAI 集成方案。
## 问题描述
很多开发者在初次接触 OpenAI API 时,会遇到以下困惑:
– 不清楚 API 的认证方式和请求格式
– 不知道如何处理流式响应
– 不知道如何实现上下文记忆
– 如何在生产环境中确保调用稳定性
本文将逐一解决这些问题,提供可直接落地的代码方案。
## 详细步骤
### 1. 环境准备
确保开发环境中已安装 Go(建议 Go 1.21 及以上版本):
“`bash
go version
“`
创建项目目录并初始化:
“`bash
mkdir openai-go-demo && cd openai-go-demo
go mod init openai-go-demo
“`
安装 OpenAI SDK:
“`bash
go get github.com/sashabaranov/go-openai
“`
### 2. 基本 API 调用
创建 `main.go` 文件,实现最基本的文本补全功能:
“`go
package main
import (
“context”
“fmt”
“log”
“github.com/sashabaranov/go-openai”
)
func main() {
client := openai.NewClient(“your-api-key-here”)
resp, err := client.CreateCompletion(
context.Background(),
openai.CompletionRequest{
Model: openai.GPT3Dot5Turbo,
Prompt: “用一句话介绍 Go 语言的特色”,
MaxTokens: 100,
Temperature: 0.7,
},
)
if err != nil {
log.Fatalf(“API 调用失败: %v”, err)
}
fmt.Println(“回复:”, resp.Choices[0].Text)
}
“`
### 3. Chat API 调用(推荐)
GPT-3.5 Turbo 及以上版本使用 Chat API,效果更好:
“`go
package main
import (
“context”
“fmt”
“log”
“os”
“github.com/sashabaranov/go-openai”
)
func main() {
apiKey := os.Getenv(“OPENAI_API_KEY”)
if apiKey == “” {
log.Fatal(“请设置 OPENAI_API_KEY 环境变量”)
}
client := openai.NewClient(apiKey)
messages := []openai.ChatCompletionMessage{
{
Role: openai.ChatMessageRoleSystem,
Content: “你是一位专业的 Go 语言技术顾问”,
},
{
Role: openai.ChatMessageRoleUser,
Content: “请解释 Go 语言中 goroutine 和 thread 的区别”,
},
}
resp, err := client.CreateChatCompletion(
context.Background(),
openai.ChatCompletionRequest{
Model: openai.GPT3Dot5Turbo,
Messages: messages,
},
)
if err != nil {
log.Fatalf(“Chat API 调用失败: %v”, err)
}
fmt.Println(“回复:”, resp.Choices[0].Message.Content)
}
“`
### 4. 流式响应处理
对于长文本生成,流式响应能显著缩短首字节时间:
“`go
package main
import (
“context”
“fmt”
“log”
“os”
“github.com/sashabaranov/go-openai”
)
func main() {
client := openai.NewClient(os.Getenv(“OPENAI_API_KEY”))
req := openai.ChatCompletionRequest{
Model: openai.GPT3Dot5Turbo,
Messages: []openai.ChatCompletionMessage{
{
Role: openai.ChatMessageRoleUser,
Content: “写一首关于代码的诗”,
},
},
Stream: true,
}
stream, err := client.CreateChatCompletionStream(context.Background(), req)
if err != nil {
log.Fatalf(“创建流失败: %v”, err)
}
defer stream.Close()
fmt.Println(“生成中: “)
for {
chunk, err := stream.Recv()
if err != nil {
break
}
if len(chunk.Choices) > 0 {
fmt.Print(chunk.Choices[0].Delta.Content)
}
}
fmt.Println()
}
“`
### 5. 上下文记忆实现
在实际对话中保持上下文,需要维护消息历史:
“`go
package main
import (
“context”
“fmt”
“log”
“os”
“github.com/sashabaranov/go-openai”
)
type ChatSession struct {
client *openai.Client
messages []openai.ChatCompletionMessage
systemPrompt string
}
func NewChatSession(apiKey, systemPrompt string) *ChatSession {
return &ChatSession{
client: openai.NewClient(apiKey),
messages: []openai.ChatCompletionMessage{
{
Role: openai.ChatMessageRoleSystem,
Content: systemPrompt,
},
},
systemPrompt: systemPrompt,
}
}
func (s *ChatSession) Ask(question string) (string, error) {
s.messages = append(s.messages, openai.ChatCompletionMessage{
Role: openai.ChatMessageRoleUser,
Content: question,
})
resp, err := s.client.CreateChatCompletion(
context.Background(),
openai.ChatCompletionRequest{
Model: openai.GPT3Dot5Turbo,
Messages: s.messages,
},
)
if err != nil {
return “”, err
}
reply := resp.Choices[0].Message.Content
s.messages = append(s.messages, openai.ChatCompletionMessage{
Role: openai.ChatMessageRoleAssistant,
Content: reply,
})
return reply, nil
}
func main() {
session := NewChatSession(
os.Getenv(“OPENAI_API_KEY”),
“你是一位专业的技术作家”,
)
// 第一轮对话
reply1, _ := session.Ask(“Go 语言有什么优势?”)
fmt.Println(“Q1: Go 语言有什么优势?”)
fmt.Println(“A1:”, reply1)
// 第二轮对话(自动包含第一轮上下文)
reply2, _ := session.Ask(“能举个例子吗?”)
fmt.Println(“\nQ2: 能举个例子吗?”)
fmt.Println(“A2:”, reply2)
}
“`
### 6. 错误处理与重试机制
生产环境中需要考虑网络波动:
“`go
package main
import (
“context”
“fmt”
“log”
“os”
“time”
“github.com/sashabaranov/go-openai”
)
func withRetry(fn func() error, maxRetries int) error {
var err error
for i := 0; i < maxRetries; i++ {
if err = fn(); err == nil {
return nil
}
// 判断是否可重试
if !isRetryable(err) {
return err
}
// 指数退避
wait := time.Duration(1<