みなさん、こんにちは。
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等を取得しています