ラベル DeepSeek の投稿を表示しています。 すべての投稿を表示
ラベル DeepSeek の投稿を表示しています。 すべての投稿を表示

2025年2月21日金曜日

rinna株式会社がHugging Faceで公開しているDeepSeek R1を利用したQwen2.5 Bakeneko 32BをLM StudioとOllamaで実行する

rinna株式会社のプレスリリースです。

Qwen2.5とDeepSeek R1を利用した日本語大規模言語モデル「Qwen2.5 Bakeneko 32B」シリーズを公開

手元のMacbook ProのLM StudioとOllamaの両方で、DeepSeek R1を利用したQwen2.5 Bakeneko 32Bを動作させてみました。

Hugging FaceのModel cardへのリンクは以下です。

GGUF formatで2, 3, 4, 5, 6, 8-bitで量子化されたモデルを選べます。

LM Studio


Model searchでは見つからないので、lmsコマンドを実行してモデルをダウンロードします。私のMacbook Proはメモリをたくさん積んでいるので、量子化に@q8_0を選択しています。

lms get https://huggingface.co/rinna/deepseek-r1-distill-qwen2.5-bakeneko-32b-gguf@q8_0

コマンドを実行するとモデルがダウンロードされます。すでにダウンロードした後なので、以下のように出力されました。Q8_0で34.82GBのサイズです。

% lms get https://huggingface.co/rinna/deepseek-r1-distill-qwen2.5-bakeneko-32b-gguf@q8_0

Searching for models with the term https://huggingface.co/rinna/deepseek-r1-distill-qwen2.5-bakeneko-32b-gguf

Downloading Deepseek R1 Distill Qwen2.5 Bakeneko 32B [Q8_0] (34.82 GB)

You already have this model. You can load it with: 


    lms load deepseek-r1-distill-qwen2.5-bakeneko-32b@q8_0


% 


LM Studioを開いてダウンロードしたモデルをロードします。コンテキスト長を大きくするため、Manulally choose model load parametersオンにします。


Context Length131072に変更し、Load Modelを実行します。


モデルがロードされます。動作を確認してみます。

デジタル庁が公開しているデジタル社会推進標準ガイドラインのページを開き、DS-100デジタル・ガバメント推進標準ガイドラインをダウンロードします。

ダウンロードしたファイルを添付し、以下のプロンプトを与えてSendをクリックします。

「スタンドアロンコンピュータを用いるときの管理者の指定方法について、説明してください。」


すごい時間がかかりましたが(30分以上?)、以下のような回答が得られました。


DS-100の資料は全部で115ページあり、別紙4の「スタンドアロンコンピュータ管理責任者等の指定」は111ページに記載されています。Contextは55.9%使用したと報告されているので、まだコンテキスト長には余裕はあるようです。

元資料と見比べてはいませんが、それほど違和感を感じない出力です。

----- ここから ----

スタンドアロンコンピュータの管理者指定ガイドライン

ユーザーがスタンドアロンコンピュータの管理者指定について尋ねているため、ガイドラインの該当部分を整理しました。別紙4に「スタンドアロンコンピュータ管理責任者等の指定」があり、その中で管理責任者と管理担当者の設置が求められています。

また、本ガイドラインの適用範囲と特例規定を見ると、管理責任者が特定の業務を担うことが明記されています。運用上の注意点やセキュリティ対策についても説明する必要があります。

1. 指定対象

  • 管理責任者:情報システム全体を統括する権限を持つ者

  • 管理担当者:具体的な運用・保守業務を行う技術員等


2. 主要な任務

管理責任者の役割

  1. 情報セキュリティの確保

    • ウイルス対策ソフトウェアの適用

    • データ暗号化や物理的アクセス制御の実施

    • 外部記憶媒体使用の許可権限保有

  2. 運用基準の策定

    • スタンドアロンコンピュータの利用範囲・目的を明確にし、府省の情報セキュリティポリシーとの整合性を維持する。

  3. 障害対応の統括

    • サービス停止時やデータ損失時の復旧計画策定

  4. 外部連携の管理

    • 他の情報システムとのデータ連携・保守契約の調整

管理担当者の役割

  1. 日常業務の実施

    • ハードウェアの保守作業(物理的点検、パーツ交換等)

    • ソフトウェア更新やライセンス管理

  2. データ運用支援

    • 指定された範囲内のデータ操作・バックアップ作業


3. 特別な措置

  • 共用環境での利用:複数の担当者がコンピュータを共有する場合は、権限管理(閲覧制限、ログ記録)が必須です。

  • 外部持ち出し許可:緊急時以外は物理的な移動を禁止し、データ転送にはウイルスチェックを義務化。


4. 違反対応

  • 権限外の操作や不適切なデータ持ち出しが発生した場合は、府省の情報セキュリティ規程に基づき処分対象とします。

この指定方法は、「デジタル社会推進会議幹事会決定」に基づき、2021年9月1日から適用されています。

------- ここまで ----

以前の記事で作成した小説生成器のアプリケーションから呼び出すには、置換文字列の設定を以下に変更します。

