Go语言接入Gemini API

使用Go语言快速接入Google Gemini API

Gemini 是 Google 开发的一系列多模态生成式 AI 模型。Gemini 模型可以接受提示中的文本和图片(具体取决于您选择的模型变体),并输出文本回复。旧版 PaLM 模型接受纯文本和输出文本响应。

在上一篇文章,我们提到了谷歌提供每分钟免费调用60次的Gemini API,现在我们来介绍下如何申请和使用它。GemniAPI支持多种编程语言,包括:

申请API KEY

aistudio.google.com 拥有谷歌账号即可点击Create API Key按钮创建API KEY.

cURL测试

我们可以看到谷歌也提供了测试的CURL代码,测试一下能不能运行。由于我是在windows平台,curl命令不太会用,就用了apipost,其实就是一个post请求,附带刚才申请的key,然后输入一个json格式的提问,返回json格式的回答。

curl \
  -H 'Content-Type: application/json' \
  -d '{"contents":[{"parts":[{"text":"Write a story about a magic backpack"}]}]}' \
  -X POST https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=YOUR_API_KEY

可以看到Gemini用英语讲了一个不算短的故事,我在使用中也发现Gemini的回复经常会很长,不过可以指定回复的最大token。

模型变体

Gemini API 提供针对特定应用场景优化的不同模型。下表介绍了每个表的属性。

官方go-quickstart文档

go-quickstart 虽然不想踩一捧一,但至少Gemini给出的文档是可以正常运行的,按照文档一步步照做就行。

前提条件

  • Go版本1.20+
  • 获取API密钥
  • 安装SDK go get github.com/google/generative-ai-go

初始化模型

您需要先导入并初始化生成模型,然后才能进行任何 API 调用。 注意事项:

  • NewClient()生成的client指针不需要重复生成,只需一次初始化,保存起来重复利用即可,并且client可以在多个goroutine中使用。
  • 不确定client是否需要手动关闭,主动关闭调用client.Close()即可。
  • 使用专门针对您的使用场景的模型(例如,gemini-pro-vision 适用于多模态输入,gemini-pro 适用于文本生成和连续对话)
import "github.com/google/generative-ai-go/genai"
import "google.golang.org/api/option"

ctx := context.Background()
// Access your API key as an environment variable (see "Set up your API key" above)
client, err := genai.NewClient(ctx, option.WithAPIKey(os.Getenv("API_KEY")))
if err != nil {
    log.Fatal(err)
}
defer client.Close()

model := client.GenerativeModel("MODEL_NAME")

文本生成

文本生成非常简单,使用gemini-pro模型,只需调用GenerateContent()即可,参数为输入文本。

ctx := context.Background()
// Access your API key as an environment variable (see "Set up your API key" above)
client, err := genai.NewClient(ctx, option.WithAPIKey(os.Getenv("API_KEY")))
if err != nil {
    log.Fatal(err)
}
defer client.Close()

// For text-only input, use the gemini-pro model
model := client.GenerativeModel("gemini-pro")
resp, err := model.GenerateContent(ctx, genai.Text("Write a story about a magic backpack."))
if err != nil {
    log.Fatal(err)
}

返回的resp对象如何获取返回的文本

if resp.Candidates != nil {
    for _, v := range resp.Candidates {
        for _, k := range v.Content.Parts {
            fmt.Println(k.(genai.Text))
        }
    }
}

连续对话

借助 Gemini,您可以构建多轮自由对话。该 SDK 通过管理对话状态简化了流程,因此与 GenerateContent 不同,您无需自行存储对话历史记录。

如需构建多轮对话(如聊天),请使用 gemini-pro 模型,并通过调用 StartChat() 来初始化聊天。然后,使用 SendMessage() 发送一条新的用户消息,系统还会将该消息和响应附加到聊天记录中。

与对话内容关联的 role 有两个可能的选项:

  • user:提供提示的角色。这是 SendMessage 调用的默认值。

  • model:提供响应的角色。在使用现有 history 调用 StartChat() 时,可以使用此角色。

ctx := context.Background()
// Access your API key as an environment variable (see "Set up your API key" above)
client, err := genai.NewClient(ctx, option.WithAPIKey(os.Getenv("API_KEY")))
if err != nil {
    log.Fatal(err)
}
defer client.Close()

