PythonからGemini APIを使ってみる。あと、JSONで返してほしい。

## 概要
先日あったハッカソンで、PythonからGeminiのAPIを使用する機会がありましたので、その手順をまとめたいと思います。基本は[公式のクイックスタート](https://ai.google.dev/tutorials/python_quickstart)通りに沿っていただければできると思いますが、この記事も理解の一助となれば幸いです。

この記事では、シンプルにプロンプトをAPIに投げ、その返答を受け取るだけというのを行います。他にも画像を渡したり、会話を続けたりなどいろいろできるようですが、そちらの詳細はまた別の記事か、公式のドキュメントをご参照ください。

### Gemini APIって？
まず、この記事を見られている方であればご存知かと思いますが、ここで言う[**Gemini**](https://deepmind.google/technologies/gemini/)は12月初旬にGoogle DeepMindが発表した言語モデルです。先進的なPVと共に印象深く残った方も多いのかなと思います。

GeminiAPIはそんなGemini[^1]を利用できるAPIで、ちょうど[12月13日(水)に公開](https://blog.google/technology/ai/gemini-api-developers-cloud/)されました。このAPIの利用にはSDKが用意されていて、今回の記事の主題である[Python](https://ai.google.dev/tutorials/python_quickstart)の他にも、[Android](https://ai.google.dev/tutorials/android_quickstart?hl=ja)、[Node.js](https://ai.google.dev/tutorials/node_quickstart?hl=ja)、[Swift](https://ai.google.dev/tutorials/swift_quickstart?hl=ja)、[JS](https://ai.google.dev/tutorials/web_quickstart?hl=ja)、[Go](https://ai.google.dev/tutorials/go_quickstart?hl=ja)などから利用する方法が紹介されています。

各言語にクイックスタートへのリンクを貼っているので、ぜひご興味のある方はご参照ください。

### GeminiAPIの利用料
この記事執筆段階（2023/12/17）ではAPIが公開されたばかりで、2024年の早い時期にある一般公開まで、1分間に60リクエストなどの一定の制限下で無料で使えるというアナウンスがされています。この辺りは、随時変わってくることかと思いますので、最新の情報をご確認ください。

> Right now, developers have free access to Gemini Pro and Gemini Pro Vision through Google AI Studio, with up to 60 requests per minute, making it suitable for most app development needs. Vertex AI developers can try the same models, with the same rate limits, at no cost until general availability early next year, after which there will be a charge per 1,000 characters or per image across Google AI Studio and Vertex AI.

[^1]: 正確にはGemini Proです。

## 実装

https://ai.google.dev/tutorials/python_quickstart

ここから、主に[クイックスタート](https://ai.google.dev/tutorials/python_quickstart)の内容に沿って進めます。実行環境はご自身のPython環境、もしくはGoogle Colabなどでお試しください。クイックスタートの中にもColabへのリンクが掲載せれていますので手軽に試してみるのはこれが便利かなと思います。この記事ではColabで試す前提で進めさせていただきます。

![Colabへのリンク](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351104/d58acf09-03c2-cf94-3370-3a752bc50c83.png)

### APIキーの取得
APIキーは[Google AI Studio](https://makersuite.google.com/?hl=ja)というところから取得できます。AI Studioにアクセスして、左のバーの「Get API Key」からAPIキーを作ります。

![APIキーの取得](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351104/1bf6bf7b-5a16-4d33-1174-c92e7ef4be94.png)

取得したAPIキーは、環境変数などに持っておきましょう。クイックスタートの例では`GOOGLE_API_KEY`という名前で持っていますので、この記事もそちらに倣います。

Google Colabでは左の鍵のアイコンから環境変数を設定できます。

![環境変数の設定](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351104/4a7da228-b00a-a6d4-4127-9f2df47ac7bd.png)

### SDKライブラリのインストール
GeminiAPIを使うためのSDKとして`google-generativeai`というライブラリが用意されていますので、そちらをインストールします。

```shell
$ pip install google-generativeai
```

### ライブラリのインポート
```python
import google.generativeai as genai
from google.colab import userdata
```

- 1行目のライブラリが先程インストールしたGeminiAPIを利用するためのSDKです。
- 2行目のライブラリはGoogle Colabの環境変数を使用するためのライブラリです。こちらは環境に合わせて他のライブラリなどに変更してください。

### 環境変数の読み込み
```python
GOOGLE_API_KEY=userdata.get('GOOGLE_API_KEY')
```

Colabで環境変数を取得する処理はこのように書けるようです。ここも上記同様、環境に合わせて変更してください。

### モデルの設定
```python
genai.configure(api_key=GOOGLE_API_KEY)
model = genai.GenerativeModel('gemini-pro')
```

- 1行目で上で読み込んだAPIキーをSDKに設定しています。
- 2行目で使用するモデルを選択しています。今回はテキストやり取りのみのモデルを選択しています。
  - 画像を入力に渡す場合などは`gemini-pro-vision`というモデルを使用すればいいようです。
  - 他に利用可能なモデルは`genai.list_models()`で確認できます。

### APIを利用して生成
```python
response = model.generate_content("日本で一番高い山は？")
print(response.text)
```

これだけです。シンプルですね。

ちなみに生成時間を記録するには、Colabであれば`%%time`というマジックコマンドで行えます。

日本一高い山に関しては御名答でした。生成までも45msと個人的には非常に早く感じました。

![生成結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351104/169c5a50-7ea3-cfe2-133c-806aa6ddd624.png)

### 文章はマークダウン形式で返答される
先ほどは一問一答のように簡単な質問をし、返答も単語1つでした。少し長めの応答になるようにプロンプトを送ると、結果はマークダウン形式で帰ってくるようです。

![文章はマークダウン形式で表示される](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351104/8b9c8294-c9b6-518f-1604-76a302eadcba.png)

そのままでは少し見にくいので、クイックスタートではマークダウンをうまく表示するための関数を用意してくれています。

```python
import textwrap
from IPython.display import Markdown

def to_markdown(text):
  text = text.replace('•', '  *')
  return Markdown(textwrap.indent(text, '> ', predicate=lambda _: True))
```

こちらを用意して、以下のように結果を表示します。

```python
to_markdown(response.text)
```

少し見やすくなりましたね。

![マークダウンで結果を整形](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351104/b0abc93b-eecc-60c1-5fa5-18294c5d8ab9.png)

### JSONで扱いたい
簡単に使ってみるのは以上まででできましたが、ハッカソンでの利用時には結果をJSON形式で出して欲しかったです。そこで次のようなプロンプトを記述して実行しました。

```python
prompt = """日本のおすすめの観光地を5つ紹介してください。
また、返答は以下のようなJSON形式のフォーマットにしてください。

フォーマット：
{"places": [
  "place1",
  "place2",
  ...
]}
"""
```

これで結果を見ると、うまくJSON形式で表示してくれることもありますが、画像のような返答をすることもありました。[^2]

![レスポンスにコードブロックが含まれる](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351104/0093c5cf-462e-1560-847c-fce74307c1eb.png)

マークダウンにおけるコードブロックとして生成したのか、バッククォートなどが入ってしまっています。このままでは`json.loads()`などでJSONをデコードできません。

そこで自分は、以下のような文字の置き換えを実施して、コードブロックを削除してからデコードをするようにしていました。

```python
json_str = response.text.replace('JSON', '').replace('json', '').replace('```', '')
```

プロンプトの改善でどうにかなるものなのかもしれませんが、私の1つの方法として共有できればと思います。

## まとめ
このような手順でGeminiAPIを利用することができました。単一レスポンスであればSDKのおかげで非常に手軽に実装できるなと感じました。あと、APIが現状無料なのが嬉しい！

ただまだ出たばかりのAPIでSDKも機能やドキュメントが十分とは言えないかなと感じました。個人的には、ChatSessionという名称で前の会話も踏まえたやりとりをしてみたり、マルチモーダルがGeminiの売りなので画像の入出力をしてみたり、いろいろ試してみたいことがありますね。まだまだこの先盛り上がりそうだなと感じた次第でした。

[^2]: もっとうまくプロンプトを作れると改善できることなのかもしれませんが……。