2025年5月23日金曜日

macOSのMicrosoft AI Foundry LocalでLLMを実行しOracle APEXのアプリケーションから呼び出す

最近パブリックプレビューがリリースされたMicrosoft AI Foundry LocalでLLMを実行し、Oracle APEXのアプリケーションから呼び出してみます。作業はmacOSで行い、LLMを呼び出すOracle APEXは、ローカルのpodmanのコンテナで実行します。

ローカル環境でのOracle APEXの構成について、以下の記事で紹介しています。

podmanを使ってOracle Database FreeとOracle REST Data Servicesをコンテナとして実行する

また、Oracle APEXの著名な開発パートナーであるUnited Codesさんが、GitHubにAPEXの構築スクリプトを含むリポジトリを公開しています。

Containerized APEX Development Environment
https://github.com/United-Codes/uc-local-apex-dev

今回の記事はMicrosoft AI Foundry Localの動作確認を目的としています。そのため、すでに以下の記事で作成済みのAPEXアプリケーションを使って動作を確認します。

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

この記事で作成したAPEXアプリケーションのエクスポートは以下です。このアプリケーションでは、データベース・サーバーからLLMを呼び出します
https://github.com/ujnak/apexapps/blob/master/exports/chat-with-generative-ai-hc-242.zip

日本語を追加学習したサイバーエージェントのDeepSeek-R1のモデルで小説を生成して共有するアプリを作る
https://apexugj.blogspot.com/2025/01/deepseek-r1-novel-generator.html

少々アプリケーションを改変したエクスポートを以下に置きました。このアプリケーションでは、ブラウザからLLMを呼び出します
https://github.com/ujnak/apexapps/blob/master/exports/novel-generator-242.zip

以下よりMicrosoft AI Foundry LocalをmacOSで実行するために行なった作業を記録します。

作業にあたって、Microsoftのサイトから参照できるFoundry Localの概要クイックスタートを参照しました。

macOSでは最初に以下のコマンドを実行します。

brew tap microsoft/foundrylocal

% brew tap microsoft/foundrylocal

==> Tapping microsoft/foundrylocal

Cloning into '/opt/homebrew/Library/Taps/microsoft/homebrew-foundrylocal'...

remote: Enumerating objects: 126, done.

remote: Counting objects: 100% (22/22), done.

remote: Compressing objects: 100% (14/14), done.

remote: Total 126 (delta 12), reused 8 (delta 8), pack-reused 104 (from 1)

Receiving objects: 100% (126/126), 313.79 KiB | 1.92 MiB/s, done.

Resolving deltas: 100% (65/65), done.

Tapped 2 formulae (17 files, 335.2KB).


foundrylocalをインストールします。

brew install foundrylocal

% brew install foundrylocal

==> Downloading https://formulae.brew.sh/api/formula.jws.json

==> Downloading https://formulae.brew.sh/api/cask.jws.json

==> Fetching microsoft/foundrylocal/foundrylocal

==> Downloading https://github.com/microsoft/Foundry-Local/releases/download/v0.3.9267/FoundryLocal-osx-arm64-0.3.9267.42993.zip

==> Downloading from https://objects.githubusercontent.com/github-production-release-asset-2e65be/958239663/158c7cec-ee9a-4694-a3b7-cb3091759f6c?X-Amz-Al

################################################################################################################################################## 100.0%

==> Installing foundrylocal from microsoft/foundrylocal

🍺  /opt/homebrew/Cellar/foundrylocal/0.3.9267.42993: 9 files, 75.6MB, built in 1 second

==> Running `brew cleanup foundrylocal`...

Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP.

Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).

               


以上でFoundry Localのインストールは完了です。

Foundry Localで利用可能なモデルを一覧します。

foundry model list

% foundry model list

Alias                          Device     Task               File Size    License      Model ID            

-----------------------------------------------------------------------------------------------

phi-4                          GPU        chat-completion    8.37 GB      MIT          Phi-4-generic-gpu   

                               CPU        chat-completion    10.16 GB     MIT          Phi-4-generic-cpu   

--------------------------------------------------------------------------------------------------------

