🙆

A2A を基礎から学ぶ (4) Function Calling による複数 Agent の選択的呼び出しの実装

に公開

https://zenn.dev/sakura_internet/articles/e29d64a9d211a4
https://zenn.dev/sakura_internet/articles/0f77e58048f906
https://zenn.dev/sakura_internet/articles/8e049c64fc00b1

前回までの手順で、PlannerAgentが与えられたプロンプトを元に、呼び出すべきAgentをLLMが判断する、というものを実装しました。

今日第4回はFunction Callingを使っていきます。

Function Calling とは

https://zenn.dev/kameoncloud/articles/68840c605ddca1

シンプルに言えばLLMに利用可能な外部ツールを登録しておき、LLMがプロンプトに応じてそれらの呼び出しをクライアント(エージェント)に指示する仕組みです。
この際LLMはかならずAgentを呼び出すわけではなく自身が保有する知識で回答するケースもあります。

2023年6月にOpenAIが発表した規格でGPT-3.5-turboとGPT-4のアップデートとして導入されました。そのあとClaude、Geminiにも採用されて広がりを見せました。

tools

Function Callingでは外部ツールをtoolsとして登録します。MCPやAgentなどもtoolsの一部です。
toolsは以下の形式でLLMに登録されます。

const tools = [
  {
    type: "function",
    function: {
      name: "get_weather",
      description: "指定した場所の現在の天気を取得します",
      parameters: {
        type: "object",
        properties: {
          location: {
            type: "string",
            description: "都市名(例:東京、大阪)"
          }
        },
        required: ["location"]
      }
    }
  }
];

const response = await fetch(API_URL, {
  body: JSON.stringify({
    model: "gpt-4",
    messages: [
      { role: "user", content: "東京の天気を教えて" }
    ],
    tools: tools
  })
});

その後プロンプトに応じて
LLMがクライアント(エージェント)に対して関数呼び出しを指示します。

{
  "choices": [
    {
      "message": {
        "role": "assistant",
        "content": null,
        "tool_calls": [
          {
            "id": "call_abc123",
            "type": "function",
            "function": {
              "name": "get_weather",
              "arguments": "{\"location\":\"東京\"}"
            }
          }
        ]
      },
      "finish_reason": "tool_calls"
    }
  ]
}

クライアントはツールを呼び出した後LLMにその結果を戻します。

const response = await fetch(API_URL, {
  body: JSON.stringify({
    model: "gpt-4",
    messages: [
      { role: "user", content: "東京の天気を教えて" },
      { role: "assistant", tool_calls: [...] },
      { role: "tool", tool_call_id: "call_abc123", content: '{"temperature":20,"condition":"晴れ"}' }
    ]
  })
});

tool_choice

Function Callingは類似のコンセプトを実現するMCPより、tool呼び出しのパラメータが備わっており制御しやすくなっているのが特徴です。

Function Calling と MCP

このようにFunction Callingは非常に便利なのですが、2024年にAnthropicが発表したMCPによりコミュニティの関心は一気にそっちに移動しました。主な違いは以下の通りです。

AnthropicはUSB-Cのようなものだ、と表現しましたが、通信レイヤーとして実装されプロトコールがオープンであったため、あとから別の開発者が開発したMCPをAI Agentに実装することが容易でした。
一方Function Callingはこの図の通り構成がモノリシックであるため後から別の開発者が作成したものを加えることが少し大変でした。

A2A と Function Calling

A2AとFunction Callingは非常に相性が良い組み合わせです。
Function Callingでは、Planner AgentがWorker Agentをtoolsとして登録できます。
つまり「AgentB」「AgentC」といった外部Agentを関数として定義し、LLMに「どのAgentを呼ぶべきか」を判断させることができます。
モノリシックだったFunction Callingの構成を、A2Aが通信レイヤーで補完することで、外部Agentを動的にtoolとして登録・呼び出しできるようになりました。

さっそくやってみる

では前回のagentA.jsを以下に置き換えます。

