さらにLM StudioもしくはOllamaを使用することで、OpenAI互換のAPIサーバーを実行することができます。つまり、APIのエンドポイントを変更することにより、コードを変更せずにOpenAIとローカルLLMを切り替えることができます。
また、オラクルよりARM64版のOracle Database 23ai Freeがリリースされたことにより、Oracle APEXを手元のMacで実行できるようになりました。以前はIntelのエミュレータ(Colima)での実行だったため、非常に遅くアプリケーションの開発に使うには苦しい状況でした。ARM64のOracle Database 23ai Freeで作成したOracle APEX環境では、ほとんどストレスなくアプリケーション開発を行えます。
Ollamaのブログにて、Tool supportについて案内されていました。いわゆるOpenAIのFunction Callingです。OllamaのOpenAI互換サーバーでも、OpenAIと同様のメッセージの仕様がサポートされているようです。
以下より、Apple MシリーズのMacでOracle APEXを動かし、OllamaのローカルLLMのTool supportを確認してみます。
最終的に、以下のGIF動画のように動作しました。
Apple MシリーズのMacでOracle APEXの環境を作る手順については、以下の記事を参考にします。
Oracle Database 23ai FreeとOracle REST Data Servicesともに、コンテナ・イメージを使ってOracle APEXの環境を作成します。APEXワークスペースもスクリプトを使って作成します。
[参考記事] podmanを使ってOracle Database FreeとOracle REST Data Servicesをコンテナとして実行する[参考記事] APEXのワークスペースを作成するコードを記述する
[参考記事] Apple M SeriesのpodmanでOracle Database Freeのコンテナを作成しOracle APEXを実行するOracle APEXの環境を作成する方法としては、VirtualBoxを使う方法や、Oracle Database 23ai FreeのコンテナにORDSを実装する方法も紹介しています。今回は、これらの手順は採用しません。
[参考記事] Apple M Series + VirtualBox + Oracle Database Freeの組み合わせでOracle APEXを構成するOpenAIのAPIを呼び出すAPEXのサンプル・アプリケーションの作成に関する記事です。
OpenAIのChat Completions APIを呼び出すAPEXアプリケーションの作成記事です。
上記のアプリケーションにツール呼び出し(ファンクション・コーリング)の実装を追加しています。
[参考記事] OpenAI Chat Completions APIのツール呼び出しを使ってPL/SQLファンクションを呼び出すこれまでのアプリに、ローカルLLMへの対応を追加しています。Ollamaで動作を確認しています。この記事で紹介されているAPEXアプリケーションを、本記事では使用します。
[参考記事] Oracle APEXのアプリからいろいろなローカルLLMを呼び出してみるアプリケーションのエクスポート:
https://github.com/ujnak/apexapps/blob/master/exports/chat-with-generative-ai-hc.zip
LM Studioで動作確認をした記事です。本記事ではLM Studioは使いませんが、Oracle APEXの環境作成から、表OPENAI_TOOLSの作成およびパッケージUTL_OPENAI_CHAT_APIまでは同じ手順になります。
以下より、実際の作業について紹介します。Oracle APEXの環境、ワークスペースの作成およびOllamaのインストールまでは完了しているところから始めます。
最初に表OPENAI_TOOLSを作成します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CREATE TABLE "OPENAI_TOOLS" | |
( "ID" NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE NOKEEP NOSCALE NOT NULL ENABLE, | |
"TOOL_SET" VARCHAR2(20 CHAR) NOT NULL ENABLE, | |
"TOOL_TYPE" VARCHAR2(20 CHAR) DEFAULT ON NULL 'function' NOT NULL ENABLE, | |
"DESCRIPTION" VARCHAR2(4000 CHAR) NOT NULL ENABLE, | |
"TOOL_NAME" VARCHAR2(80 CHAR) NOT NULL ENABLE, | |
"PARAMETERS" CLOB, | |
CHECK (parameters is json) ENABLE, | |
CONSTRAINT "OPENAI_TOOLS_ID_PK" PRIMARY KEY ("ID") | |
USING INDEX ENABLE | |
) ; |
単一のコマンドなのでSQLコマンドに貼り付けて実行します。複数のコマンドの場合は、SQLスクリプトを作成して実行します。
パッケージUTL_OPENAI_CHAT_APIについても、同様にSQLコマンドに貼り付けて作成します。
パッケージ定義
https://gist.github.com/ujnak/93c9d427ccef53e30dd89e623f08c880
パッケージ本体
https://gist.github.com/ujnak/a6126f7e9ee264d0e384103cea0379b5
エクスポートされたアプリケーションをダウンロードし、ワークスペースにインポートします。
https://github.com/ujnak/apexapps/blob/master/exports/chat-with-generative-ai-hc.zip
アプリケーション・ビルダーからインポートを実行します。
インポートするファイルとしてchat-with-generative-ai-hc.zipを選択します。
次へ進みます。
アプリケーションのインストールを実行します。
アプリケーションがインポートされました。
接続先となるLLMを設定するため、アプリケーションの編集をクリックします。
アプリケーションの編集画面が開きます。
アプリケーション定義の編集を開きます。
アプリケーション定義の置換タブを開きます。
置換文字列のG_API_ENDPOINTの置換値は以下です。
http://host.containers.internal:11434/v1/chat/completions
G_MODEL_NAMEにはllama3.2を設定します。Ollamaでツール呼び出しをサポートしているモデルです。これらの値はアプリケーション上で変更できます。
設定の変更を適用し、アプリケーションを実行します。
記事「OpenAI Chat Completions APIのツール呼び出しを使ってPL/SQLファンクションを呼び出す」に記載されている、指定した都市(首都)の天候を取得するファンクションget_current_weatherを設定します。
Toolsページを開き、作成をクリックします。
Tool SetはWeatherとします。Tool Typeはfunctionを指定します。DescriptionはGet the current weather in a given location、Tool Nameはget_current_weather、Parametersには以下のJSON Schemaを記述します。
{
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city name, e.g. Tokyo"
}
},
"required": [
"location"
]
}
以上で、作成をクリックします。
PL/SQLファンクションget_current_weatherが、呼び出し可能なツールとして登録されました。
実際に呼び出されるPL/SQLファンクションget_current_weatherを作成します。
ダウンロードしたファイルasti-dats59r03wm.zipに含まれるs59r03megacities_utf8.csvを、新規表AMANO_CITY_LOCATIONSにロードします。
また、weathercodeについては「気象関連コード表」に記載されているWMO 4501のHTML表を表WMO4501、WMO 4677のHTML表を表WMO4677として、データをロードします。双方の表は列CODE(NUMBER)、列DESCRIPTION(VARCHAR2(4000))の列を持ちます。以下のDDLを実行して表を作成します。
create table wmo4501(code number primary key, description varchar2(4000));
create table wmo4677(code number primary key, description varchar2(4000));
データ・ワークショップを呼び出し、HTML表をコピペします。既存表を選択し、コピペした表に応じてWMO4501またはWMO4677をロード先の表として選択します。表を選択した後、構成をクリックします。
ソース列COL001のマップ先としてCODE(Number)、COL002のマップ先としてDESCRIPTION(Varchar2)を選択します。
変更の保存をクリックします。
構成を決定したのち、データのロードをクリックします。
表WMO4501にデータがロードされます。
表WMO4677についても同様に、WebページからHTML表をコピペしたデータを表WMO4677にロードします。
WMO4677については、00から99までのコードと説明をコピペします。表へのロード手順はWMO4501と同じです。
ファンクションget_current_weatherのコードは以下です。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
create or replace function get_current_weather( | |
p_args in clob | |
) | |
return clob | |
as | |
l_args json_object_t; | |
l_location varchar2(400); | |
l_location_tokens apex_t_varchar2; | |
l_lat number; | |
l_lon number; | |
l_url varchar2(4000); | |
l_response clob; | |
l_response_json json_object_t; | |
l_weathercode number; | |
l_weather varchar2(4000); | |
begin | |
l_args := json_object_t(p_args); | |
l_location := l_args.get_string('location'); | |
/* get first part (city name) from the location */ | |
l_location_tokens := apex_string.split(l_location, '[ ,]'); | |
l_location := l_location_tokens(1); | |
apex_debug.info('location = %s', l_location); | |
/* | |
* 都市名から緯度経度の情報を取り出す。 | |
* アマノ技研さんより提供されている「世界の百万都市の位置データ Location Data of Megacities」の | |
* CSVファイルを表AMANO_CITY_LOCATIONSにロードしています。 | |
* 参照: https://amano-tec.com/data/megacities.html | |
*/ | |
select lat, lon into l_lat, l_lon | |
from amano_city_locations | |
where upper(capital_en) = upper(l_location) or capital_jp = l_location; | |
/* | |
* Open-Meteo.comのAPIを呼び出し、指定した座標の現在の天気を取得しています。 | |
* 参照: https://open-meteo.com | |
*/ | |
l_url := 'https://api.open-meteo.com/v1/forecast?latitude=' || l_lat || '&longitude=' || l_lon || '¤t=weathercode'; | |
l_response := apex_web_service.make_rest_request( | |
p_url => l_url | |
,p_http_method => 'GET' | |
); | |
if apex_web_service.g_status_code <> 200 then | |
return '{ "error": "open-meteo.com api call failed, response = ' || l_response || '" }'; | |
end if; | |
l_response_json := json_object_t(l_response); | |
/* | |
* Weather Code (WMO 4501)のコードの説明は以下の気象関係コード表のWMO 4501の部分を | |
* 表WMO4501として読み込んでいます。 | |
* 参照: https://www.jodc.go.jp/data_format/weather-code_j.html | |
* | |
* たまにWMO 4677を返しているのでは?と思えるときもあります。なのでコードが10以上の場合は、WMO4677として | |
* 取り扱います。 | |
*/ | |
l_weathercode := l_response_json.get_object('current').get_number('weathercode'); | |
if l_weathercode < 10 then | |
select description into l_weather from wmo4501 where code = l_weathercode; | |
else | |
select description into l_weather from wmo4677 where code = l_weathercode; | |
end if; | |
return '{ "weather" : "' || l_weather || '" }'; | |
exception | |
when no_data_found then | |
return apex_string.format('{ "error": %s }', l_response); | |
end get_current_weather; | |
/ |
これをSQLコマンドに貼り付けて実行します。
以上で、ファンクションget_current_weatherを呼び出せるようになりました。
アプリケーションを実行し、動作を確認してみます。
llama3.2は日本語を(あまり)学習していないとのことなので、確認は英語で行います。
ボタンStart New Conversationをクリックし、チャットを初期化します。Tool SetにWeatherを選択し、Promptとして「You are a weather forecaster.」を記述します。
以上でボタンSet Promptをクリックします。
systemメッセージとして、You are a weather forecaster. が設定されます。この時点では、OllamaのAPI呼び出しは行われていません。
Messageに「Could you tell me the current weather in Tokyo?」と記述して、ボタンSend Messageをクリックします。
チャット履歴より、正常にツールが呼び出されていることが確認できます。
Oracle APEXのアプリケーション作成の参考になれば幸いです。
完