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?

[OutSystems] Agentic AI 試験範囲補強情報: MCP と Action calling

0
Last updated at Posted at 2026-03-12

この記事は、2026-02-24(火) に行なわれた、OutSystems User Group Online Meetup のセッションで話した内容を、あらためて解説したものです。

セッションの録画は、YouTube で公開されています。

セッションで使用したスライドは、Speaker Deck で公開しています。

はじめに

OutSystems Developer Cloud (ODC) で、2025 年 9 月末から作れるようになった、Agentic app という種別のアプリでは、開発者が OutSystems 内で実装したサーバーアクションを、AI モデルが自らの判断で使えるようにする Action calling という仕組みがあります。今年(2026 年)になって受験できるようになった、Agentic AI Specialist 試験でも、この Action calling に関する出題があります。

以下は、OutSystems 公式の Agentic AI Specialist トレーニング教材にあるサンプル問題から、Action calling に関する1問を機械翻訳して転載したものです。

問題:ODC の Agentic App は、サーバーアクション(ツール)が大規模言語モデル(LLM)によって確実に解釈され呼び出されることをどのように保証しますか?
A. サーバーアクションのロジックを Agentic App のランタイム環境内で自動的に Python スクリプトへ変換することによって。
B. アクションの名前、説明、パラメータの型を分析し、それらを LLM 向けの構造化された JSON スキーマに変換することによって。 (正解はこれ)
C. LLM が入力パラメータを知る必要なく呼び出せる、直接的で同期的な REST API エンドポイントを作成することによって。
D. LLM はアクションの定義を無視し、必要な入力を推測するためにシステムプロンプトのみに依存する。

[転載元の公式トレーニング教材]
https://learn.outsystems.com/training/journeys/agentic-ai-exam-3546/sample-exam/odc/3544

資格試験を突破する、という観点では、この問題文・選択肢レベルの説明を丸呑みして、「そういうもの」と受け止めるだけでも、もちろん OK なのですが、Action calling についてもう一歩踏み込んだ理解ができれば、試験で自信をもって回答できるにとどまらず、Action calling を適切に使った開発を行なうことができるようになるはず。
本稿は、その助けになる情報の提供を試みるものです。

Action calling の使い方

ODC Studio で Agentic app を新規作成すると、AgentFlow というサーバーアクションが用意されており、このアクションフローの中に並ぶ、顔をデザインしたエージェントアクションアイコンを開きます。表示される設定ダイアログの上段では、ODC Portal で接続設定した AI モデルアクションを選択できるようになっており、その下のタブで、Action calling の設定を行なうことができます。

action-calling-ui.png

Add action ボタンから、このアプリ内の任意のサーバーアクションを登録することができます。

このダイアログの上段で選択する、ODC Portal で接続設定した AI モデルのアクションは、Logic タブの中にも直接ならんでいて、アクションフローの中にそちらを直接配置する方法でも、AI モデルの呼び出し自体は可能です。さらに言うと、その Logic タブに直接ならんでいる方のアクションは、Agentic app 以外のアプリ種別(Web app など)でも、Add public element で参照追加して使うことができます。このことから、AI モデルを「エージェント」として使う上で、このダイアログで設定できる Action calling などの項目(アプリ種別が Agentic app でないと表示されない)は、とても重要と位置づけられていることが分かります。

上のスクショは、説明のために作成したとても簡単な Agentic app で撮ったもので、ここで Action calling に登録してある2つのアクションは、どちらも、このアプリの Data タブに用意した担当者リストエンティティ内の情報を返すものです。

  • SearchContacts - 担当者リストの名前を指定された文字列で検索して、見つかった名前と Id のリストを返す(検索アクション)
  • GetContactDetail - 指定された Id を持つ担当者の詳細情報を返す(詳細取得アクション)

上のスクショ左のフローで、エージェントアクションの上の BuildMessages で設定するシステムプロンプトには、次の内容を設定してあります。

システムプロンプト
あなたは担当者リストの専門エージェントです。提供されたツールを組み合わせて、ユーザーの質問に正確に答えてください。

## ツールの特性とヒント
- 担当者を特定する必要がある場合は、まず `SearchContacts` を使用してください。
- `SearchContacts` の結果だけでは不足している情報(詳細な連絡先など)が必要な場合のみ、`GetContactDetail` を使用してください。
- リストの人数を数えたり、特定の条件でフィルタリングしたりする場合は、ツールから得られたデータをもとに、あなた自身で推論・集計して回答してください。

## 制約事項
- ツールで得られない情報を、自身の知識で捏造しないでください。
- 解決できない場合は「その質問に答えることはできません」と回答してください。

