はじめてのAI API(Python:Google Gemini)

# はじめに
備忘録として残しておきます。Google Geminiがある一定の利用回数であれば無料ということで試してみました。ただ、何故か日本語のインターネット記事はあまり見つからない・内容がちょっといきなり高度だったりしたので、まずはちょい動かすってレベルの備忘録になります。

### 環境
Pythonバージョン
```:bash
root@# python3 --version
Python 3.12.3
```

# 参考にしたドキュメント(URL)
Gemini-API ドキュメント：
https://ai.google.dev/gemini-api/docs/api-key?hl=ja

Google AI Studio API：
https://aistudio.google.com/app/apikey

Google Gemini APIリファレンス
https://ai.google.dev/gemini-api/docs?hl=ja&

# 1.APIキーの取得
Googleアカウントを取得したのち、以下URLにアクセス。
その後、キー：APIキーを作成をクリックし、どこかにメモをしておく
（プログラムの内部で使うため）

URL：Google AI Studio API：https://aistudio.google.com/app/apikey
![スクリーンショット 2025-02-15 120501.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545289/970c70ee-4aa1-482f-9655-a69d95929df1.png)

# 2.実際に試してみる。
## 2.1 pipで必要なライブラリをインストール
※最近のpipインストールは仮想環境(penv)を推奨されていますが無視します。
```:bash
pip install -q -U google-genai --break-system-packages
```
準備はこれだけ

## 2.2 AIに問い合わせしてみる
ドキュメントのサンプルコードを実行してみる
```python:python
from google import genai
from google.genai import types

client = genai.Client(api_key="「事前準備でとってきたAPIキー")

response = client.models.generate_content(
    model="gemini-2.0-flash", 
    contents="今日の福岡の天気は？",
    config=types.GenerateContentConfig(
        max_output_tokens=500,
        temperature=0.1,
    )
)

print(response.text)
```

### 実行結果：
<details><summary>結果</summary>

```:bash
福岡市中央区の今日の天気は晴れ時々曇りです。最高気温は27℃、最低気温は17℃の予報です。

より詳しい情報が必要な場合は、以下の情報源もご確認ください。

*   **日本気象協会:** [https://tenki.jp/forecast/9/42/8210/40130/](https://tenki.jp/forecast/9/42/8210/40130/)
*   **ウェザーニュース:** [https://weathernews.jp/onebox/33.59/130.40](https://weathernews.jp/onebox/33.59/130.40)

これらのサイトでは、時間ごとの天気予報や降水確率なども確認できます。
```
</details>

## 2.3の解説
ざっくばらんに解説します。
<di>
<dt>genai.Client(api_key="「事前準備でとってきたAPIキー") </dt>
<dd>・・・Clientの設定でAPIキーを設定している</dd>
<dt>model="gemini-2.0-flash"</dt>
<dd>・・・モデルの選択、ほかにもgemini-1.5proなどが選べます。</dd>
<dt>contents="今日の福岡の天気は？"</dt>
<dd>・・・contentsにAIに投げる情報を代入します</dd>
<dt>config=types.GenerateContentConfig(<br>max_output_tokens=500,<br>
temperature=0.1,<br>
)</dt>
<dd>・・・ここは"トークン"というパラメータとtemperatureという正確性のパラメータ設定になります。トークンは数字が大きければ性能上がるっぽい？temperatureは数字が大きくなると創造性が上がるらしい？</dd>
<di>

# 3.応用編
## 3.1 ユーザからのインプットを受け付ける
せっかくAIを使うので入力した情報について検索してもらうように変更してみましょう。

```python:python
from google import genai
from google.genai import types

client = genai.Client(api_key="「事前準備でとってきたAPIキー")

user_input = input("なにについて知りたいですか？: ")

response = client.models.generate_content(
    model="gemini-2.0-flash",
    contents=[user_input],
    config=types.GenerateContentConfig(
        max_output_tokens=500,
        temperature=0.9,
    )
)

print(response)  # AIからの応答を表示
```

### 実験
<details><summary>結果</summary>

