8
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Last updated at Posted at 2025-04-27

はじめに

最近、ADK(Agent Development Kit) 1の記事をいくつか目にして、
「お、これ Go でもできるかな〜」と思ったのが今回のきっかけです。

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

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

実行環境

  • Golang
    • バージョン:1.24.2
  • エディタ
    • VSCode
  • APIキーの取得

作成したものの概要

今回作成したコードはGitHubに公開しています。
ぜひこちらもあわせてご覧ください!
こちらのリポジトリを使用する際はルートディレクトリに.envを作成し、そちらに以下の環境変数を作っておいてください。

.env
GOOGLE_GENAI_USE_VERTEXAI=false         
GOOGLE_API_KEY="YOUR_API_KEY"    # 取得したAPIキーを入れてください

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

ここに適当にメッセージを入力すると、Geminiがレスポンスを返してくれる仕組みになっています。
スクリーンショット 2025-04-27 15.03.14.png

ソースコードの紹介

今回は go-genai ライブラリを使用して作成してみました。

主なファイル構成は、以下の2つに分かれています。

  • cmd/main.go:アプリケーションのエントリーポイント
  • internal/chat/chat.go:チャットロジックの本体

それぞれの役割について簡単に説明します。

main.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)

例えば、以下のようにコマンドラインで調整できるようになります。

terminal
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 ファイルについて

全体はこのようになっています。

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エージェントに発展させることは可能です。
もし興味のある方は是非、自分なりにカスタマイズしてみてください!

参考

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

8
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?