Gemini APIで質問応答のチャット

# 1.  概要

GeminiAPIで高速・高機能なモデル**gemini-1.5-flash**が無料で利用できるらしい．ちょっとした制限があるものの，入力・出力ともに無料ということで，APIを利用して簡単なチャットボットのサンプルを作成してみた．
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3725285/0c43c7fa-be2c-6171-3546-c9b3a786acc3.jpeg" width=60%>

## 内容
1. APIの登録
1. 動作確認の方法
1. **google-generativeai** ライブラリのインストール
1. **generate_content()** を利用したテキスト返答
1. **send_message()** を利用した履歴付きの返答


# 2. API登録
Gemini APIのAPIキーを発行する手順は大雑把に次のようなステップとなります．

:::note
基本的な流れ
1. [Google AI for Developers](https://ai.google.dev/aistudio?hl=ja) からGoogle AI Studioにログイン
1. Get API Key (左上)をクリック
1. 必要事項を記入して完成
:::

画像つきの詳細な手順については，他の記事に委ねたいと思います．

* 登録方法の詳細な解説

https://zenn.dev/peishim/articles/2e2e8408888f59

* 登録方法とAPIの基本的な使い方について解説

https://qiita.com/RyutoYoda/items/a51830dd75a2dac96d72


# 3. 動作確認
APIキーを発行したら，動作確認です．「[curlコマンドを実行してAPIをすばやくテスト](https://ai.google.dev/gemini-api/docs/api-key?hl=ja)」という項目があります．書かれているコマンドを実行してAPIキーが使えるか確認できます．下記の実行例は，キー取得のページに書かれているものを抜粋したもので，API keyが12345abcdeの例となります．

~~~:実行例 API key: 12345abcde
curl \
  -H 'Content-Type: application/json' \
  -d '{"contents":[{"parts":[{"text":"Explain how AI works"}]}]}' \
  -X POST 'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key=12345abcde'
~~~
問題がなければいよいよAPIを利用したチャットです．

# 4. チャット
## 4.1 ライブラリのインストールと動作確認（チュートリアルの内容）
公式ページの「[Gemini API を使用してテキストを生成する](https://ai.google.dev/gemini-api/docs/text-generation?hl=ja&lang=python)」にテキスト入力に対してテキストの返答をする方法が記載されています．

https://ai.google.dev/gemini-api/docs/text-generation?hl=ja&lang=python

APIを利用するには，**google-generativeai**ライブラリが必要となります．google-generativeaiライブラリを使うと簡単にテキストからテキストを生成させることが可能となります．


:::note
必要なライブラリー
pip install -U google-generativeai
:::


公式ページには，**「Gemini API を使用してテキストを生成する最も簡単な方法は、モデルに 単一のテキストのみの入力を使用します。」** とあります．やや分かりにくい言い回しのように感じます．どうやら英語のページを和訳しているようです．もともとの英文は，**The simplest way to generate text using the Gemini API is to provide the model with a single text-only input, as shown in this example:** でした[^1]．

テキストを入力してテキストを出力する最初のコードは次のようになります．

~~~python
import google.generativeai as genai

API_KEY = '12345abcde' 
genai.configure(api_key=API_KEY)
model = genai.GenerativeModel("gemini-1.5-flash")

input_text = "毎日暑い日が続きますが，お元気ですか？"
response = model.generate_content(input_text)
print(response.text)

# 毎日暑い日が続いてますね！ 
# 
# おかげさまで、私は元気です！ 
# あなたはいかがお過ごしですか？ 
#
# この暑さ、体調管理には十分気を付けてくださいね！ 
# 水分補給をこまめにして、無理せず過ごしましょうね。 
~~~

**genai.GenerativeModel("gemini-1.5-flash")** によって，利用するモデルを指定できます．
モデルも複数用意されているようです．
* gemini-1.5-flash: Google の最速マルチモーダル モデル
* gemini-1.5-pro: Google の最も高性能でインテリジェントなマルチモーダル モデル

チュートリアルによるとこの２種類がおすすめのようです．

:::note
**モデルの指定**
model = genai.GenerativeModel("gemini-1.5-flash")
:::

**model.generate_content(input_text)** によって，入力したテキストであるinput_textに対する返答がなされます．json形式で返答されます．返答部分は ```.text```によって取得できます．


:::note
**返答**
response = model.generate_content(input_text)
:::

generate_contentによる返答は，入力した情報・履歴を使う機能がありません．会話を行ってみたいので，履歴が利用できるように変更します．




## 4.2 会話と会話履歴の確認
会話の履歴を保存，再利用するには，```response = model.generate_content(input_text)```
の部分を次のように変更するだけです．

:::note
会話履歴保存・再利用
**chat = model.start_chat(history=[])**
**response = chat.send_message(input_text)**
:::



動作確認や会話履歴の確認のために，対話3回で終了するサンプルプログラムが下記のコードとなります．

~~~python
import google.generativeai as genai

API_KEY = '12345abcde'   # 登録時に発行されたAPI
genai.configure(api_key=API_KEY)
model = genai.GenerativeModel("gemini-1.5-flash")

# 会話の履歴がhistoryに追加される．
chat = model.start_chat(history=[])

for _ in range(3):
    text = input("ユーザー >>>")
    response = chat.send_message(text)
    print(response.text)

# chat.history：会話履歴のリスト
print(chat.history)
~~~
毎回異なる内容が返答されるのですが，会話の内容の一例を掲載してみます．```ユーザー >>> ```の下にかかれている文章（会話）が返答となります．

~~~:会話サンプル
ユーザー >>>デイキャンプしたいな．
デイキャンプしたいんだね！  
いいね！私も大好き．  
どこでやるか決まってる？  
どんな料理作るの？  
楽しみだね！

ユーザー >>>おすすめのギアある？        
おすすめのギアか！  
うーん、何がいいかな？  
まずは、どんなデイキャンプしたいか教えて！  
例えば、焚き火メイン？ご飯メイン？  
それから、何人で行きたいの？  
そうすれば、ピッタリなギアを紹介できるよ！

ユーザー >>>焚き火メイン
焚き火メインか！いいね！  
おすすめのギアは…  

**焚き火台**  
ソロなら、コンパクトで持ち運びやすいユニフレームのファイアグリルとか、  
複数人なら、安定感抜群のロゴスのピラミッドグリルとかがおすすめだよ！  

**焚き火シート**  
地面を保護してくれるし、撤収も楽になるから必須だよ！  
耐熱性のあるアルミシートとか、  
焚き火台にピッタリフィットする専用のシートとか色々あるよ！  

**その他**  
焚き火を楽しむなら、火吹き棒とか、火ばさみ、軍手も忘れずにね！  
あと、焚き火台の下に敷く網とか、  
焚き火用の椅子とかも便利だよ！
~~~


確認するべきは，会話履歴を考慮して返答している点です．返答がフレンドリーすぎる気がしますが......

:::note
**履歴を考慮した応答の基本形**
chat = model.start_chat(history=[])
text = "ユーザーの入力した文章"
response = chat.send_message(text)
:::

```chat.history```によって会話の履歴を確認できるようです．


~~~:chat.history
[parts { text: "デイキャンプしたいな．"}
role: "user", 
parts { text: "デイキャンプしたいんだね！  \nいいね！私も大好き．  \nどこでやるか決まってる？  \nどんな料理作るの？  \n楽しみだね！ \n"}
role: "model", 
parts { text: "おすすめのギアある？"}
role: "user", 
parts {  text: "おすすめのギアか！  \nうーん、何がいいかな？  \nまずは、どんなデイキャンプしたいか教えて！  \n例えば、焚き火メイン？ご飯メイン？  \nそれから、何人で行きたいの？  \nそうすれば、ピッタリなギアを紹介できるよ！ \n"}
role: "model", 
parts { text: "焚き火メイン"}
role: "user", 
parts { text: "焚き火メインか！いいね！  \nおすすめのギアは…  \n\n**焚き火台**  \nソロなら、コンパクトで持ち運びやすいユニフレームのファイアグリルとか、  \n複数人なら、安定感抜群のロゴスのピラミッドグリルとかがおすすめだよ！  \n\n**焚き火シート**  \n地面を保護してくれるし、撤収も楽になるから必須だよ！  \n耐熱性のあるアルミシートとか、  \n焚き火台にピッタリフィットする専用のシートとか色々あるよ！  \n\n**その他**  \n焚き火を楽しむなら、火吹き棒とか、火ばさみ、軍手も忘れずにね！  \nあと、焚き火台の下に敷く網とか、  \n焚き火用の椅子とかも便利だよ！\n\n  \n\n"}
role: "model"
]
~~~

## 4.3 ちょっとだけカスタマイズ
生成AIの返答に対して，文字数を短めに，かつ，フレンドリーになるように最初に指示してみたバージョンになります．pre_textの部分で最初に指示を与えています．ここがメインの変更点となります[^2]．

「フレンドリーに返答して」というかなり適当 :sweat_smile: なプロンプトですが，問題ありませんでした．

~~~python
import google.generativeai as genai

API_KEY = '12345abcde' 
genai.configure(api_key=API_KEY)
model = genai.GenerativeModel("gemini-1.5-flash")


pre_text = "フレンドリーに，200文字以内で返答してね．"

# 会話履歴保存
chat = model.start_chat(history=[])
chat.send_message(pre_text)

while True:
    text = input("ユーザー >>>")
    response = chat.send_message(text)
    print(response.text)
~~~
```print(response.text)```の部分に音声出力を与えれば喋るはず:grin::heart_eyes::sunglasses:


# 注意事項とその他
このままだと，入力するテキストの内容によっては，安全性の観点から強制的に返答が拒否されることがあります．
実際，「あつい」だけを入力すると，時々，下記のようなメッセージになりました．

~~~: 安全性を脅かす入力・回答
finish_reason: SAFETY
safety_ratings {
  category: HARM_CATEGORY_SEXUALLY_EXPLICIT
  probability: MEDIUM
}
safety_ratings {
  category: HARM_CATEGORY_HATE_SPEECH
  probability: NEGLIGIBLE
}
safety_ratings {
  category: HARM_CATEGORY_HARASSMENT
  probability: NEGLIGIBLE
}
safety_ratings {
  category: HARM_CATEGORY_DANGEROUS_CONTENT
  probability: NEGLIGIBLE
}
~~~
[次回](https://qiita.com/AzukiImo/items/46c0f5d72f4d9e42cb0a)は，この判定を緩やかにする方法に取り組んでみたいと思います．

https://qiita.com/AzukiImo/items/46c0f5d72f4d9e42cb0a

<hr>
* Gemini APIではありませんが，国産LLMであるPLaMoβ版のAPIが利用できます．

https://qiita.com/AzukiImo/items/20a58336985da680a5a5

* Gemini APIを利用した記事がいくつもありますので，参考にどうぞ

https://qiita.com/SH2/items/39314152c0a6f9a7b681

https://qiita.com/HayatoF/items/cc6477e1f7ab717c4cd6

https://qiita.com/tatsuki-tsuchiyama/items/be2a8a592d1e25a3b0ea



# 脚注
[^1]: DeepL翻訳だと「Gemini APIを使用してテキストを生成する最も簡単な方法は、この例に示すように、単一のテキストのみの入力をモデルに提供することです：」と和訳されています．DeepLのほうが読みやすいか??
[^2]: 入力する質問の内容によっては，返答が強制的に拒否される場合があるようです．