このエージェントアプリを用いると、例えば下図のようなやりとりをすることができます。

action-calling-eg.png

1つ目の質問に対しては、検索アクションで見つけた Id を使って詳細取得アクションを実行して得られた情報を返しています。2つ目では、検索アクションで得られたリストの長さを返しています。

このように、ODC Studio 内に別途実装したアクションを、問い合わせ内容に応じて AI モデルが判断して使うことができるようになる機能、それが Action calling です。

Action calling における AI モデルとの通信内容

例えば、上の例にあった「米田さんのメールアドレスを教えて」という問い合わせに対して、エージェントアプリと AI モデルの間では、実際には3回の通信が行なわれています。先に見た AgentFlow アクションフローの上で接続されていたエージェントアクションアイコンは1つだけなのに、実際に行なわれた通信は3回に分かれていたことに注目して下さい。

以下で示す通信内容は、AWS Bedrock の設定にある「モデル呼び出しのログ記録」を使って S3 に書き出した内容を、見やすいように整形したものです。開発管理の観点から、AI モデルによらず、これに相当するログが、将来的には、ODC の機能として ODC Portal で確認できるようになることを期待しています。

アプリ → AI モデル(3回中1回目)
{
    "messages": [{"role": "user", "content": [{"text": "米田さんのメールアドレスを教えて"}]}],
    "system": [{"text": "あなたは担当者リストの専門エージェントです。提供されたツールを組み合わせて、ユーザーの質問に正確に答えてください。\r\n\r\n## ツールの特性とヒント\r\n- 担当者を特定する必要がある場合は、まず `SearchContacts` を使用してください。\r\n- `SearchContacts` の結果だけでは不足している情報(詳細な連絡先など)が必要な場合のみ、`GetContactDetail` を使用してください。\r\n- リストの人数を数えたり、特定の条件でフィルタリングしたりする場合は、ツールから得られたデータをもとに、あなた自身で推論・集計して回答してください。\r\n\r\n## 制約事項\r\n- ツールで得られない情報を、自身の知識で捏造しないでください。\r\n- 解決できない場合は「その質問に答えることはできません」と回答してください。"}],
    "inferenceConfig": {},
    "toolConfig": {
        "tools": [
            {"toolSpec": {
                "name": "SearchContacts",
                "description": "提供された文字列を使って、担当者リストの名前とフリガナを、部分一致で検索するのに使う。見つかった担当者の Id と名前は、リスト形式で返される。名前以外の担当者情報が必要な場合は、このツールで得られた Id を、別のツールで使って確認すること。",
                "inputSchema": {"json": {"type": "object",
                    "properties": {
                        "QueryParam_Name": {"type": "string", "description": "担当者リストの、名前またはフリガナの部分一致検索をするのに使う文字列"}
                    },
                    "required": ["QueryParam_Name"]
                }}
            }},
            {"toolSpec": {
                "name": "GetContactDetail",
                "description": "担当者リストから、指定された Id を持つ担当者の、完全な詳細情報(メールアドレス、電話番号、住所など)を返す。指定する Id は、別のツールで確認しておく必要がある。",
                "inputSchema": {"json": {"type": "object",
                    "properties": {
                        "Id": {
                            "description": "担当者の識別子(別のツールで得ることができる)",
                            "maximum": 9223372036854775807, "type": "integer", "minimum": -9223372036854775808
                        }
                    },
                    "required": ["Id"]
                }}
            }}
        ]
    },
    "additionalModelRequestFields": {}
}

messages でユーザープロンプトが送られ、systemtext としてシステムプロンプトが、そして、toolConfig として、Action calling で設定したアクションの情報(名前、Description、入力パラメーターの情報)が送られていることが分かります。

この toolConfig の内容こそが、最初に転載したサンプル問題の正答、つまり LLM がアクションを正しく解釈されるのを、「アクションの名前、説明、パラメータの型を分析し、それらを LLM 向けの構造化された JSON スキーマに変換する」ことによって保証する、の正体です。

これを受け取った AI モデルからは、次の内容がアプリに返されます。

AI モデル → アプリ(3回中1回目)
{
    "output": {
        "message": {"role": "assistant",
            "content": [{
                "toolUse": {
                    "toolUseId": "tooluse_eMPD8yx9LZc9nVKszDoEey",
                    "name": "SearchContacts",
                    "input": {"QueryParam_Name": "米田"}
                }
            }]
        }
    },
    "stopReason": "tool_use",
    "metrics": {"latencyMs": 1075},
    "usage": {"inputTokens": 1764, "outputTokens": 57, "totalTokens": 1821}
}