agentA.js
// agentA.js - A2A Planner Agent with Function Calling
// ========================================================
// さくらAI EngineのFunction Callingを使って適切なWorkerを選択
// ========================================================

import fetch from 'node-fetch';

// Worker Agentの設定
const WORKERS = {
  call_agent_b: {
    url: 'http://localhost:3001',
    name: 'AgentB',
    description: '質問応答Agent'
  },
  call_agent_c: {
    url: 'http://localhost:3002',
    name: 'AgentC',
    description: '時刻Agent'
  }
};

// さくらAI Engineの設定
const SAKURA_API_URL = 'https://api.ai.sakura.ad.jp/v1/chat/completions';
const SAKURA_API_KEY = 'xxx3e60b-7c0a-4685-b5eb-148fbe845cc5:mFhWGnNfWmfvy4psmZgWKQ4EkrzEy5MQxu9vuyxx';
const SAKURA_MODEL = 'gpt-oss-120b';

// ========================================================
// Function Calling用のツール定義
// ========================================================
const tools = [
  {
    type: "function",
    function: {
      name: "call_agent_b",
      description: "一般的な質問に答えるAgent。知識に関する質問、説明を求める質問、技術的な質問などに使用します。例:「A2Aプロトコルとは?」「JavaScriptとは?」「天気について教えて」",
      parameters: {
        type: "object",
        properties: {
          question: {
            type: "string",
            description: "Agentに送る質問内容"
          }
        },
        required: ["question"]
      }
    }
  },
  {
    type: "function",
    function: {
      name: "call_agent_c",
      description: "現在の日時・時刻を返すAgent。時間に関する質問に使用します。例:「今何時?」「現在時刻は?」「今日は何日?」「何曜日?」",
      parameters: {
        type: "object",
        properties: {
          question: {
            type: "string",
            description: "時刻に関する質問内容"
          }
        },
        required: ["question"]
      }
    }
  }
];

// ========================================================
// ユーティリティ: JSON整形表示
// ========================================================
function displayJson(label, data) {
  console.log(`\n${label}`);
  console.log('─'.repeat(60));
  console.log(JSON.stringify(data, null, 2));
  console.log('─'.repeat(60));
}

// ========================================================
// Agent Card の取得
// ========================================================
async function getAgentCard(agentUrl, agentName) {
  try {
    const response = await fetch(`${agentUrl}/.well-known/agent.json`);
    const card = await response.json();
    console.log(`📄 ${agentName} のカード情報を取得: ${card.name}`);
    return card;
  } catch (error) {
    console.error(`${agentName} のAgent Card取得に失敗:`, error.message);
    return null;
  }
}

// ========================================================
// Function Callingでエージェントを選択
// ========================================================
async function selectAgentWithFunctionCalling(question) {
  console.log('\n🧠 Planner: Function Callingでエージェントを選択中...\n');
  
  const requestBody = {
    model: SAKURA_MODEL,
    messages: [
      {
        role: "system",
        content: "あなたはユーザーの質問を適切なAgentに振り分けるPlannerです。質問内容を分析し、最適なAgentを呼び出してください。"
      },
      {
        role: "user",
        content: question
      }
    ],
    tools: tools,
    tool_choice: "auto",
    temperature: 0.3,
    max_tokens: 200
  };
  
  // 📤 Function Callingリクエストを表示
  displayJson('📤 Planner → さくらAI Engine (Function Calling):', {
    model: requestBody.model,
    messages: requestBody.messages,
    tools: requestBody.tools.map(t => ({
      name: t.function.name,
      description: t.function.description.substring(0, 50) + '...'
    })),
    tool_choice: requestBody.tool_choice
  });
  
  try {
    const response = await fetch(SAKURA_API_URL, {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Authorization': `Bearer ${SAKURA_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(requestBody)
    });

    if (!response.ok) {
      const errorText = await response.text();
      throw new Error(`Sakura AI API error: ${response.status} - ${errorText}`);
    }

    const data = await response.json();
    
    // 📥 レスポンスを表示
    displayJson('📥 さくらAI Engine → Planner (Function Calling結果):', {
      id: data.id,
      model: data.model,
      message: data.choices[0].message,
      finish_reason: data.choices[0].finish_reason
    });
    
    // Function Callingの結果を解析
    const message = data.choices[0].message;
    
    // tool_callsがある場合(Function Callingが発動)
    if (message.tool_calls && message.tool_calls.length > 0) {
      const toolCall = message.tool_calls[0];
      const functionName = toolCall.function.name;
      const args = JSON.parse(toolCall.function.arguments);
      
      console.log(`\n🎯 Function Calling結果:`);
      console.log(`   選択された関数: ${functionName}`);
      console.log(`   引数: ${JSON.stringify(args)}`);
      
      return {
        functionName: functionName,
        arguments: args,
        worker: WORKERS[functionName]
      };
    }
    
    // tool_callsがない場合(通常のテキスト応答)
    // フォールバックとしてAgentBを選択
    console.log('\n⚠️ Function Callingが発動しませんでした。AgentBにフォールバック。');
    console.log(`   LLMの応答: ${message.content}`);
    
    return {
      functionName: 'call_agent_b',
      arguments: { question: question },
      worker: WORKERS['call_agent_b']
    };
    
  } catch (error) {
    console.error('❌ Function Calling エラー:', error.message);
    throw error;
  }
}

