Oracle APEXとOCI Generative AI Serviceで生成AIアプリをローコード開発

生成AIを活用するにはアプリが必要ですが、アプリはなるべく手間を掛けずに実装できた方がありがたいです。
という訳で、OCIのマネージド生成AIサービス「OCI Generative AI Service」とローコード開発プラットフォームの「Oracle APEX」を使って、簡単な生成AIアプリを作ってみました。
結果、シンプルなものであれば、ローコードでお手軽に作れることを確認できました。

## 環境

今回はAPEXインスタンスをADBで作成しました。
リージョンは Generative AI Service がGAとなっている「US Midwest (Chicago)」を選択しました。
ADBの作成については割愛します。

## APEXでWeb資格証明を作成

まずはAPEXからOCIの各サービスを利用するために「Web資格証明」を作成します。
APEXワークスペースのトップ画面にて「アプリケーション・ビルダー」を選択します。
![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/c42e4fc6-7b44-879d-2068-38c4feb586e7.png)

｢ワークスペース・ユーティリティ」を選択します。
![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/77d2870d-b097-4eb4-c0b1-cf3fd4ade13c.png)

｢Web資格証明」から「作成」を選択します。
![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/3c1efd41-6fb8-ae66-a59c-4b303f92fd76.png)
![04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/d868e186-2cf0-46b7-4f32-cb919589461f.png)

赤枠の部分に記入していきます。
｢静的ID」の入力値は後ほどPL/SQLコード実装で使います。
OCI秘密キー、OCI公開キー・フィンガープリントに関しては、OCIコンソールから作成して入力します。
![05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/6afaa087-a51f-b028-056d-ff2e1a5302fe.png)

秘密キーの作成にあたり、OCIコンソールから「ユーザーの詳細」へアクセスし「APIキーの追加」を選択します。
![06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/6d732dae-9b11-f000-97e9-3d552d192401.png)

｢APIキー・ペアの生成」を選択し、秘密キーと公開キーをダウンロードします。
![07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/95b04c16-3ccd-256f-63c7-0758ba05204d.png)

OCI秘密キー、OCI公開キー・フィンガープリントの内容をWeb資格証明の作成画面で入力します。

## APEXアプリの作成(コンポーネントの配置)

APEXワークスペースのトップ画面に戻り「アプリケーション・ビルダー」へアクセスし「作成」を選択します。
![08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/936e376d-39ed-c977-4009-3ded88ae53ae.png)
![09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/c298602d-2ca1-c040-bfb5-33437dc5fbab.png)

アプリケーション名を入力し「アプリケーションの作成」を選択します。
![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/2f8221b4-f2cf-a96d-3e6b-3d8a8354afab.png)

アプリケーションのトップ画面にて「ページの作成」を選択します。
![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/1949ccdc-349d-3ebf-3723-9feff7620985.png)

｢空白ページ」を選択し「次」を選択します。
![12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/1f904028-7d91-d44e-2f88-6b536a56021a.png)

名前を記入し「ページの作成」を選択します。
![13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/ea3698bd-4af8-57e2-4e24-a19257c74306.png)

下段のアイテムからBODYに「テキスト・フィールド」をドラッグ＆ドロップします。
ここに質問を入力します。
アイテムの名前を任意に設定します。
ここで設定した名前は後ほどPL/SQLのバインド変数で使います。
![14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/01f06ca9-ab67-5987-7ebe-f44db5e7361d.png)

同様にボタンの「Text」をドラッグ＆ドロップします。
このボタンを押すとGenerative AI Serviceに質問を連携します。
アイテムの名前を任意に設定します。
ここで設定した名前は後ほどプロセスの設定で使います。
![15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/7d01f95e-3e55-ea3d-32e6-3bb15261f05e.png)

ちなみにボタンの外観は以下のように設定しました。
![15-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/676c6570-fcd9-c2c1-7ed4-7de62a2fe582.png)
![15-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/a6853950-84f9-4adc-3664-e9373f55ac72.png)

## APEXアプリの作成(OCIサービスとの連携)

以降の手順はノーコード、ローコード双方の実装方法を記載します。

### APEX標準機能を使ったノーコードの実装

