Oracle APEX 24.2に検索構成として新たに
Oracleベクトル検索 が追加されました。今回の記事では、この
Oracleベクトル検索 を使ったAPEXアプリケーションを作成してみます。また、これまでのAPEXにあった
Oracle Text による検索構成も作成し、それぞれ比較してみます。
作成したAPEXアプリケーションで、以下の文言で検索します。
「民泊を規制している条文を教えて 」
検索結果にアイコンが含まれているカードは、ベクトル検索でヒットした条文になります。表示対象を5件にしているため、結果として5件表示されています。アイコンが表示されていないカードは無いので、Oracle Text検索でヒットした条文はありません。
以下の文言で検索します。
「民泊 」
ベクトル検索だけが検索にヒットします。条文に「民泊」という単語は含まれていません。
ベクトル検索の結果より、法令では民泊のことを「住宅宿泊事業」と呼んでいることがわかります。「
住宅宿泊事業 」で検索します。
単語として住宅宿泊事業が含まれている条文がリストされました。
以下より、
Oracleベクトル検索 の
検索構成 の作成と、それを使用したAPEXアプリケーションを作成します。
検索に使用するデータの準備から始めます。以前の記事に従って表JLAW_DATA に法令(対象は観光)のデータがロード済みとします。データがロードされていると本則をビューJLAW_LAW_MAINPROVISION_XV より参照できます。
select * from jlaw_law_mainprovision_xv;
本則のデータを投入した実表を、表JLAW_LAW_SENTENCES として作成します。
create table jlaw_law_sentences as select * from jlaw_law_mainprovision_xv;
作成した表JLAW_LAW_SENTENCES には主キー列がありません。検索構成 を作成する際に主キー列は必須なので、主キー列として列SID を追加します。
alter table jlaw_law_sentences add (sid number generated by default on null as identity);
法令の条文は列
"Sentence" に含まれています。この列"Sentence"から生成したベクトル表現(embeddingのことです、以下、ベクトル表現とします)を保存する列を
SENTENCE_VECTOR として追加します。
alter table jlaw_law_sentences add (sentence_vector vector);
Oracle Text索引を列"Sentence" に作成します。Text索引に指定するレクサーを作成します。
begin ctx_ddl.create_preference('ja_vgram_lexer', 'JAPANESE_VGRAM_LEXER'); end; / ctx_ddlの実行権限がないというエラーが発生した場合は、管理者権限のあるユーザーでgrant文を実行します。
grant execute on ctx_ddl to <APEXワークスペース・スキーマ>;
Oracle Text索引JLAW_LAW_SENTENCES_CTX を列"Sentence" に作成します。
create index jlaw_law_sentences_ctx on jlaw_law_sentences("Sentence") indextype is ctxsys.context parameters(' lexer ja_vgram_lexer sync (on commit) ');
全文検索を実行し、Oracle Text索引が正しく作成されたことを確認します。
select count(*) from jlaw_law_sentences where contains("Sentence", '観光') > 0
列"Sentence" に保存されている条文のベクトル表現を生成し、列SENTENCE_VECTOR に保存します。
ベクトル表現の生成に、ローカルのLM Studioを使用します。embeddingモデルとしてtext-embedding-granite-embedding-278m-multilingual@q8_0 を使用します。(Ollamaでモデルgranite-embedding:278mを使用すると、なぜか1件だけエラーが発生してembeddingが生成されませんでした。)
LM Studioのローカル・サーバーはポート8080で接続の待受けをするように構成しています。
Oracle APEX 24.2のワークスペース・ユーティリティ に、新たにベクトル・プロバイダ が追加されています。このベクトル・プロバイダ を使用して、テキストからベクトル表現を生成します。
ベクトル・プロバイダ を開き、LM Studioの
text-embedding-granite-embedding-278m-multilingual@q8_0 を呼び出すプロバイダを作成します。
作成 をクリックします。
プロバイダ・タイプ として生成AIサービス を選択します。プロバイダ・タイプ には生成AIサービス の他にデータベースONNXモデル とカスタムPL/SQL があります。
LM StudioのOpen AI互換APIを呼び出してベクトル表現を生成するため、AIプロバイダ にはOpen AI を選択します。名前 はLocal Granite 278m 、静的ID はLOCAL_GRANITE_278M とします。APEXはローカルのpodmanでコンテナとして動作させているため、LM Studioのembedding APIを呼び出すベースURL に以下を設定します。
http://host.containers.internal:8080/v1
LM Studioでは資格証明は不要ですが必須設定なので、資格証明 として- 新規作成 - を選択し、APIキー にdummy (文字列はなんでも良い)と入力します。
詳細 のAIモデル にtext-embedding-granite-embedding-278m-multilingual@q8_0 を設定します。
以上の設定を行い、接続のテスト をクリックします。接続に成功することを確認し、作成 をクリックします。
残念ながら不具合があり(APEX 24.2.3では修正されていません)、ベースURLはhttp://またはhttps://で始まる必要があります 、というエラーが発生します。そのため、ベクトル・プロバイダを作成できません。
ワークアラウンドとして、
生成AIサービス の代わりに
カスタムPL/SQL として等価なベクトル・プロバイダを作成します。
以下のファンクションGENERATE_EMBEDDING を作成します。
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 generate_embedding(
p_content in varchar2
)
return vector
as
C_ENDPOINT constant varchar2(80) := 'http://host.containers.internal:8080/v1/embeddings';
C_MODEL constant varchar2(80) := 'text-embedding-granite-embedding-278m-multilingual@q8_0';
l_request clob;
l_request_json json_object_t;
l_response clob;
l_response_json json_object_t;
l_data json_array_t;
l_embedding json_object_t;
l_array json_array_t;
l_vector clob;
v vector;
e_call_embeddings_failed exception;
begin
l_request_json := json_object_t();
l_request_json.put('input', p_content);
l_request_json.put('model', C_MODEL);
l_request := l_request_json.to_clob();
apex_web_service.set_request_headers('Content-Type', 'application/json');
l_response := apex_web_service.make_rest_request(
p_url => C_ENDPOINT
,p_http_method => 'POST'
,p_body => l_request
);
-- dbms_output.put_line(l_response);
if apex_web_service.g_status_code <> 200 then
raise e_call_embeddings_failed;
end if;
l_response_json := json_object_t(l_response);
l_data := l_response_json.get_array('data');
l_embedding := treat(l_data.get(0) as json_object_t);
l_array := l_embedding.get_array('embedding');
l_vector := l_array.to_clob();
-- dbms_output.put_line(l_vector);
v := to_vector(l_vector);
return v;
end;
/
先ほどと同じ手順でベクトル・プロバイダの作成まで進みます。
プロバイダ・タイプ にカスタムPL/SQL を選択します。名前 はLocal Granite 278m 、静的ID はLOCAL_GRANITE_278M とし、ローカル埋込み のカスタム・ファンクション名 にGENERATE_EMBEDDING を指定します。
以上を設定し作成 をクリックします。
ベクトル・プロバイダ としてLocal Granite 278m が作成できました。
これから表JLAW_LAW_SENTENCESの列"Sentence"からベクトル表現を生成し、列SENTENCE_VECTORに保存します。
その前に、管理サービス のインスタンス管理 のセキュリティ に設定されている最大Webサービス・リクエスト の上限を確認しておきます。embeddingを生成するために、大量のAPIリクエストが発行される可能性があります。リクエスト数の制限にかからないように設定を変更しておきます。
この上限値はワークスペース単位でも設定できます。ワークスペース単位で制限している場合は、そちらも確認する必要があります。
ベクトル表現の更新には時間がかかるため、APEXのワークスペース・スキーマにsqlplusまたはSQLclといったコマンドライン・ツールで直接データベースに接続し、以下のスクリプトを実行します。APEXセッションを開始するためのアプリケーションIDなどは、対象のAPEXのワークスペースに存在する値に変更します。
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
set serveroutput on
declare
l_vector vector;
begin
/*
* ベクトル・プロバイダがあるワークスペースにある
* アプリケーションであればなんでも良い。ユーザーも
* 存在しているユーザーであれば良い。
*/
apex_session .create_session (
p_app_id => 100
,p_page_id => 1
,p_username => ' ADMIN'
);
for c in (
select sid, " Sentence" from jlaw_law_sentences
where sentence_vector is null
order by sid
-- fetch first 10 rows only
)
loop
begin
l_vector := apex_ai .get_vector_embeddings (
p_value => c." Sentence"
,p_service_static_id => ' LOCAL_GRANITE_278M'
);
-- l_vector := generate_embedding(c."Sentence");
update jlaw_law_sentences set sentence_vector = l_vector where sid = c .sid ;
exception
when others then
dbms_output .put_line (' sid = ' || c .sid );
raise;
-- null;
end;
commit ;
end loop;
apex_session .delete_session ;
end;
/
% sql wksp_apexdev/******@localhost/freepdb1
SQLcl: 金 3月 14 18:32:07 2025のリリース24.4 Production
Copyright (c) 1982, 2025, Oracle. All rights reserved.
接続先:
Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free
Version 23.6.0.24.10
SQL> set serveroutput on
SQL> declare
2 l_vector vector ;
3 begin
4 /*
5 * ベクトル・プロバイダがあるワークスペースにある
6 * アプリケーションであればなんでも良い。ユーザーも
7 * 存在しているユーザーであれば良い。
8 */
9 apex_session.create_session(
10 p_app_id => 100
11 ,p_page_id => 1
12 ,p_username => 'ADMIN'
13 );
14 for c in (
15 select sid , "Sentence" from jlaw_law_sentences
16 where sentence_vector is null
17 order by sid
18 -- fetch first 10 rows only
19 )
20 loop
21 begin
22 l_vector := apex_ai.get_vector_embeddings(
23 p_value => c."Sentence"
24 ,p_service_static_id => 'LOCAL_GRANITE_278M'
25 );
26 -- l_vector := generate_embedding(c."Sentence");
27 update jlaw_law_sentences set sentence_vector = l_vector where sid = c. sid ;
28 exception
29 when others then
30 dbms_output.put_line('sid = ' || c.sid );
31 raise ;
32 -- null ;
33 end ;
34 commit ;
35 end loop ;
36 apex_session.delete_session;
37 end ;
38* /
PL/SQLプロシージャが正常に完了しました。
SQL>
使用しているembeddingモデルtext-embedding-granite-embedding-278m-multilingual@q8_0 の最大コンテキスト長は512ですが、列"Sentence"にはそれを超える長さの条文が含まれます。そのため、生成されているembeddingは精度が高いとは言えません。今回の目的はAPEXの検索構成の確認なので、そのままにしています。(Ollamaでエラーが発生するのは、おそらくコンテキスト長を超える文字列を与えていることが理由と思われます。)
列SENTENCE_VECTORにnullがなければ、すべての行で列"Sentence"のベクトル表現が生成されています。
select count(*) from jlaw_law_sentences where sentence_vector is null; SQL> select count (*) from jlaw_law_sentences where sentence_vector is null ;
COUNT(*)
___________
0
SQL>
表に保存されているデータから一気にembeddingを生成する際は、ベクトル・プロバイダを指定してAPEX_AI.GET_VECTOR_EMBEDDINGSを呼び出す必要はあまりありません。OpenAIやCohereなどのサービスは1回のAPIリクエストで複数のembeddingを生成できます。また、OpenAIではBatch APIを呼び出すことにより、低コストでembeddingを生成できます。
ベクトル・プロバイダはこれから作成する検索構成で使用します。ベクトル検索では、検索フィールドに入力した文字列のベクトル表現を取り出す必要があります。APEXの検索構成では、この処理を行なう設定としてベクトル・プロバイダを使います。
以上で、APEXアプリケーションを作成する準備は完了しました。
空のAPEXアプリケーションを作成します。名前 はe-Gov法令検索 とします。
アプリケーションが作成されます。共有コンポーネント を開きます。
構成の検索 を開きます。これは、検索構成 のことです。
作成 をクリックします。
最初に検索タイプ が Oracleベクトル検索 の検索構成 を作成します。名前 はe-Gov法令ベクトル検索 とします。
次 へ進みます。
ベクトル・プロバイダ にLocal Granite 278m 、表/ビューの名前 にJLAW_LAW_SENTENCES を選択します。
次 へ進みます。
主キー列 はSID(Number) 、ベクトル列 はSENTENCE_VECTOR(Vector) 、タイトル列 はLawTitle(Varchar2) 、説明列 はSentence(Clob) とします。アイコン・ソース はイニシャル とします。
検索構成の作成 をクリックします。
検索タイプ がOracleベクトル検索 の検索構成 が作成されます。
設定 の静的ID をLAW_VECTOR としておきます。ベクトル属性 の検索タイプ がデフォルトでExact 、距離メトリック がCosine になっています。ベクトル索引を作成している場合は、検索タイプ としてApproximate を選択できます。
列のマッピング のカスタム列1 にSectionTitle(Varchar2) 、カスタム列2 にSubsectionTitle(Varchar2) 、カスタム列3 にArticleTitle(Varchar2) を設定します。
アイコンと表示 のデフォルト結果行テンプレート や結果のCSSクラス を設定することにより、検索結果の表示をカスタマイズできます。
同じ手順で、検索タイプ がOracle Text の検索構成を作成します。名前 はe-Gov法令テキスト検索 とします。
次 へ進みます。
ソース の
表/ビューの名前 に
JLAW_LAW_SENTENCES を選択します。
次 へ進みます。
列のマッピング は
ベクトル列 の指定が
Oracle Text索引列 の指定に変わります。
Oracle Text索引列 は
Sentence(Clob) です。
検索構成の作成 をクリックします。
検索タイプ がOracle Text の検索構成 が作成されます。静的ID にLAW_TEXT を設定します。
列のマッピング のカスタム列1、2、3 をベクトル検索の検索構成と同じ設定にします。そして、アイコンと表示 のアイコン・ソース は- アイコンなし - に変更し、ベクトル検索の検索結果とOracle Textの検索結果で、見分けがつくような表示にします。
以上で検索構成の作成は完了です。
検索ページを作成します。
ページの作成 をクリックします。
検索ページ を選択します。
作成するページの
名前 は
e-Gov法令検索 とします。
構成の検索 の
E-Gov法令テキスト検索 と
E-Gov法令ベクトル検索 の双方を
チェック します。
以上で
ページの作成 をクリックします。
検索ページが作成されます。
ページを少しだけカスタマイズします。今回は記事はベクトル検索がテーマなので、ソースの検索 にある、E-Gov法令ベクトル検索 をE-Gov法令テキスト検索 の上に移動 します。また、両方とも外観 の最大結果数 を5 に限定します。
以上でAPEXアプリケーションは完成です。
アプリケーションを実行すると、記事の先頭にあるような検索を実行できます。
今回の記事で作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/e-gov-law-search.zip
Oracle APEXのアプリケーション作成の参考になれば幸いです。
完