DelphiでAIエージェントを構築する: 関数呼び出しとMCPツール利用

· コンポーネント

手っ取り早い答え: AIエージェントとは、あなた自身のコードを呼び出せる言語モデルにすぎません。あなたは一連のツールを記述し、モデルがいつそのうちの1つを呼び出すかを決め、あなたのアプリケーションがそれを実行して結果を返し、モデルはそこから続行します — タスクが完了するまでループします。sgcWebSocketsは、これをDelphiまたはC++ Builderで構築する2つの方法を提供します。プロバイダーの関数呼び出し(モデルがOpenAI、Anthropic、Geminiのコンポーネントを通じてあなたの関数のいずれかを呼び出すよう要求する)と MCP(オープンなModel Context Protocol。あなたのサーバーがツールを公開し、MCP対応のクライアントやエージェントがそれらを発見して呼び出せる)です。どちらも同じアイデアを使います。エージェントが存在する場所に合う方を選びます。

人は「エージェント」を特別なモデルだと想像することがあります。そうではありません。それはありふれたチャットモデルに、会話の外に手を伸ばせるようにする小さな制御ループをあなたのコードに加えたものです。モデルがデータベースを照会したり、ファイルを読んだり、内部APIを叩いたりする関数を呼び出せるようになると、それはチャットボットであることをやめ、行動できるようになります。以下の2つの経路は、誰がツールをホストするかという点だけが異なります。

エージェントのループ

どのエージェントも、プロバイダーに関係なく、同じサイクルに従います。

  1. ユーザーのメッセージを、ツール定義(名前、説明、パラメーター)のリストとともに送信する。
  2. モデルはプレーンテキストで回答するか、「これらの引数でツールXを呼び出せ」と返す。
  3. ツールを要求した場合、あなたのコードが実際の作業を実行し、結果を返す。
  4. モデルは結果を読み、別のツールを呼び出すか、最終的な回答を生成する。
  5. モデルがツールを要求しなくなるまで繰り返す。

モデルは自分では何も実行しません。呼び出しを提案するだけで、実際に何が実行されるかはあなたのDelphiコードが制御し続けます。これこそが、このパターンを実際のシステムの前に置いても安全である理由です。

経路1: プロバイダーの関数呼び出し

単一のモデルにあなたのコードへのアクセスを与える最も直接的な方法は、プロバイダー自身の関数呼び出しAPIです。利用可能な関数を宣言すると、モデルは必要なときにそのうちの1つを選び、あなたのコンポーネントでイベントが発生するので、結果を供給できます。OpenAIコンポーネントでは、アシスタント上でツールを一度定義してから、呼び出しを処理します。

Assistant := TsgcAIOpenAIAssistant.Create(nil);
Assistant.OpenAIOptions.ApiKey := 'sk-...';
Assistant.AssistantOptions.Name := 'Delphi Weather Bot';
Assistant.AssistantOptions.Instructions.Text :=
  'You are a weather bot. Use the provided functions to answer questions.';
Assistant.AssistantOptions.Model := 'gpt-4o';

// Describe the callable functions as JSON tool definitions
Assistant.AssistantOptions.Tools.Functions.Functions.Text :=
  '[{"type":"function","function":{"name":"get_current_temperature",' +
  '"description":"Get the current temperature for a specific location",' +
  '"parameters":{"type":"object","properties":{"location":{"type":"string"}},' +
  '"required":["location"]}}}]';

モデルが値を必要とすると決めると、コンポーネントは OnFunctionCall を発生させます。aRequest._Function._Name を調べてどのツールが要求されたかを知り、aResponse.Output に回答を書き込みます — その結果はそのままモデルにフィードバックされ、モデルは返答を完成させられます。

procedure TForm1.AssistantFunctionCall(Sender: TObject;
  const aRequest: TsgcOpenAIClass_ToolCall;
  const aResponse: TsgcHTTPOpenAI_ToolCall_Response);
begin
  if aRequest._Function._Name = 'get_current_temperature' then
    aResponse.Output := 30
  else if aRequest._Function._Name = 'get_rain_probability' then
    aResponse.Output := 10;
end;

そのイベントハンドラーこそがエージェントのループです。モデルがツールを要求するたびに、あなたはそれを実行して返します。コンポーネントは、モデルが必要なものをすべて得るまで会話を続けます。この経路は、エージェントがあなた自身のアプリケーション内に存在し、1つのプロバイダーと対話する場合に理想的です。同じ形がAnthropicとGeminiのコンポーネントでも利用できるので、ロックインされることはありません。完全な例についてはOpenAI関数呼び出しのウォークスルーをご覧ください。

