0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Select AI Agent が DB の中でどう動いているか V$ ビューと SQL トレースで確かめてみた

0
Last updated at Posted at 2026-09-10

この記事は、私が Claude(Claude Code)と一緒に調査した内容です。V$ ビュー・SQL トレース・パッケージ仕様部から読み取った実測に加えて、そこからの推定(「〜と考えられます」と書いた箇所)を含みます。公式ドキュメントに無い挙動は今後変わる可能性があります。

1. はじめに

前回の記事1で、Oracle Autonomous AI Database の Select AI Agent に Supervisor を置いて、2 つのワーカーエージェントへ質問を振り分けさせてみました。前回確かめていなかったのが、「この Supervisor やワーカーの実体は何なのか」という点です。RUN_TEAM を呼ぶと応答が返るまで 15 秒から 60 秒ほどかかります(前回の記事の実測)。その間にどこで何が動いているのかは、公式ドキュメントに書かれていません。

公式の「About Select AI Agent」2は、ReAct(Reasoning and Acting)のループを 4 段階で説明し、Planning / Tool Use / Reflection の 3 層と、短期・長期のメモリがあると書いています。ただし、それがプロセスなのかセッションなのか、状態はどの表に入るのか、ツールはどのユーザーの権限で動くのかは出てきません。

The agent thinks, chooses a tool, observes results, and repeats until it can present a confident answer. ... The database processes those actions and returns the observations.

「The database processes those actions」の中身を、DBA が普段使う道具で確かめます。使うのは V$SESSIONV$ACTIVE_SESSION_HISTORY(以下 ASH)、DBMS_USERDIAG の SQL トレース、V$SQL、それからパッケージ仕様部のソースです。

1.1. 結論(先出し)

実行の単位について分かったことです。

  • RUN_TEAM は呼び出し元セッションの中で完結する PL/SQL である。実行中に増えるセッションもスケジューラジョブも無く、Supervisor もワーカーも「プロセス」ではない
  • LLM 呼び出しは UTL_HTTP で、待機イベントは TCP Socket (KGAS) に出る。Supervisor 経由の 1 質問で HTTP の待機区間(連続する待機行のまとまり)は 5 つ、合計 8.6 秒だった。内訳は Supervisor 2 回、ワーカー 2 回、ツール内部の自然言語から SQL を生成する処理(以下 NL2SQL)1 回である
  • ツールに登録した PL/SQL 関数は、呼び出し元ユーザーの権限で動的 PL/SQL として実行される。パッケージ仕様部は AUTHID CURRENT_USER(実行者権限)
  • WAITING_FOR_HUMAN の間、待っているセッションは無い。状態行だけが残る

状態の置き場所について分かったことです。

  • 定義・履歴・会話の実体は C##CLOUD$SERVICE スキーマの $ 付き表 15 本で、USER_AI_AGENT_* ビューはその表を呼び出しユーザーの行に絞って見せるビューである。ビュー定義文は読めない
  • チームごとにスケジューラの PROGRAM と JOB が作られるが、すべて DISABLED で実行ログも無く、トレースにもスケジューラ関連の SQL は出ない
  • チーム属性 long_term_memory_length は 0 を受け付けない。30 と 1 で組み立て後のプロンプトはバイト一致で、公式が書く「会話をまたぐタスク要約」は今回の条件(同じチームを 10 回以上実行した後の新しい会話の 1 ターン目)では観測されなかった
  • 履歴ビューの列名は公式ドキュメントと実機で一致しない(TEAM_INSTANCE_ID の有無、COVERSATION_PARAMCONVERSATION_PARAMS)。この記事は実機の列名で書いている

1.2. 検証ゴール

# 確かめること 確認できれば OK の条件
1 RUN_TEAM(Supervisor からワーカーへの委譲を含む)は呼び出し元セッションの中で動くのか、別セッションやジョブに渡すのか 別セッションから 1 秒間隔で取った V$SESSION と実行後の ASH で、LLM 待機とツール実行の SQL が呼び出し元セッションにだけ記録される。実行中に他のセッション・ジョブが増えない。WAITING_FOR_HUMAN の間も同じ
2 1 質問の中で LLM 呼び出し・ツール実行・履歴表への書き込みがどの順序と回数で走るか SQL トレース 1 質問分を時系列に並べられる。HTTP の待機区間の数が、会話ビューに残る応答の数と突き合わせられる
3 定義・履歴・会話の実体はどの表で、組み立て後の LLM プロンプトに何が入るか 表の所有者と表名が特定できる。プロンプト全文を取り出して長期メモリ設定の影響を比べられる

2. 検証環境

