
みなさん、こんにちは。
AI検証チームの宮井です。
前回の記事では、簡単な問い合わせを試してみましたが、
今回は、Oracle Database 23aiの新機能であるAI Vector Searchを使ったRAG(Retrieval-Augmented Generation)による問い合わせ機能を試してみました。
RAGとは、LLMが学習していない情報を外部の情報から補うことで、回答生成に生かす手法のことです。
データベース内の膨大なテキスト情報から関連する情報を素早く抽出し、AIが自然な文章で回答を返すことが可能になります。
RAGは、LLMの学習コストの問題であったり、頻繁にデータが更新される場合などに役に立ちます。
マニュアルの複数のページをもとにしています。
マニュアルではPDFを取り込む例となっていましたが、
テキスト形式のデータをCLOB型のテーブルに格納し、問い合わせに利用する形にしました。
Oracle AI Vector Searchユーザーズ・ガイド ベクトル埋込みの生成
この技術はAIメディアブーストでも利用しています!
AI時代の新WEBマーケティングツール AIメディアブースト

RAGの利用
まずは前回実施したPL/SQLの内容をもとに当社サイトキャラクターの名前を尋ねてみます。
「できるもん」というキャラクターは存在しませんので、誤った回答となりました。
いわゆるハルシネーションという現象です。
SQL> declare 2 input clob; 3 params clob; 4 output clob; 5 begin 6 input := 'DBひとりでできるもんのキャラクターの名前は何ですか?'; 7 params := '{"provider": "openai","credential_name": "OPENAI_CRED","url": "https://api.openai.com/v1/chat/completions","model": "gpt-4.1-nano","max_tokens": 10000,"temperature": 1.0}'; 8 utl_http.set_body_charset('UTF-8'); 9 output := dbms_vector_chain.utl_to_generate_text(input, json(params)); 10 dbms_output.put_line(output); 11 if output is not null then 12 dbms_lob.freetemporary(output); 13 end if; 14 exception 15 when OTHERS THEN 16 DBMS_OUTPUT.PUT_LINE (SQLERRM); 17 DBMS_OUTPUT.PUT_LINE (SQLCODE); 18 end; 19 / 「ひとりでできるもん!」のキャラクターの名前は「できるもん」です。 PL/SQLプロシージャが正常に完了しました。
それでは、これを解決するためにデータを投入していきます。
まずはテキストデータを格納するためのテーブルを作成し、CLOB型のカラムにキャラクターの説明文を挿入しました。
SQL> CREATE TABLE documentation_tab (id number, data clob); 表が作成されました。 SQL> INSERT INTO documentation_tab values(1, 'でぶちゃんはDBひとりでできるもんに登場するオリジナルのキャラクターです。 デザインのモチーフは、 ●ホットなCloudの帽子 ●3段階の身体はDiskを表現 ●ハートのポシェットには、夢と希望と10TBのデータが格納 ●全体の色合いは当社コーポレートカラー(赤とグレー) です。'); 1行が作成されました。 SQL> commit; コミットが完了しました。
次に説明文をベクトルデータ化していきます。
dbms_vector_chain.utl_to_chunksを利用し、テキストをチャンクと呼ばれる単位に分割し、それをdbms_vector_chain.utl_to_embeddingsを利用し、ベクトルデータとして格納します。
それを一連のコマンドで実行します。
SQL> CREATE TABLE doc_chunks as 2 (select dt.id doc_id, et.embed_id, et.embed_data, to_vector(et.embed_vector) embed_vector 3 from 4 documentation_tab dt, 5 dbms_vector_chain.utl_to_embeddings( 6 dbms_vector_chain.utl_to_chunks(dbms_vector_chain.utl_to_text(dt.data), json('{"normalize":"all"}')), 7 json('{"provider": "openai", "credential_name": "OPENAI_CRED", "url": "https://api.openai.com/v1/embeddings", "model": "text-embedding-3-small"}')) t, 8 JSON_TABLE(t.column_value, '$[*]' COLUMNS (embed_id NUMBER PATH '$.embed_id', embed_data VARCHAR2(4000) PATH '$.embed_data', embed_vector CLOB PATH '$.embed_vector')) et 9 ); 表が作成されました。
チャンクごとのテキストと対応するベクトルデータが確認できます。
ベクトルは数値の配列で、類似度検索に使われます。
全文は載せておりませんが、チャンクごとにデータ格納されていました。
SQL> select * from doc_chunks;
でぶちゃんはDBひとりでできるもんに登場するオリジナルのキャラクターです。 デザイ
ンのモチーフは、 ●ホットなCloudの帽子 ●3段階の身体はDiskを表現 ●ハートのポシェッ
トには、夢と希望と10TBのデータが格納
[6.21519126E-002,-1.28361713E-002,-1.40369739E-002,1.20494384E-002,5.85909113E-0
02,-2.8446611E-002,6.03507087E-003,8.51328E-002,-2.8301686E-002,-4.6541471E-002,
・・・省略・・・
ユーザーの質問をもとに類似度の高い上位3件のテキストを抽出してコンテキストとしてまとめています。このコンテキストをAIに渡して回答を生成します。
SQL> VAR prompt CLOB; SQL> VAR user_question CLOB; SQL> VAR context CLOB; SQL> begin 2 :user_question := 'DBひとりでできるもんのキャラクターの名前は何ですか?'; 3 :context := ''; 4 for rec in ( 5 select embed_data from doc_chunks 6 order by vector_distance(embed_vector , (select to_vector(et.embed_vector) embed_vector from dbms_vector_chain.utl_to_embeddings(:user_question, 7 json('{"provider": "openai", "credential_name": "OPENAI_CRED", "url": "https://api.openai.com/v1/embeddings", "model": "text-embedding-3-small"}')) t, json_table ( t.column_value, '$[*]' columns 8 ( embed_id number path '$.embed_id', embed_data varchar2 ( 4000 ) path '$.embed_data', embed_vector clob path '$.embed_vector' ) ) et), cosine) 9 fetch first 3 rows only 10 ) 11 loop 12 :context := :context || rec.embed_data; 13 end loop; 14 :prompt := '次のコンテキストを使用して質問に回答してください。 ' 15 || ' コンテキスト : ' 16 || :context; 17 dbms_output.put_line('Generated prompt: ' || :prompt); 18 end; 19 / Generated prompt: 次のコンテキストを使用して質問に回答してください。 コンテキスト : でぶちゃんはDBひとりでできるもんに登場するオリジナルのキャラクターです。 デザインのモチーフは、 ●ホットなCloudの帽子 ●3段階の身体はDiskを表現 ●ハートのポシェットには、夢と希望と10TBのデータが格納●全体の色合いは当社コーポレ ートカラー(赤とグレー) です。
再度質問をすると、今度は「でぶちゃん」というキャラクター名が返ってきました!
SQL> declare 2 input clob; 3 params clob; 4 output clob; 5 begin 6 input := 'DBひとりでできるもんのキャラクターの名前は何ですか?' || :prompt; 7 params := '{"provider": "openai","credential_name": "OPENAI_CRED","url": "https://api.openai.com/v1/chat/completions","model": "gpt-4.1-nano","max_tokens": 10000,"temperature": 1.0}'; 8 utl_http.set_body_charset('UTF-8'); 9 output := dbms_vector_chain.utl_to_generate_text(input, json(params)); 10 dbms_output.put_line(output); 11 if output is not null then 12 dbms_lob.freetemporary(output); 13 end if; 14 exception 15 when OTHERS THEN 16 DBMS_OUTPUT.PUT_LINE (SQLERRM); 17 DBMS_OUTPUT.PUT_LINE (SQLCODE); 18 end; 19 / 「DBひとりでできるもん」のキャラクターの名前は、「でぶちゃん」です。 PL/SQLプロシージャが正常に完了しました。
今回入れている情報が少ないので、必然的に検索にヒットしますが、
実際のデータも試すとチューニングも必要となってきます。
まとめ
いかがでしたでしょうか?
今回はRAGを利用してみました。
RAGを利用すると、これまでデータベースに格納していたデータをAI検索に利用することができ、データ活用の幅が広がるのではないかと思います。
生成AIに関する記事は随時更新予定です。是非次回もご覧ください。
生成AIに関するお問い合わせはこちら!

投稿者プロフィール

-
Oracle Database、Oracle Cloud、生成AIに関するエンジニア兼プリセールスなどを担当しています。
社内のOCI検証チームのとりまとめやAI検証チームの技術リーダーをしています。
資格はOracle Cloud Infrastructure Architect Professional 2023等を取得しています