// ========================================================
// タスクの作成
// ========================================================
async function createTask(agentUrl, agentName, instruction) {
  try {
    const requestBody = {
      jsonrpc: "2.0",
      method: "tasks.create",
      params: { instruction }
    };
    
    displayJson(`📤 Planner → ${agentName} (タスク作成):`, requestBody);
    
    const response = await fetch(`${agentUrl}/tasks`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(requestBody)
    });
    
    const data = await response.json();
    
    displayJson(`📥 ${agentName} → Planner (タスク作成応答):`, data);
    
    console.log(`\n✓ タスクを作成しました [${data.result.task_id}]`);
    return data.result.task_id;
  } catch (error) {
    console.error('❌ タスク作成に失敗:', error.message);
    throw error;
  }
}

// ========================================================
// タスク状態のポーリング
// ========================================================
async function pollTaskStatus(agentUrl, agentName, taskId, maxAttempts = 30) {
  console.log('\n⏳ タスクの完了を待機中...\n');
  
  for (let i = 0; i < maxAttempts; i++) {
    try {
      const response = await fetch(`${agentUrl}/tasks/${taskId}`);
      const data = await response.json();
      
      const status = data.result.status;
      const statusEmoji = {
        'submitted': '📋',
        'working': '⚙️',
        'completed': '✅',
        'failed': '❌'
      };
      
      console.log(`   ${statusEmoji[status] || '❓'} [${i + 1}/${maxAttempts}] 状態: ${status}`);
      
      if (status === 'completed') {
        console.log(`\n✓ ${agentName}: タスク完了を確認`);
        displayJson(`📥 ${agentName} → Planner (最終結果):`, data);
        return data.result;
      }
      
      if (status === 'failed') {
        throw new Error('タスクが失敗しました: ' + (data.result.result?.error || '不明なエラー'));
      }
      
      await new Promise(resolve => setTimeout(resolve, 1000));
      
    } catch (error) {
      console.error('❌ タスク状態取得に失敗:', error.message);
      throw error;
    }
  }
  
  throw new Error('タスク完了のタイムアウト');
}

