2025年5月1日木曜日

Qwen3 30B A3B MLXをMacのLM Studioで実行しAPEXアプリケーションからツール呼び出しを行う

2025年4月29日にアリババより発表されたQwne3をmacOSのLM Studioで実行し、ローカルにインストールしたOracle APEXのアプリケーションより呼び出してみます。Qwen3の紹介文によると、tool callingが強化されているとのことなので、以前にOllama向けに作成したツール呼び出しに対応したAPEXアプリケーション(記事「Oracle APEXのアプリよりOllamaのTool supportを呼び出す」)を使用して、主にtool callingの動作を確認します。

APEXアプリケーションのエクスポートは以下です。APEX 24.2で作成しているため、それ以前のバージョンでは読み込めません。
https://github.com/ujnak/apexapps/blob/master/exports/chat-with-generative-ai-hc-242.zip

今回の記事はAPEXアプリケーションの作成方法の紹介を意図していないため、アプリケーションをインストールすると使えるように、サポートするオブジェクトに以下のスクリプトを登録しています。
  1. openai_tools.sql - 表OPENAI_TOOLSを作成するDDL
  2. utl_openai_chat_api.pkh - パッケージUTL_OPENAI_CHAT_APIの定義部
  3. utl_openai_chat_api.pkb - パッケージUTL_OPENAI_CHAT_APIの本体
  4. get_schema - データベースのスキーマ情報を返すファンクションget_schemaを作成
  5. run_sql - 受け取ったSELECT文を実行し、JSONでレスポンスを返すファンクションrun_sqlを作成
  6. tool definitions - 表OPENAI_TOOLSに登録するファンクションget_schemaとrun_sqlのツール定義
最終的に表OPENAI_TOOLS、パッケージUTL_OPENAI_CHAT_API、ファンクションGET_SCHEMAおよびRUN_SQLが作成され、表OPENAI_TOOLSに2行データが挿入されます。

APEXのパーシング・スキーマにサンプル・データセットをインストールし、そのデータについて問い合わせてみました。


システム・プロンプトとして以下を与えています。SQLの生成はQwen3が行うため、Oracle DatabaseのSQL方言を話すようにプロンプトで指示しています。また、スキーマWKSP_APEXDEVを参照するように、プロンプトで指示しています。
あなたは日本語を話すデータ・アナリストです。
実行するSELECT文はOracle Database向けに生成してください。
参照するスキーマは常にWKSP_APEXDEVです。
最初に以下のメッセージを与えました。(GIF動画とは結果が異なります)
日本の人口を教えて。
レスポンスは以下でした。ツール呼び出しとしては、get_schemaを一回、run_sqlは3回呼び出しています。これは最初の2回は結果を返さなかったためです。

日本の人口は126,860,301人です(データソース: WKSP_APEXDEV.EBA_COUNTRIES テーブル)。

この数値は2023年時点の推定値となります。より詳細な統計や最新データが必要な場合は、別途調整を検討してください。

次に以下のメッセージを与えてみました。
アジアで最も人口の少ない国は?
レスポンスは以下でした。

アジアで最も人口の少ない国はブルネイ・ダルサラーム(Brunei Darussalam)です。人口は433,285人(データソース: WKSP_APEXDEV.EBA_COUNTRIES_V テーブル)です。

この結果は、2023年時点の推定値に基づいています。より詳細な統計や最新データが必要な場合は、別の基準(例: 経済協力開発機構のデータなど)を検討することをお勧めします。

次に以下のメッセージを与えてみました。
アメリカで一番人口の多い国は?
レスポンスは以下でした。

アメリカ大陸(North America)で人口最多の国はアメリカ合衆国(United States of America)です。人口は3億2,906万4,917人(データソース: WKSP_APEXDEV.EBA_COUNTRIES_V テーブル)です。

ただし、データベースの構造上、「REGION = 'North America'」の条件で検索した際にはデータが取得できませんでした。このため、明示的に「United States of America」を指定して確認しました。もし大陸全体のランキングが必要な場合は、データの正確性を再検証する必要があります。

正確なランキングが必要です。以下の指示を与えました。
リージョンは表EBA_COUNTRY_REGIONSに定義されています。この情報を覚えてください。
以下のレスポンスが返されました。

リージョンの定義が表 EBA_COUNTRY_REGIONS に保存されていることを認識しました。この情報から、リージョン名(例: 'Asia'、'North America')は EBA_COUNTRIES_V テーブルの REGION カラムに直接記録されている可能性がありますが、正確なマッピングは EBA_COUNTRY_REGIONS の ID と NAME を参照する必要があります。

今後のクエリでリージョンを扱う際は、必要に応じて EBA_COUNTRIES_V と EBA_COUNTRY_REGIONS を結合するクエリを構築することで、より正確な結果を得られるようにします。

以下のメッセージを与えました。
では、アメリカ大陸全体で二番の人口を持つ国を教えて。
レスポンスは以下でした。

アメリカ大陸(Americas)で人口が2番目に多い国はブラジル(Brazil)です。人口は2億1,104万9,527人(データソース: WKSP_APEXDEV.EBA_COUNTRIES_V テーブル)です。

