サクッとGoで AI エージェントを構築してみる

# はじめに
最近、[**ADK（Agent Development Kit）** ](https://google.github.io/adk-docs/)[^1]の記事をいくつか目にして、
「お、これ Go でもできるかな〜」と思ったのが今回のきっかけです。

そこで今回は、まずはシンプルな形で**Gemini API × Go** 公式SDKを使ったAIエージェント風なものを作ってみることにしました。

本記事では、その最小構成のサンプルを紹介しつつ、 今後さらに拡張していくためのベース作りも意識しています。

# 実行環境
- Golang 
    - バージョン：`1.24.2`
- エディタ
    - VSCode
- APIキーの取得
    - [**Gemini API**](https://ai.google.dev/gemini-api/docs/api-key?utm_source=chatgpt.com&hl=ja#send-gemini-api-request)

# 作成したものの概要
今回作成したコードはGitHubに公開しています。
ぜひこちらもあわせてご覧ください！
こちらのリポジトリを使用する際はルートディレクトリに`.env`を作成し、そちらに以下の環境変数を作っておいてください。
```bash:.env
GOOGLE_GENAI_USE_VERTEXAI=false         
GOOGLE_API_KEY="YOUR_API_KEY"    # 取得したAPIキーを入れてください
```

https://github.com/anton-fuji/go-genai-sdk


実際に `go run cmd/main.go` を実行すると、  
以下のように「**>>>**」とプロンプトが表示されます。

ここに適当にメッセージを入力すると、Geminiがレスポンスを返してくれる仕組みになっています。
![スクリーンショット 2025-04-27 15.03.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3763692/b37c510f-d884-45f4-bc0a-5037d5932fd1.png)


# ソースコードの紹介
今回は [**go-genai**](https://github.com/googleapis/go-genai) ライブラリを使用して作成してみました。

主なファイル構成は、以下の2つに分かれています。
- `cmd/main.go`：アプリケーションのエントリーポイント
- `internal/chat/chat.go`：チャットロジックの本体

それぞれの役割について簡単に説明します。
## main.go ファイルについて
全体はこのようになっています。
```golang: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)
	}
}
```
###  モデルと生成結果のランダム性をフラグで調整
```golang
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）
    
例えば、以下のようにコマンドラインで調整できるようになります。
```sh:terminal
go run cmd/main.go -model gemini-2.0-flash -temp 0.7
```

### .envファイルからAPIキーを読み込む
```golang
_ = godotenv.Load()
apiKey := os.Getenv("GOOGLE_API_KEY")
if apiKey == "" {
	log.Fatal("GOOGLE_API_KEYが未設定です")
}
```
APIキーなどのシークレット情報はハードコーディングを避けたかったので、`.env`ファイルを作成し、`godotenv`を使用して読み込んでいます。

### Gemini APIクライアントを初期化する
```golang
client, err := genai.NewClient(ctx, option.WithAPIKey(apiKey))
defer client.Close()
```
環境変数から取得したAPIキーを使って、Gemini APIクライアントを作成します。
`defer client.Close()`で、プログラム終了時にクライアントを安全に閉じるようにしています。（`defer`を忘れないようにしましょう）



## chat.go ファイルについて
全体はこのようになっています。
```golang: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インスタンスの作成、初期化
```golang
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関数について
```golang
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**](https://github.com/googleapis/go-genai)に詳しい仕様が載っているのでそちらを参照してください。
**⑤ エラー処理**

**⑥ 再び「>>>」を表示して次の入力待ち**
- `fmt.Fprint(out, ">>>")`で次の入力を待ちます

# Gemini API 無料枠について
Gemini APIには、月あたり**60リクエスト**まで無料で利用できる枠が用意されています。  
Gemini 1.0 Pro や Gemini 1.5 Pro に加えて、最近リリースされた Gemini 2.0 Flash も無料枠の対象モデルに含まれています。

# さいごに
今回は、[**go-genai**](https://github.com/googleapis/go-genai)ライブラリを使用してGemini APIを使った簡易的なAIエージェント風なものを作成してみました。
こちらは工夫次第でツールを追加したりすることで、AIエージェントに発展させることは可能です。
もし興味のある方は是非、自分なりにカスタマイズしてみてください！


# 参考
https://github.com/nvcnvn/adk-golang

https://github.com/googleapis/go-genai

https://ai.google.dev/gemini-api/docs/models?hl=ja


[^1]:複数のエージェントが連携するマルチエージェントを手軽に実装する機能