本記事は日本オラクルが運営する下記Meetupで発表予定の内容になります。発表までに今後、内容は予告なく変更される可能性があることをあらかじめご了承ください。
はじめに
OCI Autonomous Database(ADB)には、データベース内(In-Database)で動作するAIエージェントを開発・運用できる Select AI Agent が用意されています。
データベースに近い場所で動作するため、データ分析やデータ処理を得意とするAIエージェントを実装できます。
さらに、ADBが A2A(Agent2Agent)プロトコル に対応したことで、Select AI Agentを他のAIエージェントと容易に連携できるようになりました。
A2AはGoogleが開発・オープンソース化したエージェント間通信プロトコルであり、異なるフレームワークで開発されたエージェント同士でも相互に連携できます。
本記事では、これらを組み合わせ、ADBのSelect AI Agentをデータベース分析に特化したエージェントとして、さまざまなAIエージェントと連携させる実装方法を紹介します。
※ Google Agent2Agent(A2A)の仕組みの詳細を知りたいという方はこちらの記事も併せてご参照ください。
実装シナリオ
今回実装するシナリオは、下図のとおりです。
まず、OCI Autonomous Database(ADB)の Select AI Agent を利用して、データ分析に特化した「ワインデータ分析エージェント」(上図右)を構築します。分析対象には、scikit-learnに付属する著名なワイン成分データセットを使用します。(このデータセットの詳細はこちら
次に、OpenAI Agents SDK を利用して、ユーザーの要望に応じて楽天市場からワインを検索する「ワイン検索エージェント」(上図左)を構築します。
両エージェントはそれぞれ単体でも動作しますが、本記事ではA2A(Agent2Agent)プロトコルを利用して、異なるフレームワークで実装した2つのエージェントを連携させます。
これにより、ワイン成分データの分析結果を基に、ユーザーの要望に適したワインを楽天市場で検索するマルチエージェントを実装するイメージです。
Select AI Agentの設定
はじめに、上述した図の右側にあるADBのSelect AI Agentから定義してゆきます。Select AI Agentでは主に下図の4つのコンポーネントを設定することでエージェントを定義することができるようになります。
上図の4つのコンポーネントの概要は下記の通りです。
-
Agent Profile
エージェントが利用するLLMを設定するコンポーネントです。今回はOCI Generative AI Serviceを利用します。 -
Agent
エージェントの大まかな役割を定義するコンポーネントです。 -
Agent Task
エージェントが実行する具体的な処理内容を定義するコンポーネントです。 -
Agent Team
複数のAgentとTaskを束ねて目的の処理を実行するマルチエージェントシステムの実体です。事実上このコンポーネントがSelect AI Agentの本体ということになります。
以降、ADBインスタンスの作成を作成し、上述の4つのコンポーネントを順番に設定します。
ADBインスタンスの作成
まず、ADBインスタンスを作成します。今回はデータベース分析を担うエージェントを構築するため、あわせてデータベース用のMCPサーバーも設定します。ADBでは、AIエージェントだけでなくMCPサーバーもデータベース内で定義・運用できます。
具体的な手順は下記記事の「前提条件」から「ツールの作成」までの設定を行います。
Agent Profileの作成
Select AI AgentからOCI Generative AIのLLM(今回はGemini)を利用するため、その認証設定を下記のようにCREATE_CREDENTIAで定義します。
BEGIN
DBMS_CLOUD.CREATE_CREDENTIAL(
credential_name => 'OCI_GENAI_CRED',
user_ocid => '<ユーザーOCID>',
tenancy_ocid => '<テナンシOCID>',
private_key => '<PEM形式の秘密鍵>',
fingerprint => '<フィンガープリント>'
);
END;
/
そして、上記のクレデンシャルを使って書きのようにCREATE_PROFILEでAgent Profileを定義します。
BEGIN
DBMS_CLOUD_AI.CREATE_PROFILE(
profile_name => 'OCI_GENAI_PROFILE',
attributes => '{
"provider": "oci",
"credential_name": "OCI_GENAI_CRED",
"model": "google.gemini-2.5-flash",
"temperature": 0.2,
"max_tokens": 8192,
"oci_compartment_id": "ocid1.compartment.oc1..xxxxxxxxx"
}',
status => 'ENABLED',
description => 'OCI Generative AI profile using Gemini 2.5 Flash'
);
END;
/
下記のようにこのプロファイルが動作するか確認します。
SELECT DBMS_CLOUD_AI.GENERATE(
prompt => 'こんにちは',
profile_name => 'OCI_GENAI_PROFILE',
action => 'chat'
) AS response
FROM dual;
うまくLLMが設定されていると下記のような応答を得ることができます。
これでAgent Profile作成と動作確認完了。
Agentの作成
続いて、下記のようにCREATE_AGENTでエージェントを定義します。ここまで定義してきたTool、Agent Profileも使います。
BEGIN
DBMS_CLOUD_AI_AGENT.CREATE_AGENT(
agent_name => 'WINE_ANALYSIS_AGENT',
attributes => '{
"profile_name": "OCI_GENAI_PROFILE",
"role": "あなたは読み取り専用のワイン品質データ分析エージェントです。データを問い合わせる前に、LIST_SCHEMAS、LIST_OBJECTS、GET_OBJECT_DETAILSを使用してスキーマとオブ
ジェクトのメタデータを確認してください。EXECUTE_SQLはSELECT文またはWITH句を使用する読取り専用のSQLにのみ使用してください。ワイン品質データについて、スキーマ、データ品
質、基本統計量、分布、欠損値、外れ値、相関係数、品質スコアとの関係を分析してください。DDL、DML、権限変更、登録済みツール以外の操作は行わないでください。分析方法、前提
条件、制約事項を日本語で簡潔に説明してください。",
"tools": [
"LIST_SCHEMAS",
"LIST_OBJECTS",
"GET_OBJECT_DETAILS",
"EXECUTE_SQL"
],
"enable_human_tool": true,
"short_term_memory_length": 30
}',
status => 'ENABLED',
description => 'ワイン品質データを読み取り専用で分析するエージェント'
);
END;
/
Agent Taskの作成
続いて、下記のようにCREATE_TASKでAgent Taskを定義します。ここにも作成済のToolを定義します。
BEGIN
DBMS_CLOUD_AI_AGENT.CREATE_TASK(
task_name => 'WINE_ANALYSIS_TASK',
attributes => '{
答してください。必要な情報が不足している場合は、推測せずに確認事項を質問してください。DDL、DML、権限変更、登録済みツール以外の操作は行わないでください。",
"tools": [
"LIST_SCHEMAS",
"LIST_OBJECTS",
"GET_OBJECT_DETAILS",
"EXECUTE_SQL"
],
"enable_human_tool": true
}',
status => 'ENABLED',
description => 'ユーザーの自由な依頼に応じてワイン品質データを読み取り専用で分析するタスク'
);
END;
これでAgent Task作成完了です。
Agent Teamの作成
続いてAgent Teamの作成です。
ここまで定義してきたAgentとAgent Taskを関連付け、下記のようにCREATE_TEAMでAgent Teamを定義します。
BEGIN
DBMS_CLOUD_AI_AGENT.CREATE_TEAM(
team_name => 'WINE_ANALYSIS_TEAM',
attributes => '{
"agents": [
{
"name": "WINE_ANALYSIS_AGENT",
"task": "WINE_ANALYSIS_TASK"
}
],
"process": "sequential"
}',
status => 'ENABLED',
description => 'ワイン品質分析エージェントの実行チーム'
);
END;
/
これでAgent Teamの定義完了です。
Agent Teamに指示を出してみる
このエージェントに指示を出して動作を確認します。
そのためにまず、下記のSQLで会話IDを生成します。
SELECT DBMS_CLOUD_AI.CREATE_CONVERSATION() AS conversation_id
FROM dual;
下記のようなIDが生成されます。
この会話IDを使ってAIエージェントとチャットラリーを行います。
SELECT DBMS_CLOUD_AI_AGENT.RUN_TEAM(
team_name => 'WINE_ANALYSIS_TEAM',
user_prompt => '表wine_tableのCLASS_LABELの値3を高品質、値1を低品質として品質スコアが高いワインの特徴を分析してください。',
params => '{"conversation_id":"<会話ID>"}'
) AS response
FROM dual;
出力結果は下記の通り。
conversation_id: 57705C34-1FB9-48AB-E063-EE17000AEEB6
**分析方法のまとめ:**
`WINE_DATA`テーブルから、`CLASS_LABEL`が3のワインを高品質、`CLASS_LABEL`が1のワインを低品質と定義し、それぞれのグループにおける各化学成分の平均値を算出しました。これらの平均値を比較することで、高品質ワインと低品質ワインの間に見られる特徴量の違いを特定しました。
**仮定と限界:**
* **仮定:** `CLASS_LABEL`が3のワインは一貫して高品質であり、`CLASS_LABEL`が1のワインは一貫して低品質であるという前提で分析を行っています。中間的な品質のワイン(`CLASS_LABEL`=2など)は考慮していません。
* **限界:** この分析は平均値の比較に基づいています。各特徴量の分布、ばらつき、および他の特徴量との相関関係は考慮していません。また、これらの特徴量がワインの品質に与える因果関係は、この分析だけでは断定できません。さらに、データセットのサイズや代表性も結果の一般化に影響を与える可能性があります。
**高品質ワイン(CLASS_LABEL=3)の特徴分析結果:**
高品質ワイン(`CLASS_LABEL`=3)と低品質ワイン(`CLASS_LABEL`=1)の各特徴量の平均値を比較した結果、高品質ワインは低品質ワインと比較して以下の特徴を持つ傾向があることが分かりました。
* **ALCOHOL (アルコール):** 高品質ワインの平均アルコール度数は13.15%で、低品質ワインの13.74%よりもやや低い傾向にあります。
* **MALIC_ACID (リンゴ酸):** 高品質ワインの平均リンゴ酸値は3.33で、低品質ワインの2.01よりも**高い**です。
<中略>
**結論として、品質スコアが高い(CLASS_LABEL=3)ワインは、低品質ワイン(CLASS_LABEL=1)と比較して、一般的に以下の特徴を持つ傾向があります。**
* **リンゴ酸、灰分のアルカリ度、非フラボノイドフェノール類、色の濃さが高い。**
* **アルコール度数、マグネシウム、総フェノール類、フラボノイド、プロアントシアニン、色相、希釈ワインのOD280/OD315、プロリンが低い。**
特に、**リンゴ酸、灰分のアルカリ度、非フラボノイドフェノール類、色の濃さ**が高いことが、高品質ワインの顕著な特徴として挙げられます。一方で、**フラボノイド、希釈ワインのOD280/OD315、プロリン**は低品質ワインで顕著に高い値を示しており、これらの成分が低いことが高品質ワインの特徴と言えるかもしれません。
PL/SQL procedure successfully completed.
Elapsed: 00:02:48.410
Select AI Agentで作成したAIエージェント(エージェントチーム)がうまく動作していることがわかります。
ここまでの作業で一つ目のエージェントが完成しました。
ADBでA2Aサーバーを有効化
ここまでで定義したエージェント(Agent Team)をA2Aサーバーとして公開します。これによって他のエージェントからこのエージェントにA2A通信ができるようになります。
と言ってもやることは、Autonomous AI Databaseの詳細画面内の「その他のアクション」>「タグの管理」からフリーフォーム・タグを追加するだけです。
- Key: adb$feature
- Value: {"name":"a2a_server","enable":true}
ADBのMCP機能を設定している場合は既に、同一のキーで{"name":"mcp_server","enable":true}が設定されていますが、下記A2Aのタグに変更します。(変更してもADBのMCP Server機能は維持されます。)
A2Aサーバーを有効化すると、ADB上のAgent TeamのAgent Cardが自動的に生成されます。このAgent CardとはA2Aで規定されているコンポーネントで、エージェントの役割やエンドポイントなどの情報が定義されており、他のエージェントはこのAgent Cardを取得することにより、ADB上のAgent Team(A2Aサーバー)と通信(連携)ができるようになります。
他のエージェントがADB上のAgent Teamと連携するためにはOAuthの認証・認可をパスする必要があります。
そのため事前にOAuthクライアントの登録をしておき、A2Aクライアントから接続する際に、OAuthのBearerトークンを取得し、認証・認可をパスします。
これをパスすると、Agent Cardを取得することができ、晴れてAgent Teamにメッセージを送信することができるようになるという仕組みです。
以降はこのOAuthの設定手順となります。
ちなみに、このようなクラウドサービスの機能を使わず、オープンソースのA2A SDKを使ってスクラッチでA2Aのエージェントを開発する場合、以前の記事で説明したようなコードを頑張って書くことになります。
これと比較すると何とも拍子抜けするくらい簡単な作業でエージェントをA2A化することができることがわかります。
A2AサーバーへOAuthクライアントを登録
下記のようにcurlコマンドでOAuthクライアントを登録し認証の設定を行います。
(※ADBのADMINユーザーのパスワード入力を求められます)
curl -X POST \
'https://dataaccess.adb.ap-osaka-1.oraclecloudapps.com/adb/auth/v1/connect/databases/<ADB_OCID>/register' \
-u 'ADMIN' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
"redirect_uris": [
"http://localhost:8080/oauth/callback"
],
"client_name": "wine-langgraph-a2a"
}'
実行後、下記のような結果が得られます。
{
"client_name": "wine-langgraph-a2a",
"client_id": "f8EAnuHGWhj-xxxxxxx-xxxxxxx",
"client_secret": "-hZM3afF_xxxxxxx_xxxxxxx_xxxxxxx",
"client_id_issued_at": 1784989453236,
"client_secret_expires_at": 1792765453236,
"redirect_uris": [
"http://localhost:8080/oauth/callback"
],
"response_types": [
"code"
],
"grant_types": [
"authorization_code",
"refresh_token"
],
"token_endpoint_auth_method": "client_secret_basic",
"scope": "a2a openid"
}
上記出力結果のclient_id と client_secret を二つ目のエージェント(ワイン検索エージェント)に埋め込み、認証・認可のためのBearerトークンを取得できるようにするのですが、念のためその処理をエージェント開発前に確認しておきます。
A2Aサーバーアクセス用のBearerトークンを取得できるか確認
下記のようにcurlコマンドでBearerトークンを取得します。
※Adminユーザーではなく、MCPユーザーであることに注意
curl -X POST \
'https://dataaccess.adb.ap-osaka-1.oraclecloudapps.com/adb/auth/v1/databases/<ADB_OCID>/token' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
"grant_type": "password",
"username": "MCPUSER",
"password": "<MCPUSERのパスワード>"
}'
出力結果は下記のようになり、ADBのMCPユーザーの認証と、そのユーザーが持っている権限範囲での処理が認可されたBearerトークンを取得できたことがわかります。
{
"access_token": "eyJhbGciOiJxxxxxxxxxxxxxxxmAwx42O0F18CoF7aLGYQmnvng",
"refresh_token": "eyJhbGciOiJSUzI1xxxxxxxxxxxxxxxxxxxxQBdU80YYAtawZQ",
"token_type": "Bearer",
"expires_in": 3600
}
つまりこの処理をエージェントのコードに埋め込むことによって、Bearerトークンを取得し、ADBのAgent TeamとA2A通信するということになります。
Agent Teamの一覧とAgent Cardが取得できるか確認
こちらもエージェントを開発する前の確認作業です。後の工程で行うエージェントのコードに下記処理を埋め込むことでAgent Teamの一覧とAgent Cardが取得を取得します。
取得したaccess_tokenとADBのA2AサーバーのURLを環境変数として設定します。
read -rsp 'Bearer token: ' A2A_ACCESS_TOKEN
echo
<取得したaccess_tokenを入力>
A2A_BASE_URL='https://dataaccess.adb.ap-osaka-1.oraclecloudapps.com/adb/a2a/v1/databases/<ADB_OCID>'
下記コマンドで公開済みエージェントとAgent Cardの情報を確認します。
curl -sS -X GET \
"${A2A_BASE_URL}/agents/" \
-H 'Accept: application/json' \
-H "Authorization: Bearer ${A2A_ACCESS_TOKEN}"
出力は下記のようになり、作成したエージェントがA2Aサーバーとして公開され、エージェントカードのURL、チーム、エージェントの役割などがクライアントから取得できていることがわかります。
[
{
"agent_card_url": "https://dataaccess.adb.ap-osaka-1.oraclecloudapps.com/adb/a2a/v1/databases/ocid1.autonomousdatabase.oc1.ap-osaka-1.anvwsljrl7l2mtaahvd5nmgedpcvkl3wk36ymz3y7fyngd3xcwbiaox3wdia/agents/WINE_ANALYSIS_TEAM/.well-known/agent-card.json",
"name": "WINE_ANALYSIS_TEAM",
"description": "ワイン品質分析エージェントの実行チーム",
"id": ""
}
]
つまり、OAuth認証・認可をパスし、A2AクライアントがAgent Teamの一覧と、Agent Cardを取得できたということです。
※この処理は以降の作業で定義するエージェント側において実行される処理ですが、この時点でA2Aサーバーがうまく動作しているかを確認しておく方がよいでしょう。
連携する対抗のエージェントを作成する
ここまでの作業で下図の右側「ワインデータ分析エージェント」とA2Aサーバーの設定が完了しました。ここから左側の二つ目のエージェントを作成します。
このエージェントは「ワイン検索エージェント」となり、仕様は下記のようなものです。
- エージェントのフレームワークはOpen AI Agents SDKを利用
- エージェントには楽天の商品検索APIをコールするTool(function calling)を定義
- 事前にRakuten Developersにユーザー登録を行い、APIキーを入手
- A2Aの実装には Google A2A Python SDKを利用
ワイン検索エージェントのコードは以下の通り。
まず①のパートで下記項目を定義してます。
- 必要なライブラリをインポート(Google A2AのSDKなど)
- その他、先に設定したOAuthクライアントの認証情報(Bearerトークン取得のためのキー)の設定
- 楽天商品検索APIキー
- A2A連携のためのADBの情報
########################################################################
# ①必要なライブラリのインポートとアクセスキーやエンドポイントなどの固定値
########################################################################
import asyncio
import base64
import json
import sys
import uuid
from urllib.parse import urlencode
from urllib.request import Request, urlopen
import httpx
from a2a import types
from a2a.client import ClientCallContext, ClientConfig, create_client
from agents import Agent, RunConfig, Runner, function_tool
from agents.models.openai_provider import OpenAIProvider
from google.protobuf.json_format import MessageToDict
from openai import AsyncOpenAI
# 楽天商品検索APIの認証キーと検索のエンドポイント
APPLICATION_ID = "0e5004db-d0ee-4ce3-xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
ACCESS_KEY = "pk_3cMinDqK90xFzBK8adzaxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
RAKUTEN_URL = "https://openapi.rakuten.co.jp/ichibams/api/IchibaItem/Search/20260701"
# OCI Generative AI ServiceのプロジェクトのOCID
PROJECT_ID = "ocid1.generativeaiproject.oc1.us-chicago-1.axxxxxxx"
# OCI Generative AI ServiceのOpenAI互換APIキー
OCI_OPENAI_API_KEY = "sk-wUrTafmskGiNliDRGQAdgpJTQKxxxxxxxxxxxxxx"
# ADB A2A用Bearerトークンの取得に使うOAuthクライアント認証情報
CLIENT_ID = "f8EAnuHGWhj-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
CLIENT_SECRET = "-hZM3afF_KXcB9mbXhxxxxxxxxxxxxxxx"
# ADBインスタンスのOCID
DATABASE_ID = "ocid1.autonomousdatabase.oc1.ap-osaka-1.xxxxxxxxxx"
# ADBのA2A APIを呼び出すためのアクセストークンを取得する認証エンドポイント
AUTH_URL = f"https://dataaccess.adb.ap-osaka-1.oraclecloudapps.com/adb/auth/v1/databases/{DATABASE_ID}/token"
# ADBでA2Aサーバーとして公開されているワインデータ分析エージェントの接続先URL
AGENT_URL = f"https://dataaccess.adb.ap-osaka-1.oraclecloudapps.com/adb/a2a/v1/databases/{DATABASE_ID}/agents/WINE_ANALYSIS_TEAM"
次に、②でA2A通信のための定義です。
先に説明したように、登録済のOAuthクライアントのキーを使って、Bearerトークンを取得するget_access_tokenという関数を定義しています。
########################################################################
# ② A2A通信用のBearerトークンを取得する関数を定義
########################################################################
# 後で定義するcall_adb(ADBのワインデータ分析エージェントとA2A通信する関数)で利用
# ADBのワインデータ分析エージェントとA2A通信するための認証に使用
def get_access_token():
"""MCPUSERでADB A2A用のアクセストークンを取得する。"""
basic = base64.b64encode(f"{CLIENT_ID}:{CLIENT_SECRET}".encode()).decode()
body = urlencode({"grant_type": "password", "username": "MCPUSER", "password": "Welcome1#Welcome1#"}).encode()
request = Request(AUTH_URL, data=body, headers={
"Authorization": f"Basic {basic}", "Content-Type": "application/x-www-form-urlencoded",
})
with urlopen(request) as response:
return json.load(response)["access_token"]
次に③で、上述のBearerトークンを使って、ADB上のAgent Teamにメッセージを送信するcall_adbという関数を定義しています。この中の send_message()という関数が事実上A2A通信の関数です。
########################################################################
# ③ ADBのワインデータ分析エージェントへA2Aメッセージを送る関数 call_adbを定義
########################################################################
# ワイン検索エージェント(wine_search_agent)のtoolに設定する
# この関数でA2A通信を行うため、①で定義したBearerトークンを取得する関数(get_access_token)を使う
@function_tool
async def call_adb(user_request: str) -> str:
"""A2A SDKでADBのWINE_ANALYSIS_TEAMへ分析依頼を送る。"""
# ADBのA2Aサーバーへ接続するためのBearerトークンをHTTPヘッダーに設定する
headers = {"Authorization": f"Bearer {get_access_token()}"}
# Agent Cardを取得し、60秒の通信タイムアウトを設定したA2Aクライアントを作成する
a2a_client = await create_client(
AGENT_URL,
client_config=ClientConfig(httpx_client=httpx.AsyncClient(timeout=60)),
relative_card_path="/.well-known/agent-card.json",
resolver_http_kwargs={"headers": headers},
)
# ユーザー依頼へWINE_DATAの分析条件を追加する
request_text = (
f"{user_request}\nMCPUSER.WINE_DATAのCLASS_LABEL=3と1を比較してください。"
"ALCOHOL、MALIC_ACID、TOTAL_PHENOLS、FLAVANOIDS、COLOR_INTENSITY、PROLINEの"
"平均値と差を分析し、各列の高低と商品説明に現れやすい味わい特徴の対応を示してください。"
)
# A2Aで送信するユーザーメッセージを作成する
request = types.SendMessageRequest(message=types.Message(
message_id=str(uuid.uuid4()),
role=types.Role.Value("ROLE_USER"),
parts=[types.Part(text=request_text)],
))
# BearerトークンをA2Aのメッセージ送信とタスク確認で共通利用する
context = ClientCallContext(service_parameters=headers)
# ワイン検索エージェントからワインデータ分析エージェントへA2Aメッセージを送信する
print("A2A通信を開始します: ワイン検索エージェント → ワインデータ分析エージェント")
async for response in a2a_client.send_message(
request,
context=context,
):
# 送信結果から、ADBで開始された分析タスクを取得する
task = response.task
# 同じタスクを2秒間隔で確認し、分析が完了するまで最大約120秒待つ
for _ in range(60):
if task.status.state not in (
types.TaskState.Value("TASK_STATE_SUBMITTED"),
types.TaskState.Value("TASK_STATE_WORKING"),
):
break
await asyncio.sleep(2)
task = await a2a_client.get_task(
types.GetTaskRequest(id=task.id),
context=context,
)
else:
raise TimeoutError("ワインデータ分析エージェントが完了しませんでした。")
# 完了した分析タスクをワイン検索エージェントへ返す
print("A2A通信が完了しました: ワインデータ分析エージェント → ワイン検索エージェント")
return json.dumps(MessageToDict(task), ensure_ascii=False)
次に④で、楽天市場でワインを検索する「ワイン検索エージェント」の定義です。
楽天市場を検索する関数search_rakutenを定義し、それと、上述の③で定義したA2A通信によるエージェント連携のcall_adb関数の2つをToolとして、エージェントwine_search_agentを定義しています。
########################################################################
# ④ 楽天市場でワインを検索する「ワイン検索エージェント」の定義
########################################################################
# 楽天市場を検索する function calling のための関数を定義
# 次に定義するワインを検索するエージェント(wine_search_agent)のtoolに設定する
@function_tool
def search_rakuten(keyword: str) -> str:
"""楽天市場でワインを検索する。keywordには日本語検索語を渡す。"""
url = RAKUTEN_URL + "?" + urlencode({
"applicationId": APPLICATION_ID, "accessKey": ACCESS_KEY,
"keyword": keyword, "formatVersion": 2, "hits": 5,
"elements": "itemName,catchcopy,itemCaption,itemPrice,itemUrl",
})
with urlopen(url) as response:
return json.dumps(json.load(response)["Items"], ensure_ascii=False)
# 楽天市場でワインを検索するワイン検索エージェントを定義する
# ①、②、③で定義した関数をtoolとしてワイン検索エージェントに定義する
wine_search_agent = Agent(
name="Rakuten Wine Search",
model="google.gemini-2.5-flash",
tools=[call_adb, search_rakuten],
instructions="""
あなたはワイン検索エージェントです。最初にユーザー依頼を確認してください。
「評価の高いワイン」「高品質なワイン」「似た味わい」を求める依頼では、
call_adbを必ず一度呼び、ワインデータ分析エージェントへ依頼してください。
単純な商品検索で成分分析が不要な場合は、call_adbを呼び出す必要はありません。
ADB分析を行った場合は、分析結果の味わい特徴を必ず楽天検索語へ反映してください。
楽天検索語にはCLASS_LABEL、列名、数値を含めず、色・ボディ・果実味・酸味など
商品説明に現れやすい短い日本語を使い、search_rakutenを一度呼んでください。
ADB分析の味わい特徴と商品説明が最も近い商品を1件だけ選んでください。
受賞歴、評価点、ランキングは主な選定理由にしないでください。
最終回答は次の4行だけにしてください。前置き、見出し、箇条書き、補足は出力しません。
【商品タイトル】商品名
【商品価格】価格(円)
【商品ページURL】URL
【選定理由】「ADB分析では」で始め、ALCOHOL、MALIC_ACID、TOTAL_PHENOLS、
FLAVANOIDS、COLOR_INTENSITY、PROLINEの傾向と、商品説明にある共通の味わい特徴を結び付けた理由
""",
)
最後に④で、ワイン検索エージェントがユーザープロンプトを受け取り、必要に応じてcall_adbでワインデータ分析エージェントとA2A通信する流れをmain文にします。
########################################################################
# ⑤ ユーザーの入力プロンプトを元に、ワイン検索エージェントが必要に応じてADBの
# ワインデータ分析エージェントとA2A通信するmain文
########################################################################
# OCI OpenAI互換APIキーで、OCIへ接続するクライアント設定
oci_client = AsyncOpenAI(
base_url="https://inference.generativeai.us-chicago-1.oci.oraclecloud.com/openai/v1",
api_key=OCI_OPENAI_API_KEY,
project=PROJECT_ID,
)
if __name__ == "__main__":
# このPythonファイルの実行時に質問を入力し、その質問文をrequest変数として定義
request = sys.argv[1]
# ワイン検索エージェントがユーザープロンプトを受け取り、必要に応じてcall_adbでワインデータ分析エージェントとA2A通信する。
print("ワイン検索エージェントを実行しています...")
print("ワイン検索エージェントがA2A通信の必要性を判断します。")
result = Runner.run_sync(
wine_search_agent,
request,
run_config=RunConfig(
model_provider=OpenAIProvider(openai_client=oci_client, use_responses=True),
tracing_disabled=True,
),
)
print("ワイン検索エージェントの実行が完了しました。")
print("\n実行結果")
print(result.final_output)
下記のように、上記のpythonスクリプトを実行すると以下のような出力を得ました。
$ python -u wine_search_rakuten_agent.py '評価の高いワインに似た味わいの、手頃な赤ワインを楽天市場から1本探してください。'
ワイン検索エージェントを実行しています...
ワイン検索エージェントがA2A通信の必要性を判断します。
A2A通信を開始します: ワイン検索エージェント → ワインデータ分析エージェント
A2A通信が完了しました: ワインデータ分析エージェント → ワイン検索エージェント
ワイン検索エージェントの実行が完了しました。
実行結果
【商品タイトル】チリ 高評価 赤ワイン【アロモ バレルセレクション 赤】マウレ・ヴァレーのテロワールを凝縮した逸品!ヴィーニャ・アロモのワインは、手頃な価格でありながらも、その品質の高さから国際的にも高い評価を得ています。
【商品価格】3980(円)
【商品ページURL】https://item.rakuten.co.jp/best-wine/10001331/?rafcid=wsc_i_is_0e5004db-d0ee-4ce3-ad29-5ce4a35f9953
【選定理由】ADB分析では、COLOR_INTENSITYが高く、FLAVANOIDSやTOTAL_PHENOLSが豊かな傾向を示すと想定され、商品説明にある「しっかりしたルビー色」、「カシスを主として赤系果実と黒胡椒の香り」といった味わい特徴と共通しているため、このワインを選定しました。
ログと、実行結果の【選定理由】から、ここまで定義した2つのエージェントがA2A通信で連動し、商品の検索結果を出力していることがわかります。
さいごに
「In-Databaseで動作するAIエージェント」と聞いても、具体的なユースケースが思い浮かばない方も多いのではないでしょうか。データベース内で動作する以上、その主な役割はデータ処理やデータ分析となり、単体では活用範囲が限定されがちです。
しかし、ADBがA2Aをサポートしたことで、他のAIエージェントからSelect AI Agentを利用できるようになりました。これにより、データベース分析に特化したエージェントを、マルチエージェントシステムを構成する一つの専門エージェントとして組み込めます。
本記事が、データベース分析と外部サービスを組み合わせた、より実用的なマルチエージェントシステムを構築する際のヒントになれば幸いです。






