OCIから Generative AI Agent (以降、生成AIエージェント)の機能が新たに追加されました。すでにいくつかの記事がQiita上にも投稿されていますが、RAGツールを使ったものが多く、残念ながらSQLツールと関数呼び出しツールについては影が薄いようです。
というわけで、先日SQLツールについての記事を公開しましたので、今回はOCI生成AIエージェントの関数呼び出しツール(Function Calling Tool)がどのようなものなのか確認してみたいと思います。
OCI生成AIエージェントのSQLツールの概要についてはこちらをご参照ください。
ツールの作成時に「カスタム・ツール」となっているのが、関数呼び出しツールになります。
説明には「ファンクション・コール(クライアント実行)またはAPIエンドポイント(エージェント実行)用のツールを定義します」と記載されています。しかしこの記事の執筆時点では、この後のツール構成で選択できるのは「ファンクション・コール(クライアント実行)」のみで、「APIエンドポイント(エージェント実行)」は選択できませんでした。(以下のブログの記事によると limited availability となっているので2025年5月時点ではまだ限定公開のようです。)
「関数呼び出しツール」と聞くと「生成AIエージェントが関数を呼んで結果を返すところまでやってくれる」んじゃないかというような淡い期待を抱いてしまいますが、OCI生成AIエージェントの関数呼び出しツールにおける「ファンクション・コール(クライアント実行)」については残念ながらそこまでやってくれません。先ほどのブログの記事を読むと、それをやってくれるのは「APIエンドポイント(エージェント実行)」のほうだと推測します。
「ファンクション・コール(クライアント実行)」は、後ろに付いているカッコ書きの「クライアント実行」の名前の通り、処理を実行するのはクライアント側(チャットアプリ)になります。生成AIエージェントが「あなたが求めている処理はこれです、この処理をそちらで実行してください、処理が終わったらまた結果を教えてね、そしたらその結果を自然言語でそれっぽく返しますよ」ということをやってくれるものになります。ちなみにOCIでファンクションと聞くとOCIに詳しい人ならOCIのサービスにあるファンクションをイメージすると思いますが、そのサービスとは一切関係ありません。クライアント側で実行する処理は何でも良くて、外部のAPIを呼び出すでも内部的なデータベースを更新するでも何でも構いません。
言葉だけでは少し分かりにくいと思いますので、図で説明するとこのようになります。チャットアプリがクライアントになります。
生成AIエージェントを使用できるリージョンは限られていますのでご注意ください。生成AIエージェントを利用できるリージョンは以下のドキュメントで確認できます。今回は大阪リージョンを使用しています。
エージェントの作成については割愛して、ツールの作成から入ります。
Autonomous Database の状態(アクティブか停止中かなど)をチェックし、停止したり起動したりするエージェントを作成してみたいと思います。そのため、関数呼び出しツールを4つ作成します。
- コンパートメント内の Autonomous Database の一覧を取得する
- 指定の Autonomous Database の状態を確認する
- 指定の Autonomous Database を起動する
- 指定の Autonomous Database を停止する
「コンパートメント内の Autonomous Database の一覧を取得する」ツール
まず、「コンパートメント内の Autonomous Database の一覧を取得する」ツールを作成します。
ファンクション・パラメータは、その関数を実行する際のパラメータの情報をJSON形式で定義しておきます。コンパートメントIDが必要なので、その定義をしています。
ファンクション・パラメータ
{
"name":"list_databases",
"description":"List OCI Autonomous Database instances.",
"parameters":{
"type":"object",
"properties":{
"compartment_id":{
"type":"string",
"description":"The compartment id of the database instances."
}
},
"required":["compartment_id"],
"additionalProperties":false
}
}
「指定の Autonomous Database の状態を確認する」ツール
「指定の Autonomous Database の状態を確認する」ツールを作成します。
ファンクション・パラメータには Autonomous Database のOCIDが必要なので、その定義をしています。
ファンクション・パラメータ
{
"name":"check_database_status",
"description":"Checks the operational status of an OCI Autonomous Database instance.",
"parameters":{
"type":"object",
"properties":{
"db_ocid":{
"type":"string",
"description":"The OCID of the database instance."
}
},
"required":["db_ocid"],
"additionalProperties":false
}
}
「指定の Autonomous Database を起動する」ツール
「指定の Autonomous Database を起動する」ツールを作成します。
ファンクション・パラメータには Autonomous Database のOCIDが必要なので、その定義をしています。
ファンクション・パラメータ
{
"name":"start_database",
"description":"Start an OCI Autonomous Database instance.",
"parameters":{
"type":"object",
"properties":{
"db_ocid":{
"type":"string",
"description":"The OCID of the database instance."
}
},
"required":["db_ocid"],
"additionalProperties":false
}
}
「指定の Autonomous Database を停止する」ツール
「指定の Autonomous Database を停止する」ツールを作成します。
ファンクション・パラメータには Autonomous Database のOCIDが必要なので、その定義をしています。
ファンクション・パラメータ
{
"name":"stop_database",
"description":"Stop an OCI Autonomous Database instance.",
"parameters":{
"type":"object",
"properties":{
"db_ocid":{
"type":"string",
"description":"The OCID of the database instance."
}
},
"required":["db_ocid"],
"additionalProperties":false
}
}
以上で、ツールの作成は完了です。
試してみるといいつつも、クライアント実行の関数呼び出しツールはその仕組み上、RAGツールやSQLツールと違ってOCI生成AIエージェントのチャット画面からお手軽に試すということができません。SDKを使ってプログラムを書く必要があります。プログラムを書く必要があるのですが、そうは言ってもなるべくお手軽に試したいので、今回は SDK for Python が事前構成済みのクラウド・シェルを使って、内部のやり取りを見ながら Python でインタラクティブに実行してみたいと思います。
SDK for Python についてはこちらをご参照ください。
(必要な場合)Config の設定
OCI SDK を使って OCI のサービスへ接続するときに必要です。未設定の場合は事前に行ってください。
「トークンおよびキー」を選択します。次に「APIキーの追加」をクリックします。
「APIキー・ペアの生成」を選択します。続いて「秘密キーのダウンロード」をクリックし、秘密キー・ファイル(.pemファイル)をダウンロードしておきます。秘密キー・ファイルのダウンロードが終わったら「追加」ボタンをクリックします
APIキーの一覧に戻ったら先ほど作成したAPIキーの右にある三点フィーダーをクリックし、「構成ファイルの表示」をクリックします。
構成ファイルのプレビューの横にあるコピーボタンをクリックします。
コピーした構成ファイルは以下のようになっています。
【変更前】構成ファイル
[DEFAULT]
user=ocid1.user.oc1..aaaa---------------------(中略)------------------------36a
fingerprint=89:-----------(中略)------------:77
tenancy=ocid1.tenancy.oc1..aaaaaaaa-----------(中略)------------de3q
region=ap-tokyo-1
key_file= # TODO
いったんテキストエディタにペーストして、region と key_file の情報を書き換えます。
今回は生成AIエージェントが大阪リージョンなので、region は ap-osaka-1 に変更します。
key_file はこの後クラウド・シェルにこのパスのこのファイル名で先ほどダウンロードした秘密キー・ファイル(.pem)を作成するので、そのパスとファイルを記載します。
【変更後】構成ファイル
[DEFAULT]
user=ocid1.user.oc1..aaaa---------------------(中略)------------------------36a
fingerprint=89:-----------(中略)------------:77
tenancy=ocid1.tenancy.oc1..aaaaaaaa-----------(中略)------------de3q
region=ap-osaka-1
key_file=~/.oci/oci_api_keyfile.pem
開発者ツールのアイコンをクリックし、クラウド・シェルを起動します。
ホームディレクトリ直下に「.oci」ディレクトリを作成します。
作成した「.oci」ディレクトリの下に「config」ファイルを作成します。
先ほどの構成ファイルの内容で更新して保存します。
続いて同じ「.oci」ディレクトリの下に秘密キー・ファイルを作成します。
ダウンロードした秘密キー・ファイルの中身をテキストエディタで開いてコピーし、クラウド・シェル側の秘密キー・ファイルに貼り付け保存します。
作成した秘密キー・ファイルの権限を変更し、自分自身しか参照できないようにしておきます。
chmod go-rwx oci_api_keyfile.pem
(必要な場合)パブリックネットワークに変更
ホームリージョンが大阪リージョン以外の場合、クラウド・シェルから SDK を使用して大阪リージョンにあるサービスとやり取りをするためには、ネットワークをパブリックネットワークに切り替えてください。
いざ呼び出し
事前に生成AIエージェントのエンドポイントIDを取得しておきます。
エージェントの下にある作成済みのエンドポイントをクリックします。
エンドポイントのOCIDをコピーしてテキストエディタ等にペーストしておいてください。
また、今回は対象のコンパートメントIDは事前に分かっているものとします。
クラウド・シェルでホームディレクトリに戻って、pythonを起動します。
OCIとJSONをインポートします。
OCIの設定を読み込みます。
また、コンパートメントIDと生成AIエージェントのエンドポイントのIDを定義しておきます。
import oci, json
config = oci.config.from_file()
compartmentId = "ocid1.compartment.oc1..aaaaaaaa-------------(中略)------------------4cla"
agentEndpointId = "ocid1.genaiagentendpoint.oc1.ap-osaka-1.amaaaaa-------------(中略)------------------ggq"
生成AIエージェントとやり取りをおこなうクライアントを作成します。
agentClient = oci.generative_ai_agent_runtime.GenerativeAiAgentRuntimeClient(config)
sessionDetails = oci.generative_ai_agent_runtime.models.CreateSessionDetails(display_name="session1", description="session1 description")
session = agentClient.create_session(sessionDetails, agentEndpointId)
まずデータベースのリストが欲しいので、生成AIエージェントにチャットのメッセージを送信します。指定しないと日本語で質問しても英語で返してくるので、日本語で回答するように指定しています。
userMessage = "あなたは優秀なAIエージェントです。日本語で質問された場合は日本語で回答します。データベースの一覧を表示してください。"
chatDetails = oci.generative_ai_agent_runtime.models.ChatDetails(user_message=userMessage, session_id=session.data.id)
chatResponse = agentClient.chat(agent_endpoint_id=agentEndpointId, chat_details=chatDetails)
生成AIエージェントからのレスポンスの中身を見てみます。通常のチャットと異なり、ユーザーに見せるようなメッセージがありません。
このレスポンス内で重要なのは「required_actions」の情報です。リストなので、今回の検証では1つのみですが、複数アクションの場合もあります。
「required_actions」にはクライアント側で実行する処理の情報が入っています。
- action_id:クライアント側で処理を実行後、再度生成AIエージェントを呼ぶ際に必要なID
- function_call.name:クライアント側で呼び出す処理の、生成AIエージェント側で定義されているファンクション名
- function_call.arguments:クライアント側で呼び出す処理のパラメータ
- required_action_type:FUNCTION_CALLING_REQUIRED_ACTIONという値が入っていればクライアント側で処理をおこなってくださいということらしいです。
生成AIエージェントからのレスポンス
{
"guardrail_result": null,
"message": null,
"required_actions": [
{
"action_id": "2ee9805d-7970-48ac-9bcb-603b3d7fe48b",
"function_call": {
"arguments": "{\"compartment_id\": \"ocid1.compartment.oc1..aaaaaaaawjnpnpbsc5s7kxk6w55jxv2ymp7fj6wvqj7wv7wvqj7wv7wvqj\"}",
"name": "List_Databases"
},
"required_action_type": "FUNCTION_CALLING_REQUIRED_ACTION"
}
],
"tool_results": null,
"traces": [
{
"time_created": "2025-05-27T01:54:46.646000+00:00",
"trace_type": "UNKNOWN_ENUM_VALUE"
}
]
}
この「required_actions」内の情報に基づきクライアント側で処理を行います。
「function_call.arguments」にはそれっぽいコンパートメントIDが入っていますが、こちらからコンパートメントIDの情報を生成AIエージェントに渡していないので、実際には生成AIエージェント側が適当に入れた存在しない値になっています。正しいコンパートメントIDはPythonの変数で持っているのでそちらを使用し、ここでは生成AIエージェントのレスポンス内のコンパートメントIDは無視します。
「function_call.name」が「List_Databases」なので、データベースのリストを取得する処理をクライアント側で実行します。
databaseClient = oci.database.DatabaseClient(config)
databaseResponse = databaseClient.list_autonomous_databases(compartment_id=compartmentId)
レスポンスの中身は
print(databaseResponse.data)
で見ることが出来ますが、かなり長いのでここでは割愛します。今回の検証環境では2つの Autonomous Database が存在していて、その2つの Autonomous Database についての情報(名前やOCIDはもちろん、状態やその他設定など詳細な情報)がJSON形式で入っています。
クライアント側で処理が終わったら、再び生成AIエージェントに結果報告のチャットを送信します。結果報告のチャットには処理を実行した結果の情報などを含む「performed_actions」を送信する必要があるので、その情報を作成して送信します。
# performed_actions を作成するのに必要な情報
actionId = chatResponse.data.required_actions[0].action_id
actionType = "FUNCTION_CALLING_PERFORMED_ACTION"
functionCallOutput = str(databaseResponse.data)
# 上の3つの情報を使って performed_actions を作成
performedActions = [oci.generative_ai_agent_runtime.models.FunctionCallingPerformedAction(performed_action_type=actionType, action_id=actionId, function_call_output=functionCallOutput)]
# performed_actions を含むチャットを生成AIエージェントに送信
chatDetails = oci.generative_ai_agent_runtime.models.ChatDetails(user_message=userMessage, session_id=session.data.id, performed_actions=performedActions)
chatResponse = agentClient.chat(agent_endpoint_id=agentEndpointId, chat_details=chatDetails)
レスポンスの中身を見てみると、クライアント側での実行結果の情報の中から生成AIエージェントがデータベース名を抜き出して自然言語でデータベースの一覧を回答しています。
生成AIエージェントからのレスポンス
{
"guardrail_result": null,
"message": {
"content": {
"citations": null,
"paragraph_citations": null,
"text": "データベースの一覧は以下の通りです。 \n- データベース1: SalesDB \n- データベース2: GenAIAgentOsakaDB"
},
"role": "AGENT",
"time_created": "2025-05-27T02:41:00.040000+00:00"
},
"required_actions": null,
"tool_results": null,
"traces": [
{
"time_created": "2025-05-27T02:40:57.080000+00:00",
"trace_type": "UNKNOWN_ENUM_VALUE"
}
]
}
コンパートメント内にある Autonomous Database の一覧が取得できたので、続けて Autonomous Database の SalesDB が起動しているか停止しているかを生成AIエージェントに問い合わせてみます。
userMessage = "SalesDBの状態を教えて"
chatDetails = oci.generative_ai_agent_runtime.models.ChatDetails(user_message=userMessage, session_id=session.data.id)
chatResponse = agentClient.chat(agent_endpoint_id=agentEndpointId, chat_details=chatDetails)
データベースの一覧の問い合わせをしたときに送った情報の中に起動状態が含まれていたので、その情報を使用して停止中と回答したようです。(指定の Autonomous Database の状態を確認するツールによって処理をクライアント側でおこなうように依頼されませんでした。)
生成AIエージェントからのレスポンス
{
"guardrail_result": null,
"message": {
"content": {
"citations": null,
"paragraph_citations": null,
"text": "SalesDBの状態は、停止中(STOPPED)です。"
},
"role": "AGENT",
"time_created": "2025-05-28T01:32:31.526000+00:00"
},
"required_actions": null,
"tool_results": null,
"traces": [
{
"time_created": "2025-05-28T01:32:28.676000+00:00",
"trace_type": "UNKNOWN_ENUM_VALUE"
}
]
}
SalesDB は停止しているので、生成AIエージェントに SalesDB の起動を問い合わせてみます。期待通りであれば、「Start_Database」ツールの情報を返してくるはずです。
userMessage = "SalesDBを起動してください"
chatDetails = oci.generative_ai_agent_runtime.models.ChatDetails(user_message=userMessage, session_id=session.data.id)
chatResponse = agentClient.chat(agent_endpoint_id=agentEndpointId, chat_details=chatDetails)
生成AIエージェントからのレスポンスを見てみましょう。「function_call.name」が期待通り「Start_Database」となっています。
また、「function_call.arguments」にある「db_ocid」の値ですが、ありがたいことに実際の SalesDB の OCID の値が入っています。つまり先ほど生成AIエージェントに送った Autonomous Database の一覧の情報から 生成AIエージェントは 必要な SalesDB の OCID の値を取り出してレスポンスにセットしてくれているということです。
クライアント側は SalesDB の OCID が分かっていなくてもこの値をそのまま使用することで SalesDB に対する操作を SDK からおこなうことが出来ます。
生成AIエージェントからのレスポンス
{
"guardrail_result": null,
"message": null,
"required_actions": [
{
"action_id": "a55f852a-2260-47f9-b9d3-d070ef3f99a4",
"function_call": {
"arguments": "{\"db_ocid\": \"ocid1.autonomousdatabase.oc1.ap-osaka-1.anv-------------(中略)------------------swa\"}",
"name": "Start_Database"
},
"required_action_type": "FUNCTION_CALLING_REQUIRED_ACTION"
}
],
"tool_results": null,
"traces": [
{
"time_created": "2025-05-28T04:56:46.489000+00:00",
"trace_type": "UNKNOWN_ENUM_VALUE"
}
]
}
db_ocid = json.loads(chatResponse.data.required_actions[0].function_call.arguments)["db_ocid"]
databaseResponse = databaseClient.start_autonomous_database(autonomous_database_id=db_ocid)
終わったら生成AIエージェントに結果報告のチャットを送信します。データベースの一覧を取得した時と同様に「performed_actions」を送信する必要があるので、その情報を作成して送信します。
actionId = chatResponse.data.required_actions[0].action_id
functionCallOutput = str(databaseResponse.data)
performedActions=[oci.generative_ai_agent_runtime.models.FunctionCallingPerformedAction(performed_action_type=actionType, action_id=actionId, function_call_output=functionCallOutput)]
chatDetails = oci.generative_ai_agent_runtime.models.ChatDetails(user_message=userMessage, session_id=session.data.id, performed_actions=performedActions)
chatResponse = agentClient.chat(agent_endpoint_id=agentEndpointId, chat_details=chatDetails)
生成AIエージェントからのレスポンスを見てみると、このようにメッセージが入っています。起動中であることが分かります。
生成AIエージェントからのレスポンス
{
"guardrail_result": null,
"message": {
"content": {
"citations": null,
"paragraph_citations": null,
"text": "SalesDBが起動しました。現在の状態はSTARTINGです。"
},
"role": "AGENT",
"time_created": "2025-05-29T01:27:04.410000+00:00"
},
"required_actions": null,
"tool_results": null,
"traces": [
{
"time_created": "2025-05-29T01:27:00.845000+00:00",
"trace_type": "UNKNOWN_ENUM_VALUE"
}
]
}
ユーザーの見た目からはこのようにチャット上で会話が進んだイメージになります。
最後にセッションを削除して、python も終了します。
deleteSessionResponse = agentClient.delete_session(agent_endpoint_id=agentEndpointId, session_id=session.data.id)
exit()
以上のように関数呼び出しツールの「ファンクション・コール(クライアント実行)」は、生成AIエージェントとクライアント側で複数回のやり取りをおこなうことで機能を実現しています。
結局のところ実際に処理を実行するのはクライアント側なのでクライアント側に実装が必要になりますし、生成AIエージェントに丸投げできるからめちゃめちゃ楽になる!みたいなことも無いです。無いですが、LLMに関数呼び出しツールを組み合わせることで他のシステムと連携してLLM単体より高度なことが出来るチャットボットを開発できるので、アイデア次第で面白いものが出来るのではないかと思います。
また、「APIエンドポイント(エージェント実行)」が使えるようになれば、処理内容によってはクライアント側での実装も必要なくなり生成AIエージェントに丸投げできるようになるんじゃないかと期待しています。
ただし、あまりたくさんの関数呼び出しツールを作成してしまうと生成AIエージェントが誤ったファンクションを指定するリスクが増えると思いますので注意が必要かと思います。(少し異なりますが、今回のデータベースの操作に関する関数呼び出しツールとSQLツールを同じエージェントの下に作成したら、関数呼び出しツールのレスポンスが返ってくることを期待していたときにSQLツールのレスポンスが返ってくるということもありました。)
実際にPythonでSDKを使用してOCIの生成AIエージェントと連携するアプリを構築しようと考えている方は、こちらの記事がとても参考になります。
OCI生成AIエージェントのSQLツールの概要についてはこちらをご参照ください。
Views: 0