はじめに
最近、ADK(Agent Development Kit) 1の記事をいくつか目にして、
「お、これ Go でもできるかな〜」と思ったのが今回のきっかけです。
そこで今回は、まずはシンプルな形でGemini API × Go 公式SDKを使ったAIエージェント風なものを作ってみることにしました。
本記事では、その最小構成のサンプルを紹介しつつ、 今後さらに拡張していくためのベース作りも意識しています。
実行環境
- Golang
- バージョン:
1.24.2
- バージョン:
- エディタ
- VSCode
- APIキーの取得
作成したものの概要
今回作成したコードはGitHubに公開しています。
ぜひこちらもあわせてご覧ください!
こちらのリポジトリを使用する際はルートディレクトリに.env
を作成し、そちらに以下の環境変数を作っておいてください。
GOOGLE_GENAI_USE_VERTEXAI=false
GOOGLE_API_KEY="YOUR_API_KEY" # 取得したAPIキーを入れてください
実際に go run cmd/main.go
を実行すると、
以下のように「>>>」とプロンプトが表示されます。
ここに適当にメッセージを入力すると、Geminiがレスポンスを返してくれる仕組みになっています。
ソースコードの紹介
今回は go-genai ライブラリを使用して作成してみました。
主なファイル構成は、以下の2つに分かれています。
-
cmd/main.go
:アプリケーションのエントリーポイント -
internal/chat/chat.go
:チャットロジックの本体
それぞれの役割について簡単に説明します。
main.go ファイルについて
全体はこのようになっています。
package main
import (
"context"
"flag"
"log"
"os"
"github.com/anton-fuji/go-genai-sdk/internal/chat"
"github.com/google/generative-ai-go/genai"
"github.com/joho/godotenv"
"google.golang.org/api/option"
)
func main() {
model := flag.String("model", "gemini-2.0-flash", "model name")
temp := flag.Float64("temp", 0.5, "temperature 0.0-1.0")
flag.Parse()
_ = godotenv.Load()
apiKey := os.Getenv("GOOGLE_API_KEY")
if apiKey == "" {
log.Fatal("GOOGLE_API_KEYが未設定です")
}
ctx := context.Background()
client, err := genai.NewClient(ctx, option.WithAPIKey(apiKey))
if err != nil {
log.Fatal(err)
}
defer client.Close()
ch, err := chat.New(ctx, client, *model, float32(*temp))
if err != nil {
log.Fatal(err)
}
if err := ch.Run(ctx, os.Stdin, os.Stdout); err != nil {
log.Fatal(err)
}
}
モデルと生成結果のランダム性をフラグで調整
model := flag.String("model", "gemini-2.0-flash", "model name")
temp := flag.Float64("temp", 0.5, "temperature 0.0-1.0")
flag.Parse()
-
--model
- 使用するモデルを指定できます(デフォルトは gemini-2.0-flash)
-
--temp
- 生成結果のランダムさを設定できます。(0.0〜1.0)
例えば、以下のようにコマンドラインで調整できるようになります。
go run cmd/main.go -model gemini-2.0-flash -temp 0.7
.envファイルからAPIキーを読み込む
_ = godotenv.Load()
apiKey := os.Getenv("GOOGLE_API_KEY")
if apiKey == "" {
log.Fatal("GOOGLE_API_KEYが未設定です")
}
APIキーなどのシークレット情報はハードコーディングを避けたかったので、.env
ファイルを作成し、godotenv
を使用して読み込んでいます。
Gemini APIクライアントを初期化する
client, err := genai.NewClient(ctx, option.WithAPIKey(apiKey))
defer client.Close()
環境変数から取得したAPIキーを使って、Gemini APIクライアントを作成します。
defer client.Close()
で、プログラム終了時にクライアントを安全に閉じるようにしています。(defer
を忘れないようにしましょう)
chat.go ファイルについて
全体はこのようになっています。
package chat
import (
"bufio"
"context"
"fmt"
"io"
"github.com/google/generative-ai-go/genai"
)
type Chat struct {
chat *genai.ChatSession
}
func New(ctx context.Context, client *genai.Client, model string, temp float32) (*Chat, error) {
mdl := client.GenerativeModel(model)
mdl.GenerationConfig = genai.GenerationConfig{
Temperature: genai.Ptr(temp),
}
cht := mdl.StartChat()
return &Chat{chat: cht}, nil
}
func (c *Chat) Run(ctx context.Context, in io.Reader, out io.Writer) error {
sc := bufio.NewScanner(in)
fmt.Fprint(out, ">>>")
for sc.Scan() {
user := sc.Text()
resp, err := c.chat.SendMessage(ctx, genai.Text(user))
if err != nil {
fmt.Fprintln(out, "error:", err)
} else if len(resp.Candidates) > 0 {
for _, p := range resp.Candidates[0].Content.Parts {
if txt, ok := p.(genai.Text); ok {
fmt.Fprintln(out, string(txt))
}
}
}
fmt.Println(out, ">>>")
}
return sc.Err()
}
chat.go
でチャットのメイン処理を実装していて、先ほどのmain.go
の最後の処理で出ていたchat.New
の詳しい処理をchat.go
に書いています。
New関数でChatインスタンスの作成、初期化
func New(ctx context.Context, client *genai.Client, model string, temp float32) (*Chat, error) {
mdl := client.GenerativeModel(model)
mdl.GenerationConfig = genai.GenerationConfig{
Temperature: genai.Ptr(temp),
}
cht := mdl.StartChat()
return &Chat{chat: cht}, nil
}
New関数では以下のステップで処理していきます。
① モデルの選択
-
client.GenerativeModel(model)
で使うAIモデルの指定
② 生成設定を適用
-
GenerationConfig
でモデルの振る舞いを適用
③ セッションの開始
-
StartChat()
でチャット空間を作成 - 毎回新規リクエストではなく、セッションを保った連続的な対話を実現
④ インスタンスを返す
-
return &Chat{chat: cht}, nil
で新しく作成したcht
を構造体にセットして返す -
main.go
ではChatインスタンスを受け取った後、Run()
関数を呼び出す流れになっています
Run関数について
func (c *Chat) Run(ctx context.Context, in io.Reader, out io.Writer) error {
sc := bufio.NewScanner(in)
fmt.Fprint(out, ">>>")
for sc.Scan() {
user := sc.Text()
resp, err := c.chat.SendMessage(ctx, genai.Text(user))
if err != nil {
fmt.Fprintln(out, "error:", err)
} else if len(resp.Candidates) > 0 {
for _, p := range resp.Candidates[0].Content.Parts {
if txt, ok := p.(genai.Text); ok {
fmt.Fprintln(out, string(txt))
}
}
}
fmt.Fprint(out, ">>>")
}
return sc.Err()
}
こちらの関数は以下のようなステップで処理が進みます。
① プロンプトの表示
- まず、「>>>」 を表示して入力待ち状態になります
② 入力をスキャン
-
sc.Scan()
で新しい入力があるたびにtrue
を返します
③ 入力内容をGemini APIに送信
-
SendMessage
で、入力されたテキストをGemini APIに送信します
④ 応答表示
- Gemini APIは「Candidates」を複数返す仕様になっています
- ここでは最初の候補(
Candidates[0]
)だけを取り出して表示しています - Contentの中にはパーツ(
Parts
)があり、テキスト部分だけを取り出して出力しています
この辺りはgo-genaiに詳しい仕様が載っているのでそちらを参照してください。
⑤ エラー処理
⑥ 再び「>>>」を表示して次の入力待ち
-
fmt.Fprint(out, ">>>")
で次の入力を待ちます
Gemini API 無料枠について
Gemini APIには、月あたり60リクエストまで無料で利用できる枠が用意されています。
Gemini 1.0 Pro や Gemini 1.5 Pro に加えて、最近リリースされた Gemini 2.0 Flash も無料枠の対象モデルに含まれています。
さいごに
今回は、go-genaiライブラリを使用してGemini APIを使った簡易的なAIエージェント風なものを作成してみました。
こちらは工夫次第でツールを追加したりすることで、AIエージェントに発展させることは可能です。
もし興味のある方は是非、自分なりにカスタマイズしてみてください!
参考
-
複数のエージェントが連携するマルチエージェントを手軽に実装する機能 ↩