mistral-7b-v0.2                GPU        chat-completion    4.07 GB      apache-2.0   mistralai-Mistral-7B-Instruct-v0-2-generic-gpu

                               CPU        chat-completion    4.07 GB      apache-2.0   mistralai-Mistral-7B-Instruct-v0-2-generic-cpu

-------------------------------------------------------------------------------------------------------------------------------------

phi-3.5-mini                   GPU        chat-completion    2.16 GB      MIT          Phi-3.5-mini-instruct-generic-gpu

                               CPU        chat-completion    2.53 GB      MIT          Phi-3.5-mini-instruct-generic-cpu

------------------------------------------------------------------------------------------------------------------------

phi-3-mini-128k                GPU        chat-completion    2.13 GB      MIT          Phi-3-mini-128k-instruct-generic-gpu

                               CPU        chat-completion    2.54 GB      MIT          Phi-3-mini-128k-instruct-generic-cpu

---------------------------------------------------------------------------------------------------------------------------

phi-3-mini-4k                  GPU        chat-completion    2.13 GB      MIT          Phi-3-mini-4k-instruct-generic-gpu

                               CPU        chat-completion    2.53 GB      MIT          Phi-3-mini-4k-instruct-generic-cpu

-------------------------------------------------------------------------------------------------------------------------

phi-4-mini-reasoning           GPU        chat-completion    3.15 GB      MIT          Phi-4-mini-reasoning-generic-gpu

                               CPU        chat-completion    4.52 GB      MIT          Phi-4-mini-reasoning-generic-cpu

-----------------------------------------------------------------------------------------------------------------------

deepseek-r1-14b                GPU        chat-completion    10.27 GB     MIT          deepseek-r1-distill-qwen-14b-generic-gpu

-------------------------------------------------------------------------------------------------------------------------------

deepseek-r1-7b                 GPU        chat-completion    5.58 GB      MIT          deepseek-r1-distill-qwen-7b-generic-gpu

------------------------------------------------------------------------------------------------------------------------------

phi-4-mini                     GPU        chat-completion    3.72 GB      MIT          Phi-4-mini-instruct-generic-gpu

----------------------------------------------------------------------------------------------------------------------

qwen2.5-0.5b                   GPU        chat-completion    0.68 GB      apache-2.0   qwen2.5-0.5b-instruct-generic-gpu

                               CPU        chat-completion    0.80 GB      apache-2.0   qwen2.5-0.5b-instruct-generic-cpu

------------------------------------------------------------------------------------------------------------------------

qwen2.5-coder-0.5b             GPU        chat-completion    0.52 GB      apache-2.0   qwen2.5-coder-0.5b-instruct-generic-gpu

                               CPU        chat-completion    0.80 GB      apache-2.0   qwen2.5-coder-0.5b-instruct-generic-cpu

------------------------------------------------------------------------------------------------------------------------------

qwen2.5-1.5b                   GPU        chat-completion    1.51 GB      apache-2.0   qwen2.5-1.5b-instruct-generic-gpu

                               CPU        chat-completion    1.78 GB      apache-2.0   qwen2.5-1.5b-instruct-generic-cpu

------------------------------------------------------------------------------------------------------------------------

qwen2.5-7b                     GPU        chat-completion    5.20 GB      apache-2.0   qwen2.5-7b-instruct-generic-gpu

                               CPU        chat-completion    6.16 GB      apache-2.0   qwen2.5-7b-instruct-generic-cpu

----------------------------------------------------------------------------------------------------------------------

qwen2.5-coder-1.5b             GPU        chat-completion    1.25 GB      apache-2.0   qwen2.5-coder-1.5b-instruct-generic-gpu

                               CPU        chat-completion    1.78 GB      apache-2.0   qwen2.5-coder-1.5b-instruct-generic-cpu

------------------------------------------------------------------------------------------------------------------------------

qwen2.5-coder-7b               GPU        chat-completion    4.73 GB      apache-2.0   qwen2.5-coder-7b-instruct-generic-gpu

                               CPU        chat-completion    6.16 GB      apache-2.0   qwen2.5-coder-7b-instruct-generic-cpu

----------------------------------------------------------------------------------------------------------------------------

qwen2.5-14b                    GPU        chat-completion    9.30 GB      apache-2.0   qwen2.5-14b-instruct-generic-gpu

                               CPU        chat-completion    11.06 GB     apache-2.0   qwen2.5-14b-instruct-generic-cpu

