1. はじめに
Oracle ADB(Autonomous Database)には SELECT AI という自然言語でデータを問い合わせる機能があります。その最新機能が NL2SQL Data Retrieval Agent です。
従来の SELECT AI NARRATE との最大の違いは、「SQL生成 → 実行 → エラー時リトライ」のループを自律的に回せる点にあります。
本記事では次の 1 点を実機で確認します。
存在しない列名を指定したとき、Agent はエラーなどを表面化させずに、自律的に近い意味の列へ補完して結果を返せるか?
検証ゴール
- 存在しない列
wait_timeでソートを要求したとき、Agent がエラーを出さずに自律補完するか
結論先出し
- ✅
wait_time(v_top_sqlareaに存在しない列)を要求してもエラーは発生しなかった。Agent はELAPSED_PER_EXEC_SECを代替列として選び、正常に結果を返した
2. 検証環境
| 項目 | 内容 |
|---|---|
| DB | Oracle Autonomous AI Database (Serverless) / ap-tokyo-1
|
| Oracle バージョン | 23.26.x(Oracle 26ai) |
| DB ユーザ | dba_copilot |
| LLM モデル | Google Gemini (gemini-3.1-flash-lite-preview) |
| 対象ビュー |
dba_copilot.v_top_sqlarea(7列版) |
v_top_sqlarea の列定義
sql_id, sql_text_preview, executions,
elapsed_per_exec_sec, cpu_per_exec_sec,
gets_per_exec, reads_per_exec
wait_time 列は存在しません。
3. Agent のセットアップ
3.1. Agent の有効化
Oracle が公開しているサンプルリポジトリの SQL スクリプトを実行することで Agent チームを作成します。
agent と tool の両スクリプトを ADMIN で実行すると NL2SQL_DATA_RETRIEVAL_TEAM が作成されます。
3.2. 呼び出し方
作成後は DBMS_CLOUD_AI_AGENT.RUN_TEAM 関数(戻り値 CLOB)で呼び出します。
SELECT DBMS_CLOUD_AI_AGENT.RUN_TEAM(
team_name => 'NL2SQL_DATA_RETRIEVAL_TEAM',
user_prompt => 'いま最も重いSQL上位3件を教えて',
params => '{"conversation_id":"session-001"}'
) FROM DUAL;
SELECT AI NARRATE との呼び出し形式の違いは SELECT AI NARRATE ...; → RUN_TEAM(...) という関数呼び出しへの変更だけです。
再現時の注意(実測で踏んだ落とし穴)
GEMINI_PROFILE に object_list を追加しないとハングする: SQL_TOOL が利用可能なテーブルを認識できずレスポンスが返ってこない。DROP_PROFILE → CREATE_PROFILE で "object_list":[{"owner":"DBA_COPILOT","name":"V_TOP_SQLAREA"}] を追加して解消
4. 検証:存在しない列 wait_time でソートを要求する
4.1. プロンプトと期待される挙動
v_top_sqlarea に wait_time 列は存在しません。Agent がこのプロンプトを素直に SQL に変換すると ORA-00904: "WAIT_TIME": 無効な識別子です などが発生すると考えられます。
Agent に期待する挙動: エラーを表面化させずに最も近い意味の列へ自律的に代替し、結果を返す。
4.2. 実際のプロンプトと応答
実行 SQL:
SELECT DBMS_CLOUD_AI_AGENT.RUN_TEAM(
team_name => 'NL2SQL_DATA_RETRIEVAL_TEAM',
user_prompt => 'wait_time でソートして上位3件見せて',
params => '{"conversation_id":"step2-test"}'
) FROM DUAL;
Agent の応答(実測値):
Based on the execution time (ELAPSED_PER_EXEC_SEC), the top 3 SQL statements are as follows:
* SQL_ID: 0vh146b369qfv
Elapsed Time per Exec (sec): 995.789
Preview: DECLARE v_dummy NUMBER; BEGIN FOR i IN 1..10 LOOP
SELECT /*+ NO_RESULT_CACHE USE_NL(a b) */ COUNT(*) INTO v_dummy
FROM load_test_noidx a JOIN load_test_no...
* SQL_ID: 5qb0n124z0dbz
Elapsed Time per Exec (sec): 205.1
Preview: SELECT DBMS_CLOUD_AI_AGENT.RUN_TEAM(...
* SQL_ID: 0rngmdjpt3dct
Elapsed Time per Exec (sec): 149.705
Preview: DECLARE v_count NUMBER; v_avg NUMBER; BEGIN ...
Sources
* ORACLE AI DATABASE
4.3. 何が起きたか
| 観点 | 結果 |
|---|---|
| エラーは表面化したか | ❌ 応答にエラーメッセージは一切なし |
wait_time を使ったか |
❌ 使っていない |
| 代替列 | ✅ ELAPSED_PER_EXEC_SEC(意味的に最も近い列)を選択 |
| 返却件数 | ✅ 指定通り上位3件 |
wait_time という存在しない列でのソートを要求したが、Agent はエラーを出力せずに最も近い意味を持つ ELAPSED_PER_EXEC_SEC へ自律的に代替して結果を返しました。
5. 考察:内部で何が起きたか
LLM が Planning フェーズでユーザープロンプトと object_list から渡されたビュー定義(列一覧)を照合し、wait_time が存在しないことを SQL 実行前に把握。最初から ELAPSED_PER_EXEC_SEC を使った SQL を生成します。
Agent アーキテクチャでは Planning → Execute というループを持つため、このような流れになっていると推察できます。
「曖昧さ」の検証範囲について
今回検証したのは 「列名スキーマミスマッチ」型の曖昧さです(v_top_sqlarea の列定義と齟齬のある列名を投げた)。「ヤバいSQL教えて」のような自然言語の曖昧プロンプトへの対応は今回の検証スコープ外です。
6. まとめ
| # | 検証項目 | 結論 |
|---|---|---|
| 1 | 存在しない列 wait_time を指定したとき Agent はエラーを出さずに自律補完するか |
✅ ELAPSED_PER_EXEC_SEC へ自律代替。ORA-00904 は表面化しなかった |
| 2 | 内部動作の考察 | Planning フェーズでのスキーマ照合(パターン A)が有力だが出力から区別不能 |
NL2SQL Data Retrieval Agent は「列名がスキーマ定義と合っていなくても、意味的に近い列を自律的に選んで結果を返す」という能力を確認できました。
ちなみに、実際には NARRATE でもこのくらいなら同じような補完は実施してくれてました・・