G_ENDPOINT_URL = http://localhost:8080/v1/chat/completions
G_MODEL_NAME = deepseek-r1-distill-qwen2.5-bakeneko-32b@q8_0




Ollama



Ollamaでは以下のコマンドを実行します。量子化はq8_0を指定しています。

ollama run hf.co/rinna/deepseek-r1-distill-qwen2.5-bakeneko-32b-gguf:q8_0

% ollama run hf.co/rinna/deepseek-r1-distill-qwen2.5-bakeneko-32b-gguf:q8_0

>>> Send a message (/? for help)


以前の記事で作成した小説生成器のアプリケーションから呼び出すには、置換文字列の設定を以下に変更します。

G_ENDPOINT_URL = http://localhost:11434/v1/chat/completions
G_MODEL_NAME = hf.co/rinna/deepseek-r1-distill-qwen2.5-bakeneko-32b-gguf:q8_0



今回の記事は以上です。

謝辞: 参考にしたサイト


Use Models from the Hugging Face Hub in LM Studio
https://huggingface.co/blog/yagilb/lms-hf

rinna さんの Qwen2.5 Bakeneko 32B を試す
https://note.com/schroneko/n/n261cb1028c4b

2025年2月6日木曜日

Macbook Pro M4Max 128GB上のLM StudioでDeepSeek-R1 UD IQ1_Sを動かす

本ブログのテーマであるOracle APEXにまったく関係ありませんが、Macbook Pro M4Max 128GBという高価なノートを持っているので、LM StudioでDeepSeek-R1 UD IQ1_Sを動かしてみました。

先人による以下のブログ記事を参考にさせていただいています。

金のニワトリさん:1.58-bit版DeepSeek R1の日本語性能評価
https://zenn.dev/robustonian/articles/deepseek_r1-ud-iq1_s

npakaさん:DeepSeek R1 Dynamic 1.58-bit の概要
https://note.com/npaka/n/na1126a4e5fda

unsloth: Run DeepSeek R1Dynamic 1.58-bit

LM Studio 0.3.9 Build 5を使っています。

最初にDeekSeek-R1-GGUFのIQ1_Sをダウンロードします。親切にLikely too large for this macと教えてくれますが、構わずダウンロードします。


ダウンロードしたらモデルをロードします。UnsolthのブログのRunning on Mac / Apple devicesのセクションに"For a 128GB unified memory machine, you should be able to offload 59 layers or so."と書いてあるのですが、GPU Offloadの数値を上げるとMacがハングしたり、プロンプトを入力するだけでエラーになったりしたので、0にしています。Context Lengthも大きくするとメモリが足りなくなるので、4096にしました。

K Cache Quantization Typeにチェックを入れQ4_0を選択しています。

以上の設定でDeepSeek-R1 UD IQ1_Sをロードしています。


V Cache Quantization Typeを設定すると以下のようなエラーが発生するため、使用できませんでした。もちろんflash_attnはオンにした上でV Cache Quantization TypeにQ4_Oを設定しています。


LM Studioにモデルをロードすると、すごい勢いでメモリ使用量が増加します。メモリプレッシャーの表示が黄色になると、もともと遅いのにさらに遅くなります。LM Studio以外の実行中のアプリをすべて停止する必要がありました。画面収録のために、QuickTime Playerは動かしています。


以下のプロンプトを入力しました。

関数 f(x) = x^2 - 4x + 3 の最小値を求めよ。

以下は1秒で1枚のGIF動画です。


以下、回答です。

<think>
まず、問題を見てみましょう。関数f(x)=x²-4x+3の最小値を見つける必要があります。

最初に、これは二次関数で、グラフが上向きか下向きかを判断するために係数を確認しますね。x²の項の係数は1ですから正の数なので、放物線は上に向かって開きます。つまり頂点は最低点になり、最小値があることになります。

次に、その頂点を見つけることが必要ですね。一般的な二次関数y=ax²+bx+cにおいて、頂点の座標は(-b/(2a), f(-b/(2a)))で求められることを覚えていますか?この式ではaが1、bが-4なので、まず頂点のx座標を計算します。

つまり、x = -(-4)/(2*1) = 4/2=2。これが頂点のx座標です。次にf(2)の値を求めます:f(2)= (2)^2 -4*(2)+3 → 4-8+3 ⇒ (-4)+(+3)=-1。なので、この関数はx=2で最小値-1を取ると分かりますね。