// ========================================================
// メイン処理
// ========================================================
async function main() {
  console.log('\n' + '='.repeat(60));
  console.log('🚀 A2A Planner Agent デモ (Function Calling版)');
  console.log('   さくらAI EngineのFunction Callingで適切なWorkerを選択');
  console.log('='.repeat(60));
  
  try {
    // 1. すべてのWorker AgentのCardを取得
    console.log('\n📋 Worker Agentの情報を収集中...\n');
    
    const agentCards = {};
    for (const [key, worker] of Object.entries(WORKERS)) {
      agentCards[key] = await getAgentCard(worker.url, worker.name);
    }
    
    // 利用可能なAgentを表示
    console.log('\n🤖 利用可能なWorker Agent:');
    for (const [key, worker] of Object.entries(WORKERS)) {
      console.log(`   - ${key}: ${worker.name} - ${worker.description}`);
    }
    
    // 2. テスト質問を準備
    const questions = [
      "今何時ですか?",
      "A2Aプロトコルとは何ですか?簡潔に説明してください。",
      "現在の日時を教えてください",
    ];
    
    // 3. 各質問を処理
    for (const question of questions) {
      console.log('\n' + '='.repeat(60));
      console.log(`📝 ユーザーの質問: "${question}"`);
      console.log('='.repeat(60));
      
      // Function Callingで適切なAgentを選択
      const selection = await selectAgentWithFunctionCalling(question);
      
      console.log(`\n🎯 選択結果: ${selection.worker.name} (${selection.worker.description})`);
      
      // 選択されたAgentにタスクを依頼
      const taskId = await createTask(
        selection.worker.url,
        selection.worker.name,
        selection.arguments.question || question
      );
      
      // 完了を待つ
      const result = await pollTaskStatus(
        selection.worker.url,
        selection.worker.name,
        taskId
      );
      
      // 結果を表示
      console.log('\n' + '='.repeat(60));
      console.log(`🎯 ${selection.worker.name} からの回答:`);
      console.log('='.repeat(60));
      console.log(result.result.answer);
      
      if (result.result.model) {
        console.log(`\n📊 モデル: ${result.result.model}`);
      }
      if (result.result.raw) {
        console.log(`📊 詳細: ${JSON.stringify(result.result.raw)}`);
      }
      console.log('='.repeat(60));
      
      // 次の質問の前に少し待機
      if (questions.indexOf(question) < questions.length - 1) {
        console.log('\n⏳ 次の質問まで2秒待機...\n');
        await new Promise(resolve => setTimeout(resolve, 2000));
      }
    }
    
    console.log('\n✅ すべての質問が完了しました!\n');
    
  } catch (error) {
    console.error('\n❌ エラーが発生しました:', error.message);
    console.error(error.stack);
    process.exit(1);
  }
}

// 実行
main();

実行

npm run agent-a

> a2a-simple-demo@2.0.0 agent-a
> node agentA.js


============================================================
🚀 A2A Planner Agent デモ (Function Calling版)
   さくらAI EngineのFunction Callingで適切なWorkerを選択
============================================================

📋 Worker Agentの情報を収集中...

📄 AgentB のカード情報を取得: SakuraAIAgent
📄 AgentC のカード情報を取得: TimeAgent

🤖 利用可能なWorker Agent:
   - call_agent_b: AgentB - 質問応答Agent
   - call_agent_c: AgentC - 時刻Agent

============================================================
📝 ユーザーの質問: "今何時ですか?"
============================================================

🧠 Planner: Function Callingでエージェントを選択中...


📤 Planner → さくらAI Engine (Function Calling):
────────────────────────────────────────────────────────────
{
  "model": "gpt-oss-120b",
  "messages": [
    {
      "role": "system",
      "content": "あなたはユーザーの質問を適切なAgentに振り分けるPlannerです。質問内容を分析し、最適なAgentを呼び出してください。"
    },
    {
      "role": "user",
      "content": "今何時ですか?"
    }
  ],
  "tools": [
    {
      "name": "call_agent_b",
      "description": "一般的な質問に答えるAgent。知識に関する質問、説明を求める質問、技術的な質問などに使用します。例..."
    },
    {
      "name": "call_agent_c",
      "description": "現在の日時・時刻を返すAgent。時間に関する質問に使用します。例:「今何時?」「現在時刻は?」「今..."
    }
  ],
  "tool_choice": "auto"
}
────────────────────────────────────────────────────────────