項目
データベース Oracle Autonomous AI Database Serverless(以下 ADB-S。26ai、Oracle AI Database 26ai EE 23.26.3.2.0)、2 ECPU、ap-tokyo-1
クライアント SQLcl 26.1.0、Windows 11 Pro
接続ユーザー DBA_COPILOT(エージェント・チームの所有者)。権限付与とスケジューラ全体の確認だけ ADMIN
AI プロファイル GEMINI_PROFILE(provider = google、model = gemini-3.5-flash-lite
チーム OPS_SUPERVISOR_TEAM。Supervisor OPS_SUPERVISOR と、ワーカー NL2SQL_DATA_RETRIEVAL_AGENT(Top SQL 解析)・STATS_AGENT(統計が古くないかの確認)の 2 つ。構成は前回の記事1のまま
質問 「いま一番重いSQLはどれ?」(Supervisor が Top SQL ワーカーへ 1 回委譲する質問)と「統計が古くないか確認して」(表名が無いので WAITING_FOR_HUMAN になる質問)

DBA_COPILOT には事前に ADMIN から次を付与しました。V$ 系はロール経由ではなく直接付与にしています。定義者権限のサブプログラムの中では、呼び出し元のロールは無効になり PUBLIC だけが有効になるためです3

grant execute on dbms_userdiag to dba_copilot;
grant select on sys.v_$session to dba_copilot;
grant select on sys.v_$active_session_history to dba_copilot;
grant select on sys.v_$sql to dba_copilot;
grant select on sys.dba_scheduler_running_jobs to dba_copilot;
grant select on sys.dba_scheduler_jobs to dba_copilot;
grant read on c##cloud$service.session_cloud_trace to dba_copilot;

最後の SESSION_CLOUD_TRACE への READ は、付与しないと ORA-41900 になります。5 月のトレース記事4ADMIN で実行していたため、この付与は不要でした。


3. 公式ドキュメントとパッケージ仕様部から分かること

3.1. 公式が書いていること

About Select AI Agent2の説明を表にまとめます。

観点 公式の記述
実行ループ ReAct。Query → Thought and Action → Observation → Final Response を繰り返す
構造 Planning(計画)、Tool Use(ツール実行)、Reflection(結果の評価)の 3 層
短期メモリ 「recent messages and intermediate results per agent team」を保持
長期メモリ 「preferences, history, and strategies」を記録し「across sessions」で使われる
思考の確認 USER_CLOUD_AI_CONVERSATION_PROMPTS で読める

DBMS_CLOUD_AI_AGENT パッケージのページ5には、CREATE_AGENT の属性 short_term_memory_length(デフォルト 30、「maximum number of conversation turns included in each LLM call for a task run」)と、CREATE_TEAM の属性 long_term_memory_length(デフォルト 30、「maximum number of prior task-level summaries included in the system prompt as memory context at the start of each new run」)があります。RUN_TEAM が同期か非同期か、バックグラウンドジョブを使うか、ツールがどのユーザーの権限で動くかは、このページにも About にも書かれていません。

3.2. パッケージ仕様部に書いてあること

C##CLOUD$SERVICE.DBMS_CLOUD_AI_AGENT は、仕様部(PACKAGE)だけなら ALL_SOURCE で読めます。本体(PACKAGE BODY)は出てきません。

select text from all_source
 where owner = 'C##CLOUD$SERVICE' and name = 'DBMS_CLOUD_AI_AGENT' and type = 'PACKAGE'
 order by line;

非空行 1,498 行で、先頭は次の 3 行です。

PACKAGE dbms_cloud_ai_agent
AUTHID CURRENT_USER
AS

AUTHID CURRENT_USER は実行者権限です3。パッケージの中で動く SQL やツール呼び出しは、呼び出したユーザー(今回は DBA_COPILOT)の権限で解決されます。後の 5.4 章で、トレース上もそうなっていることを確かめます。

仕様部のコメントには、公式ドキュメントに無い説明がいくつかあります。

  • human_tooldelegate_tool は「never actually executed directly」で、内部の agent_task_execute がその呼び出しを受け取って、WAITING_FOR_HUMAN への遷移や委譲先タスクの起動を行う。前回の記事で「委譲用のツールは Oracle 側が内部で持つ」と書いた部分の実体である
  • RUN_TEAM のコメントには「orchestrates the creation of a scheduler chain, chain job, and program to execute the agent tasks in sequence」とあり、agent_task_execute_chain_job など Oracle Scheduler から呼ばれる前提のプロシージャが 3 本宣言されている。4.4 章でこのとおり動いているかを見る
  • show_agent_prompt(team_name, user_prompt) は「fetches the latest persisted conversation prompts, and composes the full LLM prompt」とあり、組み立て後の LLM プロンプトを返す。6.2 章で使う

このうち show_agent_promptset_attribute は、2026 年 9 月時点の serverless の Package ページ5にも、Oracle Database 19c の PL/SQL Packages リファレンス6にも載っていません。仕様部にだけ存在する API なので、記事では「公式未掲載」として扱い、今後変わる可能性があるものとして読んでください。


4. 実行の単位を V$ で見る

4.1. 2 セッション体制

セッション A で RUN_TEAM を実行し、別のセッション B が 1 秒間隔で V$SESSIONDBA_SCHEDULER_RUNNING_JOBS を作業表へ記録します。A には CLIENT_IDENTIFIER を付けて、後から SID を追えるようにしました。

-- セッション A
exec dbms_session.set_identifier('RTI_A');
var conv varchar2(100)
exec :conv := dbms_cloud_ai.create_conversation(attributes => '{"title":"rti-phase1"}');
select dbms_cloud_ai_agent.run_team(
         team_name   => 'OPS_SUPERVISOR_TEAM',
         user_prompt => 'いま一番重いSQLはどれ?',
         params      => '{"conversation_id":"' || :conv || '"}') as answer
  from dual;

作業表は次の 3 つです。PROGRAM 列は V$SESSION と同じ VARCHAR2(48) で作ると 60 バイトの値が入って ORA-12899 になったので、128 にしています。

create table rti_session_samples (
  sample_ts   timestamp default systimestamp,
  sid number, serial# number, username varchar2(128), status varchar2(8), state varchar2(19),
  event varchar2(64), wait_class varchar2(64), seconds_in_wait number,
  sql_id varchar2(13), prev_sql_id varchar2(13), plsql_entry_object_id number, plsql_entry_subprogram_id number,
  plsql_object_id number, module varchar2(64), action varchar2(64), client_identifier varchar2(64),
  type varchar2(10), program varchar2(128)
);
create table rti_job_samples (
  sample_ts timestamp default systimestamp, owner varchar2(128), job_name varchar2(128), session_id number
);
create table rti_markers (marker varchar2(64), at_ts timestamp default systimestamp, note varchar2(4000));
-- セッション B(180 秒ループ)
for i in 1 .. 180 loop
  insert into rti_session_samples (...)
  select sid, serial#, username, status, state, event, wait_class, seconds_in_wait,
         sql_id, plsql_entry_object_id, plsql_object_id, module, action, client_identifier, type, program
    from v$session
   where (username = 'DBA_COPILOT' or (status = 'ACTIVE' and type = 'USER'))
     and sid <> sys_context('userenv','sid');
  insert into rti_job_samples select owner, job_name, session_id from dba_scheduler_running_jobs;
  commit;
  dbms_session.sleep(1);
end loop;

4.2. RUN_TEAM 中の呼び出し元セッション

RUN_TEAM は 14.9 秒で返りました。その間のセッション A(SID 46283)のサンプル 15 点を、同じ値が続く区間でまとめます。

時刻 event wait_class PL/SQL の入口 実行中の PL/SQL
17:54:30 Disk file operations I/O User I/O C##CLOUD$SERVICE.DBMS_CLOUD_AI_AGENT (無し)
17:54:31 〜 36 TCP Socket (KGAS) Network C##CLOUD$SERVICE.DBMS_CLOUD_AI_AGENT SYS.UTL_HTTP
17:54:37 Allocate UGA memory from OS Other 同上 (無し)
17:54:38 TCP Socket (KGAS) Network 同上 SYS.UTL_HTTP
17:54:39 〜 40 Allocate PGA memory from OS Other 同上 (無し)
17:54:41 〜 44 TCP Socket (KGAS) Network 同上 SYS.UTL_HTTP

PLSQL_ENTRY_OBJECT_ID は一貫して DBMS_CLOUD_AI_AGENT(object_id 111235)で、15 点中 11 点で PLSQL_OBJECT_IDUTL_HTTP、待機が TCP Socket (KGAS) でした。この待機イベントは Database Reference7で「A session is waiting for an external host to provide requested data over a network socket」と説明されていて、UTL_HTTP などが外部ホストの応答を待つときに出ます。V$SESSION にもトレースにも接続先の URL は出ないので、相手が LLM であることの直接の証拠はありません。ただ、この構成で UTL_HTTP を使う外部呼び出しは AI プロファイル経由の LLM 呼び出しだけなので、呼び出し元のセッション自身が LLM の応答を待っていると考えられます。なお 1 秒サンプルは 1 秒未満の CPU 区間を拾わないので、待機の比率は 5.3 章のトレース集計(19.0 秒中 8.6 秒)より高く見えます。

同じ 15 秒間の DBA_COPILOT のセッション数は常に 1 本(A 自身)で、DBA_SCHEDULER_RUNNING_JOBS のサンプルは 0 行でした。ACTIVE な他ユーザーのセッションも見えていません。

実行後に ASH を A の実行時間帯で絞ると 14 行あり、SESSION_ID はすべて 46283、内訳は TCP Socket (KGAS) が 11 行、ON CPU が 3 行でした。ASH の SAMPLE_TIME は UTC で入っていて、SYSTIMESTAMP で取ったマーカー(セッションのタイムゾーンは Asia/Tokyo)のまま絞ると 0 行になります。マーカー側を SYS_EXTRACT_UTC で UTC に揃えて絞り直しました。

4.3. WAITING_FOR_HUMAN の間に何が待っているか

表名を省いた質問「統計が古くないか確認して」を実行すると、RUN_TEAM は 2.6 秒で返り、GET_TEAM_STATEWAITING_FOR_HUMAN になりました。応答は「統計情報の古さを確認する対象のテーブル名(スキーマ名.テーブル名、またはテーブル名)を教えていただけますか?」です。

その後 30 秒間のサンプルで、DBA_COPILOT のセッションは A 自身の 1 本だけでした。A の待機は PL/SQL lock timer(自分の dbms_session.sleep)で、SECONDS_IN_WAIT が 1 から 29 まで 1 秒ごとに増えています。実行中ジョブは 0 件です。

同じ conversation_idSCOTT.LOAD_TEST_NOIDX を渡すと SUCCEEDED に遷移し、統計の最終解析日時と STALE_STATS 列の値が返りました。待っているプロセスは無く、履歴表の状態行だけが WAITING_FOR_HUMAN を保持しています。次の RUN_TEAM がそれを読んで再開します。

4.4. スケジューラの定義は作られるが動かない

3.2 章の仕様部コメントには「スケジューラのチェーン・ジョブ・プログラムを作って実行する」と書かれています。4.2 章の実測では実行中ジョブが 0 件だったので、定義とログの両方を見ました。

DBA_COPILOT スキーマには、チームごとに PROGRAM と JOB が作られていました。

オブジェクト 名前 内容
PROGRAM OPS_SUPERVISOR_TEAM_SUPERVISED_PROGRAM STORED_PROCEDURE、action は dbms_cloud_ai_agent.agent_task_execute_supervised_job
PROGRAM OPS_SUPERVISOR_TEAM_SEQUENTIAL_PROGRAM ほか 3 本 action は dbms_cloud_ai_agent.agent_task_execute_sequential_job
JOB OPS_SUPERVISOR_TEAM_TASK_0 / _TASK_1 / _TASK_2 ほか 3 本 すべて state = DISABLED
CHAIN (無し) USER_SCHEDULER_CHAINS は 0 行

PROGRAM の作成日は、以前の記事8で作った別チームのものが 5 月 4 日、今回のチームのものが 9 月 9 日でした。チーム作成時か初回実行時かは、今回の記録では区別できていません。一方、USER_SCHEDULER_JOB_LOGUSER_SCHEDULER_JOB_RUN_DETAILS に検証当日の AI 系ジョブの実行は 0 件、ADMIN で見た DBA_SCHEDULER_JOB_RUN_DETAILS(全所有者、検証当日 102 行)にも 0 件でした。検証当日は RUN_TEAM を 10 回以上実行しています。

ジョブを DBMS_SCHEDULER.RUN_JOBuse_current_session => TRUE で呼び出し元セッションの中で同期実行している、という可能性も考えました。その場合も 4.2 章の観測(別セッションが増えない)とは矛盾しません。ただし 5 章の SQL トレース 30 万行の中に、DBMS_SCHEDULER やスケジューラの内部表(SCHEDULER$ 系)に触れる SQL は 1 行も出ていません。V$SQL にもありません。観測できた事実はここまでで、解釈は 7.3 章に書きます。


5. 1 質問の中身を SQL トレースで見る

5.1. DBMS_USERDIAG で取る

ADB では ALTER SESSION SET EVENTS が使えないので、5 月の記事4と同じく DBMS_USERDIAG9 で SQL トレースを取り、SESSION_CLOUD_TRACE ビューから読みます。待機イベント(waits => 1)とバインド(binds => 1)を有効にし、前後に DBMS_USERDIAG.TRACE でマーカーを入れました。

exec dbms_userdiag.set_tracefile_identifier('rti_phase3');
exec dbms_userdiag.enable_sql_trace_event(waits => 1, binds => 1, plan_stat => 'ALL_EXECUTIONS');
exec dbms_userdiag.trace('=== RTI RUN_TEAM START ===');
select dbms_cloud_ai_agent.run_team(team_name => 'OPS_SUPERVISOR_TEAM',
         user_prompt => 'いま一番重いSQLはどれ?',
         params => '{"conversation_id":"' || :conv || '"}') from dual;
exec dbms_userdiag.trace('=== RTI RUN_TEAM END ===');
exec dbms_userdiag.enable_sql_trace_event(disable => 1);

ここでつまずいた点が 2 つあります。

1 つ目は SESSION_CLOUD_TRACE の見える範囲です。Application Tracing のページ10に「available in the SESSION_CLOUD_TRACE view in the session where the tracing was enabled」「After you log out or close the session, the data is no longer available」とあるとおり、トレースを取ったセッションでしか読めません。SQLcl をスクリプトごとに起動し直していたため、最初の抽出は 0 行でした。取得と抽出を 1 つのスクリプトにまとめています。

2 つ目は抽出の速さです。「PARSING IN CURSOR 行の次の行(SQL 文)を取る」ための自己結合が、SESSION_CLOUD_TRACE に対しては 31 万行で終わりませんでした。同じセッションの中で実表にコピーして索引を付けると 1 秒で終わります。

create table rti_trace_copy as select row_number, trace from session_cloud_trace;
create index rti_trace_copy_ix on rti_trace_copy(row_number);

コピーに絞り込みを付けていないのは、新しいセッションで SESSION_CLOUD_TRACE が 0 行であることを確認してから始めたためです。同じセッションでトレースを何度も取る場合は、公式10にあるとおり前の分も累積して残るので、マーカーで範囲を切る必要があります。

5.2. 時系列

RUN_TEAM は 19.0 秒で返り、トレースは 308,339 行、うち PARSING IN CURSOR は 199 件でした。4.2 章の 14.9 秒より長いのは、別の実行であることに加え、待機とバインドを含む SQL トレースを有効にしているためだと考えられます。

トレースの PARSING IN CURSOR 行に付く uid は、その SQL を解析したユーザーの ID です。マーカーの間から、履歴・会話の表に対する DML、ツール呼び出しの動的 PL/SQL、TCP Socket (KGAS) の待機区間を拾って並べると次の流れになります。行番号はトレースの ROW_NUMBER です。

何が起きたか SQL(先頭)
4451 呼び出し元(uid = DBA_COPILOT)が RUN_TEAM を実行 select dbms_cloud_ai_agent.run_team(...)
16309 チーム実行の履歴行を作成 INSERT INTO dbms_ai_agent_team_hist$ (TEAM_EXEC_ID, TEAM_ID#, START_TS, STATE, CONVERSATION_ID, PARAMS)
21781 Supervisor のタスク履歴行を作成 INSERT INTO dbms_ai_agent_task_hist$ (team_exec_id, team_id#, task_order#, agent_id#, task_id#, params, input)
29434 状態を更新 UPDATE dbms_ai_agent_task_hist$ SET state = :state, start_ts = SYSTIMESTAMP ...
〜82642 TCP Socket (KGAS) 20 回・1.93 秒(Supervisor の LLM 呼び出し)
82642 / 83942 プロンプトと応答を会話表へ INSERT INTO dbms_cloud_ai_conversation_prompt$ (... prompt ...) / (... rsp ...)
93195 ワーカー用の会話を新規作成(委譲) INSERT INTO dbms_cloud_ai_conversation$ (title, description, user#, ...)
97022 ワーカーのタスク履歴行を作成 INSERT INTO dbms_ai_agent_task_hist$ (...)
〜211924 TCP Socket (KGAS) 18 回・1.56 秒(ワーカーの LLM 呼び出し)
224978 ツールを動的 PL/SQL で実行(uid = DBA_COPILOT) BEGIN :result := "DBA_COPILOT"."NL2SQL_DATA_RETRIEVAL_FUNCTIONS"."RUNSQL_FUNC"(USER_PROMPT => :USER_PROMPT); END;
〜265469 TCP Socket (KGAS) 18 回・1.30 秒(ツール内部の NL2SQL 生成)
265469 ツール履歴行を作成 INSERT INTO C##CLOUD$SERVICE.dbms_ai_agent_tool_hist$ (team_exec_id, task_order#, tool_id#, agent_id#, task_id# ...)
〜275181 TCP Socket (KGAS) 18 回・1.75 秒(ワーカーの LLM 呼び出し)
275181 / 277718 ワーカーの結果と状態を更新 UPDATE dbms_ai_agent_task_hist$ SET result = :result ... / SET state = :state, end_ts = ...
〜297786 TCP Socket (KGAS) 21 回・2.07 秒(Supervisor の LLM 呼び出し)
297786 Supervisor の結果を更新 UPDATE dbms_ai_agent_task_hist$ SET result = :result ...
301591 / 306819 チーム実行の状態と終了時刻を更新 UPDATE C##CLOUD$SERVICE.dbms_ai_agent_team_hist$ SET STATE = :state, END_TS = CURRENT_TIMESTAMP
308103 会話表へ最終の問答を記録 INSERT INTO dbms_cloud_ai_conversation_prompt$ (... prompt, rsp ...)

ツール呼び出しの行(224978)の直前には、DBA_COPILOT の uid で ALL_PROCEDURESALL_ARGUMENTS を参照する SQL が並んでいます。ツール属性の function に書かれた関数名からシグネチャを調べ、引数名を :USER_PROMPT のようにバインドして動的 PL/SQL を組み立てている様子が見えます。

流れを図にします。すべて 1 本のセッションの中で順に起きています。番号は 5.2 章の表の順で、矢印の数字がどの手順でその先に触れるかを示します。

Select AI Agent の RUN_TEAM が呼び出し元セッションの中で行う処理と、状態の置き場所

5.3. LLM の回数と待機時間

トレース側の TCP Socket (KGAS) は 95 回、合計 8.61 秒でした。連続する WAIT 行を行番号の間隔で区切ると、待機区間は 5 つでした。区切りの閾値を 50 行から 3,000 行まで変えても数は変わりません。各区間の WAIT 行数は 20 / 18 / 18 / 18 / 21 です。1 回の HTTP 往復が複数の WAIT 行に分かれるので、回数そのものではなく区間の数を見ます。

会話ビュー側では、USER_AI_AGENT_TASK_HISTORYCONVERSATION_PARAMS に入っている会話 ID で USER_CLOUD_AI_CONVERSATION_PROMPTS を検索すると、Supervisor の会話に 3 行、ワーカーの会話に 3 行ありました。行の中身を見ると、各会話とも「プロンプトだけの行が 1 行、応答だけの行が 2 行」です。

会話 PROMPT PROMPT_RESPONSE
Supervisor 1 いま一番重いSQLはどれ?(13 文字) (無し)
Supervisor 2 (無し) Thought: ユーザーは現在一番重い(負荷の高い)SQLについて尋ねている ...(1,315 文字)
Supervisor 3 (無し) Thought: I now have all the information needed to answer the question. Final Answer: ...(902 文字)
ワーカー 1 ユーザーから「いま一番重いSQLはどれ?」という質問が寄せられました ...(150 文字) (無し)
ワーカー 2 (無し) Thought: データベース内で最も負荷の高いSQLクエリを特定するため ...(1,589 文字)
ワーカー 3 (無し) Thought: I now have all the information needed ... Final Answer: ...(902 文字)

応答の行が LLM の返答 1 回分にあたるので、LLM 呼び出しは Supervisor 2 回(委譲の判断と最終回答)、ワーカー 2 回(ツール呼び出しの判断と最終回答)です。これにツールの中で動く NL2SQL の生成(DBMS_CLOUD_AI.GENERATE)を足すと 5 回で、HTTP の待機区間 5 つと一致します。ツールの中の生成が会話ビューに残らないのは、トレース上でツール開始(行 224978)からツール履歴の INSERT(行 265469)までの間に dbms_cloud_ai_conversation_prompt$ への INSERT が 0 件であることから確認しています。待機区間の数は行番号の間隔で区切る方法なので、間に SQL を挟まない連続した呼び出しは 1 区間に見える可能性があります。会話ビューの応答行数と一致したことで、5 回と判断しています。行数をそのまま数えると 6 回です。前回の記事1では行数で LLM 回数を出していたので、1 会話あたり 1 回多く数えていました。

待機イベントの上位を表にまとめます。

待機イベント 回数 合計秒
TCP Socket (KGAS) 95 8.61
PX Deq: Execute Reply 182 0.34
SQL*Net message from client 15 0.31
PX Deq: Parse Reply 70 0.17

19.0 秒のうち HTTP 待機が 8.6 秒です。残りは、RUN_TEAM を実行した最上位カーソルの FETCH 行に c=9.39 秒(CPU)、e=18.92 秒(経過)とあるので、ほぼ CPU 時間です。ただし 4.2 章のトレース無しの実行では、ASH 14 点のうち ON CPU は 3 点(約 3 秒)でした。トレース有りの実行で CPU が 9 秒に増えているのは、待機とバインドを含むトレースを 30 万行書き出す負荷だと考えられます。

5.4. どのユーザーとして解析されたか

PARSING IN CURSOR 199 件の uid の内訳は、C##CLOUD$SERVICE が 129 件で最多でした。

uid ユーザー 件数 主な SQL
118 C##CLOUD$SERVICE 129 履歴・会話・定義の $ 付き表への SELECT / INSERT / UPDATE
0 SYS 35 権限チェック(sysauth$)、監査(AUD$UNIFIED)など
238 DBA_COPILOT 34 RUN_TEAM の呼び出し、ALL_PROCEDURES / ALL_ARGUMENTS の参照、ツールの動的 PL/SQL
105 LBACSYS 1 Label Security のポリシー確認

ツールの動的 PL/SQL BEGIN :result := "DBA_COPILOT"."NL2SQL_DATA_RETRIEVAL_FUNCTIONS"."RUNSQL_FUNC"(...) は uid 238、つまり DBA_COPILOT として解析されています。V$SQLPARSING_SCHEMA_NAME でも同じ文が DBA_COPILOT でした。3.2 章の AUTHID CURRENT_USER と整合します。関数の所有者が DBA_COPILOT なのはツールをそのスキーマに登録したからで、実行時の権限は関数の所有者でなく呼び出し元ユーザーで決まります。

一方、履歴表への書き込みは C##CLOUD$SERVICE の uid で解析されていて、DBA_COPILOT にこれらの表の権限は無くても書けます。uid 118 の 129 件は、実行者権限のパッケージから呼ばれた定義者権限の内部サブプログラム側の SQL と考えられます(7.2 章)。


6. 状態の置き場所

6.1. $ 付き表 15 本と USER ビュー

ビューの定義文は ALL_VIEWS.TEXT が空で読めませんでした。SQLcl の表示形式を変えても DBMS_XMLGEN 経由でも空です。代わりに、トレースと V$SQL の実文から元表を拾いました。

V$SQLAI_AGENT または CLOUD_AI を含む文は 317 件、PARSING_SCHEMA_NAME の内訳は C##CLOUD$SERVICE 210、SYS 59、DBA_COPILOT 48 でした。文に出てくる表を役割ごとに整理します。

役割 表(所有者は C##CLOUD$SERVICE 対応する USER ビュー
エージェント定義 DBMS_AI_AGENT$ / DBMS_AI_AGENT_ATTR$ USER_AI_AGENTS / USER_AI_AGENT_ATTRIBUTES
タスク定義 DBMS_AI_AGENT_TASK$ / DBMS_AI_AGENT_TASK_ATTR$ USER_AI_AGENT_TASKS / USER_AI_AGENT_TASK_ATTRIBUTES
ツール定義 DBMS_AI_AGENT_TOOL$ / DBMS_AI_AGENT_TOOL_ATTR$ USER_AI_AGENT_TOOLS / USER_AI_AGENT_TOOL_ATTRIBUTES
チーム定義 DBMS_AI_AGENT_TEAM$ / DBMS_AI_AGENT_TEAM_ATTR$ USER_AI_AGENT_TEAMS / USER_AI_AGENT_TEAM_ATTRIBUTES
実行履歴 DBMS_AI_AGENT_TEAM_HIST$ / TASK_HIST$ / TOOL_HIST$ USER_AI_AGENT_TEAM_HISTORY / TASK_HISTORY / TOOL_HISTORY
会話 DBMS_CLOUD_AI_CONVERSATION$ / DBMS_CLOUD_AI_CONVERSATION_PROMPT$ USER_CLOUD_AI_CONVERSATIONS / USER_CLOUD_AI_CONVERSATION_PROMPTS
AI プロファイル DBMS_CLOUD_AI_PROFILE$ / DBMS_CLOUD_AI_PROFILE_ATTR$ USER_CLOUD_AI_PROFILES / USER_CLOUD_AI_PROFILE_ATTRIBUTES

定義表への SELECT には WHERE name = :agent_name AND user# = :invoker_schema_id という条件が付いていて、呼び出しユーザーのスキーマ ID で絞っています。エージェントやチームが USER_OBJECTS に出てこないのは、データベースオブジェクトではなくこれらの表の行だからです。

履歴ビューの列は、History Views のページ11の定義と実機の列が一致しませんでした。公式の USER_AI_AGENT_TEAM_HISTORY には TEAM_INSTANCE_ID があって CONVERSATION_IDPARAMS が無いのですが、実機は逆です。公式の TASK_HISTORY の列名 COVERSATION_PARAM は実機では CONVERSATION_PARAMS でした。この記事は実機の列名で書いています。

なお、DBA_COPILOT 自身のスキーマにも SELECTAI_AGENT_CONFIG という表と、チームごとに AI$AGENT$<チーム名> という SQL 翻訳プロファイルがありました。前者は 5 行だけで、AI プロファイル名と Vault 用のプレースホルダしか入っていません。後者の translator は DBMS_CLOUD_AI です。select ai 構文と同じ仕組みがチーム単位でも用意されていると考えられますが、今回の RUN_TEAM の流れには現れませんでした。

6.2. show_agent_prompt で組み立て後のプロンプトを見る

USER_CLOUD_AI_CONVERSATION_PROMPTSPROMPT 列には、Supervisor の会話ではユーザーの質問文(13 文字)がそのまま入っているだけで、システムプロンプトは見えません。3.2 章で見つけた show_agent_prompt を使います。

select dbms_cloud_ai_agent.show_agent_prompt('OPS_SUPERVISOR_TEAM', 'いま一番重いSQLはどれ?') from dual;

0.5 秒で 13,457 文字の JSON が返りました。所要時間から、LLM 呼び出しは無いと考えられます。構造は {"prompt":{"system":"You are NL2SQL_DATA_RETRIEVAL_AGENT. ...", ..., "logs":[...]}} で、直前の実行で最後に動いたタスク、つまりワーカーのプロンプトが返っています。system にはエージェントの role、タスクの instruction、ツールの一覧が展開され、logs にはチーム名・タスク名・プロンプト・応答の組が 3 件入っていました。

この出力から、ワーカーの LLM 呼び出しで何を送っているかが分かります(Supervisor 側のプロンプト構造は今回取っていません)。system の中には、AI プロファイルの object_list に登録したビュー(V_$SQL_PLAN など 5 本)の列名と型が CREATE TABLE 文の書式で展開されています。送っているのは、質問文、エージェントの role とタスクの instruction、ツールの一覧、登録したオブジェクトの列定義で、表の中身そのものは含まれません。ただし、ツールの実行結果は次の思考の入力として送られるので、V$SQLAREA から取った SQL 文や実行統計は、後述する 2 ターン目のプロンプトにそのまま入っています。

送り先は AI プロファイルのプロバイダごとの HTTPS エンドポイントで、Google なら generativelanguage.googleapis.com です12。接続先の URL 自体は V$SESSION にもトレースにも出ません。

同じ会話で 2 ターン目(「そのSQLの実行計画も見せて」)を実行してから呼ぶと、先頭に long_term_memory というキーが現れます。

{"prompt":{"long_term_memory":[
  {"role":"user","content":"いま一番重いSQLはどれ?"},
  {"role":"assistant","content":"データベース内で現在最も負荷が高い(重い)SQL文の実行状況 ...(1 ターン目の回答全文)"},
  {"role":"user","content":"IMPORTANT: The above historical conversation, including prior Assistant responses, may contain stale earlier tool snapshots. Use it only to understand prior discussion, references, or user intent. Do not treat previous database rows, counts, object metadata, or tool results as current facts. ..."}
],"system":"You are NL2SQL_DATA_RETRIEVAL_AGENT. ..."

1 ターン目の質問と回答が会話形式でそのまま入り、その後ろに Oracle が差し込んだ注意文(過去の問答を最新の事実として扱わず、最新のツール観測を優先せよ)が続きます。長さは 16,411 文字でした。名前は long_term_memory ですが、中身は同じ会話の前ターンで、公式が短期メモリとして説明する「recent messages and intermediate results per agent team」にあたります。キーの名前と公式の説明は一致していません。2 ターン目の回答は 1 ターン目の SQL_ID を使って実行計画を返しており、前ターンの内容がプロンプト経由で渡っていると考えられます。

6.3. long_term_memory_length を変えても変わらなかった(今回の条件では)

チーム属性 long_term_memory_length は、作成時に指定していないので属性行がありません。仕様部にある set_attribute で値を入れてみました。

begin
  dbms_cloud_ai_agent.set_attribute(
    object_name => 'OPS_SUPERVISOR_TEAM', object_type => 'TEAM',
    attribute_name => 'long_term_memory_length', attribute_value => '1');
end;
/

'1' は受け付けられましたが、'0' と NULL は ORA-20053: Invalid value for team attribute で拒否されました。下限は 1 です。今回試した範囲(set_attribute で NULL と '0')では、属性行を消してデフォルトに戻す方法は見つかりませんでした。

30 と 1 で、新しい会話の 1 ターン目に対する show_agent_prompt の出力はバイト単位で一致しました。前のターンの問答がまだ無い状態なので、同じ会話の中での差は出ない条件です。一方で、この時点で同じチームは検証当日に 10 回以上実行を終えていました。公式の説明どおり「prior task-level summaries」が新しい実行の開始時にシステムプロンプトへ入るなら、ここで他の会話に由来する要約が現れるはずですが、30 でも 1 でもプロンプトに要約は入っていませんでした。2026 年 9 月時点の ADB-S、gemini-3.5-flash-lite、新しい会話の 1 ターン目という条件では、会話をまたぐ長期メモリは観測されていません。同じ会話を複数ターン続けたときに 1 と 30 で差が出るかは、この記事では測っていません。


7. 考察

7.1. エージェントはプロセスではなく、呼び出し元セッションのループ

4 章と 5 章の結果から、RUN_TEAM は呼び出し元セッションの中で、LLM 呼び出し(UTL_HTTP)、ツールの動的 PL/SQL、履歴表への DML を順に繰り返す 1 本の PL/SQL だと言えます。Supervisor からワーカーへの委譲も、別セッションに渡すのではなく、同じセッションが別の会話 ID でタスク履歴行を作り、続けて LLM を呼ぶだけです。

運用面では、次のことがそのまま当てはまります。

  • RUN_TEAM の所要時間は、呼び出したセッションの占有時間である。APEX や接続プールから呼ぶなら、前回の記事1で見た 15 秒から 60 秒のブロッキングを前提にする
  • 並列に動かしたければ、呼び出し側でセッションを分ける。Supervisor Agent Pattern13に委譲は逐次に行われ並列実行は無いとあるのは、この実装と整合している
  • WAITING_FOR_HUMAN の間にデータベース側で占有されるものは無い。状態は履歴表の行にあるので、続きの RUN_TEAM は同じセッションでなくてもよいと考えられる(今回確かめたのは同じセッションからの再開まで)

7.2. ツールは呼び出しユーザーの権限で動く

パッケージは AUTHID CURRENT_USER で、ツールの動的 PL/SQL は DBA_COPILOT として解析されました。ツールに何ができるかは、エージェントを実行するユーザーの権限で決まります。前回の記事1で、Web 検索ツール(WEBSEARCH)がダミーの資格証明で失敗したのも、ツールの中身が DBA_COPILOT の権限と設定で動いているからです。

運用上の境界はここにあります。LLM が選んだツールと引数、NL2SQL が生成した SQL は、すべてエージェントを実行したユーザーの権限で実行されます。ツールの入力に混ざった文字列(たとえば表の中のデータ)が LLM の判断を変えたとしても、その影響が及ぶ範囲は実行ユーザーの権限までです。ADMIN のような広い権限のユーザーで RUN_TEAM を呼ばず、必要なビューとツールだけを持つ専用ユーザーで動かすのが、この仕組みに合った設計だと考えられます。

一方で履歴表への書き込みは C##CLOUD$SERVICE の uid で行われていて、ユーザーには表の権限が不要です。PL/SQL Language Reference3にあるとおり、定義者権限のユニットが呼び出しスタックに入ると CURRENT_USERCURRENT_SCHEMA はそのユニットの所有者に変わります。実行者権限のパッケージから定義者権限の内部サブプログラムを呼ぶ、Oracle では普通の組み合わせです。DROP_TEAM で履歴が消える(前回の記事で確認)のも、履歴がユーザーの表ではなく Oracle 側の表にあって、チーム ID で紐付いているからだと考えられます。

7.3. スケジューラの定義と実測の不一致

仕様部のコメントには「スケジューラのチェーンとジョブで実行する」と書かれていて、実際にチームごとの PROGRAM と JOB が作られていますが、すべて DISABLED で実行ログは 0 件、トレースにもスケジューラ関連の SQL はありませんでした。agent_task_execute_supervised_job には persistent_mode という引数があります。仕様部では _persistent_mode がタスク属性の定数として宣言されていて(先頭の _ は内部用の属性に付く形式)、RUN_TEAMparams に列挙されたキー(state_variables / instruction_variables / attribute_variables / conversation_id / team_exec_id)には入っていません。この属性で切り替わる別経路か、以前の設計の名残かは、仕様部と実測だけでは決められません。この記事で書けるのは、いま ADB-S で RUN_TEAM を呼んだときにスケジューラは動いていない、というところまでです。

7.4. LLM に出ていく情報の境界

6.2 章のとおり、LLM へ送るプロンプトには質問文、エージェントとタスクの指示、ツール一覧、登録したオブジェクトの列定義が入り、さらに ReAct の観測としてツールの実行結果が次の呼び出しに入ります。今回の質問では V$SQLAREA から取った SQL 文(リテラルを含む)と実行統計が、Google の HTTPS エンドポイントへ送られていました。表の中身を直接送る仕組みではありませんが、ツールが返した結果はそのまま外へ出ます。本番の SQL 文や結果行に社外へ出せない値が含まれるなら、ツールの側で返す内容を絞るか、AI プロファイルのプロバイダを社内やクラウド内に閉じたものにするか、という判断が要ります。どのプロバイダを選んでも UTL_HTTP で外に出るという経路は同じで、変わるのは送り先です。


8. まとめ

# 確かめること 結果
1 RUN_TEAM は呼び出し元セッションの中で動くか OK。実行中の DBA_COPILOT セッションは 1 本、ジョブ 0、待機は TCP Socket (KGAS)UTL_HTTPWAITING_FOR_HUMAN の間も何も待っていない
2 1 質問の中身の順序と回数 OK。履歴行の作成 → Supervisor の LLM → 委譲(会話とタスク履歴の作成)→ ワーカーの LLM → ツールの動的 PL/SQL(内部で NL2SQL の LLM)→ ワーカーと Supervisor の最終回答 → 状態の更新。HTTP の待機区間は 5 つで 8.6 秒
3 状態の置き場所と組み立て後のプロンプト 置き場所は OK。C##CLOUD$SERVICE$ 付き表 15 本。show_agent_prompt で全文が取れ、2 ターン目には前ターンの問答が long_term_memory として入る。長期メモリ設定の影響は NG。30 と 1 で差が無く、会話をまたぐ要約も観測されなかった(複数ターンでの比較は未実施)

公式ドキュメントが ReAct や 3 層で説明している部分は、データベースの中では「1 本のセッションが UTL_HTTP と動的 PL/SQL と DML を順に実行している」という、DBA には見慣れた仕組みでした。V$SESSION と SQL トレースで追えるということは、遅いときの切り分けも普段の SQL と同じ道具でできる、ということでもあります。

参考

  1. Oracle Autonomous AI Database の Select AI Supervisor Agent を試してみた(前回の記事。今回使ったチームの構成と、振り分け・人間確認・所要時間の検証) 2 3 4 5

  2. About Select AI Agent(ReAct の 4 段階、Planning / Tool Use / Reflection の 3 層、短期・長期メモリの説明) 2

  3. PL/SQL Language Reference: Invoker's Rights and Definer's Rights (AUTHID Property)(定義者権限ユニットが呼び出しスタックに入ると CURRENT_USER と CURRENT_SCHEMA が所有者に変わり、ロールは PUBLIC だけが有効になる) 2 3

  4. ADB 26ai 新パッケージ DBMS_USERDIAG で SQL トレースを取る(SQL トレースの取り方と SESSION_CLOUD_TRACE の読み方) 2

  5. DBMS_CLOUD_AI_AGENT Package(CREATE_AGENT / CREATE_TEAM の属性。short_term_memory_lengthlong_term_memory_length の説明はここ) 2

  6. DBMS_CLOUD_AI_AGENT(Oracle Database 19c PL/SQL Packages and Types Reference)(2026 年 9 月時点で SET_ATTRIBUTE と SHOW_AGENT_PROMPT の記載が無いことを確認したページ)

  7. Database Reference: TCP Socket (KGAS)(外部ホストの応答をネットワークソケットで待つ待機イベント。12.1 版のページ。現行版にも同名のイベントがある)

  8. Oracle NL2SQL Data Retrieval Agentの自律補完を試してみた(5 月に作った Top SQL 解析チームの記事。今回のワーカーの 1 つ)

  9. DBMS_USERDIAG(ENABLE_SQL_TRACE_EVENT / TRACE / SET_TRACEFILE_IDENTIFIER)

  10. Autonomous AI Database でのアプリケーション・トレースの実行(SESSION_CLOUD_TRACE はトレースを有効にしたセッションでだけ見え、セッションを閉じると消える) 2

  11. DBMS_CLOUD_AI_AGENT History Views(履歴ビュー 6 本の列定義。STATE の値に RESUMING がある)

  12. Manage AI Profiles(プロバイダごとの接続先ホストの一覧と、ネットワーク ACL の設定例)

  13. Supervisor Agent Pattern(委譲は逐次のみ。委譲用ツールは内部で提供)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?