-----------------------------------------------------------------------------------------------------------------------

qwen2.5-coder-14b              GPU        chat-completion    8.79 GB      apache-2.0   qwen2.5-coder-14b-instruct-generic-gpu

                               CPU        chat-completion    11.06 GB     apache-2.0   qwen2.5-coder-14b-instruct-generic-cpu

% 


今の所、利用できるモデルは、Phi(Microsoft - 米国)、Mistral(Mistral AI - フランス)、DeepSeek-R1(DeepSeek - 中国)、Qwen2.5(アリババ - 中国)のようです。

確認作業にPhi-4を使うことにします。

model runコマンドを実行します。実行するモデルに別名のphi-4を指定すると、ローカルのマシンにあった(GPUの有無など)モデルが自動的に選択されます。

foundry model run phi-4
 
モデルがローカルになければ、モデルのダウンロードが開始します。その後、モデルがロードされ呼び出し可能になります。

% foundry model run phi-4

Downloading model...

[####################################] 100.00 % [Time remaining: about 0s]        29.3 MB/s

🕚 Loading model... 

🟢 Model Phi-4-generic-gpu loaded successfully


Interactive Chat. Enter /? or /help for help.


Interactive mode, please enter your prompt

> /exit

% 


-----------------------------------------------------------------------------------------------------------------------

foundry modelコマンドを使っていて気になる点がいくつかありました。

model runコマンドのヘルプを見ると--interactiveというオプションがあり、falseの指定で対話モードにならないと解釈できます。実際には--interactive falseをつけて実行しても、対話モードになります。

foundry model run phi-4 --interactive false

% foundry model run phi-4 --interactive false

Model phi-4 was found in the local cache.


Interactive Chat. Enter /? or /help for help.


Interactive mode, please enter your prompt

> 


非対話モードでモデルを動かすにはmodel loadコマンドを使用します。

foundry model load phi-4

% foundry model load phi-4              

🕓 Loading model... 

🟢 Model phi-4 loaded successfully

%


ただし、model loadコマンドはモデルのダウンロードをしません。

% foundry model load phi-4              

🕛 Loading model... Exception: Failed: Loading model phi-4 from http://localhost:5273/openai/load/Phi-4-generic-gpu?ttl=600&ep=webgpu

Bad Request

Failed loading model Phi-4-generic-gpu

Model was not found locally. Please run 'foundry model download <model name>'.

% 


そのためmodel loadの前にmodel downloadを実行し、あらかじめモデルをキャッシュしておきます。

foundry model download phi-4
foundry cache ls

% foundry model download phi-4

Downloading model...

[####################################] 100.00 % [Time remaining: about 0s]        31.0 MB/s

Tips:

- To find model cache location use: foundry cache location

- To find models already downloaded use: foundry cache ls

% foundry cache ls

Models cached on device:

   Alias                         Model ID

💾 phi-4                         Phi-4-generic-gpu

% 


TTLのデフォルトは600、つまり未使用状態が10分続くとモデルはアンロードされます。TTLを延長するには、--ttlオプションに秒数を与えます。

foundry model load phi-4 --ttl 3600

% foundry model load phi-4 --ttl 3600

🕓 Loading model... 

🟢 Model phi-4 loaded successfully

% 


model runコマンドで別のモデルをロードすると、ロード済みのモデルがアンロードされます。ロード済みのモデルを維持するには、オプションとして--retain trueを与えます。model loadでロードする場合は、ロード済みのモデルは維持されます。

foundry model run deepseek-r1-14b

% foundry model run deepseek-r1-14b                            

Model deepseek-r1-14b was found in the local cache.

Unloading existing models. Use --retain true to keep additional models loaded.

🕔 Loading model... 

🟢 Model deepseek-r1-distill-qwen-14b-generic-gpu loaded successfully


Interactive Chat. Enter /? or /help for help.


Interactive mode, please enter your prompt

/exit

% foundry service ps

Models running in service:

    Alias                          Model ID            

🟢  deepseek-r1-14b                deepseek-r1-distill-qwen-14b-generic-gpu

% 


Aliasが指定できるのはmodel runmodel downloadmodel loadコマンドです。model unloadコマンドではModel IDを指定する必要があります。

foundry model unload deepseek-r1-distill-qwen-14b-generic-gpu

% foundry model unload deepseek-r1-14b

Exception: Failed: Unloading model deepseek-r1-14b

Bad Request

Failed unloading model deepseek-r1-14b

Model was not found locally. Please run 'foundry model download <model name>'.

% foundry model unload deepseek-r1-distill-qwen-14b-generic-gpu

Model deepseek-r1-distill-qwen-14b-generic-gpu was unloaded

ynakakoshi@Ns-Macbook ~ % 


-----------------------------------------------------------------------------------------------------------------------

モデルが実行されているどうかは、service psコマンドで確認できます。

foundry service ps

% foundry service ps

Models running in service:

    Alias                          Model ID            

🟢  phi-4                          Phi-4-generic-gpu   

% 


Foundry LocalはOpenAI互換のChat Completions APIをサポートしています。APIのエンドポイントはservice statusコマンドで確認できます。

foundry service status

Foundry Localのサービスがhttp://localhost:5273/で待ち受けしていることが確認できます。これよりOpenAI Chat Completions APIのエンドポイントは以下になります。

http://localhost:5273/v1/chat/completions

% foundry service status

🟢 Model management service is running on http://localhost:5273/openai/status

% 


Foundry Localがサービスを待ち受けるサービスURLのポートは固定ではありません。Microsoftから提供されているFoundry Local SDKでは、FoundryLocaclManagerというクラスが提供されていて、そのクラスからFoundry LocalのサービスURLを取得することができます。APEXからFoundry Localを呼び出す場合はSDKが使えないため、必ずサービスURLを確認してアプリケーションに設定する必要があります。


ツール呼び出しの確認


最初にChat with Generative AI(chat-with-generative-ai-hc-242.zip)をインストールして、Foundry Localで実行しているPhi-4を呼び出してみます。

コンテナで動作しているデータベースからFoundry Localを呼び出すため、API Endpointhttp://host.containers.internal:5273/v1/chat/completionsModel NamePhi-4-generic-gpuを指定します。

最終的な応答は以下でした。
日本の人口を取得するために、WKSP_APEXDEVスキーマ内の関連するテーブルをクエリする必要があります。通常、人口データは「COUNTRIES」テーブルに格納されています。以下は、日本の人口を取得するためのSQL SELECT文です: ```sql SELECT POPULATION FROM WKSP_APEXDEV.COUNTRIES WHERE COUNTRY_NAME = 'Japan'; ``` このクエリは、国名が「Japan」であるレコードの人口を取得します。
質問の回答にはなっていますが、ツール呼び出しが行われていません。


Foundry Local REST APIリファレンスを参照すると、リクエストボディにfunctionsやfunction_callが含まれていて、toolsは追加のファウンドリローカルプロパティになっています。OpenAIのChat Completions APIではtoolsとtool_callsの使用が推奨されていて、このAPEXアプリケーションが使用しているパッケージUTL_OPENAI_CHAT_APIでもtoolsとtool_callsを使用しています。

この部分は少し気になりますが、実際にはモデルがツール呼び出しに対応していないようです。

Foundry Localが提供しているカスタムAPIの/foundry/listを呼び出すと、Foundry Localで使用可能なモデルの一覧をJSON形式で取得できます。その中のモデルの属性にsupportsToolCallingがあり、すべてのモデルについてfalseになっていました。つまり、Foundry Localが提供しているモデルの中でツール呼び出しに対応しているモデルは、現時点では無いみたいです。

curl http://localhost:5273/foundry/list | jq -r '.[] | [.name, .maxOutputTokens, .supportsToolCalling ] | @tsv' | column -t

% curl http://localhost:5273/foundry/list | jq -r '.[] | [.name, .maxOutputTokens, .supportsToolCalling ] | @tsv' | column -t

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current

                                 Dload  Upload   Total   Spent    Left  Speed

100 38716  100 38716    0     0  26890      0  0:00:01  0:00:01 --:--:-- 26886

Phi-4-generic-gpu                               2048  false

Phi-4-generic-cpu                               2048  false

mistralai-Mistral-7B-Instruct-v0-2-generic-gpu  2048  false

mistralai-Mistral-7B-Instruct-v0-2-generic-cpu  2048  false

Phi-3.5-mini-instruct-generic-gpu               2048  false

Phi-3.5-mini-instruct-generic-cpu               2048  false

Phi-3-mini-128k-instruct-generic-gpu            2048  false

Phi-3-mini-128k-instruct-generic-cpu            2048  false

Phi-3-mini-4k-instruct-generic-gpu              2048  false

Phi-3-mini-4k-instruct-generic-cpu              2048  false

Phi-4-mini-reasoning-generic-gpu                2048  false

Phi-4-mini-reasoning-generic-cpu                2048  false

deepseek-r1-distill-qwen-14b-generic-gpu        2048  false

deepseek-r1-distill-qwen-7b-generic-gpu         2048  false

Phi-4-mini-instruct-generic-gpu                 2048  false

qwen2.5-0.5b-instruct-generic-gpu               2048  false

qwen2.5-0.5b-instruct-generic-cpu               2048  false

qwen2.5-coder-0.5b-instruct-generic-gpu         2048  false

qwen2.5-coder-0.5b-instruct-generic-cpu         2048  false

qwen2.5-1.5b-instruct-generic-gpu               2048  false

qwen2.5-1.5b-instruct-generic-cpu               2048  false

qwen2.5-7b-instruct-generic-gpu                 2048  false

qwen2.5-7b-instruct-generic-cpu                 2048  false

qwen2.5-coder-1.5b-instruct-generic-gpu         2048  false

qwen2.5-coder-1.5b-instruct-generic-cpu         2048  false

qwen2.5-coder-7b-instruct-generic-gpu           2048  false

qwen2.5-coder-7b-instruct-generic-cpu           2048  false

qwen2.5-14b-instruct-generic-gpu                2048  false

qwen2.5-14b-instruct-generic-cpu                2048  false

qwen2.5-coder-14b-instruct-generic-gpu          2048  false

qwen2.5-coder-14b-instruct-generic-cpu          2048  false

% 



ストリーム出力の確認


小説生成器(novel-generator-242.zip)をインストールして、Foundry Localで実行しているPhi-4を呼び出してみます。

Foundry Localをブラウザから呼び出すため、Endpoint URLhttp://localhost:5273/v1/chat/completionsになります。Model NamePhi-4-generic-gpuです。


ブラウザからFoundry Localのモデルを呼び出すことができ、また、ストリーミングも上手く動作しています。

気になった点としてtemperatureがあります。temperatureに2を指定しても(Foundry Localでは0から2の範囲で指定可)、まったく同じ小説が生成されます。送信メッセージにtemperatureは正しく含まれているはずなのですが、理由は不明です。

モデルのロード時にTTLを指定していないとデフォルトの10分間になります。10分間が過ぎてモデルがアンロードされていると、そのモデルへのリクエストを受け付けた時点でモデルのロードが始まります。Foundry Localを実行している環境に依存すると思いますが、Phi-4-generic-gpuで30秒程度ロードに時間がかかり、ロードが完了するまで画面はエラー・メッセージも出さずに止まった状態になります。これはFoundry Localの応答が遅いわけではありません。作業内容によってはTTLは延長した方がよいでしょう。


カスタムAPI


Foundry LocalはOpenAI互換のChat Completions APIに加えて、Foundry Local固有のカスタムAPIを提供しています。

GET /foundry/listとGET /openai/modelsの結果を表示する簡単なAPEXアプリケーションを作成して、動作を確認してみました。
https://github.com/ujnak/apexapps/blob/master/exports/foundry-local-custom-api.zip

GET /foundry/listはfoundry model lsに対応する情報が得られますが、それよりも遥かに詳細です。


GET /openai/modelsでは、foundry cache lsに対応する情報が得られます。


ロード済みのモデルを一覧したり、モデルのロードやアンロードもAPI呼び出しで実行できるので、便利かもしれません。

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

MicrosoftがAI Foundry Localに登録しているモデルはすべてONNXです。Oracle Databaseが組み込めるモデルも(基本的に)ONNXなので、将来のいつか、MicrosoftのAI Foundry LocalにロードできるONNXモデルを、Oracle Databaseにもロードできるようになることを期待しています。