📥 さくらAI Engine → Planner (Function Calling結果):
────────────────────────────────────────────────────────────
{
  "id": "chatcmpl-59a1dcadd35844babe0d86b6a6321579",
  "model": "gpt-oss-120b",
  "message": {
    "role": "assistant",
    "content": "{\n  \"question\": \"今何時ですか?\"\n}",
    "refusal": null,
    "annotations": null,
    "audio": null,
    "function_call": null,
    "tool_calls": [
      {
        "id": "chatcmpl-tool-030595f359414a1abd1fb5aecacf7162",
        "type": "function",
        "function": {
          "name": "call_agent_c",
          "arguments": "{\n  \"question\": \"今何時ですか?\"\n}"
        }
      }
    ],
    "reasoning_content": "User asks \"今何時ですか?\" which is a time query. Should call agent_c."
  },
  "finish_reason": "tool_calls"
}
────────────────────────────────────────────────────────────

🎯 Function Calling結果:
   選択された関数: call_agent_c
   引数: {"question":"今何時ですか?"}

🎯 選択結果: AgentC (時刻Agent)

📤 Planner → AgentC (タスク作成):
────────────────────────────────────────────────────────────
{
  "jsonrpc": "2.0",
  "method": "tasks.create",
  "params": {
    "instruction": "今何時ですか?"
  }
}
────────────────────────────────────────────────────────────

📥 AgentC → Planner (タスク作成応答):
────────────────────────────────────────────────────────────
{
  "jsonrpc": "2.0",
  "id": "89cdcd58-94f1-4fb0-8433-208373e1108e",
  "result": {
    "task_id": "89cdcd58-94f1-4fb0-8433-208373e1108e",
    "status": "submitted"
  }
}
────────────────────────────────────────────────────────────

✓ タスクを作成しました [89cdcd58-94f1-4fb0-8433-208373e1108e]

⏳ タスクの完了を待機中...

   📋 [1/30] 状態: submitted
   ✅ [2/30] 状態: completed

✓ AgentC: タスク完了を確認

📥 AgentC → Planner (最終結果):
────────────────────────────────────────────────────────────
{
  "jsonrpc": "2.0",
  "id": "89cdcd58-94f1-4fb0-8433-208373e1108e",
  "result": {
    "task_id": "89cdcd58-94f1-4fb0-8433-208373e1108e",
    "status": "completed",
    "result": {
      "answer": "現在の日時は 2025/11/22土曜日 09:15:37 です。",
      "raw": {
        "iso": "2025-11-22T00:15:37.435Z",
        "timestamp": 1763770537435,
        "timezone": "Asia/Tokyo"
      },
      "timestamp": "2025-11-22T00:15:37.435Z"
    }
  }
}
────────────────────────────────────────────────────────────

============================================================
🎯 AgentC からの回答:
============================================================
現在の日時は 2025/11/22土曜日 09:15:37 です。
📊 詳細: {"iso":"2025-11-22T00:15:37.435Z","timestamp":1763770537435,"timezone":"Asia/Tokyo"}
============================================================

⏳ 次の質問まで2秒待機...


============================================================
📝 ユーザーの質問: "A2Aプロトコルとは何ですか?簡潔に説明してください。"
============================================================

🧠 Planner: Function Callingでエージェントを選択中...


📤 Planner → さくらAI Engine (Function Calling):
────────────────────────────────────────────────────────────
{
  "model": "gpt-oss-120b",
  "messages": [
    {
      "role": "system",
      "content": "あなたはユーザーの質問を適切なAgentに振り分けるPlannerです。質問内容を分析し、最適なAgentを呼び出してください。"
    },
    {
      "role": "user",
      "content": "A2Aプロトコルとは何ですか?簡潔に説明してください。"
    }
  ],
  "tools": [
    {
      "name": "call_agent_b",
      "description": "一般的な質問に答えるAgent。知識に関する質問、説明を求める質問、技術的な質問などに使用します。例..."
    },
    {
      "name": "call_agent_c",
      "description": "現在の日時・時刻を返すAgent。時間に関する質問に使用します。例:「今何時?」「現在時刻は?」「今..."
    }
  ],
  "tool_choice": "auto"
}
────────────────────────────────────────────────────────────