補足

  • リージョンの定義:データベース上の REGION カラムでは、「Americas」(アメリカ大陸)が1つのリージョンとして扱われており、北米と南米の国々が含まれます。
  • 順位:上位2国のリストは以下の通りです。
    1. United States of America(3億2,906万4,917人)
    2. Brazil(2億1,104万9,527人)

この結果は、リージョン「America」に属する国の人口に基づいています。

最後の回答を得るためにQwen3は以下のツール呼び出しを行なっています。

最初に以下のSELECT文を引数としてファンクションrun_sqlを呼び出しています。

SELECT NAME, POPULATION FROM WKSP_APEXDEV.EBA_COUNTRIES_V WHERE REGION IN ('North America', 'South America') ORDER BY POPULATION DESC FETCH FIRST 2 ROWS ONLY

run_sqlの回答は、"no data found, please consider to change the condition supplied with this select statment."です。ファンクションrun_sqlは、結果が得られないと条件を変えて、と応答するように作っています。

Qwen3は言われた通りにSELECT文の条件を変えてrun_sqlを呼び出しています。

SELECT NAME, POPULATION FROM WKSP_APEXDEV.EBA_COUNTRIES_V WHERE REGION = 'Americas' ORDER BY POPULATION DESC FETCH FIRST 2 ROWS ONLY

この場合もno data foundが返されます。Qwen3は諦めて表EBA_COUNTRY_REGIONSを読み込んでいます。以下のSELECT文をrun_sqlで実行しています。

SELECT ID, NAME FROM WKSP_APEXDEV.EBA_COUNTRY_REGIONS

run_sqlは以下のレスポンスを返します。

[{"ID":10,"NAME":"America"},{"ID":20,"NAME":"Europe"},{"ID":30,"NAME":"Asia"},{"ID":40,"NAME":"Oceania"},{"ID":50,"NAME":"Africa"}]

この回答を使って、REGIONにAmericaを指定したSELECT文を生成し、run_sqlを呼び出しています。

SELECT NAME, POPULATION FROM WKSP_APEXDEV.EBA_COUNTRIES_V WHERE REGION = 'America' ORDER BY POPULATION DESC FETCH FIRST 2 ROWS ONLY

run_sqlは以下のレスポンスを返します。

[{"NAME":"United States of America","POPULATION":329064917},{"NAME":"Brazil","POPULATION":211049527}]

2番目の国としてBrazilが得られたので、それが回答になっています。

ローカルで動かしているLLMが、ここまでプロンプトに追従するとは思いませんでした。

以下に若干、作業環境や設定などを紹介します。

LM Studioは0.3.15 (Build 11)を使っています。LM Studioにロードして実行したモデルは以下です。Content Lengthに40960を指定しています。


メモリは32.46GBと表示されています。4bit量子化のモデルであれば、32GBのメモリがあれば動きそうです。


問合せに使用するデータとして、サンプル・データセットをインストールしています。


APEXアプリケーションのアプリケーション定義置換文字列を設定しています。

APEXのデータベースはpodmanのコンテナとして実行しているため、LM StudioのAPIのエンドポイントの指定である置換文字列G_API_ENDPOINTには、http://host.containers.internal:8080/v1/chat/completionsを設定しています。LM Studioのローカル・サーバーはポート8080で待ち受けするように設定しています。

モデルの指定である置換文字列G_MODEL_NAMEにはqwen3-30b-a3b-mlxを設定しています。


スキーマ情報を返すファンクションget_schemaのコードは以下です。

create or replace function get_schema(
p_args in clob
)
return clob
as
l_owner_object json_object_t;
l_owner varchar2(128);
l_schema clob;
begin
l_owner_object := json_object_t(p_args);
l_owner := l_owner_object.get_string('schema');
if l_owner is null then
/* set default owner */
l_owner := SYS_CONTEXT('USERENV', 'CURRENT_USER');
else
l_owner := upper(l_owner);
end if;
select json_arrayagg(objects) into l_schema from (
select json_object(table_name, columns) objects from (
select table_name, json_arrayagg(json_object(column_name, data_type)) columns
from all_tab_columns where owner = l_owner group by table_name
)
);
return l_schema;
end get_schema;
/
view raw get_schema.sql hosted with ❤ by GitHub

ツール定義は以下になります。


SELECT文を受け取って実行するファンクションrun_sqlのコードは以下です。

create or replace function run_sql(
p_args in clob
)
return clob
as
C_STMT constant varchar2(4000) := q'~select json_arrayagg(object) from (
select json_object(*) object from (
%s
)
)~';
l_stmt clob;
l_result clob;
l_args json_object_t;
l_sql varchar2(32767);
begin
l_args := json_object_t(p_args);
l_sql := trim(l_args.get_string('sql'));
/* remove trailing ; from the sql */
l_sql := rtrim(l_sql, ';');
l_stmt := apex_string.format(C_STMT, l_sql);
apex_debug.info('sql: %s', l_stmt);
execute immediate l_stmt into l_result;
if l_result is null then
l_result := '{ "result": "no data found, please consider to change the condition supplied with this select statment." }';
end if;
return l_result;
end run_sql;
/
view raw run_sql.sql hosted with ❤ by GitHub

ツール定義は以下になります。


最後にインストール時に作成されたオブジェクトは、以下のコマンドで削除できます。
drop function run_sql;
drop function get_schema;
drop package  utl_openai_chat_api;
drop table    openai_tools;
今回の記事は以上になります。

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