// For text-only input, use the gemini-pro model
model := client.GenerativeModel("gemini-pro")
// Initialize the chat
cs := model.StartChat()
cs.History = []*genai.Content{
    &genai.Content{
    Parts: []genai.Part{
        genai.Text("Hello, I have 2 dogs in my house."),
    },
    Role: "user",
    },
    &genai.Content{
    Parts: []genai.Part{
        genai.Text("Great to meet you. What would you like to know?"),
    },
    Role: "model",
    },
}

resp, err := cs.SendMessage(ctx, genai.Text("How many paws are in my house?"))
if err != nil {
    log.Fatal(err)
}

连续对话官方文档说的非常清楚了,首先调用StartChat(),接着调用SendMessage(),每次调用后,系统会将对话记录附加到聊天历史中,不需要每次手动附带History对话历史。注意事项:

  • model.StartChat() 返回的ChatSession对象可以保存,接下来每次使用相当于继续对话
  • 不需要每次发送消息手动附带History对话历史,ChatSession每次发送消息都会保存历史记录
  • 对话中断也可以恢复,此时需要手动给ChatSession附带History
  • 在具体业务中还是需要手动保存每次对话到数据库中

多模态输入

Gemini 提供了一个多模态模型 (gemini-pro-vision),因此您可以同时输入文本和图片。请务必查看提示的图片要求。

当提示输入同时包含文本和图片时,请将 gemini-pro-vision 模型与 GenerateContent 方法结合使用以生成文本输出:

ctx := context.Background()
// Access your API key as an environment variable (see "Set up your API key" above)
client, err := genai.NewClient(ctx, option.WithAPIKey(os.Getenv("API_KEY")))
if err != nil {
  log.Fatal(err)
}
defer client.Close()

// For text-and-image input (multimodal), use the gemini-pro-vision model
model := client.GenerativeModel("gemini-pro-vision")

imgData1, err := os.ReadFile(pathToImage1)
if err != nil {
  log.Fatal(err)
}

imgData2, err := os.ReadFile(pathToImage1)
if err != nil {
  log.Fatal(err)
}

prompt := []genai.Part{
  genai.ImageData("jpeg", imgData1),
  genai.ImageData("jpeg", imgData2),
  genai.Text("What's different between these two pictures?"),
}
resp, err := model.GenerateContent(ctx, prompt...)

if err != nil {
  log.Fatal(err)
}

流失传输

默认情况下,模型会在完成整个生成过程后返回响应。通过不等待整个结果,而是使用流式传输来处理部分结果,您可以实现更快的互动。

以下示例展示了如何使用 GenerateContentStream 方法实现流式传输,以根据文本和图片输入提示生成文本。

ctx := context.Background()
// Access your API key as an environment variable (see "Set up your API key" above)
client, err := genai.NewClient(ctx, option.WithAPIKey(os.Getenv("API_KEY")))
if err != nil {
  log.Fatal(err)
}
defer client.Close()

// For text-and-image input (multimodal), use the gemini-pro-vision model
model := client.GenerativeModel("gemini-pro-vision")

imageBytes, err := os.ReadFile(pathToImage)

img := genai.ImageData("jpeg", imageBytes)
prompt := genai.Text("Tell me a story about this animal")
iter := model.GenerateContentStream(ctx, img, prompt)

for {
  resp, err := iter.Next()
  if err == iterator.Done {
    break
  }
  if err != nil {
    log.Fatal(err)
  }

  // ... print resp
}

计算token

虽然目前Gemini并不是按照token长度计费,但在自己的系统中,使用长提示时,在向模型发送任何内容之前统计令牌数可能很有用。以下示例展示了如何针对各种用例使用 CountTokens():

// For text-only input
text := "Parrots can be green and live a long time."
resp, err := model.CountTokens(ctx, genai.Text(text))
if err != nil {
  log.Fatal(err)
}
fmt.Println(resp.TotalTokens)

// For text-and-image input (multimodal)
text := "Parrots can be green and live a long time."
imageBytes, err := os.ReadFile(pathToImage)
if err != nil {
  log.Fatal(err)
}

resp, err := model.CountTokens(
    ctx,
    genai.Text(text),
    genai.ImageData("png", imageBytes))
  if err != nil {
    log.Fatal(err)
}
fmt.Println(resp.TotalTokens)