📥 さくらAI Engine → Planner (Function Calling結果):
────────────────────────────────────────────────────────────
{
  "id": "chatcmpl-ef9eff272d04489584e3ebf949be8820",
  "model": "gpt-oss-120b",
  "message": {
    "role": "assistant",
    "content": "{\n  \"question\": \"A2Aプロトコルとは何ですか?簡潔に説明してください。\"\n}",
    "refusal": null,
    "annotations": null,
    "audio": null,
    "function_call": null,
    "tool_calls": [
      {
        "id": "chatcmpl-tool-6f38c2d84f064d9cad4414fe6d5ed848",
        "type": "function",
        "function": {
          "name": "call_agent_b",
          "arguments": "{\n  \"question\": \"A2Aプロトコルとは何ですか?簡潔に説明してください。\"\n}"
        }
      }
    ],
    "reasoning_content": "User asks: \"A2Aプロトコルとは何ですか?簡潔に説明してください。\" This is a knowledge question. Use call_agent_b."  
  },
  "finish_reason": "tool_calls"
}
────────────────────────────────────────────────────────────

🎯 Function Calling結果:
   選択された関数: call_agent_b
   引数: {"question":"A2Aプロトコルとは何ですか?簡潔に説明してください。"}

🎯 選択結果: AgentB (質問応答Agent)

📤 Planner → AgentB (タスク作成):
────────────────────────────────────────────────────────────
{
  "jsonrpc": "2.0",
  "method": "tasks.create",
  "params": {
    "instruction": "A2Aプロトコルとは何ですか?簡潔に説明してください。"
  }
}
────────────────────────────────────────────────────────────

📥 AgentB → Planner (タスク作成応答):
────────────────────────────────────────────────────────────
{
  "jsonrpc": "2.0",
  "id": "58b1c6dd-321c-4621-91a5-ce37c9f44cf4",
  "result": {
    "task_id": "58b1c6dd-321c-4621-91a5-ce37c9f44cf4",
    "status": "submitted"
  }
}
────────────────────────────────────────────────────────────

✓ タスクを作成しました [58b1c6dd-321c-4621-91a5-ce37c9f44cf4]

⏳ タスクの完了を待機中...

   📋 [1/30] 状態: submitted
   ⚙️ [2/30] 状態: working
   ⚙️ [3/30] 状態: working
   ⚙️ [4/30] 状態: working
   ✅ [5/30] 状態: completed

✓ AgentB: タスク完了を確認

📥 AgentB → Planner (最終結果):
────────────────────────────────────────────────────────────
{
  "jsonrpc": "2.0",
  "id": "58b1c6dd-321c-4621-91a5-ce37c9f44cf4",
  "result": {
    "task_id": "58b1c6dd-321c-4621-91a5-ce37c9f44cf4",
    "status": "completed",
    "result": {
      "answer": "**A2A(Application‑to‑Application)プロトコル**は、**アプリケーション同士が直接データや機能をやり取りするための通信手順**を指 します。主に以下の特徴があります。\n\n| 項目 | 内容 |\n|------|------|\n| **目的** | 異なるシステム・サービス間で業務ロジックやデータをリアルタイムに連携させる |\n| **レイヤー** | OSI参照モデルの上位、**アプリケーション層**で動作(ネットワーク層は TCP/IP など既存プロトコルに委任) |\n| **実装例** | - HTTP/HTTPS + REST API<br>- SOAP Web Service<br>- メッセージキュー(RabbitMQ、Kafka、IBM MQ など)<br>- gRPC |\n| **利用シーン** | - ERP と CRM のデータ同期<br>- マイクロサービス間の相互呼び出し<br>- IoT デバイスとバックエンドシステムの連携 |\n| **利点** | - システム間の結合度が低く、変更に強い<br>- 異種プラットフォーム(Java, .NET, Python 等)でも相互利用可能 |\n| **課題** | - インターフェース設計・バージョニ ング管理が必要<br>- セキュリティ(認証・暗号化)とトランザクション管理の設計が重要 |\n\n要するに、A2Aプロトコルは「**アプリケーションレベルでの直接通信**」を標準化・抽象化した仕組みで、企業システムやクラウドサービスの統合に広く利用されています",
      "model": "gpt-oss-120b",
      "usage": {
        "prompt_tokens": 124,
        "total_tokens": 624,
        "completion_tokens": 500,
        "prompt_tokens_details": null
      },
      "timestamp": "2025-11-22T00:15:44.924Z"
    }
  }
}
────────────────────────────────────────────────────────────

