ゆるエンジニアはいろいろ遊びたい

FAエンジニアが週末にいろいろ遊ぶブログです

Raspberry Pi5 でGoogle Gemini APIを使う

生成AIが当たり前に使われるようになっており、私もこのラズパイのプログラミングはすべてAIに任せっきりで全然コーディング力が身に付きません。
今回もAIの力を借りて、ラズパイ5でGoogleの生成AI「Gemini API」を使いたいと思います。
APIを使用するためにはAPIキーが必要です。これはネット上にいろいろ情報が転がっているのですぐに取得できると思います。
ただし、中には有料プランに誘導するような記事もあるので要注意です。
基本的には無料で使用できますが、トークン数の制限だったり、送信した情報が再学習に使われるか否かなどで変わってくるので、使用状況に応じて有料プランにするかの判断をした方がいいです。
今回はAPIキーはすでにあるものとして進めます。(決して説明が面倒だからってことじゃナイヨ!)
まずは、環境変数APIキーを登録します。
ターミナルで、以下を入力

nano ~/.bashrc

nanoエディターが開き、いろいろ文字が出てきますが、一番下に追記します。

export GOOGLE_API_KEY="APIキー名"

APIキー名はあなたのAPIキー名です。=の前後はスペースはつけないようにしましょう。python慣れしているとどうしてもつけてしまいますが、つけるとエラーがでます。
「ctl + O」で保存「ctl + x」で閉じる。
その後、ターミナルで以下を入力すると、反映し永続化します。

source ~/.bashrc

続いて、仮想環境下で、モジュールをインストールします。

pip install google-generativeai

コードは以下の通り

import os
import json
import google.generativeai as genai

class GeminiClient:
    def __init__(self, model_name="gemini-2.5-flash", history_file="gemini_history.json"):
        self.model_name = model_name
        self.history_file = history_file

        # 🔹 APIキー確認
        api_key = os.environ.get("GOOGLE_API_KEY")
        if not api_key:
            raise ValueError("環境変数 GOOGLE_API_KEY が設定されていません!")
        genai.configure(api_key=api_key)

        # 🔹 Gemini モデル
        self.model = genai.GenerativeModel(model_name)

        # 🔹 履歴読み込み
        self.history = self.load_history()

        # 🔹 チャット開始
        self.chat = self.model.start_chat(history=self.history)

    # ==========================
    # 履歴の読み込み
    # ==========================
    def load_history(self):
        if not os.path.exists(self.history_file):
            return []

        try:
            with open(self.history_file, "r", encoding="utf-8") as f:
                data = f.read().strip()
                if not data:
                    return []
                items = json.loads(data)
                # 最新100件だけ返す
                if isinstance(items, list):
                    return items[-100:]
                return []
        except (json.JSONDecodeError, OSError):
            print("⚠ 履歴ファイルが壊れていたのでリセットしました。")
            return []

    # ==========================
    # 履歴の保存
    # ==========================
    def save_history(self):
        serializable = []

        # chat.history をリスト化して最新100件にトリム
        try:
            history_list = list(self.chat.history)
        except Exception:
            history_list = getattr(self.chat, "history", []) or []

        history_list = history_list[-100:]

        # 可能ならチャットオブジェクト側の履歴も更新
        try:
            self.chat.history = history_list
        except Exception:
            pass

        for item in history_list:
            role = getattr(item, "role", None)

            # parts は item.parts に text が入っている
            parts = []
            for p in getattr(item, "parts", []):
                if hasattr(p, "text"):
                    parts.append({"text": p.text})
                else:
                    parts.append({"text": str(p)})

            serializable.append({
                "role": role,
                "parts": parts
            })

        with open(self.history_file, "w", encoding="utf-8") as f:
            json.dump(serializable, f, ensure_ascii=False, indent=2)
    # ==========================
    # メッセージ送信
    # ==========================
    def fetch(self, prompt):
        try:
            res = self.chat.send_message(prompt)
            self.save_history()
            return res.text
        except Exception as e:
            print("Gemini エラー:", e)
            return "データ取得エラーです。"


# 直接実行されたときだけ動く
if __name__ == "__main__":
    gem = GeminiClient()
    print("終了するには 'exit' または Ctrl+C を押してください。")
    try:
        while True:
            question = input("\n質問を入力してください: ").strip()
            if not question:
                print("入力が空です。")
                continue
            if question.lower() in ("exit", "quit", "q"):
                print("終了します。")
                break
            response = gem.fetch(question)
            print(response)
    except KeyboardInterrupt:
        print("\nユーザーによって中断されました。終了します。")

今回は少し長いコードとなってしまったので、他のpythonスクリプトでも使えるようにクラス化してみました。といってもAIに作ってもらったので細かい部分はわかりません。
gemini APIを使っていて気になったのは、結構同じような返答が多いなという点です。
これは、無料枠ではやり取りの履歴管理ができないからのようです。有料版ではできるらしい。
この対策として、ラズパイにやり取りの履歴をjson形式で保存しておいて、geminiに履歴を送信してあげます。これで過去のやり取りを判断して学習してくれるみたいです。
あまり多いとパンクしそうなので、履歴を100件まで保存するようにし、100件を超えると削除するようにしました。
def __init__(self, model_name="gemini-2.5-flash", history_file="gemini_history.json"):
この「gemini-2.5-flash」が使用するAPIの種類です。最新は3.0proですが、無料枠では使えません。
このコードは直接実行すると会話形式のやり取りができますので、いろいろ試してみると、gemini APIの特徴がつかめると思います。
賢くなったラズパイ5で、更に愛でるのが捗りますね。