2024年4月25日木曜日

OpenAIのChat Completions APIを呼び出すAPEXアプリを作成する

以前にOpenAIChat Completions APIが使えるようになった頃に、同じテーマで記事を書いています。それから時間も経ち、OllamaLlama.cppといったOpenAIのChat Completions API互換のAPIをサポートするソフトウェアも利用できるようになりました。Chat Completions API自体には互換性があるため、これらを同様に扱えるひとつのAPEXアプリを作ってみることにしました。

とにかくChat Completions APIを呼び出せれば良いので、ユーザー・インターフェースの作り込みは最小限にします。APIの呼び出しはPL/SQLのパッケージUTL_OPENAI_CHAT_APIにまとめて実装しています。パッケージのコードは記事の末尾に添付します。パッケージUTL_OPENAI_CHAT_APIはツール呼び出しに関する実装も含んでいます。

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

以下よりAPEXアプリケーションの作成手順を紹介します。

今回はChat Completions APIを呼び出す対象をOpenAI、モデルは廉価なgpt-3.5-turboを使います。そのため、OpenAIのAPIキーをAPEXのWeb資格証明として作成しておきます。

ワークスペース・ユーティリティWeb資格証明を作成します。


Web資格証明名前OpenAI API Keyとしています。使用するWeb資格証明の指定には静的識別子を使用するため、この名前はなんでもかまいません。

静的識別子としてOPENAI_API_KEY認証タイプHTTPヘッダー資格証明名Authorizationとします。資格証明シークレットしてBearerで始めて空白で区切った後、OpenAIのAPIキーを続けた文字列を設定します。


アプリケーション・ビルダーからアプリケーションの作成を呼び出し、空のアプリケーションを作成します。

名前Chat with Generative AIとします。

アプリケーションの作成をクリックします。


空のアプリケーションが作成されます。Chat Completions APIを呼び出す機能はすべてホーム・ページに実装します。

最初に置換文字列に各種パラメータを設定します。アプリケーション定義の編集を開きます。


アプリケーション定義置換のセクションを開きます。

置換文字列として呼び出すサービスとモデルの指定に必要な情報を設定します。

呼び出す対象はOpenAIなので、G_API_ENDPOINTとしてhttps://api.openai.com/v1/chat/completionsを設定します。G_CHAT_HISTORYにはチャット履歴を保存するAPEXコレクションの名前としてCHAT_HISTORYを設定します。これはアプリケーション内で同じ名前のコレクションを参照させるためのもので、どのような名前でも構いませんが、あえて変更する必要もないでしょう。G_MODEL_NAMEgpt-3.5-turboを指定します。G_CREDENTIALとしてOpenAIのAPIキーを登録したWeb資格証明であるOPENAI_API_KEYを設定します。

以上を設定して、変更の適用をクリックします。


ページ・デザイナホーム・ページを開きます。

最初にチャット履歴を初期化するボタンを作成します。ボタンはブレッドクラムのリージョンChat with Generative AIに作成します。

識別ボタン名INITラベルStart New Conversationとします。レイアウト位置Nextを選択します。

動作アクションとしてこのアプリケーションのページにリダイレクトを選択します。


ターゲットをクリックしリンク・ビルダーを開きます。

ターゲットページ&APP_PAGE_ID.を記述します。これはこのボタンがあるページ番号、つまり自ページの呼び出しを指定しています。詳細リクエストINIT_CONVERSATIONを設定します。

OKをクリックし、リンク・ビルダーを閉じます。


このボタンを押した時に実行されるプロセスを作成します。

レンダリング前ヘッダーの前にプロセスを作成します。

識別名前Init Conversationとします。タイプコードの実行です。ソースPL/SQLコードとして以下を記述します。APEXコレクションが作成されていないか(サインイン直後)、またはREQUESTにINIT_CONVERSATIONが設定されている場合(ボタンStart New Conversationが押されたとき)に、APEXコレクションを初期化します。最初にロールがsystemの空のプロンプトを挿入します。このsystemプロンプトは変更できるように、画面にUIを実装します。



