1. はじめに
Oracle ADB の SELECT AI Agent には、conversation_id を使ったマルチターン対話の機能があります。同じ conversation_id で複数回 RUN_TEAM を呼ぶと、Agent が前のターンの発言をコンテキストとして保持し、「さっき教えてくれた1番目のSQL」「その実行計画を見せて」のような自然な引き継ぎができるようになります。
前記事(Oracle ADB の SELECT AI Agent で自律補完を試す)では Agent の自律補完を確認しました。今回は「前のターンの結果をどこまで自然に引き継げるか」を実機で確認します。
検証ゴール
| # | ゴール |
|---|---|
| G1 | 前ターンで返った SQL_ID・数値を「1番目の」「さっきの」という表現だけで参照できるか |
| G2 | 実行計画がホットな状態(V$SQL_PLAN に存在)で Agent が計画を返せるか |
| G3 | カーソルがエイジアウトした場合に「取得不可+AWR 推奨」と誠実に報告するか |
結論先出し
- ✅ G1: 「さっき教えてくれた1番目のSQL」と聞くだけで、SQL_ID も数値も前ターンの会話から正確に引き継げた
- ✅ G2: V$SQL_PLAN にカーソルが存在する SQL については、実行計画を正しく返せた
- ✅ G3: エイジアウト済みの場合は V$SQL_PLAN を確認したうえで「取得不可・AWR を参照」と誠実に報告した
- ⚠️ レスポンスタイム: 1ターンにつき10秒以上かかる。対話的な DBA 補助ツールとしての用途向きで、即時クエリ向きではない
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) |
| Agent チーム |
NL2SQL_DATA_RETRIEVAL_TEAM(※前回の検証で作成したチームをそのまま流用しています) |
| object_list |
V_$SQL_PLAN, V_$SQL_AREA, V_$SQL, V_$SYSTEM_EVENT
|
3. セットアップ
Agent チームの作成手順は前記事を参照してください。ここでは今回の検証に必要な プロファイルの設定 と conversation_id の取得方法 を補足します。
3.1. プロファイルに実行計画ビューを設定する
実行計画を取得するには、プロファイルの object_list に V_$SQL_PLAN 等の動的パフォーマンスビューを追加しておく必要があります。まず、実行ユーザー(dba_copilot)に対象ビューへの SELECT 権限を付与します。
GRANT SELECT ON SYS.V_$SQL_PLAN TO dba_copilot;
GRANT SELECT ON SYS.V_$SQL_AREA TO dba_copilot;
GRANT SELECT ON SYS.V_$SQL TO dba_copilot;
GRANT SELECT ON SYS.V_$SYSTEM_EVENT TO dba_copilot;
次に、プロファイルを再作成して object_list に追加します。
BEGIN
DBMS_CLOUD_AI.DROP_PROFILE(profile_name => 'GEMINI_PROFILE');
DBMS_CLOUD_AI.CREATE_PROFILE(
profile_name => 'GEMINI_PROFILE',
attributes => '{"provider": "google",
"credential_name": "GEMINI_CRED",
"model": "gemini-3.1-flash-lite-preview",
"max_tokens": 4096,
"object_list": [
{"owner": "SYS", "name": "V_$SQL_PLAN"},
{"owner": "SYS", "name": "V_$SQL_AREA"},
{"owner": "SYS", "name": "V_$SQL"},
{"owner": "SYS", "name": "V_$SYSTEM_EVENT"}
]}'
);
END;
/
3.2. conversation_id を取得する
マルチターン対話を有効にするには、DBMS_CLOUD_AI.CREATE_CONVERSATION() で会話を正式に作成し、返ってきた UUID を使います。
SELECT DBMS_CLOUD_AI.CREATE_CONVERSATION(
attributes => '{"conversation_length":10, "retention_days":7}'
) FROM DUAL;
-- 結果例
-- 5171D145-xxxx-xxxx-xxxx-F615000AF0EE
| 属性 | 説明 |
|---|---|
conversation_length |
LLM に渡す過去ターン数(今回は 10 を指定) |
retention_days |
会話履歴の保持日数 |
coversation_id は USER_CLOUD_AI_CONVERSATIONS ビューで確認できます。
また、プロンプト・レスポンスは USER_CLOUD_AI_CONVERSATION_PROMPTS ビューで確認できます。
4. 検証
今回の検証は 4 ターンで構成します。全ターンで同じ conversation_id を使い、会話の文脈がどこまで引き継がれるかを確認します。
T1: 「いま重いSQL上位3件教えて」
↓ 同一 conversation_id で継続
T2: 「さっき教えてくれた1番目のSQLの経過時間は何マイクロ秒だった?」 ← G1
↓ 同一 conversation_id で継続
T3: 「さっき教えてくれた1番目のSQLの実行計画取れる?」 ← G2
↓ 同一 conversation_id で継続
T4: 「さっき教えてくれた2番目のSQLの実行計画取って、良くするためのアドバイスも出力して」 ← G3
4.1. ターン 1 — 「いま重いSQL上位3件教えて」
SELECT DBMS_CLOUD_AI_AGENT.RUN_TEAM(
team_name => 'NL2SQL_DATA_RETRIEVAL_TEAM',
user_prompt => 'いま重いSQL上位3件教えて',
params => '{"conversation_id":"5171D145-xxxx-xxxx-xxxx-F615000AF0EE"}'
) FROM DUAL;
Agent の応答(実測値):
データベース内で現在負荷が高い(経過時間が長い)SQLの上位3件は以下の通りです。
* SQL_ID: 0rngmdjpt3dct
経過時間: 302,352,761 マイクロ秒★←G1の数値
内容: 負荷テスト用のPL/SQLブロック。フルテーブルスキャンを伴う集計処理や、
非効率なソート処理、前方一致検索をループ内で繰り返し実行しており、
システムリソースを大きく消費しています。
* SQL_ID: 8vyk5xwcpjmyu
経過時間: 244,430,883 マイクロ秒
内容: APP_TEST_USERS テーブルに対する前方一致検索(LIKE 'USER_9999%')
およびソート処理。インデックスが適切に利用されていない可能性があります。
* SQL_ID: cbcbzyt28c25m
経過時間: 197,383,048 マイクロ秒
内容: DBMS_SCHEDULER.RUN_JOB を使用したジョブの実行。
Sources
* ORACLE AI DATABASE
以降、そのまま会話を続けます。
4.2. ターン 2 — 「1番目の経過時間は?」 ← G1
SQL_ID を一切書かずに、前ターンの数値を問い返します。
SELECT DBMS_CLOUD_AI_AGENT.RUN_TEAM(
team_name => 'NL2SQL_DATA_RETRIEVAL_TEAM',
user_prompt => 'さっき教えてくれた1番目のSQLの経過時間は何マイクロ秒だった?',
params => '{"conversation_id":"5171D145-xxxx-xxxx-xxxx-F615000AF0EE"}'
) FROM DUAL;
Agent の応答(実測値):
先ほど提示した1番目のSQL(SQL_ID: 0rngmdjpt3dct)の経過時間は、
302,352,761 マイクロ秒です。
Sources
* ORACLE AI DATABASE
プロンプトに SQL_ID も数値も書いていないのに、Agent は T1 の会話履歴から 0rngmdjpt3dct と 302,352,761 μs を正確に返しました。
4.3. ターン 3 — 「1番目の実行計画取れる?」 ← G2
SELECT DBMS_CLOUD_AI_AGENT.RUN_TEAM(
team_name => 'NL2SQL_DATA_RETRIEVAL_TEAM',
user_prompt => 'さっき教えてくれた1番目のSQLの実行計画取れる?',
params => '{"conversation_id":"5171D145-xxxx-xxxx-xxxx-F615000AF0EE"}'
) FROM DUAL;
Agent の応答(実測値):
申し訳ございませんが、指定されたSQL_ID(0rngmdjpt3dct)の実行計画は、
現在データベースのライブラリキャッシュ(V_$SQL_PLAN)上に存在しないため、
取得することができませんでした。
当該SQLは既に実行が完了し、メモリ上からフラッシュされたか、あるいは統計情報が
保持されていない可能性があります。実行計画を確認するためには、再度対象のSQLを
実行した直後に取得するか、AWR(Automatic Workload Repository)などの
履歴情報から取得する必要があります。
Sources
* ORACLE AI DATABASE
Agent は 0rngmdjpt3dct を T1 の文脈から正しく特定し、V$SQL_PLAN にカーソルが存在しないことを確認したうえで AWR の利用を推奨しました。
では、2番目の SQL はどうでしょうか。こちらは実行後それほど時間が経っておらず、カーソルが V$SQL_PLAN に残っている可能性があります。
4.4. ターン 4 — 「2番目の実行計画取れる?」 ← G3
実行計画の取得と、改善案も出してもらう形で依頼してみました。
SELECT DBMS_CLOUD_AI_AGENT.RUN_TEAM(
team_name => 'NL2SQL_DATA_RETRIEVAL_TEAM',
user_prompt => 'さっき教えてくれた2番目のSQLの実行計画取って、良くするためのアドバイスも出力して',
params => '{"conversation_id":"5171D145-xxxx-xxxx-xxxx-F615000AF0EE"}'
) FROM DUAL;
Agent の応答(実測値):
2番目のSQL(SQL_ID: 8vyk5xwcpjmyu)の実行計画および改善アドバイスは以下の通りです。
### 実行計画の分析
* 実行計画:
* SELECT STATEMENT: コスト 168,942
* TABLE ACCESS (BY INDEX ROWID): 対象テーブル APP_TEST_USERS、コスト 168,942
* INDEX (FULL SCAN): 対象インデックス PK_APP_TEST_USERS、コスト 2,229
* 分析結果:
現在、インデックスに対して「INDEX FULL SCAN」が発生しており、その後、膨大な行数
(推定999,999行)に対して「TABLE ACCESS BY INDEX ROWID」が実行されています。
インデックスを全件走査した後にテーブル本体へアクセスする非常に非効率な状態です。
### パフォーマンス改善アドバイス
1. インデックスの最適化:
現在の PK_APP_TEST_USERS は主キーである可能性が高く、前方一致検索
(LIKE 'USER_9999%')には適していない場合があります。検索条件に使用している
カラムに対して適切な B-Tree インデックスが作成されているか確認してください。
2. 統計情報の更新:
DBMS_STATS.GATHER_TABLE_STATS を実行し、統計情報を最新の状態に更新してください。
3. 検索条件の見直し:
LIKE による前方一致検索でインデックスが効いていれば INDEX RANGE SCAN になるはずです。
暗黙の型変換が発生していないか確認してください。
4. カバリングインデックスの検討:
必要なカラムをすべて含んだカバリングインデックスを作成することで、テーブルへの
アクセスを回避できる可能性があります。
Sources
* ORACLE AI DATABASE
8vyk5xwcpjmyu(LIKE 'USER_9999%' の前方一致検索)は V$SQL_PLAN にカーソルが残っており、実行計画を正しく返しました。また、インデックス最適化・統計情報更新・カバリングインデックスの検討まで含む改善アドバイスも出してくれました。
4.5. 評価
| ゴール | ターン | プロンプト(抜粋) | 結果 |
|---|---|---|---|
| G1: 前ターンの引き継ぎ | T2 | 「さっき教えてくれた1番目の…経過時間は?」 | ✅ 302,352,761 μs を会話履歴から正確に返した |
| G2: エイジアウト時の誠実な報告 | T3 | 「1番目の実行計画取れる?」 | ✅ V$SQL_PLAN になし → AWR 推奨と報告 |
| G3: ホット状態での実行計画取得 | T4 | 「2番目の実行計画取れる?」 | ✅ INDEX FULL SCAN を含む実行計画を返した |
5. 考察
5.1. conversation_id によるコンテキスト保持の仕組み
conversation_id を指定すると、ADB 内部の会話テーブルに各ターンのプロンプトと応答が蓄積されます。次の RUN_TEAM 呼び出し時に、同じ conversation_id の過去ターン(conversation_length で指定した件数分)が LLM へのコンテキストとして渡されます。
CREATE_CONVERSATION 時に "conversation_length":10 を指定すると、直近 10 ターン分の会話が次のプロンプトに含まれます。
5.2. 実行計画の取得可否と Agent の振る舞い
同じ会話の中で、1番目(エイジアウト済み)と2番目(ホット)の実行計画を聞いた結果が対照的でした。
| SQL | V$SQL_PLAN の状態 | Agent の応答 |
|---|---|---|
0rngmdjpt3dct(1番目) |
エイジアウト済み | 取得不可を報告し AWR を推奨 |
8vyk5xwcpjmyu(2番目) |
カーソル存在 | 実行計画を返した |
「答えられないときに正直に答えられない理由を伝え、代替手段を示す」という挙動は、DBA 補助ツールとして信頼性の高い振る舞いといえます。
さらなる活用の余地: AWR ビューも object_list に追加する
今回は V$ ビューのみを Agent に連携しましたが、AWR のビュー(DBA_HIST_SQL_PLAN や DBA_HIST_SQLSTAT など)も object_list に追加しておけば、エイジアウト後の実行計画も Agent が過去の履歴から自律的に取得してくれる可能性があります。「取得不可 → AWR を参照してください」で終わらず、Agent 自身が AWR まで掘り下げてくれる構成にできそうです。
5.3. レスポンスタイムについて
1回の RUN_TEAM 呼び出しに体感で10秒以上かかります(環境・モデル・クエリ内容により変動します)。これは Agent アーキテクチャの特性によるものです。1回の呼び出しの中で、プランニング(LLM への API 呼び出し)→ ツール実行(DB クエリ)→ 応答生成(LLM への API 呼び出し)が複数回走るため、即時クエリ向けではなく、対話的な DBA 補助ツールとしての用途に適していると考えています。
6. まとめ
| # | 検証項目 | 結論 |
|---|---|---|
| G1 | 前ターンの SQL_ID・数値を「さっきの」「1番目の」で参照できるか | ✅ 同じ conversation_id を使い続けることで、SQL_ID を再入力せずに「さっきの」「1番目の」という自然な表現で前ターンの結果を引き継げる |
| G2 | ホット状態で実行計画を取得できるか | ✅ V$SQL_PLAN にカーソルが存在する場合、実行計画行を正しく返す |
| G3 | エイジアウト時に誠実に報告するか | ✅ ライブラリキャッシュを確認したうえで「取得不可・AWR を参照」と報告する |
SELECT AI Agent の conversation_id を使うと、「さっき教えてくれたSQL」「その実行計画は取れる?」のように会話を積み重ねながらデータベースを掘り下げていける DBA 補助スタイルの対話が実現できます。ただし 1 ターンあたり10秒以上かかるため、インタラクティブな分析用途での活用が適していると考えています。