============================================================
🎯 AgentB からの回答:
============================================================
**A2A(Application‑to‑Application)プロトコル**は、**アプリケーション同士が直接データや機能をやり取りするための通信手順**を指します。主に以下の特徴があります。

| 項目 | 内容 |
|------|------|
| **目的** | 異なるシステム・サービス間で業務ロジックやデータをリアルタイムに連携させる |
| **レイヤー** | OSI参照モデルの上位、**アプリケーション層**で動作(ネットワーク層は TCP/IP など既存プロトコルに委任) |
| **実装例** | - HTTP/HTTPS + REST API<br>- SOAP Web Service<br>- メッセージキュー(RabbitMQ、Kafka、IBM MQ など)<br>- gRPC |
| **利用シーン** | - ERP と CRM のデータ同期<br>- マイクロサービス間の相互呼び出し<br>- IoT デバイスとバックエンドシステムの連携 |
| **利点** | - システム間の結合度が低く、変更に強い<br>- 異種プラットフォーム(Java, .NET, Python 等)でも相互利用可能 |
| **課題** | - インターフェース設計・バージョニング管理が必要<br>- セキュリティ(認証・暗号化)とトランザクション管理の設計が重要 |

要するに、A2Aプロトコルは「**アプリケーションレベルでの直接通信**」を標準化・抽象化した仕組みで、企業システムやクラウドサービスの統合に広く利用されています

📊 モデル: gpt-oss-120b
============================================================

⏳ 次の質問まで2秒待機...


============================================================
📝 ユーザーの質問: "現在の日時を教えてください"
============================================================

🧠 Planner: Function Callingでエージェントを選択中...


📤 Planner → さくらAI Engine (Function Calling):
────────────────────────────────────────────────────────────
{
  "model": "gpt-oss-120b",
  "messages": [
    {
      "role": "system",
      "content": "あなたはユーザーの質問を適切なAgentに振り分けるPlannerです。質問内容を分析し、最適なAgentを呼び出してください。"
    },
    {
      "role": "user",
      "content": "現在の日時を教えてください"
    }
  ],
  "tools": [
    {
      "name": "call_agent_b",
      "description": "一般的な質問に答えるAgent。知識に関する質問、説明を求める質問、技術的な質問などに使用します。例..."
    },
    {
      "name": "call_agent_c",
      "description": "現在の日時・時刻を返すAgent。時間に関する質問に使用します。例:「今何時?」「現在時刻は?」「今..."
    }
  ],
  "tool_choice": "auto"
}
────────────────────────────────────────────────────────────

📥 さくらAI Engine → Planner (Function Calling結果):
────────────────────────────────────────────────────────────
{
  "id": "chatcmpl-7b22f224ae504e8a9a5b55bd64ea9690",
  "model": "gpt-oss-120b",
  "message": {
    "role": "assistant",
    "content": "{\n  \"question\": \"現在の日時を教えてください\"\n}",
    "refusal": null,
    "annotations": null,
    "audio": null,
    "function_call": null,
    "tool_calls": [
      {
        "id": "chatcmpl-tool-26b07689490f4e6dbe7bffe539c12d3d",
        "type": "function",
        "function": {
          "name": "call_agent_c",
          "arguments": "{\n  \"question\": \"現在の日時を教えてください\"\n}"
        }
      }
    ],
    "reasoning_content": "User asks for current date and time. Use call_agent_c."
  },
  "finish_reason": "tool_calls"
}
────────────────────────────────────────────────────────────

🎯 Function Calling結果:
   選択された関数: call_agent_c
   引数: {"question":"現在の日時を教えてください"}

🎯 選択結果: AgentC (時刻Agent)