ここにある toolUse が、toolConfig にあったツールをこのパラメーターで使え、の指示。ここでは、検索アクション SearchContacts をパラメーター 米田 で使え、という指示が返ってきています。

この指示を受けて SearchContacts アクションを実行したアプリは、2回目の通信を行ないます。

アプリ → AI モデル(3回中2回目)
{
    "messages": [
        {"role": "user", "content": [{"text": "米田さんのメールアドレスを教えて"}]},
        {"role": "assistant", "content": [{"toolUse": {
            "toolUseId": "tooluse_eMPD8yx9LZc9nVKszDoEey",
            "name": "SearchContacts",
            "input": {"QueryParam_Name": "米田"}
        }}]},
        {"role": "user", "content": [{"toolResult": {
            "toolUseId": "tooluse_eMPD8yx9LZc9nVKszDoEey",
            "content": [{"text": "{\"List\":[{\"Id\":495,\"Name\":\"\\u7C73\\u7530 \\u8B19\\u4FE1\"}]}"}]
        }}]}
    ],
    "system": [{"text": "あなたは担当者リストの専門エージェントです。提供されたツールを組み合わせて、ユーザーの質問に正確に答えてください。\r\n\r\n## ツールの特性とヒント\r\n- 担当者を特定する必要がある場合は、まず `SearchContacts` を使用してください。\r\n- `SearchContacts` の結果だけでは不足している情報(詳細な連絡先など)が必要な場合のみ、`GetContactDetail` を使用してください。\r\n- リストの人数を数えたり、特定の条件でフィルタリングしたりする場合は、ツールから得られたデータをもとに、あなた自身で推論・集計して回答してください。\r\n\r\n## 制約事項\r\n- ツールで得られない情報を、自身の知識で捏造しないでください。\r\n- 解決できない場合は「その質問に答えることはできません」と回答してください。"}],
    "inferenceConfig": {},
    "toolConfig": {"tools": [
        {"toolSpec": {"name": "SearchContacts",
            "description": "提供された文字列を使って、担当者リストの名前とフリガナを、部分一致で検索するのに使う。見つかった担当者の Id と名前は、リスト形式で返される。名前以外の担当者情報が必要な場合は、このツールで得られた Id を、別のツールで使って確認すること。",
            "inputSchema": {"json": {"type": "object",
                "properties": {"QueryParam_Name": {"type": "string", "description": "担当者リストの、名前またはフリガナの部分一致検索をするのに使う文字列"}},
                "required": ["QueryParam_Name"]
            }}
        }},
        {"toolSpec": {"name": "GetContactDetail",
            "description": "担当者リストから、指定された Id を持つ担当者の、完全な詳細情報(メールアドレス、電話番号、住所など)を返す。指定する Id は、別のツールで確認しておく必要がある。",
            "inputSchema": {"json": {"type": "object",
                "properties": {"Id": {"description": "担当者の識別子(別のツールで得ることができる)", "maximum": 9223372036854775807, "type": "integer", "minimum": -9223372036854775808}},
                "required": ["Id"]
            }}
        }}
    ]},
    "additionalModelRequestFields": {}
}

見ると、1回目のときにユーザープロンプトのみだった messages で、1回目で受け取った「ツール使え指示」の内容とその結果(ここでは、Id 495 の1レコードのリスト)が、ユーザープロンプトと共に送られており、 system には、1回目と同じシステムプロンプトとツール情報 toolConfig が、再度フルセットで送られていることが分かります。

それを受けて、AI モデルからは、2度目の「ツール使え指示」 toolUse をアプリに送り返します。今度は、1回目で見つけた Id 495 を使って、詳細取得アクション GetContactDetail を使え、という指示が返ってきています。

AI モデル → アプリ(3回中2回目)
{
    "output": {"message": {"role": "assistant",
        "content": [
            {"text": "検索結果では、\"米田 祥信\"という担当者が見つかりました。さらに詳細な情報が必要であれば、次のようにして取得できます。"},
            {"toolUse": {
                "toolUseId": "tooluse_MHjsFTQmwe3uTpN2scw5tP",
                "name": "GetContactDetail",
                "input": {"Id": 495}
            }}
        ]
    }},
    "stopReason": "tool_use",
    "metrics": {"latencyMs": 1812},
    "usage": {"inputTokens": 1867, "outputTokens": 106, "totalTokens": 1973}
}

そして3回目です。アプリは、2回目の「ツール使え指示」の結果(Id 495 の詳細レコード)だけでなく、1回目2回目両方の「ツール使え指示」の内容と結果、そしてユーザープロンプトとシステムプロンプト、およびツール情報 toolConfig を AI モデルに送っています。