```bash
root@# python3 google-ai.py
なにについて知りたいですか？: 日本について
(省略～)text='日本についてですね！どんな情報にご興味がありますか？\n\n例えば、以下のような情報があります。\n\n**基本的な情報:**\n\n*   **国名:** 日本 (にほん / にっぽん)\n*   **首都:** 東京\n*   **人口:** 約1億2500万人\n*   **言語:** 日本語\n*   **通貨:** 円 (JPY)\n*   **面積:** 約37万8千平方キロメートル\n*   **政治:** 立憲君主制\n*   **元首:** 天皇\n*   **首相:** 岸田文雄\n\n**文化:**\n\n*   **伝統文化:** 茶道、華道、書道、武道、歌舞伎、能、日本舞踊など\n*   **現代文化:** アニメ、漫画、ゲーム、音楽 (J-POP)、ファッションなど\n*   **食文化:** 寿司、ラーメン、天ぷら、懐石料理、和菓子など\n\n**観光:**\n\n*   **人気観光地:** 東京、京都、大阪、北海道、沖縄など\n*   **自然:** 富士山、桜、紅葉、温泉など\n*   **歴史的建造物:** 寺社仏閣、城郭など\n\n**経済:**\n\n*   **主要産業:** 自動車、エレクトロニクス 、機械、金融など\n*   **特徴:** 技術力が高く、品質を重視する\n\n**その他:**\n\n*   **祝日:** 元日、成人の日、建国記念の日、春分の日、昭和の日、憲法記念日、みどりの日、こどもの日、海の日、山の日、敬老の日、秋分の日、スポーツの日、文化の日、勤労感謝の日、天皇誕生日\n*   **気候:** 四季がはっきりしており、地域によって異なる\n\n**さらに詳しく知りたい場合は、以下の点を教えてください。**\n\n*   **知りたい分野:** (例: 歴史、文化、観光、経済、政治など)\n*   **具体的な質問:** (例: 日本の歴史について詳しく教えて、東京のおすすめ観光スポットは？)\n*   **興味のあるキーワード:** (例: 侍、アニメ、ラーメン、富士山など)\n\n教えていただいた情報に基づいて、より詳しく、そしてあなたに合った情報を提供できます。\n')],(～省略～)
```
</details>

### 解説
追加事項はシンプルです
<di>
<dt>user_input = input("なにについて知りたいですか？: ")</dt>
<dd>・・・user_inputにユーザからの入力内容を渡している</dd>
</di>

そして、代入された「user_input」を「contents」に渡しているだけです。

## 3-2 インターネット検索を有効にする
今までの結果をちょくちょく見ていると気づかれた方もいるかもしれません。実は、デフォルトではインターネット検索が有効になっていません。(実際、先ほどの回答見ずらいですが日本の総理大臣が岸田さんになってます)そのため、以下のようにコードを書き換えます。
### 参考URL 
https://ai.google.dev/gemini-api/docs/grounding?hl=ja&lang=python

```python:python
from google import genai
from google.genai import types

client = genai.Client(api_key="「事前に取ってきたAPIキー」")

user_input = input("なにについて知りたいですか？: ")

response = client.models.generate_content(
    model="gemini-2.0-flash",
    contents=[user_input],
    config=types.GenerateContentConfig(
        max_output_tokens=500,
        temperature=0.9,
        tools=[types.Tool(
            google_search=types.GoogleSearchRetrieval()
        )]
    )
)

print(response)  # AIからの応答を表示
```

### 解説
追加事項はシンプルです
<di>
<dt>tools=[types.Tool(</dt>
<dt>google_search=types.GoogleSearchRetrieval()</dt>
<dt>)]</dt>
</dt>
<dd>・・・google検索エンジンを参照する旨を追加</dd>
</di>

### 結果
<details><summary>結果</summary>

```bash:bash
root@LLM# python3 gemini_ai.py
なにについて知りたいで 今日は何月何日ですか？
（省略）, text='今日は2025年2月15日、土曜日です。\n')], role='model'), (省略)
root@LLM-Program:~/python-test#
```
</details>

## 3.3 プロンプトエンジニアリングの実装
プロンプトエンジニアリングとは、AIが目的の情報を出力できるように"入力(プロンプト)"を調整する技術のことです。今回は簡単な実装例として、出力を一定にするためテンプレートを用意します。

