はじめに
すっかり定着した(?)RAG(Retrieval-Augmented Generation)ですが、従来のRAGシステムは決められた1つの知識ベースから情報を検索し、回答を生成するという単一の流れを辿ります。しかし、実際のビジネスシーンでは、質問の内容によって異なる専門知識や異なる処理方式が必要になることが多くあります。
そこで、この本記事では、Autonomous Database のインデータベースAIエージェントである SELECT AI / SELECT AI with RAG と DBMS_CLOUD_AI パッケージを活用して、質問内容に応じて自律的にAIエージェントを選択・実行するエージェンティックRAGシステムをSQLだけで実現する方法にチャレンジしてみました。そうなんです!データベースの中で完結するエージェンティック RAG を目指します。
このブログの内容は、2025/7/30 の Oracle AI Jam Session #26 のコンテンツの一部となる予定です。
従来のRAGとエージェンティックRAGの違い
従来のRAG
- あらかじめ決められた1つの流れをなぞる(単一パス)
- 固定された知識ベースからの検索のみ
- 検索は一度だけ
- 単一のLLMモデルでの応答生成
エージェンティックRAG
RAG パイプラインの様々な場所でエージェンティックAIの考え方を活用することが可能ですが、今回は次のようなことを試してみました。
- 質問内容によって適切なエージェント(SQLエージェント、RAGエージェント)を自律選択
- 各エージェントが専門分野に特化した知識ベースを持つ
- ルーターエージェントが最適なエージェントに処理を移譲
Autonomous Database によるインデータベース・エージェンティックRAG
今回は、このエージェンティックRAGのアーキテクチャをすべての処理がデータベース内で完結するように実装してみました。
システムアーキテクチャ
利用技術
SELECT AI
Autonomous Database 内で稼働するインデータベースAIエージェントです。
SELECT AI は、chat、showsql、runsql、narrate などのアクションを実行することができます。
-
chat
アクションは、LLM を活用して自然言語タスクを処理することができます。 -
showsql
アクションは、LLM を活用して自然言語で与えられた質問・指示に答えるための SQL文を生成し、生成したSQLを返します -
runsql
アクションは、LLM を活用して自然言語で与えられた質問・指示に答えるための SQL文を生成し、生成したSQLを実行して、検索結果を返します -
narrate
アクションは、runsql
で得られた検索結果を元に自然言語で与えられた質問・指示に対する回答を生成して返します
LLMには、OCI Generative AI サービスの CohereAI/Meta/xAI の各モデルの他、OpenAI、Azure OpenAI Service、Google、Anthropic、Hugging Face といった様々なサービスプロバーダーの API 経由のモデルを利用することができます。今回は、OCI Generative AI サービスの xAI Grok 3 を使用してみました。
また、ベクトル埋め込みを生成する埋め込みモデルにも、OCI Generative AI サービスの CohereAIのモデルの他、OpenAI、Azure OpenAI Service、Google、といった様々なサービスプロバーダーの埋め込みモデルを利用できます。今回は、OCI Generative AI サービスの Cohere Embed 4 を使っています。
利用できるモデルはこちらでご確認いただけます。
検索の対象となるテーブル群やドキュメント群は、AI Profile によって定義され、AI Profile 毎に、どの知識ベースに基づいて回答を生成できるかが決まります。
AI Profile で、アクセスできるテーブル群(スキーマ名や具体的なテーブル名のリスト)を指定すると、それらのテーブル群を知識ベースとして SQLを生成、検索、回答生成が可能です。指定しない場合は、セッションのデフォルトスキーマが使用されます。
AI Profile に、ベクトルインデックスを指定すると、ベクトル検索による回答生成が可能となります。これを SELECT AI with RAG と呼びます。PDFやテキスト文書などの非構造化データに対するセマンティック検索の基づく回答生成の用途で利用できます。
SELECT AI with RAG 用のベクトルインデックスは、専用のプロシージャ DBMS_CLOUD_AI.CREATE_VECTOR_INDEX
で作成しておきます。なお、ここで言う”ベクトルインデックス”は、純粋なベクトルデータベースになぞらえた表現で、Oracle Database 上は、テキストのチャンクとそのベクトル埋め込みを格納するテーブル(ベクトルストア)と、ベクトル検索を高速に効率よく検索するためのベクトル索引から構成されます。
DBMS_CLOUD_AI.CREATE_VECTOR_INDEX
は、ベクトルストアとベクトル索引、そして、オブジェクトストレージからドキュメントを取り込み、チャンキング、埋め込み、ベクトルストアへの登録を行うパイプラインを自動的に生成します。
SELECT AI with RAG については下記のブログもご参照ください。
主要コンポーネント
1. インデータベース・ルーターエージェント(SELECT AI chat)
SELECT AI chat がルーターエージェントとして機能し、質問内容を分析して最適な知識ベース検索AIエージェント(AI Profile)を選択します。
2. インデータベース・知識ベース検索AIエージェント(SELECT AI narrate)
SELECT AI narrate によって、1 で選択された AI Profile で指定された知識ベースを検索して、自然言語による質問文・指示文に対する回答を生成するインデータベースAIエージェントです。
今回は、次の 3つの AI Profile を定義しています。
- SALES_HISTORY_PROFILE – 売上データに対するSQL生成・実行エージェント(ベクトル検索を伴わない SELECT AI)
- GENERATIVE_AI_RAG_PROFILE – Oracle AI技術のRAGエージェント(ベクトル検索を伴う SELECT AI with RAG)
- MAGIC_RAG_PROFILE – 魔法関連のRAGエージェント(ベクトル検索を伴う SELECT AI with RAG)
実装の詳細
ルーターエージェント
質問文に基づいて最適な知識ベース検索AIエージェント(AI Profile)を 選択する PL/SQL ファンクションです。
SELECT AI chat によって、外部LLM(xAI Grok 3)を使って自然言語処理を行い適切な検索エージェントを決定します。
AI Profile には、description
を定義することができますので、この内容を元に適切な AI Profile を選択します。
Function Calling や MCP と同様に description
が大切です!
CREATE OR REPLACE FUNCTION GET_OPTIMAL_AI_PROFILE(
p_question IN VARCHAR2
) RETURN VARCHAR2
AS
v_profiles_info CLOB;
v_chat_prompt CLOB;
v_response CLOB;
v_selected_profile VARCHAR2(128);
v_current_profile VARCHAR2(128);
v_profile_count NUMBER;
BEGIN
-- 入力チェック
IF p_question IS NULL OR LENGTH(TRIM(p_question)) = 0 THEN
RETURN NULL;
END IF;
-- 現在のプロファイルを保存
BEGIN
SELECT DBMS_CLOUD_AI.GET_PROFILE() INTO v_current_profile FROM DUAL;
EXCEPTION
WHEN OTHERS THEN
v_current_profile := NULL;
END;
-- まず有効なプロファイルが存在するかチェック
SELECT COUNT(*) INTO v_profile_count
FROM USER_CLOUD_AI_PROFILES
WHERE STATUS = 'ENABLED';
IF v_profile_count = 0 THEN
RETURN NULL;
END IF;
-- USER_CLOUD_AI_PROFILESからプロファイル情報を取得
BEGIN
SELECT LISTAGG(
'プロファイル名: ' || PROFILE_NAME ||
', 説明: ' || NVL(DESCRIPTION, '説明なし'),
CHR(10)
) WITHIN GROUP (ORDER BY PROFILE_NAME)
INTO v_profiles_info
FROM USER_CLOUD_AI_PROFILES
WHERE STATUS = 'ENABLED';
-- プロファイルが見つからない場合(LISTAGGがNULLを返すケース)
IF v_profiles_info IS NULL OR LENGTH(v_profiles_info) = 0 THEN
-- デフォルトプロファイルを返す
SELECT PROFILE_NAME
INTO v_selected_profile
FROM USER_CLOUD_AI_PROFILES
WHERE STATUS = 'ENABLED'
AND ROWNUM = 1;
RETURN v_selected_profile;
END IF;
EXCEPTION
WHEN OTHERS THEN
-- エラー時もデフォルトプロファイルを返す
BEGIN
SELECT PROFILE_NAME
INTO v_selected_profile
FROM USER_CLOUD_AI_PROFILES
WHERE STATUS = 'ENABLED'
AND ROWNUM = 1;
RETURN v_selected_profile;
EXCEPTION
WHEN OTHERS THEN
RETURN NULL;
END;
END;
-- SELECT AI chatに渡すプロンプトを構築
v_chat_prompt :=
'以下の質問文に最も適したAI Profileを1つだけ選択してください。プロファイル名のみを回答してください。' || CHR(10) || CHR(10) ||
'【質問文】' || CHR(10) ||
p_question || CHR(10) || CHR(10) ||
'【利用可能なAI Profiles】' || CHR(10) ||
v_profiles_info || CHR(10) || CHR(10) ||
'回答はプロファイル名のみを返してください(例:SALES_HISTORY_PROFILE)';
-- 適当なプロファイルを一時的に設定して SELECT AI chat を実行
-- SELECT AI chat では、検索エージェントと異なる LLM を使用したい場合は、別途専用の AI Profile を用意することができます。今回はすべてのAIエージェントで同じ LLM を使用するため簡易的に検索エージェント用の AI Profile を ルーターエージェントでも使用しています。
DECLARE
v_temp_profile VARCHAR2(128);
BEGIN
SELECT PROFILE_NAME
INTO v_temp_profile
FROM USER_CLOUD_AI_PROFILES
WHERE STATUS = 'ENABLED'
AND ROWNUM = 1;
-- 一時的にプロファイルを設定
DBMS_CLOUD_AI.SET_PROFILE(v_temp_profile);
-- SELECT AI chatを実行
SELECT DBMS_CLOUD_AI.GENERATE(
prompt => v_chat_prompt,
action => 'chat'
) INTO v_response FROM DUAL;
EXCEPTION
WHEN OTHERS THEN
-- エラー時はデフォルトのプロファイルを返す
SELECT PROFILE_NAME
INTO v_selected_profile
FROM USER_CLOUD_AI_PROFILES
WHERE STATUS = 'ENABLED'
AND ROWNUM = 1;
-- 元のプロファイルを復元
IF v_current_profile IS NOT NULL THEN
BEGIN
DBMS_CLOUD_AI.SET_PROFILE(v_current_profile);
EXCEPTION
WHEN OTHERS THEN NULL;
END;
END IF;
RETURN v_selected_profile;
END;
-- レスポンスから選択されたプロファイル名を抽出
BEGIN
-- CLOBを文字列に変換(最初の4000文字まで)
DECLARE
v_response_str VARCHAR2(4000);
BEGIN
v_response_str := DBMS_LOB.SUBSTR(v_response, 4000, 1);
-- 改行とスペースを整理
v_response_str := TRIM(REPLACE(REPLACE(v_response_str, CHR(10), ' '), CHR(13), ' '));
-- 有効なプロファイル名かチェック
SELECT PROFILE_NAME
INTO v_selected_profile
FROM USER_CLOUD_AI_PROFILES
WHERE STATUS = 'ENABLED'
AND (UPPER(PROFILE_NAME) = UPPER(v_response_str)
OR UPPER(v_response_str) LIKE '%' || UPPER(PROFILE_NAME) || '%'
OR UPPER(PROFILE_NAME) LIKE '%' || UPPER(v_response_str) || '%')
AND ROWNUM = 1;
END;
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- マッチするプロファイルが見つからない場合はデフォルトを使用
SELECT PROFILE_NAME
INTO v_selected_profile
FROM USER_CLOUD_AI_PROFILES
WHERE STATUS = 'ENABLED'
AND ROWNUM = 1;
WHEN OTHERS THEN
-- その他のエラー時もデフォルトを使用
SELECT PROFILE_NAME
INTO v_selected_profile
FROM USER_CLOUD_AI_PROFILES
WHERE STATUS = 'ENABLED'
AND ROWNUM = 1;
END;
-- 元のプロファイルを復元
IF v_current_profile IS NOT NULL THEN
BEGIN
DBMS_CLOUD_AI.SET_PROFILE(v_current_profile);
EXCEPTION
WHEN OTHERS THEN NULL;
END;
END IF;
RETURN v_selected_profile;
EXCEPTION
WHEN OTHERS THEN
-- 元のプロファイルを復元
IF v_current_profile IS NOT NULL THEN
BEGIN
DBMS_CLOUD_AI.SET_PROFILE(v_current_profile);
EXCEPTION
WHEN OTHERS THEN NULL;
END;
END IF;
-- エラー時はNULLを返す
RETURN NULL;
END GET_OPTIMAL_AI_PROFILE;
エージェンティック RAG メインエージェント
質問文を受け取って、ルーターエージェントを呼び出して適切なプロファイルを受け取って、検索AIエージェントを呼び出して検索実行と回答生成を実行するワークフロー全体を担うエージェントです。
最初にルーターエージェント GET_OPTIMAL_AI_PROFILE()
にプロファイル(検索AIエージェント)を選択させて、次にそのプロファイルをセッションに設定、実行します。
CREATE OR REPLACE FUNCTION EXECUTE_AI_WITH_OPTIMAL_PROFILE(
p_question IN VARCHAR2,
p_action IN VARCHAR2 DEFAULT 'chat'
) RETURN CLOB
AS
v_selected_profile VARCHAR2(128);
v_current_profile VARCHAR2(128);
v_response CLOB;
BEGIN
-- 現在のプロファイルを保存
SELECT DBMS_CLOUD_AI.GET_PROFILE() INTO v_current_profile FROM DUAL;
-- 最適なプロファイルを選択
v_selected_profile := GET_OPTIMAL_AI_PROFILE(p_question);
-- 選択されたプロファイルを設定
DBMS_CLOUD_AI.SET_PROFILE(v_selected_profile);
-- AI処理を実行
SELECT DBMS_CLOUD_AI.GENERATE(
prompt => p_question,
action => p_action
) INTO v_response FROM DUAL;
-- 元のプロファイルを復元
IF v_current_profile IS NOT NULL THEN
DBMS_CLOUD_AI.SET_PROFILE(v_current_profile);
END IF;
-- 結果を整形して返す
RETURN '【使用したProfile】: ' || v_selected_profile || CHR(10) || CHR(10) ||
'【回答】: ' || CHR(10) || v_response;
END EXECUTE_AI_WITH_OPTIMAL_PROFILE;
知識ベース検索AIエージェントの AI Profileの定義例
売上分析エージェント
売上分析エージェント
BEGIN
DBMS_CLOUD_AI.CREATE_PROFILE(
profile_name =>'SALES_HISTORY_PROFILE',
attributes =>'{"provider": "oci",
"credential_name": "OCI_CREDENTIAL",
"model": "xai.grok-3",
"max_tokens": 4000,
"object_list": [
{"owner": "SH"}
]
}',
description => '売上履歴データベースを使って自然言語からSQLを生成したり、そのSQLを実行したり、SQLを実行した結果に基づいてAIを使って質問に対する回答を生成するプロファイル。SQLの生成は、SELECT AI showsql 質問文 の形式。SQLの生成とその実行は、SELECT AI runsql 質問文 の形式。SQLの実行結果に基づいて回答を生成する場合は、SELECT AI narrate 質問文 の形式。質問文は自然文です。'
);
END;
Oracle AI技術RAGエージェント
Oracle AI技術RAGエージェント
BEGIN
DBMS_CLOUD_AI.CREATE_PROFILE(
profile_name =>'GENERATIVE_AI_RAG_PROFILE',
attributes =>'{"provider": "oci",
"credential_name": "OCI_CREDENTIAL",
"embedding_model": "cohere.embed-v4.0",
"model": "xai.grok-3",
"max_tokens": 4000,
"vector_index_name": "GENERATIVE_AI_RAG_INDEX"
}',
description => '生成AI、RAG、Oracle Database の Select AI や DBMS_CLOUD_AI パッケージ(SQLの生成、実行、説明、LLMとのチャット、RAG(検索拡張生成)などのタスク)やOracle AI Vector Searchに関する質問に答えるRAGのプロファイル'
);
END;
魔法関連のRAGエージェント
魔法関連のRAGエージェント
BEGIN
DBMS_CLOUD_AI.CREATE_PROFILE(
profile_name =>'MAGIC_RAG_PROFILE',
attributes =>'{"provider": "oci",
"credential_name": "OCI_CREDENTIAL",
"embedding_model": "cohere.embed-v4.0",
"model": "xai.grok-3",
"max_tokens": 4000,
"vector_index_name": "MAGIC_RAG_VEC_INDEX"
}',
description => '魔法に関する質問に答えるRAGのプロファイル 。SQLの確認は、SELECT AI showsql 質問文 の形式。SQLを実行して検索結果を得るには、SELECT AI runsql 質問文 の形式。SQLを実行してその実行結果に基づいて回答を生成する場合は、SELECT AI narrate 質問文 の形式。質問文は自然文です'
);
END;
使用例
基本的な使用例
ルーターエージェントによる検索エージェント(AI Profile)の選択
売上が最も大きかったのはどの製品カテゴリーでしたか?
SELECT
'売上が最も大きかったのはどの製品カテゴリーでしたか?' AS QUESTION,
GET_OPTIMAL_AI_PROFILE('売上が最大の製品カテゴリーで最も売上が少ない製品は何ですか?') AS SELECTED_PROFILE
FROM DUAL;
実行結果はこちらです。
QUESTION | SELECTED_PROFILE |
---|---|
売上が最も大きかったのはどの製品カテゴリーでしたか? | SALES_HISTORY_PROFILE |
SALES_HISTORY_PROFILE(売上データに対するSQL生成・実行エージェント)が選択されました。正解です!
ハリー・ポッターに彼が魔法使いであることを告げたのは誰で、それはいつのことでしたか?
SELECT
'ハリー・ポッターに彼が魔法使いであることを告げたのは誰で、それはいつのことでしたか?' AS QUESTION,
GET_OPTIMAL_AI_PROFILE('ハリー・ポッターに彼が魔法使いであることを告げたのは誰で、それはいつのことでしたか?') AS SELECTED_PROFILE
FROM DUAL;
実行結果はこちらです。
QUESTION | SELECTED_PROFILE |
---|---|
ハリー・ポッターに彼が魔法使いであることを告げたのは誰で、それはいつのことでしたか? | MAGIC_RAG_PROFILE |
MAGIC_RAG_PROFILE(魔法関連のRAGエージェント)が選択されました。正解です!
エージェンティック RAG 実行
それでは、いよいよエージェンティック RAG を実行してみます。
売上が最も大きかったのはどの製品カテゴリーでしたか?
SELECT
EXECUTE_AI_WITH_OPTIMAL_PROFILE(
'売上が最も大きかったのはどの製品カテゴリーでしたか?',
'narrate'
) AS AI_RESPONSE
FROM DUAL;
実行結果はこちらです。
AI_RESPONSE |
---|
【使用したProfile】: SALES_HISTORY_PROFILE 【回答】: 売上が最も大きかった製品カテゴリーは「周辺機器およびアクセサリ」でした。 |
正解です! 正しく、SALES_HISTORY_PROFILE プロファイル(売上分析エージェント)が選択されて正しい回答が導かれています。
ハリー・ポッターに彼が魔法使いであることを告げたのは誰で、それはいつのことでしたか?
SELECT
EXECUTE_AI_WITH_OPTIMAL_PROFILE(
'ハリー・ポッターに彼が魔法使いであることを告げたのは誰で、それはいつのことでしたか?',
'narrate'
) AS AI_RESPONSE
FROM DUAL;
実行結果はこちらです。
正解です! 正しく、MAGIC_RAG_PROFILE プロファイル(魔法関連のRAGエージェント)が選択されて正しい回答が導かれています。
MCPは、アプリ開発者にとってどんなメリットがありますか?
SELECT
EXECUTE_AI_WITH_OPTIMAL_PROFILE(
'MCPは、アプリ開発者にとってどんなメリットがありますか?',
'narrate'
) AS AI_RESPONSE
FROM DUAL;
実行結果はこちらです。
AI_RESPONSE |
---|
【使用したProfile】: GENERATIVE_AI_RAG_PROFILE 【回答】: MCP(Model Context Protocol)は、アプリ開発者にとって以下のようなメリットがあります 1. 多様な外部ツールやリソースへのアクセス:MCP対応アプリケーションを一度開発すれば、様々な外部ツールやリソース(ファイルシステム、データベース、APIなど)を活用できるようになります。これにより、サービス固有のAPIを知る必要がなくなります。 2. AIモデルの能力拡張:MCPは、AIモデルが外部リソースと容易に連携できるようにし、AIモデルの能力を拡張します。例えば、データベースの検索だけでなく、DBA業務の実行やデータベースの更新も可能になります。 3. 標準化された接続方法:USB-Cがデバイスを様々な周辺機器に接続する標準的な方法を提供するように、MCPはAIモデルを異なるデータソースやツールに接続する標準的な方法を提供します。これにより、企業は既存のシステムやデータベースをAIアプリケーションと簡単に連携させることができます。 4. 自律的なツール選択と実行:AIモデルが状況を分析し、自律的に適切なツールを選択して実行することができます。例えば、SQLの実行など、AIモデルが自ら判断してアクションを実行します。 Sources: – DevDay2025-MCP-Select-AI-with-RAG.txt (https://orasejapan.objectstorage.ap-tokyo-1.oci.customer-oci.com/n/orasejapan/b/devday25-arakawa-bucket/o/DevDay2025-MCP-Select-AI-with-RAG.txt) – how_to_make_mcp_server_for_oracle_db.txt (https://orasejapan.objectstorage.ap-tokyo-1.oci.customer-oci.com/n/orasejapan/b/devday25-arakawa-bucket/o/how_to_make_mcp_server_for_oracle_db.txt) |
正解です! 正しく、GENERATIVE_AI_RAG_PROFILE プロファイル(Oracle AI技術エージェント)が選択されて正しい回答が導かれています。
課題
Function CallingとMCP未対応への対応
SELECT AI chat は現時点(2025/7/16)では Function Calling や MCP(Model Context Protocol)に対応していないため、PL/SQLでエージェント間の協調処理を実装しています。対応してくれたら面白くなりますね!期待です。
質問のルーティング精度
今回は、売上分析と生成AI系の技術的な内容、そして、魔法に関する内容を担当する知識ベースを用意しました。全く異なる分野ですので、比較的簡単なプロンプトでも LLM は適切な AI Profile(検索AIエージェント)を選択することができています。しかし、実世界での応用では適切な選択が難しい場合も多くあります。Few Shot(Few が数百に及ぶかもしれません)プロンプティングなどのプロンプトエンジニアリングはもちろんのこと、自然言語処理用の機械学習モデルやベクトル埋め込みベースの分類アルゴリズムの活用などユースケース毎の丁寧なエンジニアリングが必要となります。
ベクトル埋め込みベースの分類アルゴリズムについては、画像のベクトル埋め込みを活用した分類の例ですが下記のようなブログも書いていますので参考になるかと思います。
インデータベースAIエージェントの利点
セキュリティ: データやメタデータがデータベース外に出ない!
自然言語による質問や指示文からSQLを生成するためには、スキーマ情報、テーブルの構造、参照制約などのメタデータが必要です。精度良く SQL を生成するためには、テーブルやカラムの属性に関するコメントやアノテーションも重要になってきます。しかり、これらのメタデータをどこまで開示してよいのかと問題があります。インデータベースAIエージェントであれば、メタデータはデータベース内に閉じるためこのような問題を最小化することができます。また、ベクトル埋め込みを生成する埋め込みモデルもインデータベースのモデルを利用することもできます。
インデータベースの埋め込みモデルについては下記のブログをご参照ください。
※ LLM は、データベースに内蔵することはできませんが、OCI Generative AI サービスをご利用いただく場合には、データ/メタデータは OCI に閉じることになります、OCI Generative AI サービス側に保存されることもありません。
まとめ
本記事では、Autonomous Database を活用して、質問内容に応じて自律的にAIエージェントを選択・実行するエージェンティックRAGシステムをSQLだけで実現する方法をご紹介しました。
主な特徴
- 完全インデータベース実行: すべての処理がデータベース内で完結してセキュリティも安心!
- 自律的エージェント選択: SELECT AI chatによるルーティングによる自動化!
- 専門性を持つエージェント: 各分野に特化したAI Profileで多様な知識ベースに対応!
- SQLだけでの実装: PL/SQL の魔術師ならヒーローになれる!
今後の展望
- SELECT AI chat が、Function Calling、MCPに対応して欲しい!
- より高度なルーティングアーキテクチャ
- SQLエージェント、RAGエージェントの実行結果を判定して、別のプロファイルやクエリーを書き換えて再実行するエージェントの実装など RAG パイプラインの各所をインテリジェント化
エージェンティック AI の考え方を RAG に適用することにより、従来の単一パターンのRAGから、質問に応じて最適な知識とモデルを動的に選択するエージェンティックRAGへの進化が可能となり、より柔軟で実用的なAIシステムの構築が実現できます。
参考リソース
Views: 0