まずはノーコードで実装する手順になります。
アプリケーションのトップ画面から「共有コンポーネント」を選択します。
![24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/2f40bae0-ff8b-953c-26e2-43a0891bc2fb.png)

左下の「RESTデータ・ソース」から「作成」を選択します。
![25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/6234bc8c-f885-6c2c-e159-a9a7a72de92a.png)
![26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/eb9d568d-fa58-4898-529f-061ac1961de5.png)

そのまま「次」を選択します。
![27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/c0756267-8041-2155-7aa2-680d2aadf2e6.png)

以下画像の通りに入力します。
![28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/f53fb06e-dd6e-3acf-3fde-520dd17ecd97.png)
URLエンドポイントは下記になります。
```
https://inference.generativeai.us-chicago-1.oci.oraclecloud.com/20231130/actions/generateText
```

そのまま「次」を2回選択します。
![29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/35adaf7f-ed31-732a-ce7f-799f1d6f0369.png)
![30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/e25928e5-9095-1d05-9611-2ab37133b3b4.png)

資格証明は先ほど作成したものを選択します。
記入したら「詳細」を選択します。
![31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/84e880a4-0c7e-7ef0-af68-0fcc00f32f89.png)

赤枠の通りに入力したら「検出」を選択します。
![32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/d895643c-2022-0678-e058-e2082d332a38.png)
なおリクエスト本文は下記になります。
コンパートメントIDはご自分のものを入力します。
```json
{ 
    "inferenceRequest": {
        "runtimeType": "COHERE",
            "prompt": "#AI_INPUT#",
            "maxTokens": 500,
            "numGenerations": 1,
            "returnLikelihoods": "GENERATION",
            "isStream": false
    }, 
        "servingMode": { 
            "servingType": "ON_DEMAND",
            "modelId": "cohere.command"
        }, 
        "compartmentId": "<Your-Compartment-OCID>"
}
```

以下のように出力されれば Generative AI Service との連携が出来ています。
問題なければ「RESTデータ・ソースの作成」を選択します。
![33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/cc854b7e-44c7-efd6-9a8d-25a8a21d4e4a.png)

作成されたRESTデータ・ソースの名前を選択します。
![34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/0dd0e6c1-e045-e70b-ec84-e929b90497d6.png)

｢パラメータ」タブにて「パラメータの追加」を選択します。
![35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/9464b9ae-df9b-ceb9-da7a-b1caa49e8303.png)

赤枠の通りに入力したら「パラメータの追加」を選択します。
![36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/08726b5b-68aa-e3a7-a65e-87f55c9f2532.png)
これでリクエスト本文に動的な値を格納できるようになりました。

ページ・デザイナに戻り、下段の「リージョン」から「クラシック・レポート」をドラッグ＆ドロップし、BODYに配置します。
![37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/2fde114e-642b-d730-65a6-b2f1cc940d61.png)

ID と LIKELIHOOD はコメントアウトし、画面に表示されないようにします。
タイトルには任意の名前を入力します。
ソースは「RESTソース」を選択し、先ほど作成したRESTデータ・ソースを設定します。
![38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/87c739b4-748d-819c-6c94-5874a3767ff2.png)

パラメータの「AI_INPUT」を選択した状態で、右カラムの値のタイプを「アイテム」にします。
対応するアイテムとして、プロンプトを入力するテキストフィールドに付けたアイテム名を設定します。
![39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/7f02b3bf-dc44-f633-bb02-dfd49f801fbe.png)

クラシック・レポートの列にある「TEXT」を選択し、右カラムのタイプを「リッチ・テキスト」に設定します。
![40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/fa588a23-c466-70b3-7c35-946582bcb103.png)

以上で完成です。
最後に出来たアプリの動作を確認します。
プロンプトに以下文章を入力すると、C++のサンプルコードが出力されました。
> Please show me a sample code to implement an application calculating fibonacci sequence by C++.

![41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/3947e72f-1a5a-ac8d-fa6c-21cde740236a.png)

### PL/SQLを使ったローコードの実装