systemプロンプトを設定するUIを作成します。

新規にリージョンを作成します。

識別タイトルSystem Promptとします。タイプ静的コンテンツです。


作成したリージョンに、プロンプトを入力するページ・アイテムを作成します。

識別名前P1_PROMPTタイプテキスト領域とします。ラベルPromptです。セッション・ステートデータ型CLOBストレージセッションごと(永続)を選択します。


入力したsystemプロンプトをチャット履歴に設定するボタンを作成します。

識別ボタン名SET_PROMPTラベルSet Promptとします。動作アクションはデフォルトのページの送信です。


ボタンSET_PROMPTをクリックしたときに実行するプロセスを作成します。

左ペインでプロセス・ビューを開き、新規にプロセスを作成します。

識別名前Set Promptとします。タイプコードを実行です。ソースPL/SQLコードとして以下を記述します。すでに登録済みのsystemプロンプトを置き換えます。


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


ユーザーによるメッセージを送信するUIを作成します。

新規にリージョンを作成します。

識別タイトルUser Messageとします。タイプ静的コンテンツです。


作成したリージョンに、ユーザーによるメッセージを入力するページ・アイテムを作成します。

識別名前P1_MESSAGEタイプテキスト領域とします。ラベルMessageです。セッション・ステートデータ型CLOBストレージリクエストごと(メモリーのみ)を選択します。


入力したメッセージを含めて、Chat Completions APIを呼び出すボタンを作成します。

識別ボタン名SEND_MESSAGEラベルSend Messageとします。動作アクションはデフォルトのページの送信です。


Chat Completions APIのレスポンスを含めた、チャット履歴を表示するリージョンを作成します。

識別タイトルChat Historyタイプカードを選びます。ソースのタイプSQL問合せを選択し、SQL問合せとして以下を記述します。
select seq_id, c001, c002, clob001, n001, n002, n003
from apex_collections
where collection_name = :G_CHAT_HISTORY order by seq_id desc

カードに表示する内容を設定します。プロパティ・エディタ属性タブを開きます。

外観レイアウトとして水平(行)を選びます。カード主キー列1SEQ_IDです。

タイトルC001サブタイトルC002本体CLOB001を選択します。2次本体拡張フォーマットオンにし、HTML式として以下を記述します。
{if N001/}
prompt_tokens: &N001. completion_tokens: &N002. total_tokens: &N003.
{endif/}

デバッグ用にChat Completions APIに送信したリクエストと、受信したレスポンスを表示するページ・アイテムを作成します。

送信したリクエストを保持するページ・アイテムはP1_REQUESTとして作成します。ラベルRequest設定ページの送信時に送信オフにします。セッション・ステートデータ型CLOBストレージセッションごと(永続)を選択します。


同様にレスポンスを保持するページ・アイテムP1_RESPONSEを作成します。ラベルResponseとします。


左ペインでプロセス・ビューを開き、ボタンSEND_MESSAGEをクリックしたときに実行されるプロセスを作成します。

識別名前Send MessageタイプAPIの呼出しとします。設定パッケージUTL_OPENAI_CHAT_APIプロシージャまたはファンクションCHATを選択します。

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


パラメータp_contentタイプアイテムアイテムP1_MESSAGEとします。


パラメータp_collection_nameタイプ静的値静的値&G_CHAT_HISTORY.とします。


パラメータp_api_endpointタイプ静的値静的値&G_API_ENDPOINT.とします。


パラメータp_model_nameタイプ静的値静的値&G_MODEL_NAME.とします。


パラメータp_credential_static_idタイプ静的値静的値&G_CREDENTIAL.とします。


パラメータp_request_outの値のアイテムにP1_REQUESTを指定します。


パラメータp_response_outの値のアイテムにP1_RESPONSEを指定します。


その他のパラメータはAPIデフォルトのまま変更しません。

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

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

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


コードにはツール呼び出しの対応が含まれていて、ツールに関する定義を保存する表OPENAI_TOOLSへの参照があります。そのため、以下のDDLを実行して表をあらかじめ作成しておく必要があります。