アプリ → AI モデル(3回中3回目)
{
    "messages": [
        {"role": "user", "content": [{"text": "米田さんのメールアドレスを教えて"}]},
        {"role": "assistant", "content": [{"toolUse": {
            "toolUseId": "tooluse_eMPD8yx9LZc9nVKszDoEey", "name": "SearchContacts", "input": {"QueryParam_Name": "米田"}
        }}]},
        {"role": "user", "content": [{"toolResult": {
            "toolUseId": "tooluse_eMPD8yx9LZc9nVKszDoEey", "content": [{"text": "{\"List\":[{\"Id\":495,\"Name\":\"\\u7C73\\u7530 \\u8B19\\u4FE1\"}]}"}]
        }}]},
        {"role": "assistant", "content": [
            {"text": "検索結果では、\"米田 祥信\"という担当者が見つかりました。さらに詳細な情報が必要であれば、次のようにして取得できます。"},
            {"toolUse": {"toolUseId": "tooluse_MHjsFTQmwe3uTpN2scw5tP", "name": "GetContactDetail", "input": {"Id": 495}}}
        ]},
        {"role": "user","content": [{"toolResult": {
            "toolUseId": "tooluse_MHjsFTQmwe3uTpN2scw5tP", "content": [{"text": "{\"Contact\":{\"Id\":495,\"Name\":\"\\u7C73\\u7530 \\u8B19\\u4FE1\",\"NameKana\":\"\\u30E8\\u30CD\\u30C0 \\u30B1\\u30F3\\u30B7\\u30F3\",\"PostalCode\":\"312-0056\",\"Address\":\"\\u8328\\u57CE\\u770C\\u3072\\u305F\\u3061\\u306A\\u304B\\u5E02\\u9752\\u8449\\u753A2-19\",\"Phone\":\"0293-14-1429\",\"Fax\":null,\"Email\":\"kenshin05586@tpwg.zyc.ttj\"}}"}]
        }}]}
    ],
    "system": [{"text": "あなたは担当者リストの専門エージェントです。提供されたツールを組み合わせて、ユーザーの質問に正確に答えてください。\r\n\r\n## ツールの特性とヒント\r\n- 担当者を特定する必要がある場合は、まず `SearchContacts` を使用してください。\r\n- `SearchContacts` の結果だけでは不足している情報(詳細な連絡先など)が必要な場合のみ、`GetContactDetail` を使用してください。\r\n- リストの人数を数えたり、特定の条件でフィルタリングしたりする場合は、ツールから得られたデータをもとに、あなた自身で推論・集計して回答してください。\r\n\r\n## 制約事項\r\n- ツールで得られない情報を、自身の知識で捏造しないでください。\r\n- 解決できない場合は「その質問に答えることはできません」と回答してください。"}],
    "inferenceConfig": {},
    "toolConfig": {"tools": [
        {"toolSpec": {"name": "SearchContacts",
            "description": "提供された文字列を使って、担当者リストの名前とフリガナを、部分一致で検索するのに使う。見つかった担当者の Id と名前は、リスト形式で返される。名前以外の担当者情報が必要な場合は、このツールで得られた Id を、別のツールで使って確認すること。",
            "inputSchema": {"json": {"type": "object",
                "properties": {"QueryParam_Name": {"type": "string", "description": "担当者リストの、名前またはフリガナの部分一致検索をするのに使う文字列"}},
                "required": ["QueryParam_Name"]
            }}
        }},
        {"toolSpec": {"name": "GetContactDetail",
            "description": "担当者リストから、指定された Id を持つ担当者の、完全な詳細情報(メールアドレス、電話番号、住所など)を返す。指定する Id は、別のツールで確認しておく必要がある。",
            "inputSchema": {"json": {"type": "object",
                "properties": {"Id": {"description": "担当者の識別子(別のツールで得ることができる)", "maximum": 9223372036854775807, "type": "integer", "minimum": -9223372036854775808}},
                "required": ["Id"]
            }}
        }}
    ]},
    "additionalModelRequestFields": {}
}

そうしてようやく、最初のユーザープロンプト「米田さんのメールアドレスを教えて」への回答が、AI モデルからアプリに返されます。

AI モデル → アプリ(3回中3回目)
{
    "output": {"message": {"role": "assistant", "content": [{"text": "この結果より、米田さんのメールアドレスは「kenshin05586@tpwg.zyc.ttj」であることがわかりました。"}]}},
    "stopReason": "end_turn",
    "metrics": {"latencyMs": 976},
    "usage": {"inputTokens": 2157, "outputTokens": 48, "totalTokens": 2205}
}