```python:python
from google import genai
from google.genai import types

client = genai.Client(api_key="「事前準備でとってきたAPIキー")

user_input = input("RFC番号を入力してください): ")

prompt = f"""
以下の標準化規格について、以下の形式で情報をまとめてください。
この時マークダウン形式で出力してください。

RFC標準化規格: {user_input}

形式:
【標準化された技術内容】
この技術は○○に関する技術です。この技術を応用すると○○のようなことができます。

【具体的な活用システム名】
・製品名：○○に活用されている。

【標準化時期】
○○年○○月ごろ

【参考URL】
〇〇
"""

response = client.models.generate_content(
    model="gemini-2.0-flash",
    contents=[prompt],
    config=types.GenerateContentConfig(
        max_output_tokens=500,
        temperature=0.9,
        tools=[types.Tool(
            google_search=types.GoogleSearchRetrieval() #types.GoogleSearchRetrieval()に修正
        )]
    )
)

print(response)  # AIからの応答を表示

```

### 結果
<details><summary>結果</summary>

```bash:bash
root@# python3 google-ai.py
RFC番号を入力してください): RFC 1034
(省略：)## RFC 1034\n\n【標準化された技術内容】\nこの技術はドメイン名システム (DNS) に関する技術です。具体的には、DNS の概念、ドメイン名の仕様 、クエリとレスポンスのメッセージ形式、権威サーバーとキャッシュサーバーの役割などを定義しています。この技術を応用すると、人間が理解しやすいドメイン名（例：www.example.com）を使って、コンピュータが理解できる IP アドレス（例：192.0.2.1）を検索し、インター ネット上のリソースにアクセスできるようになります。また、電子メールの転送にも利用されます。\n\n【具体的な活用システム名】\n・製品名：BIND (Berkeley Internet Name Domain) などの DNS サーバーソフトウェアに活用されている。\n・製品名：各種オペレーティングシ ステムに組み込まれた DNS リゾルバに活用されている。\n\n【標準化時期】\n1987年11月\n\n【参考URL】\n*   (省略)
```
※文章形成したところ以下のような感じでした。
## (出力結果)RFC 1034

【標準化された 技術内容】
この技術はドメイン名システム (DNS) に関する技術です。具体的には、DNS の概念、ドメイン名の仕様、クエリとレスポンスのメッセージ形式、権威サーバーとキャッシュサーバーの役割などを定義しています。この技術を応用すると、人間が理解しやすいドメイン名（例：www.example.com）を使って、コンピュータが理解できる IP アドレス（例：192.0.2.1）を検索し、インターネット上のリソースにアクセスできるようになります。

【具体的な活用システム名】
・製品名：BIND (Berkeley Internet Name Domain) などの DNS サーバー ソフトウェアに活用されている。
・製品名：各種オペレーティングシステムに組み込まれた DNS リゾルバに活用されている。

【標準化時期】
1987年11月ごろ