配置模型参数

您发送到模型的每个提示都包含用于控制模型如何生成回答的参数值。对于不同的参数值,模型会生成不同的结果。详细了解模型参数。配置将在模型实例的生命周期内保持有效。

模型参数包括:

  • max_tokens:输出令牌数量上限:指定响应中可生成的令牌数量上限。一个词元约为 4 个字符。100 个令牌大约对应 60-80 个单词。
  • temperature:温度: 温度用于控制令牌选择的随机性。该温度用于在响应生成过程中进行采样(在应用 topP 和 topK 时发生)。较低的温度适合需要更具确定性或不够开放的回答的提示,而较高的温度可以产生更加多样化或更具创意的结果。温度为 0 表示确定性,即始终选择概率最高的响应。
  • topK:topK参数可更改模型选择输出令牌的方式。topK 为 1 表示所选词元是模型词汇表的所有词元中概率最高的词元(也称为贪心解码)。如果 topK 为 3,则表示系统将根据该温度从 3 个概率最高的词元中选择下一个词元。对于每个词元选择步骤,系统都会对概率最高的 topK 词元进行采样。然后,系统会根据 topP 进一步过滤令牌,并使用温度采样选择最终令牌。
  • topP:topP 参数可更改模型选择输出令牌的方式。系统会按照概率从最高到最低的顺序选择词元,直到所选词元的概率总和等于 topP 值。例如,如果词元 A、B 和 C 的概率分别是 0.3、0.2 和 0.1,并且 topP 值为 0.5,则模型将根据温度选择 A 或 B 作为下一个词元,并将 C 作为候选词元。topP 的默认值为 0.95。
  • stop_sequences :停止序列。告知模型停止生成内容。停止序列可以是任何字符序列。请尽量避免使用可能出现在生成内容中的字符序列。
// 在model := client.GenerativeModel("gemini-pro") 后定义模型参数
var temperature float32 = 0.9
var topP float32 = 0.95
var topK int32 = 40
var maxOutputTokens int32 = 512
model.MaxOutputTokens = &maxOutputTokens
model.Temperature = &temperature
model.TopP = &topP
model.TopK = &topK
model.StopSequences = []string{"story ends", "this story ends"}

使用安全设置

您可以使用安全设置来调整收到可能被视为有害的响应的可能性。默认情况下,安全设置会在所有维度上屏蔽概率中等和/或高可能属于不安全内容的内容。

分别有四个安全设置:HarmCategoryHarassment、HarmCategorySexuallyExplicit、HarmCategoryHateSpeech、HarmCategoryDangerousContent。

分别有五种屏蔽设置:HarmBlockNone、HarmBlockOnlyHigh、HarmBlockMediumAndAbove、HarmBlockLowAndAbove、HarmBlockUnspecified。

下面是设置一项安全设置的具体方法:

model := client.GenerativeModel("gemini-pro")
model.SafetySettings = []*genai.SafetySetting{
    {
        Category:  genai.HarmCategoryHarassment,
        Threshold: genai.HarmBlockNone,
    },
    {
        Category:  genai.HarmCategorySexuallyExplicit,
        Threshold: genai.HarmBlockNone,
    },
    {
        Category:  genai.HarmCategoryHateSpeech,
        Threshold: genai.HarmBlockNone,
    },
    {
        Category:  genai.HarmCategoryDangerousContent,
        Threshold: genai.HarmBlockNone,
    },
}

总结

通过跟着官方文档或者这篇博客一步步做,我相信您已经掌握了如何使用 Gemini API,将其应用在自己的应用中。我自己也使用了Gemini API一段时间,体验下来还可以。

  • 访问需要魔法
  • 访问速度不算太快,可能使用stream流式响应效果更好
  • 对于中文的理解能力、词库都还不错,偶尔会以繁体中文输出
  • 对于prompt的理解能力还有欠缺,有时候我让它以100个词之内输出之类的限制会被他忽略,这点chatgpt会做的更好
  • Gemini擅长讲故事,给他一个很短的提示词,他就会给你讲一大段故事,不太会偷懒

如果你需要更多的帮助和进阶使用,可以查看官方的提示策略多模态提示。希望这篇博客对您有所帮助。

Licensed under CC BY-NC-SA 4.0
加载中...
感谢Jimmy 隐私政策