ここまで見てきた、アプリと AI モデル(LLM)との通信をシーケンス図にすると、次のようになります(アクションが1回だけ使われた場合)。

action-calling-seq.png

このシーケンスを眺めると、OutSystems で実装したアクションを AI モデルに使わせるための専用のやりとりのようにも見えます。どうして、世の中にリリースされている多くの AI モデルに対して、このような「OutSystems 専用に見える」やりとりが可能なのでしょうか。
それは、このやりとりが、MCP という、世間の多くの AI モデルが対応している機能拡張を使うときのやりとりに沿ったものだからです。

Action calling と MCP の共通点

下図は、世間一般の AI ツール(デスクトップにインストールするものや、クラウドサービスツール)で、MCP - Model Context Protocol を使って機能拡張するときのシーケンスです。上の、Action calling のシーケンスと見比べると、とてもよく似ていることが分かります。
それもそのはずで、実際は、Action calling の方が、MCP を使うときと同じ構成で通信を行なっています。そうすることで、OutSystems 独自の仕組みを、世間の多くの AI モデルに使わせることを可能にしているのです。

mcp-seq.png

[MCP アイコン画像の転載元]
https://github.com/modelcontextprotocol/modelcontextprotocol

ここまで一緒なんだったら、Action calling なんて独自の名前にしないで、MCP と呼べば良いのでは?と思うかもですが、MCP というのは、シーケンス図にある「AI ツール」と「MCP サーバー(各種ツール)」の間の通信のプロトコルのこと(シーケンス図の MCP アイコンの箇所)なので、アプリ内部でサーバーアクションを呼び出す Action calling では、MCP というプロトコル自体は使っていません。その前後の、AI モデル(LLM)との通信の部分で、Action calling の方式が、MCP の場合のそれに揃えられているだけなのです。
そのため、AI モデル(LLM)の側からは、Action calling を使っている場合も、MCP で機能拡張されたものとまったく同じものに見えていることになります。

MCP の解説で見かけることのある「LLM にとっての USB ハブのようなもの」という説明からは、ユーザーから見て、LLM の向こう側に MCP を介して外部ツールがつながるようなイメージが想起されますが、実際には、上のシーケンス図の通り、ユーザーが LLM の手前に相対している AI ツールが、MCP を介して、外部ツールと接続します。

Action calling を使うときの注意点

最後に、OutSystems で Action calling を使うにあたっての注意点です。

  • 名前や Description が大事 - AI モデルに渡される情報は、アクション名、Description、入力パラメーター情報(名前、データ型、Description)なので、AI モデルが理解しやすい内容を設定すること。
  • システムプロンプトも大事 - どういうときにどのアクションが適しているか、といった情報をシステムプロンプトに含めておくと適切に使ってもらいやすい。
  • 詳細かつ簡潔に - Action calling では、同じプロンプトやアクション情報が何度も送られることがあり、やりとりが肥大化しがちなので、詳細でありつつ簡潔に書くことを心掛ける。AI ツールと相談しながら設定すると良い。
  • (悲報)PE 無料枠では試しにくい - OutSystems Developer Cloud の無料で使える Personal Edition(PE)には、1,000 リクエスト/100 万トークンまで無料で使える AI モデルが用意されているが、この無料枠には、速度制限も設定されているため、裏で勝手に何度も AI モデルと通信を行なってしまう Action calling を使うと、この速度制限に割と容易に引っ掛かってしまう。1回目は通ったのに2回目は失敗、というのも起こりがち。

2025 年末に確認したときは、PE の AI モデル無料枠には、12,000 トークン/分、3 リクエスト/分の速度制限がありました。この制限下では、上で見た3回の通信を行なう実行は、頑張っても、1分間に1度までしか実行できないことになります。

まとめ

今年(2026 年)になって受験できるようになった、Agentic AI Specialist 試験で出題されることのある、OutSystems の機能 Action calling について、AI モデルとの間で行なわれる通信を中心に、あれこれ見てきました。

  • エージェントアクションに Action calling でサーバーアクションを設定すると、AI モデル(LLM)が判断してそのアクションを実行する
  • Action calling に設定するアクションの名前や Description を適切に記述しておく必要がある
  • Action calling は、世間の AI ツールが MCP で外部ツールを使うときの仕組みを利用している

これらの情報が、Agentic AI Specialist 試験のための予備知識の補強、そして楽しい開発に、すこしでもお役に立てていたら幸いです。

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?