📤 Planner → AgentC (タスク作成):
────────────────────────────────────────────────────────────
{
  "jsonrpc": "2.0",
  "method": "tasks.create",
  "params": {
    "instruction": "現在の日時を教えてください"
  }
}
────────────────────────────────────────────────────────────

📥 AgentC → Planner (タスク作成応答):
────────────────────────────────────────────────────────────
{
  "jsonrpc": "2.0",
  "id": "8f49c32b-5fbd-4228-b1e2-47a09cbc4e7d",
  "result": {
    "task_id": "8f49c32b-5fbd-4228-b1e2-47a09cbc4e7d",
    "status": "submitted"
  }
}
────────────────────────────────────────────────────────────

✓ タスクを作成しました [8f49c32b-5fbd-4228-b1e2-47a09cbc4e7d]

⏳ タスクの完了を待機中...

   📋 [1/30] 状態: submitted
   ✅ [2/30] 状態: completed

✓ AgentC: タスク完了を確認

📥 AgentC → Planner (最終結果):
────────────────────────────────────────────────────────────
{
  "jsonrpc": "2.0",
  "id": "8f49c32b-5fbd-4228-b1e2-47a09cbc4e7d",
  "result": {
    "task_id": "8f49c32b-5fbd-4228-b1e2-47a09cbc4e7d",
    "status": "completed",
    "result": {
      "answer": "現在の日時は 2025/11/22土曜日 09:15:48 です。",
      "raw": {
        "iso": "2025-11-22T00:15:48.214Z",
        "timestamp": 1763770548214,
        "timezone": "Asia/Tokyo"
      },
      "timestamp": "2025-11-22T00:15:48.214Z"
    }
  }
}
────────────────────────────────────────────────────────────

============================================================
🎯 AgentC からの回答:
============================================================
現在の日時は 2025/11/22土曜日 09:15:48 です。
📊 詳細: {"iso":"2025-11-22T00:15:48.214Z","timestamp":1763770548214,"timezone":"Asia/Tokyo"}
============================================================

✅ すべての質問が完了しました!

今回の肝は以下の部分です。

📤 Planner → さくらAI Engine (Function Calling):
────────────────────────────────────────────────────────────
{
  "model": "gpt-oss-120b",
  "messages": [
    {
      "role": "system",
      "content": "あなたはユーザーの質問を適切なAgentに振り分けるPlannerです。質問内容を分析し、最適なAgentを呼び出してください。"
    },
    {
      "role": "user",
      "content": "今何時ですか?"
    }
  ],
  "tools": [
    {
      "name": "call_agent_b",
      "description": "一般的な質問に答えるAgent。知識に関する質問、説明を求める質問、技術的な質問などに使用します。例..."
    },
    {
      "name": "call_agent_c",
      "description": "現在の日時・時刻を返すAgent。時間に関する質問に使用します。例:「今何時?」「現在時刻は?」「今..."
    }
  ],
  "tool_choice": "auto"
}
────────────────────────────────────────────────────────────

📥 さくらAI Engine → Planner (Function Calling結果):
────────────────────────────────────────────────────────────
{
  "id": "chatcmpl-59a1dcadd35844babe0d86b6a6321579",
  "model": "gpt-oss-120b",
  "message": {
    "role": "assistant",
    "content": "{\n  \"question\": \"今何時ですか?\"\n}",
    "refusal": null,
    "annotations": null,
    "audio": null,
    "function_call": null,
    "tool_calls": [
      {
        "id": "chatcmpl-tool-030595f359414a1abd1fb5aecacf7162",
        "type": "function",
        "function": {
          "name": "call_agent_c",
          "arguments": "{\n  \"question\": \"今何時ですか?\"\n}"
        }
      }
    ],
    "reasoning_content": "User asks \"今何時ですか?\" which is a time query. Should call agent_c."
  },
  "finish_reason": "tool_calls"
}
────────────────────────────────────────────────────────────

function callingにより登録されたツールからLLMが"name": "call_agent_c",というレスポンスでAgentCの利用を指定しています。

さくらインターネット株式会社

Discussion