次にPL/SQLを使ったローコード実装の手順になります。
下段のアイテムからBODYに「リッチ・テキスト・エディタ」をドラッグ＆ドロップします。
ここにGenerative AI Serviceからの回答が出力されます。
アイテムの名前を任意に設定します。
ここで設定した名前は後ほどPL/SQLのバインド変数で使います。
![16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/c4dcca42-3150-63ee-b425-5b577f14c98b.png)

このアイテムはユーザは編集しないため、以下の通り読取り専用にしておきます。
![16-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/3e349c0e-8ae9-132a-e667-7c5244931a0e.png)

ページ・デザイナのプロセスから「プロセスの作成」を選択します。
![17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/5da76174-7a12-7c8d-e886-4f9f41e54f4f.png)

作成したプロセスの設定について、以下赤枠の通りになっているか確認します。
｢ボタン押下時」の設定値はボタンのアイテム名を選択します。
![18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/851bb9bd-bcf0-95ce-a909-4293111d024c.png)
![19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/d5600d73-7b1a-9302-2815-028b633e28b5.png)

ソース・プロパティの「PL/SQLコード」にGenerative AI Serviceと連携するコードを実装します。
今回のコードは下記サイトを参考にさせて頂きました。
https://www.linkedin.com/pulse/how-build-intelligent-apps-oracle-generative-ai-apex-madhusudhan-rao-z423f


```plsql
DECLARE
 
  l_genai_rest_url    VARCHAR2(4000) := 'https://inference.generativeai.us-chicago-1.oci.oraclecloud.com/20231130/actions/generateText'; 

  l_web_cred        CONSTANT VARCHAR2(50)    := 'OCI_WebCred';  -- Web資格証明で入力した「静的ID」を記入
  l_input varchar2(4000) := :AI_INPUT;  -- テキスト・フィールドのアイテム名を記入
  l_response_json CLOB;
  l_text varchar2(4000);
   
  -- JSON形式のリクエスト情報 (compartmentIdにはご自分のコンパートメントIDを記入)
    l_ocigabody varchar2(1000) := '{ 
            "inferenceRequest": {
                    "runtimeType": "COHERE",
                     "prompt": "'||l_input||'",
                     "maxTokens": 500,
                     "numGenerations": 1,
                     "returnLikelihoods": "GENERATION",
                     "isStream": false
            }, 
            "servingMode": { 
                "servingType": "ON_DEMAND",
                "modelId": "cohere.command"
            }, 
            "compartmentId": "<Your-Compartment-OCID>"
    }';

  -- レスポンス取得用のカーソル
  CURSOR C1  IS 
            SELECT jt.* 
            FROM   JSON_TABLE(l_response_json, '$' 
                     COLUMNS (text VARCHAR2(4000)  PATH '$.inferenceResponse[0].generatedTexts[0].text' )) jt; 

BEGIN

  if l_input is not null then

        apex_web_service.g_request_headers.DELETE; 
        apex_web_service.g_request_headers(1).name  := 'Content-Type'; 
        apex_web_service.g_request_headers(1).value := 'application/json';  

         l_response_json := apex_web_service.make_rest_request 
           (p_url                  => l_genai_rest_url, 
            p_http_method          => 'POST', 
            p_body                 => l_ocigabody, 
            p_credential_static_id => l_web_cred); 

    For row_1 In C1 Loop
        l_text := row_1.text; 
        -- 生成AIからの回答を表示 (AI_ANSWERの部分はリッチ・テキスト・エディタのアイテム名を記入)
        :AI_ANSWER := l_text; 
    End Loop;

    end if;

END;
```

以上で完成です。
出来上がった画面が以下になります。
例として以下の質問を投げており、サンプル・コードが表示されています。
> Please show me a sample code to implement an application calculating fibonacci sequence by JavaScript.

![22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/cb686dfe-0e15-d207-a78c-79497e8caefe.png)

もう一つ例としてタワー型PCの組み立て手順を聞いてみました。
> What is the process to build a tower PC?

![23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33182/3df85da9-5434-d4b2-b294-4c67480a96fb.png)

以上の通りOCI Generative AI Serviceとの連携、リッチ・テキストを使った回答の表示ができました。
APEXを使えば生成AIアプリもノーコード / ローコードで手軽に開発できますね。