2024年11月22日金曜日

OpenAI o1を呼び出して推論を行なうOracle APEXアプリケーションを作成する

OpenAI APIのPMの方より、APIからo1-previewo1-miniのモデルを使えるようになりました、とのメールを受け取りました。推論モデル(Reasoning model)に関しては、以下のドキュメントを参照して、とのことです。


これらの推論モデルをOracle APEXのアプリから呼び出してみました。呼び出しの確認が目的なので、簡単に実装しています。o1のモデルはStreamingをサポートしていますが、Oracle APEXのバックエンド、つまりデータベース・サーバーではStreamingを扱うのは難しいため、使っていません。

作成したAPEXアプリケーションは以下のように動作します。例では、以下のつるかめ算の回答をお願いしています。

「ある動物園に鶴と亀が合わせて20匹います。足の合計は56本です。鶴と亀はそれぞれ何匹いるでしょうか。」

回答は「鶴12匹、亀8匹」でした。正解です。


作成したアプリケーションのエクスポートは以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/reasoning-with-openai-o1-mini.zip

OpenAIのAPIの呼び出しに使用するWeb資格証明が、静的IDOPENAI_API_KEYとして作成されていることが前提です。静的IDが異なる場合は、APIを呼び出しているAPEX_WEB_SERVICE.MAKE_REST_REQUESTの引数p_creadential_static_idの値を変更します。


空のAPEXアプリケーションを作成し、デフォルトで作成されるホーム・ページにすべての機能を実装しています。


ページ・アイテムP1_QUESTIONにプロンプトを入力します。複数行の文字列を入力するため、タイプテキスト領域を選んでいます。


OpenAI o1(費用の関係からo1-miniを呼んでいます)からの回答は、ページ・アイテムP1_ANSWERに出力しています。タイプとしてリッチ・テキスト・エディタを選択しています。

Oracle APEXのパッチ・レベルが24.1.4であれば、設定のライブラリよりOracleリッチ・テキスト・ライブラリを選択できます。Oracle APEXの将来のバージョンでTinyMCEは非推奨になる予定です。Oracle APEXが採用しているリッチ・テキスト・エディタのライブラリは、CKEditor4、CKEditor5、TinyMCEと置き換えられてきましたが、これがOracleリッチ・テキスト・ライブラリに置き換わります。ただし、Oracleリッチ・テキスト・ライブラリの実体はCKEditor5なので、TinyMCEからCKEditor5に戻ったともいえます。


その他にAPIのレスポンスに含まれるfinish_reasonを表示するページ・アイテムとしてP1_FINISH_REASONモデル名を表示するP1_MODEL、利用状況(usage)を表示するP1_USAGEを作成しています。P1_USAGEについては、数値がわかれば十分なので、JSONドキュメントをそのまま出力しています。


ボタンSUBMITをクリックして、OpenAIのAPI呼び出しを呼び出します。動作アクションは、ボタンのデフォルトのページの送信です。


ボタンSUBMITをクリックしたときに実行されるプロセスとしてCall OpenAI APIを作成します。このプロセスに、以下のコードを記述します。

declare
l_request clob;
l_request_json json_object_t;
l_messages json_array_t;
l_message json_object_t;
l_response clob;
l_response_json json_object_t;
e_openai_api_failed exception;
l_model_name varchar2(80);
l_response_content clob;
l_usage_json json_object_t;
l_usage clob;
l_choices json_array_t;
l_choice json_object_t;
l_finish_reason varchar2(80);
l_count integer;
begin
l_request_json := json_object_t();
l_request_json.put('model','o1-mini');
l_message := json_object_t('{ "role": "user" }');
l_message.put('content', :P1_QUESTION);
l_messages := json_array_t();
l_messages.append(l_message);
l_request_json.put('messages', l_messages);
-- max_completion_tokens can be applied here.
-- o1-preview: Up to 32,768 tokens, o1-mini: Up to 65,536 tokens
-- l_request_json.put('max_completion_tokens', 65536);
l_request := l_request_json.to_clob();
/*
* Call OpenAI o1-mini for reasonong.
*/
apex_web_service.clear_request_headers();
apex_web_service.set_request_headers('Content-Type', 'application/json');
l_response := apex_web_service.make_rest_request(
p_url => 'https://api.openai.com/v1/chat/completions'
,p_http_method => 'POST'
,p_body => l_request
,p_credential_static_id => 'OPENAI_API_KEY'
,p_transfer_timeout => 360
);
if apex_web_service.g_status_code <> 200 then
raise e_openai_api_failed;
end if;
/*
* レスポンスのパース
*/
l_response_json := json_object_t(l_response);
-- 実際のモデル名
:P1_MODEL := l_response_json.get_string('model');
l_usage_json := l_response_json.get_object('usage');
l_usage := l_usage_json.to_clob();
-- usage - JSONそのままで印刷する。
select json_serialize(l_usage pretty) into :P1_USAGE from dual;
-- レスポンス・メッセージの取得
l_choices := l_response_json.get_array('choices');
l_count := l_choices.get_size();
l_response_content := '';
l_finish_reason := '';
-- choicesに含まれるメッセージは1つだけだと思うが、念の為配列を処理する。
for i in 1 .. l_count
loop
l_choice := treat(l_choices.get(i-1) as json_object_t);
l_message := l_choice.get_object('message');
l_response_content := l_response_content || l_message.get_string('content') || apex_application.LF;
l_finish_reason := l_finish_reason || l_choice.get_string('finish_reason') || ',';
end loop;
-- ページ・アイテムに出力する。
:P1_ANSWER := l_response_content;
:P1_FINISH_REASON := l_finish_reason;
end;


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

今回の記事は以上になります。

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