2023年12月24日日曜日

Google Geminiのマルチターンと関数呼び出しを実装する

Google Gemniを呼び出すアプリケーションにマルチターン(いわゆるチャット)と関数呼び出しを行うページを追加します。どちらの呼び出し方も、OpenAIのChat Completions APIに非常に近いため、以下の記事の実装を援用します。

OpenAIのChatGPTのAPIを呼び出すAPEXアプリを作る

作成したアプリケーションは以下のように動作します。

関数呼び出しが行われた場合(Chat HistoryのタイプでfunctionResponseが現れた場合)関数呼び出しの戻り値をGeminiに渡すため、Textを空白にしてRunをクリックする必要があります。


最初に関数呼び出しの、呼び出し対象となるファンクションを登録する表を作成します。

クイックSQLの以下のモデルより、表GEMINI_FUNCTIONSを作成します。

この表はOpenAIでの作業の際に作成した表OPENAI_FUNCTIONSとOPENAI_FUNCTION_SETを(不具合の修正も合わせて)簡素にしています。この表にサンプルとして設定するファンクションとして、OpenAIのときと同じget_current_weatherを使用します。


表を作成した後、この表にデータを投入するためにフォーム付き対話モード・レポートのページを作成します。

ページの作成をクリックします。


対話モード・レポートを選択します。


対話モード・レポートのページのページ名はFunctionsとします。フォーム・ページを含めるチェックします。フォーム・ページ名Function Detailとします。

データ・ソース表/ビューの名前GEMINI_FUNCTIONSを指定します。

へ進み、主キー列1ID (Number)を選択しページの作成をクリックすると、表GEMINI_FUNCTIONS対話モード・レポートフォームのページが作成されます。


ファンクションGET_CURRENT_WEATHERも作成しておきます。元記事に説明がありますが、このファンクションは引数である都市名から座標を得るために、アマノ技研さんより提供されている「世界の百万都市の位置データ Location Data of Megacities」のCSVファイルを表AMANO_CITY_LOCATIONSにロードしています。

アプリケーションを実行しFunctionsのページを開き、作成をクリックします。


ファンクションGET_CURRENT_WEATHERを登録します。

Function nameとしてget_current_weatherFunction Set NameとしてWeatherDescriptionとしてGet the current weather in a given locationParametersには以下のJSONドキュメントを記述します。
{
  "type": "object",
  "properties": {
    "location": {
      "type": "string",
      "description": "The city name"
    },
    "unit": {
      "type": "string",
      "enum": [
        "celsius",
        "fahrenheit"
      ]
    }
  },
  "required": [
    "location"
  ]
}
これらはOpenAIでFunction Callingを行う際に設定した内容とほぼ同じです。双方とも関数呼び出しの引数の定義は、OpenAPI 3.0に準拠しています。

作成をクリックします。


ファンクションget_current_weatherが設定されます。


APEXアプリケーションへマルチターンの処理を行うページを追加します。

ページの作成をクリックします。


空白のページを選択します。


ページ番号は、ページの名前Chatとします。

ページの作成をクリックします。


空白のページが作成されます。

会話を初期化するボタンINIT_CONVERSATIONを作成します。ラベルはInitialize Conversation動作アクションはデフォルトのページの送信です。テンプレート・オプションWidthStretchに変更しています。


プロセス・ビューを開き、このボタンINIT_CONVERSATIONがクリックされたときに実行されるプロセスを作成します。

作成したプロセスの識別名前Initialize Conversationとします。タイプコードを実行です。ソースPL/SQLコードに以下を記述します。チャット履歴を保持するAPEXコレクションGEMINIを初期化しています。
apex_collection.create_or_truncate_collection(
    p_collection_name => 'GEMINI'
);
サーバー側の条件ボタン押下時INIT_CONVERSATIONを指定します。


レンダリング・ビューに戻ります。

テキストを入力するページ・アイテムP6_TEXTを作成します。タイプテキスト領域ラベルTextとします。

セッション・ステートデータ型CLOBを選択し、続けて同じテキストを送信してしまわないように、ストレージとしてリクエストごと(メモリーのみ)を選択します。


ファンクション・セットを選択するページ・アイテムP6_FUNCTION_SETを作成します。

タイプ選択リストラベルFunction Setとします。LOVタイプSQL問合せを選択し、SQL問合せとして以下を記述します。
select function_set_name d, function_set_name r from gemini_functions group by function_set_name order by 1 asc
追加値の表示オフNULL値の表示オンとし、NULL表示値として- ファンクション・セットを選択 -と記述します。

セッション・ステートストレージセッションごと(永続)を選択します。


会話を実行するボタンRUNを作成します。ラベルはRun動作アクションはデフォルトのページの送信です。外観ホットオンにします。テンプレート・オプションWidthStretchに変更しています。


プロセス・ビューを開き、このボタンRUNがクリックされたときに実行されるプロセスを作成します。

作成したプロセスの識別名前Run Conversationとします。タイプコードを実行です。ソースPL/SQLコードに以下を記述します。

サーバー側の条件ボタン押下時RUNを指定します。


レンダリング・ツリーに戻り、チャット履歴を表示するクラシック・レポートのリージョンを作成します。

識別タイトルChat HistoryソースタイプとしてSQL問合せを選択し、SQL問合せとして以下を記述します。
select c001, c002, clob001 from apex_collections where collection_name = 'GEMINI' order by seq_id

C001ヘッダーroleとします。


C002のヘッダーをtypeとします。


CLOB001タイプリッチ・テキストに変更します。ヘッダーtextです。設定書式としてマークダウンを選択します。


以上でアプリケーションは完成です。アプリケーションを実行すると、記事の先頭のGIF動画のように動作します。

今回作成したアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/google-gemini2.zip

Oracle APEXのアプリケーション作成の参考になれば幸いです。