【参考URL】
*   [RFC 1034 - Domain Names - Concepts and Facilities](https://www.rfc-editor.org/rfc/rfc1034),
</details>

## 3.4 JSON形式への変換
さて出力結果が非常にひどいです。GeminiはJSONと言い張りますが、絶対違います。ということでJSON形式で出力されるようにしてみましょう。
```python:python
import json
from google import genai
from google.genai import types

client = genai.Client(api_key="「事前準備でとってきたAPIキー")

user_input = input("RFC番号を入力してください): ")

prompt = f"""
以下の標準化規格について、以下の形式で情報をまとめてください。
この時マークダウン形式で出力してください。

RFC標準化規格: {user_input}

形式:
【標準化された技術内容】
この技術は○○に関する技術です。この技術を応用すると○○のようなことができます。

【具体的な活用システム名】
・製品名：○○に活用されている。

【標準化時期】
○○年○○月ごろ

【参考URL】
〇〇
"""

response = client.models.generate_content(
    model="gemini-2.0-flash",
    contents=[prompt],
    config=types.GenerateContentConfig(
        max_output_tokens=500,
        temperature=0.9,
        tools=[types.Tool(
            google_search=types.GoogleSearchRetrieval() #types.GoogleSearchRetrieval()に修正
        )]
    )
)

#print(response)  # AIからの応答を表示
response_json = json.dumps(response, default=lambda o: o.__dict__, ensure_ascii=False, indent=4)
print(response_json)
```

### 結果

<details><summary>結果</summary>

```bash:bash
root@L# python3 python-test/google_ai.py
RFC番号を入力してください): 01111
{
    "candidates": [
        {
            "content": {
                "parts": [
                    {
                        "video_metadata": null,
                        "thought": null,
                        "code_execution_result": null,
                        "executable_code": null,
                        "file_data": null,
                        "function_call": null,
                        "function_response": null,
                        "inline_data": null,
                        "text": "承知いたしました。RFC 01111 について、以下の形式で情報をまとめます。\n\n"
                    },
                    {
                        "video_metadata": null,
                        "thought": null,
                        "code_execution_result": null,
                        "executable_code": null,
                        "file_data": null,
                        "function_call": null,
                        "function_response": null,
                        "inline_data": null,
                        "text": "```markdown\n【標準化された技術内容】\nRFC 01111 は、ホスト間の情報伝達のための機能を定義した文書です。この技術は、ネットワーク上でのデータ交換における基本的な手順やプロトコルを確立することを目的としています。この技術を応用すると、異なるコンピュータシステム間で情報を確実に送受信することができ、現代のインターネット通信の基盤となっています。\n\n【具体的な活用システム名】\nRFC 01111 は非常に基礎的なプロトコルを定義しているため、直接的に特定の製品名として活用されているわけではありません。しかし、すべてのインターネット通信、例えば以下のようなシステムで間接的に活用されています。\n・Webブラウザ：ウェブサイトの閲覧\n・メールクライアント：電子メールの送受信\n・ファイル転送ソフト：ファイルのアップロード・ダウンロード\n\n【標準化時期】\nRFC 01111 は1971年6月ごろに標準化されました。\n\n【参考URL】\n* RFC 01111:  [https://datatracker.ietf.org/doc/html/rfc1111](https://datatracker.ietf.org/doc/html/rfc1111)\n```"
                    }
                ],
                "role": "model"
            },
            "citation_metadata": null,
            "finish_message": null,
            "token_count": null,
            "avg_logprobs": null,
            "finish_reason": "STOP",
            "grounding_metadata": {
                "grounding_chunks": null,
                "grounding_supports": null,
                "retrieval_metadata": null,
                "retrieval_queries": null,
                "search_entry_point": null,
                "web_search_queries": null
            },
            "index": null,
            "logprobs_result": null,
            "safety_ratings": null
        }
    ],
    "model_version": "gemini-2.0-flash",
    "prompt_feedback": null,
    "usage_metadata": {
        "cached_content_token_count": null,
        "candidates_token_count": 285,
        "prompt_token_count": 103,
        "total_token_count": 388
    },
    "automatic_function_calling_history": [],
    "parsed": null
}
```
</details>

## 3.5 APIで反応するようにする
ではでは、jsonでレスポンスすることができるようになったので、お次はAPIを作ってJSONを返してくるようにします。(FAST APIを使います。）

### 3.5.1 pipでfast apiをインストール
※サーバーとして動作する"uvicorn"も一応入れておきますので一括インストールします
```bash:pip-install
pip install "fastapi[all]" --break-system-packages
```
### 3.5.2 実際のコード
では実際のコードです。
```python:gemini_ai.py
import json
from google import genai
from google.genai import types

app = FastAPI()

# Google Gemini APIクライアントの初期化
client = genai.Client(api_key="「事前準備でとってきたAPIキー")

#RFCRequest：RFCRequestクラスを使ってリクエストボディの構造を定義しています。
#BaseModel:入力データが正しい形式と型を持っているかを自動的に確認できます。今回str型かを確認しています。
#class RFCRequest(BaseModel):BaseModelを継承する新しいデータモデルクラスRFCRequestを定義します。
class RFCRequest(BaseModel):
    rfc_number: str

#FAST APIのエンドポイントの定義
@app.post("/generate_rfc_info")