でも確認として導関数を使っても良いかもしれません。微分するとf'(x)=2x−4。これを0に置くと解く:2x−4=0 ⇒ x=2。これも同じ結果ですね。そして、この時点の前後で符号が変わるかを確かめると、例えばx<2では導関数は負(例: f'(1)= -2)、x>2では正(f'(3)=+2)なので、x=2で極小値を持つことが分かる。

これに加えて頂点の座標を求めることで最小値を確定できる。二次関数が上向きであるため、その最低値が存在し、それが-1です。
</think>

ステップバイステップで解説します:

**Step 1: 確認する関数**
与えられた関数はf(x) = x² -4x +3。

**Step 2: 二次関数の形状を判断**
係数a=1(正)なので放物線は上に開き、最小値が存在。頂点が最低点となる。

**Step 3: 頂点の座標を見つける方法**
一般式y = ax² + bx + cの場合、
頂点x座標:
\[ x_v = -\frac{b}{2a} \]
この場合:
\[ x_v = -\frac{-4}{2×1} = \frac{4}{2}= 2\]

**Step 4: 最小値の計算**
f(x_v)= f(2)
\[ f(2) = (2)^2 -4×(2)+3=4-8+3=-1\]
よって、頂点は(2, -1)

**結論:この関数の最小値はx=2における-1**

補足として導関数による確認も可能:
f'(x)=2x−4。
f'(x)=0 ⇒ x=2。
符号変化からここで極小値を取るため、やはり同じ結果。

したがって**最小値=-1(x=2)にて達成される**

OpenAIのo1は数秒で回答しました。


Macbook Proは高価なノートではありますが、時間がかかっても手元のコンピュータで正解を返すモデルを実行できたのは結構な驚きです。また、DeepSeek-R1では、OpenAIのモデルでは表示されない推論の過程が表示されます。その内容にも驚かされます。

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

2025年1月31日金曜日

日本語を追加学習したサイバーエージェントのDeepSeek-R1のモデルで小説を生成して共有するアプリを作る

ローカルで動作するLLMにプロンプトを与えて小説を生成し、生成された小説をデータベースに保存して共有するAPEXアプリケーションを作成します。

アプリケーションを実行する環境はMシリーズのMacbookで、ローカルLLMの実行にLM Studioを使用します。モデルとしてサイバーエージェントが日本語の追加学習を行なったcyberagent-deepseek-r1-distill-qwen-32b-japaneseの使用を想定しています。エンドポイントとモデルは設定で変更できるので、Ollamaや14bのモデルでもアプリケーションは利用可能です。ローカル環境でのAPEXの環境構築については、こちらの記事を参照してください。

追記:LM StudioまたはOllamaでCORSの対応さえできていれば、APEXのアプリケーションはどこでも作成できます。

作成したAPEXアプリケーションは以下のように動作します。ローカルで実行するLLMでも、ここまでできるようになったのか、と感じます。DeepSeek-R1の他のモデルでも確認してみましたが、英語や中国語やその他の言語が混ざることが多々あり、日本語の生成は今ひとつでした。モデルを公開していただいたサイバーエージェント様、ありがとうございます。


(GIF動画は1秒2枚で作成しています。実際の出力はもっと連続しています。)

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

推論モデルは回答に時間がかかります。そのため、OpenAI互換APIの呼び出しはブラウザのJavaScriptで実行します。受け取ったストリーミングのレスポンスをページに表示します。そのページ上に表示されている文章を、データベースに書き込みます。

最初に生成した小説を保存する表EBAJ_STORIESを作成します。以下のDDLを実行します。
create table ebaj_stories (
    id        number generated by default on null as identity
              constraint ebaj_stories_id_pk primary key,
    title     varchar2(80 char) not null,
    prompt    clob,
    story     clob
);
以下はSQLワークショップSQLコマンドでの実行例です。


空のAPEXアプリケーションを作成します。名前小説生成器とします。


アプリケーションが作成されます。アプリケーション定義の編集を開きます。


アプリケーション定義置換にAPIのエンドポイントURLモデル名を設定します。コンテナとして動作しているデータベースからではなくブラウザからアクセスするため、接続先のホストはlocalhostになります。

LM Studioが宛先の場合は、以下のように設定します。

G_ENDPOINT_URL: http://localhost:8080/v1/chat/completions
G_MODEL_NAME: cyberagent-deepseek-r1-distill-qwen-32b-japanese

LM Studioのローカル・サーバーのSettingsServer Port8080でない場合は、そのポート番号をURLに含めます。また、Enable CORSオンにします。


今回の用途に合わせて、Context Lengthを最大の131072に設定してモデルをロードしています。


Ollamaの場合は、以下のような設定になります。Ollamaではページが生成されたURLとAPI呼び出し先のポート番号の違いは見ていないのか、CORSに関する設定は不要でした。

G_ENDPOINT_URL: http://localhost:11434/v1/chat/completions
G_MODEL_NAME: yuma/DeepSeek-R1-Distill-Qwen-Japanese:32b

以下はLM Studioのローカル・サーバーを呼び出す設定です。


ページ・デザイナでホーム・ページを開き、小説を生成するユーザー・インターフェースを作成します。

置換文字列G_ENDPOINT_URLとして設定した値を、このページで実行するJavaScriptのコードから参照できるように、ページ・アイテムP1_ENDPOINT_URLを作成します。JavaScriptから参照するだけなので、タイプ非表示設定保護された値オンにします。

ソースタイプアイテムを選択し、アイテムとしてG_ENDPOINT_URLを指定します。HTMLページ上に値があればよく、データベースに保存する必要はないので、セッション・ステートストレージリクエストごと(メモリーのみ)を選択します。


同様にページ・アイテムP1_MODEL_NAMEを作成し、ソースアイテムG_MODEL_NAMEを指定します。


静的コンテンツのリージョンとしてPromptを作成します。

このリージョンにプロンプトを入力するテキスト領域、APIリクエストの送信ボタン、生成された小説をデータベースに保存するボタン、保存する際に付けるタイトルを設定するテキスト・フィールドを作成します。

表示上の修飾は不要なので、外観テンプレートBlack with Attributesを選択します。このリージョンの下に小説を表示するリージョンを作成しますが、リージョン間の隙間を空けるために、テンプレート・オプション詳細Bottom MarginMediumを指定します。

ボタンをクリックしたときに実行される処理は、APEXアクションとして実装します。APEXアクションを保持するコンテキストをこのリージョンに作成するため、詳細静的IDとしてPROMPTを設定します。


LLMへの指示を入力するページ・アイテムを作成します。識別名前P1_PROMPT、複数行に渡る長い文章の入力を想定して、タイプテキスト領域とします。外観高さ10行としました。

このページの処理はすべてJavaScriptで実装します。ページ・アイテムの値をセッション・ステートとして(データベースに)保存する必要は無いため、セッション・ステートストレージリクエストごと(メモリーのみ)を選択します。


小説の生成を依頼するボタンGENERATEを作成します。ラベル生成とします。

外観ホットオンテンプレート・オプションWidthStretchを設定し、画面の横幅いっぱいに強調色でボタンを表示します。

ボタンの処理はGENERATEという名前のAPEXアクションとして実装します。ボタンを押した時にこの処理が呼び出されるように、動作アクション動的アクションで定義を選択し、詳細カスタム属性としてdata-action="GENERATE"を指定します。


生成された小説を保存する際に付けるタイトルを入力するページ・アイテムを作成します。

識別名前P1_TITLEタイプテキスト・フィールドラベルTitleとします。セッション・ステートストレージリクエストごと(メモリーのみ)です。


小説をデータベースに保存するボタンSAVEを作成します。ラベル保存とします。

ボタンGENERATEと同様に、ボタンをクリックしたときにAPEXアクションSAVEを呼び出します。詳細カスタム属性data-action="SAVE"を指定します。


生成された小説を保存するデータベース操作は、Ajaxコールバックとして実装します。左ペインでプロセス・ビューを開き、Ajaxコールバックとしてプロセスを作成します。

識別名前SAVE_STORYタイプコードを実行です。ソースPL/SQLコードとして以下を記述します。
begin
    insert into ebaj_stories(title, prompt, story) values(:P1_TITLE, :P1_PROMPT, apex_application.g_x01);
    htp.p('{ "success": true }');
end;

LLMが生成した小説を記述するリージョンを作成します。

識別名前小説タイプ静的コンテンツです。LLMからストリームとして受け取った文字列を、JavaScriptより逐次、リージョンのinnerTextに追記します。

外観テンプレート・オプションHeightとして320pxを選択します。リージョンの高さを固定し、出力が溢れた場合は自動的にスクロールするようにします。

JavaScriptより出力先となるリージョンを特定するため、詳細静的IDとしてNOVEL_CONTAINERを設定します。


生成AIの呼び出しや小説の保存を行うコードを記述したJavaScriptのファイルを、静的アプリケーション・ファイルとして保存します。

共有コンポーネント静的アプリケーション・ファイルを開きます。


以下のファイルをjs/app.jsとして保存します。


ファイルの作成をクリックします。


ディレクトリjsファイル名app.jsを指定し、作成をクリックします。


ファイルの内容をコピペし、変更の保存をクリックします。


メッセージにファイルが保存され、縮小されましたと表示されます。

これで作成したファイルが、静的アプリケーション・ファイルとして参照できるようになりました。参照リンクをコピーした後、取消をクリックしてスクリプト・エディタを終了します。


ホーム・ページのページ・プロパティJavaScriptファイルURLに、以下を記述します。

[module,defer]#APP_FILES#js/app#MIN#.js


以上で小説の生成と保存を行うページは完成しました。

ページを実行して、小説の生成と保存を行います。

Promptに適当な指示を入力し、ボタン生成をクリックします。アプリケーションの名前は小説生成器としましたが、Promptに入力した文字をそのまま生成AIに渡しているだけです。そのため、実際に生成される文章は小説に限られるわけではありません。


生成された小説をデータベースに保存します。

適当なタイトルを入力し、ボタン保存をクリックします。

saved!とメッセージが表示されれば、生成された小説の保存は成功です。


保存された小説を表示するページを作成します。

このページは概ねAPEXの標準の機能を使って作成します。

ページの作成をクリックし、空白のページを作成します。


空白ページを選択します。


作成するページの名前小説を読むとします。ページの作成をクリックします。


空白ページが作成されたら、読みたい小説を選択するページ・アイテムと、いくつかのボタンを配置するためのリージョンを作成します。

リージョンの名前Item Containerとし、タイプ静的コンテンツとします。アイテムとボタンを整列させるため、外観テンプレートItem Containerを指定します。


読みたい小説を選択するページ・アイテムを作成します。

識別名前P2_STORYタイプポップアップLOVにします。ラベル小説としました。

設定表示形式インライン・ポップアップを選択します。レイアウトリージョンItem Containerスロットはページ・アイテムの規定の位置であるItemに配置します。

LOVタイプSQL問合せを選択し、SQL問合せとして以下を記述します。

select title d, id r from ebaj_stories

追加値の表示オフNULL値の表示オンとし、NULL表示値- 読みたい小説を選んで -を設定します。


このページ・アイテムはサーバー側の処理で参照するため、セッション・ステートストレージとしてセッションごと(永続)を選択します。


小説を表示するボタンREADを作成します。ラベル読むとします。動作アクションページの送信を設定します。

レイアウトのリージョンはItem ContainerスロットButton Endを選択し、アイテムP2_STORYの右側に配置します。

小説の取り出しと表示は、実際には動的コンテンツのリージョンで行います。このボタンはページを送信することにより、ページ・アイテムP2_STORYの値をセッション・ステートに保存します。


小説を削除するボタンDELETEを作成します。ラベル削除とします。動作アクションページの送信を設定します。

動作確認の要求オンにし、確認メッセージとして本当に削除しますか?スタイル危険を指定します。


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

識別名前DELETEタイプコードの実行です。

ソースPL/SQLコードとして以下の1行を記述します。

delete from ebaj_stories where id = :P2_STORY;

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


P2_STORYに指定された小説が削除されたので、P2_STORYとして保存されているセッション・ステートの値もクリアします。

プロセスを作成します。識別名前P2_STORYのクリアタイプとしてセッション・ステートのクリアを選択します。設定タイプアイテムのクリアを選択し、アイテムとしてP2_STORYを選びます。

サーバー側の条件ボタン押下時DELETEを選択します。


最後に小説を表示するリージョンを作成します。

識別名前小説タイプとして動的コンテンツを選択します。

ソースCLOBを返すPL/SQLファンクション本体に以下を記述します。
declare
    l_story clob;
begin
    if :P2_STORY is not null then
        select story into l_story from ebaj_stories where id = :P2_STORY;
        return apex_markdown.to_html(l_story);
    end if;
    return '';
end;

以上でアプリケーションは完成です。

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

Autonomous DatabaseのAPEX 24.1で作成したエクスポートはこちらに置きました。
https://github.com/ujnak/apexapps/blob/master/exports/novel-generator-241.zip

アプリケーションをAutonomous Databaseで実行した場合は、OllamaでもCORSのエラーが発生します。そのため、ollama serveを実行する前に環境変数OLLAMA_ORIGINSを設定して、CORSの制限を緩和します。

export OLLAMA_ORIGINS='*'

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

2025年1月28日火曜日

APEX 24.2のデータ・モデルの生成をLM StudioとDeepSeek-R1 Distill Llama 70Bで実行する

Oracle APEX 24.2に、AIに問い合わせてデータ・モデルを作成する機能が追加されました。この機能の動作確認を、最近話題のDeepSeek-R1を使って実施してみます。オンラインでAPIを呼び出す代わりに、LM Studioのローカル・サーバーにモデルをロードし、OpenAI互換のChat Completions APIで呼び出します。

Apple Macbook ProのM4 Max/128GBで動作する最大のモデルdeepseek-r1-distill-llama-70bを使います。性能は変わるとは思いますが、10GBメモリ以下で動かせるDeepSeek-R1の蒸留モデルもあるようです。

作業結果について、OpenAI GPT-4oとほとんど同じ回答が得られる印象です。ただし、すごい時間がかかる - これはモデルの問題というより、ローカルの環境が遅いから - また、処理時間での制限のため、エラーが発生して回答が得られないことがあります。Chat Completions APIはAPEX_WEB_SERVICE.MAKE_REST_REQUESTで呼び出していますが、その引数p_transfer_timeoutのデフォルト値が180秒のため、それ以上処理に時間がかかるとタイムアウトが発生するようです。

OpenAIのo1でも同様ですが、推論モデルは回答を生成するまでに比較的長い時間がかかります。また、Oracle APEXではデータベース・サーバーからリクエストを送信するため、ストリーミングを扱うことができません。結果として、問い合わせが難しいとAPIリクエストのタイムアウトが発生する可能性が高くなります。

以下より実際に行った作業を紹介します。

使用したLM Studioのバージョンは0.3.8です。

Discoverのアイコンをクリックして、Model Searchを開き、DeepSeek-R1を検索します。LM Studioを動かしているホストで実行可能なモデルをダウンロードします。

本記事ではmlx-community/DeepSeek-R1-Distill-Llama-70B-8bitを使用することにしました。MシリーズのMacでLM Studioを実行しているため、MLXフォーマットのモデルを優先しています。


Developerアイコンをクリックします。Settingsを開き、Server Portを設定します。本記事では8080にしています。他のモデルが自動的にロードされてメモリが不足しないように、Just-in-Time Model LoadingOffにします。

Statusを切り替え、Runningにします。


Select a model to loadをクリックし、ダウンロード済みのモデルDeepSeek R1 Distill Llama 70Bをロードします。


Context Lengthに最大値の131072を設定し、Load Modelを実行します。


モデルのステータスがREADYになれば、APIから呼び出せます。


Oracle APEXの設定に移ります。Oracle APEX 24.2は同じMacのpodmanで動作しています。Oracle APEX環境の構築手順については、こちらの記事で紹介しています。

Oracle APEXのワークスペースにサインインし、ワークスペース・ユーティリティ生成AIを開きます。


作成済みの生成AIサービスが一覧されます。作成をクリックします。


識別AIプロバイダとしてOpenAIを選択します。名前DeepSeek-R1 Distilled Llama 70Bとします。

ユーティリティAIを使用したデータ・モデルの作成から呼び出せるように、アプリケーション・ビルダーで使用オンにします。ベースURLは、コンテナで実行されているOracle Databaseから外の環境にアクセスするため、以下を指定します。

http://host.containers.internal:8080/v1

認証は不要ですが資格証明が必須項目であるため、設定だけは行います。資格証明- 新規作成 -を選択し、APIキーとして適当な文字列を設定します。

AIモデルdeepseek-r1-distill-llama-70bを設定します。


ここに設定するモデル名はLM StudioのAPI Usageのセクションに、This model's API identifierとして表示されています。


追加属性をクリックして開き、JSON属性として以下を記述します。

{ "max_tokens": 131072 }

以上でダイアログを閉じます。


接続のテストをクリックして、接続に成功することを確認します。

作成をクリックします。


以上で、生成AIサービスとしてDeepSeek-R1(の蒸留モデル)を呼び出せるようになりました。


SQLワークショップユーティリティAIを使用したデータ・モデルの作成を開きます。


APEXアシスタントのダイアログが開きます。SQL形式としてOracle SQLを選びます。


以下のメッセージを入力します。

国名、ISO3166-1 Alpha-2コード、首都名、首都の位置の経度と緯度を保存する表をEBAJ_COUNTRIESとして作成してください。日本語で会話してください。

Oracle APEXのプロンプトは英語なので(結果として英語と日本語が混在したメッセージがLLMに送信される)、回答が英語になることもあります。その点を考慮して、日本語で回答するように指示しています。


2分強の処理時間を費やした後、以下の回答が得られました。


生成されたSQLスクリプトは以下です。実行すると定義通りに表が作成されます。
-- create tables
create table ebaj_countries (
    id                            number         generated by default on null as identity
                                                constraint ebaj_countries_pk primary key,
    country_name                  varchar2(255)  not null,
    iso_code                      varchar2(2)    not null
                                                constraint ebaj_countries_iso_code_uk unique,
    capital_name                  varchar2(255),
    longitude                     number(9,6),
    latitude                      number(9,6),
    row_version                   integer        not null,
    created_on                    date           not null,
    created_by                    varchar2(255)  not null,
    updated_on                    date           not null,
    updated_by                    varchar2(255)  not null )
/
-- triggers
create or replace trigger ebaj_countries_biu
    before insert or update
    on ebaj_countries
    for each row
begin
    :new.updated_on := sysdate;
    :new.updated_by := coalesce(sys_context('APEX$SESSION','APP_USER'),user);
    if inserting then
        :new.row_version := 1;
        :new.created_on := :new.updated_on;
        :new.created_by := :new.updated_by;
    elsif updating then
        :new.row_version := nvl(:old.row_version, 0) + 1;
    end if;
end ebaj_countries_biu;
/
今回の用途では、列ISO_CODEを主キーとして、監査列も行バージョンの列も不要です。これらの列は、Oracle APEXが生成するプロンプトで作成するよう指示されているため、SQLスクリプトに含まれています。

これらを削除します。

列ISO_CODEを主キーとして、列IDは削除してください。

3分弱考えて、以下の回答が返されました。


生成されたDDLは以下です。指示通りに列IDが削除され、列ISO_CODEが主キーになっています。
-- create tables
create table ebaj_countries (
    iso_code                      varchar2(2)    not null
                                                constraint ebaj_countries_pk primary key,
    country_name                  varchar2(255)  not null,
    capital_name                  varchar2(255),
    longitude                     number(9,6),
    latitude                      number(9,6),
    row_version                   integer        not null,
    created_on                    date           not null,
    created_by                    varchar2(255)  not null,
    updated_on                    date           not null,
    updated_by                    varchar2(255)  not null )
/
-- triggers
create or replace trigger ebaj_countries_biu
    before insert or update
    on ebaj_countries
    for each row
begin
    :new.updated_on := sysdate;
    :new.updated_by := coalesce(sys_context('APEX$SESSION','APP_USER'),user);
    if inserting then
        :new.row_version := 1;
        :new.created_on := :new.updated_on;
        :new.created_by := :new.updated_by;
    elsif updating then
        :new.row_version := nvl(:old.row_version, 0) + 1;
    end if;
end ebaj_countries_biu;
/
監査列と列ROW_VERSIONを削除します。

監査列と列row_versionを削除してください。

3分弱考えて、以下の回答が返されました。


生成されたDDLは以下です。監査列と列ROW_VERSIONが削除されています。監査列を正しく認識しているのもそうですが、トリガーも削除されています。
-- create tables
create table ebaj_countries (
    iso_code                      varchar2(2)    not null
                                                constraint ebaj_countries_pk primary key,
    country_name                  varchar2(255)  not null,
    capital_name                  varchar2(255),
    longitude                     number(9,6),
    latitude                      number(9,6) )
/
これで完成なので、SQLスクリプトの生成を実行します。

このまま継続してサンプル・データの生成まで行おうとすると、表を作成するDDLが保存されれずに、サンプル・データのINSERT文に置き換わることがありました。少なくとも、LM StudioとDeepSeek-R1を使う場合は、DDLの作成で終了した方が良さそうです。


スクリプト・エディタが開きます。

スクリプト名を入力し、実行します。


即時実行します。


表が作成されます。

ここからアプリケーションの作成を行うことで、AIによるデータ・モデルの作成からAPEXアプリケーションの作成まで、一気に実行できます。


今回の確認作業については、以上になります。

Oracle APEXからLM StudioにDDLを生成するように指示をしたときに、LM Studio(DeepSeek-R1)から、推論の経過が<think>...</think>で囲んで返されています。

最初の指示に対して、以下の推論をしています。

よし、Oracleデータベースに「EBAJ_COUNTRIES」という名前のテーブルを作成する必要があります。このテーブルは、国名、ISO3166-1 Alpha-2コード、首都名、およびその経度と緯度を格納します。要件を整理します。

まず、テーブルには次のカラムが必要です:国名、ISOコード、首都名、経度、緯度。ガイドラインに従い、テーブル名には「ebaj」というプレフィックスを付けます。

主キーは数値型のIDで、デフォルトで生成されるものとします。このカラム名を「id」とし、オートインクリメントにはアイデンティティを使用します。

国名と首都名は文字列なので、varchar2(255)型にします。一方、ISOコードは短い文字列なので、varchar2(2)型が適切です。

経度と緯度は数値型とし、小数点が必要なので精度を9、スケールを6に設定します。

監査用カラムとして、created_on、created_by、updated_on、updated_byを追加します。これらは、データがいつ、誰によって挿入または更新されたかを記録します。

また、行のバージョン管理のためにrow_versionカラムを追加します。これは初期値を1とし、行が更新されるたびにインクリメントされるようにします。

トリガーも作成し、挿入や更新の前にcreatedおよびupdatedのタイムスタンプやユーザーを設定するようにします。これにより、両方のケースに対応できます。

インデックスについては、外部キーのためのものが必要ですが、今回外部キーはないため、もしISOコードが頻繁に検索される場合にはiso_codeにインデックスを追加するのが良いでしょう。

次の指示では、以下の推論をしています。

よし、ユーザーからのSQLテーブル変更のリクエストに対応する必要があります。以前、ユーザーはebaj_countriesというテーブルを作成し、IDを主キーとした特定のカラムを含むよう求めていました。しかし、今回はIDカラムを削除し、代わりにISO_CODEを主キーにしたいというリクエストです。

まず、この変更の理由を理解する必要があります。ISO_CODEを主キーとして使用するのは理にかなっています。なぜなら、これは各国を一意に識別する固定値であり、クエリや結合を簡素化できるからです。また、IDカラムを削除するのは、冗長性を減らし、テーブル構造をシンプルに保つためかもしれません。

元のSQLコードを見ると、IDカラムは主キーとして定義されており、アイデンティティシーケンスを使用しています。このカラムを削除するには、IDカラムとその主キー制約を削除し、代わりにISO_CODEを主キーとして設定する必要があります。

次に、ISO_CODEカラムは現在一意制約(UNIQUE)が付いていますが、これを主キーに変更します。このためには、一意制約を主キー制約に変更し、正しくインデックスが設定されていることを確認します。

依存関係も考慮する必要があります。IDが削除されるため、このテーブル内ではIDを参照する外部キーがないので、その点は簡単です。ただし、他のテーブルがebaj_countries.idを参照している場合、その参照をISO_CODEに変更する必要があります。ただし、ユーザーが他のテーブルについて言及していないため、今回はこのテーブルに集中します。

テーブル作成スクリプトを調整し、IDカラムを削除してISO_CODEを主キーに設定します。その後、トリガーを確認します。このトリガーはIDカラムをrow_versionで参照していますが、待てよ……トリガーはテーブル構造に基づく:new:oldを使用しているので、IDが削除されても問題ありません。row_versionはそのまま残っているので、トリガーは正常に動作するはずです。

次に、SQLコードを以下のように再構築します。IDカラムとその制約を削除し、ISO_CODEの制約を主キーに変更し、その他のカラムはそのままにします。トリガーについては、挿入や更新の前に動作するようになっており、IDカラムに直接依存していないので変更は不要です。

この変更の影響も考慮する必要があります。ISO_CODEが主キーになるため、必ずNOT NULLかつ一意(UNIQUE)である必要があります。これは元々の設定と一致しているので問題ありません。一意制約は主キー制約が代わりに適用されるため削除できます。

最後に、更新されたSQLコードを提示し、変更内容を説明します。IDを削除し、新しい主キーを設定したことを要約し、ユーザーのニーズに合致しているか、さらに調整が必要か確認するためのフォローアップ質問をします。

これで十分だと思います。更新されたコードは、ユーザーの要件を正確に反映するはずです。


最後の指示では、以下の推論をしています。


ユーザーがEBAJ_COUNTRIESというテーブルを作成してほしいと依頼しています。このテーブルには、国名、ISO 3166-1 Alpha-2コード、首都名、そしてその首都の経度と緯度を格納する必要があります。また、会話は日本語で進めてほしいとのことです。

まず、ユーザーの要件をデータモデルに変換する必要があります。Oracleでは、キーワードやカラム名は小文字を使用し、予約語を避けるのがベストプラクティスです。また、各テーブルには主キーを設定する必要があり、できればシーケンスやトリガーを使用したIDカラムを自動生成するのが望ましいです。

まず、IDを主キーとしたテーブルを作成します。IDはOracleのアイデンティティ機能を使用して生成します。この機能は、従来のシーケンスを使用する方法よりも簡単です。その後、国名をvarchar2(255)、ISOコードを2文字なのでvarchar2(2)、首都名もvarchar2(255)として追加します。

次に、経度と緯度については、精度とスケールを設定した数値型を使用します。座標は最大6桁の小数点を含む可能性があるため、number(9,6)として定義します。

さらに、監査用のカラムを追加します:created_oncreated_byupdated_onupdated_byです。これらはデータが挿入または更新された日時と操作したユーザーを記録します。また、row_versionカラムを追加して楽観的同時実行制御をサポートします。これにより、複数のユーザーが変更を上書きしないようにできます。

ガイドラインに従い、監査用カラムやrow_versionを自動的に更新するトリガーを作成します。このトリガーは挿入または更新の前に実行され、適切なタイムスタンプとユーザー情報を設定します。

テーブル作成後、インデックスも作成する必要があります。主キーはすでにインデックス化されているため、再度インデックスを作成する必要はありません。ただし、外部キーや頻繁に検索されるカラムがある場合、それらに対するインデックスを作成します。

次に、ユーザーの2回目のメッセージを見ると、ISO_CODEを主キーにしてIDカラムを削除したいとのことです。ISOコードは一意性があり、自然キーとして利用できるため、この変更は合理的です。そのため、テーブルを調整し、IDカラムを削除してISO_CODEを主キーとして設定します。

さらに、3回目のやり取りでは、監査用カラムとrow_versionを削除したいとのリクエストがあります。この場合、これらのカラムを削除し、トリガーもそれらを処理しないように更新する必要があります。

このプロセス全体を通じて、Oracleのベストプラクティスを守る必要があります。適切なデータ型や命名規則、制約を使用し、テーブル作成後にトリガーを設定するなど、正しい順序で作業を進めます。

最後に、各変更後にはSQLを検証し、エラーがないか確認します。また、パフォーマンスやデータの整合性を最適化することにも注意を払います。


追記1


LM Studioのチャットに指示を入力すると、長考してもタイムアウトは発生しません。ただし、Oracle APEXが挿入しているプロンプトが無いため、生成されるDDLの形式が今ひとつです。

表名や列名のルール、型の選択ルールなどを指示に含める必要があるでしょう。


Oracle APEXから指示しているときは、プロンプトは英語と日本語の混合だったためか、推論の過程は英語で出力されていました。LM Studioのチャットより日本語だけの指示を与えると、推論の過程が中国語で出力されました。



追記2


開発ツールのデバッグは以下の手順で実施します。

管理ツールのアクティビティのモニターを開きます。


アクティブ・セッションを開きます。


デバッグ対象のセッションを見つけて開きます。


デバッグ・レベル情報に変更します。変更の適用をクリックします。デバッグ・レベルトレースはアプリケーションがとても遅くなるため、本当に必要な場合に限り設定します。


エラーが発生した処理を再実行します。


先ほどデバッグ・レベルを情報に設定した画面を開きます。

エラーが補足できていれば、デバッグ・レベル無効に戻します。変更の適用をクリックします。

該当するエラーのデバッグIDを開きます。


デバッグ・メッセージからエラーの原因を探します。


デバッグ・メッセージより、上記のエラーの原因はORA-29276: 転送がタイムアウトしましたであることがわかります。


この手順は開発ツールのデバッグに限らず、一般ユーザーのセッションを対象にして実施することもできます。

追記終了