経路2: MCPツール

関数呼び出しは、あなたのツールを1つのアプリ内の1つのモデルに結びつけます。Model Context Protocolは、同じアイデアを再利用可能なサービスに変えます。あなたのアプリケーションが一連のツールを標準的なJSON-RPCエンドポイント上で公開し、あらゆるMCP対応クライアントやエージェント — IDEアシスタント、デスクトップAIアプリ、あるいはあなた自身のエージェントループ — が接続し、それらのツールを発見して呼び出せます。ツールは一度書けば、すべてのMCPホストがそれを使えます。

sgcWebSocketsでは、サーバー側は TsgcWSAPIServer_MCP です。これをsgcWebSockets HTTPサーバーにアタッチし、Tools.AddTool でツールを登録し、受信した呼び出しを OnMCPRequestTool で処理します。

uses
  sgcWebSocket_Server, sgcAI, sgcAI_MCP_Classes, sgcAI_MCP_Server;

procedure TForm1.SetupMCPServer;
begin
  MCPServer.Server := Server;
  MCPServer.EndpointOptions.Endpoint := '/mcp';
  MCPServer.MCPOptions.ServerInfo.Name    := 'sgc-mcp-server';
  MCPServer.MCPOptions.ServerInfo.Version := '1.0.0';

  // Register a callable tool with a typed argument
  with MCPServer.Tools.AddTool('GetTemperature',
    'Get the actual temperature in a city.') do
    InputSchema.Properties.AddProperty('city', True);

  MCPServer.OnMCPRequestTool := MCPRequestTool;
  Server.Port   := 8080;
  Server.Active := True;
end;

接続されたクライアントがツールを呼び出すと、強く型付けされたリクエストとレスポンスのオブジェクトとともにイベントが発生します。ツール名を aRequest.Params.Name から、その引数を aRequest.Params.Arguments から読み取り、aResponse.Result.Content.AddText で回答を書き込みます。

procedure TForm1.MCPRequestTool(Sender: TObject;
  const aSession: TsgcAI_MCP_Session;
  const aRequest: TsgcAI_MCP_Request_ToolsCall;
  const aResponse: TsgcAI_MCP_Response_ToolsCall);
begin
  if aRequest.Params.Name = 'GetTemperature' then
    aResponse.Result.Content.AddText('The current temperature in ' +
      aRequest.Params.Arguments.Item[0].Value + ' is 22 Celsius');
end;

このハンドラーは関数呼び出しのイベントと同じ仕事をしますが、今やツールはネットワーク越しにあらゆるMCPホストから到達可能です。MCPは公開された標準なので、会話を駆動するエージェントはあなたが書いたものである必要はまったくありません。詳しくはMCP概要MCP Serverコンポーネントのページをご覧ください。

どちらの経路を使うべきか?

こうしたい場合…使うものコンポーネント
自分のアプリ内に存在し、1つのプロバイダーと対話するエージェントプロバイダーの関数呼び出しTsgcAIOpenAIAssistant
あらゆるMCP対応クライアントやエージェントが発見して呼び出せるツールMCPサーバーTsgcWSAPIServer_MCP
他人のMCPサーバーがホストするツールを呼び出すMCPクライアントTsgcWSAPIClient_MCP

この2つは互いに排他的ではありません。よくある設計は、自分自身のプライベートなツールにはプロバイダーの関数呼び出しを使い、同時にMCPクライアントとして組織内の他の場所にある共有ツールサーバーに接続する、アプリ内エージェントです。どちらを選んでも、あなたが書くループはほぼ同一に見えるので、後で両者を行き来するのは安価です。

はじめに

どちらの経路もsgcWebSocketsに付属しています(MCPサーバーとクライアントはEnterpriseコンポーネントです)。無料トライアルを入手し、関数呼び出しには TsgcAIOpenAIAssistant を、MCPサービスには TsgcWSAPIServer_MCP を配置し、ツールイベントを配線すれば、数行で自分自身のコードに対して行動できるエージェントが手に入ります。AI & LLM コンポーネントハブですべてのAI構築ブロックをご覧ください。

ご質問や、エージェントの設計にお手伝いが必要ですか? お問い合わせください — コードを書いた本人たちから返信が届きます。

関連