#FAST APIの関数定義を行っている。「FAST APIの(async」型のアプリケーションのため」
#エンドポイントに来たものに対して応答する関数
async def generate_rfc_info(request: RFCRequest):
    try:
        prompt = f"""
        以下の標準化規格について、以下の形式で情報をまとめてください。
        この時マークダウン形式で出力してください。

        RFC標準化規格: {request.rfc_number}

        形式:
        【標準化された技術内容】
        この技術は○○に関する技術です。この技術を応用すると○○のようなことができます。

        【具体的な活用システム名】
        ・製品名：○○に活用されている。

        【標準化時期】
        ○○年○○月ごろ

        【参考URL】
        〇〇
        """

        response = client.models.generate_content(
            model="gemini-2.0-flash",
            contents=[prompt],
            config=types.GenerateContentConfig(
                max_output_tokens=500,
                temperature=0.9,
                tools=[types.Tool(
                    google_search=types.GoogleSearchRetrieval()
                )]
            )
        )

        # API応答をJSON形式に変換
        response_json = json.dumps(response, default=lambda o: o.__dict__, ensure_ascii=False, indent=4)
        return json.loads(response_json)
    #エラー時に500を返すように
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

#FAST APIを起動
if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)
```
### 動作結果
<details><summary>結果</summary>

```bash:bash
#サーバ待機状態にする
root# python3 google_ai.py
INFO:     Started server process [9296]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
INFO:     127.0.0.1:48674 - "POST /generate_rfc_info HTTP/1.1" 200 OK

#(別ウィンドウで)curlで0001リクエストをjson形式で投げる
root#curl -X POST "http://localhost:8000/generate_rfc_info" -H "Content-Type: application/json" -d '{"rfc_number": "0001"}'

#レスポンス結果
{"candidates":[{"content":{"parts":[{"video_metadata":null,"thought":null,"code_execution_result":null,"executable_code":null,"file_data":null,"function_call":null,"function_response":null,"inline_data":null,"text":"了解しました。RFC 0001について、ご希望の形式で情報をまとめます。\n\n"},{"video_metadata":null,"thought":null,"code_execution_result":null,"executable_code":null,"file_data":null,"function_call":null,"function_response":null,"inline_data":null,"text":"```\n## RFC標準化規格: 0001\n\n【標準化された技術内容】\nこのRFC 1は、インターネット技術に関するメモ（Request for Comments: RFC）シリーズの導入を記述したもの です。RFC自体が技術標準を定めるものではありませんが、その後のインターネット技術の標準化プロセスにおいて重要な役割を果たす、ドキュメントシリーズの開始を告げるものです。この技術（というよりはRFCシリーズという仕組み）を応用すると、インターネット技術の仕様、プロトコル、手順などを公開し、議論し、標準化することができます。\n\n【具体的な活用システム名】\n・RFCというドキュメントシリーズ全体：インターネット技術標準化に不可欠な基盤として活用されている。\n（特定の製品名に直接活用されているわけではありませ ん。）\n\n【標準化時期】\n1969年4月7日\n\n【参考URL】\n* RFC 1: [https://www.rfc-editor.org/rfc/rfc1.txt](https://www.rfc-editor.org/rfc/rfc1.txt)\n```"}],"role":"model"},"citation_metadata":null,"finish_message":null,"token_count":null,"avg_logprobs":null,"finish_reason":"STOP","grounding_metadata":{"grounding_chunks":null,"grounding_supports":null,"retrieval_metadata":null,"retrieval_queries":null,"search_entry_point":null,"web_search_queries":null},"index":null,"logprobs_result":null,"safety_ratings":null}],"model_version":"gemini-2.0-flash","prompt_feedback":null,"usage_metadata":{"cached_content_token_count":null,"candidates_token_count":247,"prompt_token_count":115,"total_token_count":362},"automatic_function_calling_history":[],"parsroot@LLM-Program:~#
```
</details>

# 感想
今回は、Google Geminiについて記述しました。ほぼほぼドキュメント通りです。また、一応APIを受け付けることができるようになりました。

# 余談
わからないのが、Geminiが出力してくる内容です。これ、JSONぽくないのにJSONって言い張るんですよね、こいつ・・・絶対違うだろ・・・。本当にGeminiは嘘つきすぎる・・・
![スクリーンショット 2025-02-15 132034.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545289/d2a5d036-00d0-46ab-a4cd-c129ebd51ed1.png)