A2A を基礎から学ぶ (3) さくらのAI Engine で複数Agentを管理するPlanner Agent を作る
今日は前回に引き続きA2Aを見ていきます。
今までのサンプルではA2AによるAgent間の情報の受け渡しにフォーカスをあてていました。このためAgentは2つしか存在しておらず、AgentAがコードの中で明示的にAgentBを呼び出す、という形をとっていました。
実際の利用状況はもっと複雑で複数のAgentが登録されており、必要な時に必要なAgentが呼び出される、という形態をとります。
この判断はプログラムで行うこともできますが、当然LLM側にあらかじめ利用可能なAgentの情報を渡しておいて、LLMに判断させるということができます。
Planner Agent と Worker Agent
Planner AgentというのはA2Aの規格で定められているものではありません。一般的な実装パターンとして複数のAgentが存在している場合、それらAgentをどのように利用すべきか?という流れを入力されたプロンプトに応じて動的に判断を行い、各Agentの呼び出しとタスク完了後のレスポンス入手、最終回答の生成までをつかさどります。
┌─────────────────────────────────────────────────────┐
│ AgentA (Planner) │
│ さくらAI Engineで「どのAgentに聞くべきか」を判断 │
└─────────────────────────────────────────────────────┘
│ │
↓ ↓
┌──────────────┐ ┌──────────────┐
│ AgentB │ │ AgentC │
│ 質問応答 │ │ 現在時刻 │
│ (さくらAI) │ │ (ローカル) │
└──────────────┘ └──────────────┘
質問に応じでAgentBを呼び出すか、AgentCを呼び出すかを判断し、呼び出したのちはその実行を見守り(ポーリングし)ながらレスポンスを最終的な結果として出力します。
またPlannerから呼び出され実際の処理を行うAgentをWorkerと呼びます。これもPlannerと同様にA2Aで定義されているものではありませんが、実装上の概念としてよくつかわれる呼び方です。
似たような呼称として、Orchestrator Agent、Coordinator Agent、Manager Agent、Dispatcher Agent、Router Agent 等がありますがおおよそ同じものを指しています。
さっそくやってみる
前回までのサンプルをもとにまずはagentA.js,agentC.js,package.jsonを以下に置き換えます。
// agentA.js - A2A Planner Agent
// ========================================================
// さくらAI Engineを使って適切なWorkerを選択するPlannerエージェント
// Function Callingは使用せず、LLMの判断で振り分け
// ========================================================
import fetch from 'node-fetch';
// Worker Agentの設定
const WORKERS = {
AGENT_B: {
url: 'http://localhost:3001',
name: 'AgentB'
},
AGENT_C: {
url: 'http://localhost:3002',
name: 'AgentC'
}
};
// さくらAI Engineの設定
const SAKURA_API_URL = 'https://api.ai.sakura.ad.jp/v1/chat/completions';
const SAKURA_API_KEY = 'xxf3e60b-7c0a-4685-b5eb-148fbe845cc5:mFhWGnNfWmfvy4psmZgWKQ4EkrzEy5MQxu9vuyV5';
const SAKURA_MODEL = 'gpt-oss-120b';
// ========================================================
// ユーティリティ: 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;
}
}
// ========================================================
// さくらAI Engineを呼び出す関数
// ========================================================
async function callSakuraAI(userMessage, systemPrompt) {
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({
model: SAKURA_MODEL,
messages: [
{ role: "system", content: systemPrompt },
{ role: "user", content: userMessage }
],
temperature: 0.3, // 判断の一貫性のため低めに
max_tokens: 100,
stream: false
})
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Sakura AI API error: ${response.status} - ${errorText}`);
}
const data = await response.json();
return data.choices[0].message.content.trim();
} catch (error) {
console.error('❌ さくらAI Engine呼び出しエラー:', error.message);
throw error;
}
}
// ========================================================
// Planner: 適切なAgentを選択
// ========================================================
async function selectAgent(question, agentCards) {
console.log('\n🧠 Planner: どのAgentに依頼すべきか判断中...\n');
// Agent Cardの情報をプロンプトに含める
const agentDescriptions = Object.entries(agentCards)
.map(([key, card]) => {
if (!card) return null;
const skills = card.skills.map(s => `${s.name}: ${s.description}`).join(', ');
return `- ${key}: ${card.name} - ${card.description} [スキル: ${skills}]`;
})
.filter(Boolean)
.join('\n');
const systemPrompt = `あなたはタスクを適切なAgentに振り分けるPlannerです。
以下のAgentが利用可能です:
${agentDescriptions}
ユーザーの質問を見て、どのAgentに依頼すべきか判断してください。
回答は必ず「AGENT_B」または「AGENT_C」のどちらか一方のみを返してください。
それ以外の文字は含めないでください。
判断基準:
- 時刻、日時、今何時などの質問 → AGENT_C
- それ以外の一般的な質問、知識に関する質問 → AGENT_B`;
const userMessage = `質問: ${question}
どのAgentに依頼すべきですか?「AGENT_B」または「AGENT_C」のみで答えてください。`;
// 📤 Plannerのリクエスト内容を表示
displayJson('📤 Planner → さくらAI Engine (Agent選択):', {
system: systemPrompt.substring(0, 200) + '...',
user: userMessage
});
const decision = await callSakuraAI(userMessage, systemPrompt);
// 📥 Plannerのレスポンス内容を表示
displayJson('📥 さくらAI Engine → Planner (判断結果):', {
decision: decision
});
// 判断結果をパース(AGENT_B または AGENT_C を抽出)
if (decision.includes('AGENT_C')) {
console.log('🎯 Planner判断: AGENT_C (時刻Agent) を選択\n');
return 'AGENT_C';
} else {
console.log('🎯 Planner判断: AGENT_B (質問応答Agent) を選択\n');
return 'AGENT_B';
}
}
// ========================================================
// タスクの作成
// ========================================================
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 デモ');
console.log(' さくらAI Engineで適切な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, card] of Object.entries(agentCards)) {
if (card) {
console.log(` - ${key}: ${card.name} - ${card.description}`);
}
}
// 2. テスト質問を準備
const questions = [
"今何時ですか?",
"A2Aプロトコルとは何ですか?簡潔に説明してください。",
"現在の日時を教えてください",
// "JavaScriptとは何ですか?",
];
// 3. 各質問を処理
for (const question of questions) {
console.log('\n' + '='.repeat(60));
console.log(`📝 ユーザーの質問: "${question}"`);
console.log('='.repeat(60));
// Plannerが適切なAgentを選択
const selectedAgent = await selectAgent(question, agentCards);
const worker = WORKERS[selectedAgent];
// 選択されたAgentにタスクを依頼
const taskId = await createTask(worker.url, worker.name, question);
// 完了を待つ
const result = await pollTaskStatus(worker.url, worker.name, taskId);
// 結果を表示
console.log('\n' + '='.repeat(60));
console.log(`🎯 ${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();
// agentC.js - A2A Server (Time Worker Agent)
// ========================================================
// 現在時刻を返すシンプルなWorkerエージェント
// ========================================================
import express from 'express';
import { v4 as uuidv4 } from 'uuid';
const app = express();
app.use(express.json());
const PORT = 3002;
// タスクの状態を保存(メモリ内)
const tasks = new Map();
// ========================================================
// Agent Card - エージェントのメタデータ
// ========================================================
const agentCard = {
name: "TimeAgent",
version: "1.0.0",
description: "現在時刻を返すエージェント。時間に関する質問に答えます。",
capabilities: ["text"],
skills: [
{
name: "getCurrentTime",
description: "現在の日時を返します。「今何時?」「現在時刻は?」などの質問に対応します。"
}
],
endpoint: `http://localhost:${PORT}`,
provider: "Local",
model: "none"
};
// ========================================================
// GET /.well-known/agent.json - エージェントカードの公開
// ========================================================
app.get('/.well-known/agent.json', (req, res) => {
res.json(agentCard);
});
// ========================================================
// POST /tasks - タスクの作成
// ========================================================
app.post('/tasks', (req, res) => {
// JSON-RPC形式のparamsから取得
const { instruction } = req.body.params || req.body;
const taskId = uuidv4();
const task = {
id: taskId,
status: "submitted",
instruction,
createdAt: new Date().toISOString(),
result: null
};
tasks.set(taskId, task);
console.log(`\n📥 Agent C: タスク受信 [${taskId}]`);
console.log(` 指示: "${instruction || '(指示なし)'}"`);
// タスクを非同期で処理
setTimeout(() => {
processTask(taskId);
}, 100);
res.status(201).json({
jsonrpc: "2.0",
id: taskId,
result: {
task_id: taskId,
status: "submitted"
}
});
});
// ========================================================
// GET /tasks/:taskId - タスク状態の取得
// ========================================================
app.get('/tasks/:taskId', (req, res) => {
const { taskId } = req.params;
const task = tasks.get(taskId);
if (!task) {
return res.status(404).json({
jsonrpc: "2.0",
error: {
code: -32001,
message: "Task not found"
}
});
}
res.json({
jsonrpc: "2.0",
id: taskId,
result: {
task_id: task.id,
status: task.status,
result: task.result
}
});
});
// ========================================================
// タスク処理(現在時刻を返す)
// ========================================================
function processTask(taskId) {
const task = tasks.get(taskId);
if (!task) return;
try {
// 状態を "working" に更新
task.status = "working";
console.log(`⚙️ Agent C: タスク処理中 [${taskId}]`);
// 現在時刻を取得
const now = new Date();
// 日本時間でフォーマット
const options = {
timeZone: 'Asia/Tokyo',
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
weekday: 'long'
};
const formattedTime = now.toLocaleString('ja-JP', options);
// 状態を "completed" に更新
task.status = "completed";
task.result = {
answer: `現在の日時は ${formattedTime} です。`,
raw: {
iso: now.toISOString(),
timestamp: now.getTime(),
timezone: 'Asia/Tokyo'
},
timestamp: now.toISOString()
};
console.log(`✅ Agent C: タスク完了 [${taskId}]`);
console.log(` 回答: "${task.result.answer}"`);
} catch (error) {
// エラーが発生した場合
task.status = "failed";
task.result = {
error: error.message,
timestamp: new Date().toISOString()
};
console.error(`❌ Agent C: タスク失敗 [${taskId}]`, error.message);
}
}
// ========================================================
// サーバー起動
// ========================================================
app.listen(PORT, () => {
console.log(`\n${'='.repeat(60)}`);
console.log(`🕐 Agent C (Time Worker) が起動しました`);
console.log(`${'='.repeat(60)}`);
console.log(`📍 エンドポイント: http://localhost:${PORT}`);
console.log(`📄 Agent Card: http://localhost:${PORT}/.well-known/agent.json`);
console.log(`🔧 機能: 現在時刻を返す`);
console.log(`${'='.repeat(60)}\n`);
});
{
"name": "a2a-simple-demo",
"version": "2.0.0",
"description": "A2Aプロトコル Planner + Worker エージェント連携デモ",
"type": "module",
"scripts": {
"agent-a": "node agentA.js",
"agent-b": "node agentB.js",
"agent-c": "node agentC.js"
},
"keywords": [
"a2a",
"agent",
"planner",
"sakura",
"ai"
],
"dependencies": {
"express": "^4.18.2",
"node-fetch": "^3.3.2",
"uuid": "^9.0.1"
}
}
npm installを実行して以下のコマンドでそれぞれAgentを起動します。それぞれ異なるターミナルで実行してください。
npm run agent-b
npm run agent-c
npm run agent-a
npm run agent-a
> a2a-simple-demo@2.0.0 agent-a
> node agentA.js
============================================================
🚀 A2A Planner Agent デモ
さくらAI Engineで適切なWorkerを選択
============================================================
📋 Worker Agentの情報を収集中...
📄 AgentB のカード情報を取得: SakuraAIAgent
📄 AgentC のカード情報を取得: TimeAgent
🤖 利用可能なWorker Agent:
- AGENT_B: SakuraAIAgent - さくらのAI Engineを使って質問に答えるエージェント
- AGENT_C: TimeAgent - 現在時刻を返すエージェント。時間に関する質問に答えます。
============================================================
📝 ユーザーの質問: "今何時ですか?"
============================================================
🧠 Planner: どのAgentに依頼すべきか判断中...
📤 Planner → さくらAI Engine (Agent選択):
────────────────────────────────────────────────────────────
{
"system": "あなたはタスクを適切なAgentに振り分けるPlannerです。\n以下のAgentが利用可能です:\n\n- AGENT_B: SakuraAIAgent - さくらのAI Engine を使って質問に答えるエージェント [スキル: answerQuestion: LLMを使って質問に答えます, generateText: 指示に基づいてテキストを生成します]\n- AGENT_C: TimeAgent ...",
"user": "質問: 今何時ですか?\n\nどのAgentに依頼すべきですか?「AGENT_B」または「AGENT_C」のみで答えてください。"
}
────────────────────────────────────────────────────────────
📥 さくらAI Engine → Planner (判断結果):
────────────────────────────────────────────────────────────
{
"decision": "AGENT_C"
}
────────────────────────────────────────────────────────────
🎯 Planner判断: AGENT_C (時刻Agent) を選択
📤 Planner → AgentC (タスク作成):
────────────────────────────────────────────────────────────
{
"jsonrpc": "2.0",
"method": "tasks.create",
"params": {
"instruction": "今何時ですか?"
}
}
────────────────────────────────────────────────────────────
📥 AgentC → Planner (タスク作成応答):
────────────────────────────────────────────────────────────
{
"jsonrpc": "2.0",
"id": "79cd2946-44cf-44ee-be7e-052f0ca562d5",
"result": {
"task_id": "79cd2946-44cf-44ee-be7e-052f0ca562d5",
"status": "submitted"
}
}
────────────────────────────────────────────────────────────
✓ タスクを作成しました [79cd2946-44cf-44ee-be7e-052f0ca562d5]
⏳ タスクの完了を待機中...
📋 [1/30] 状態: submitted
✅ [2/30] 状態: completed
✓ AgentC: タスク完了を確認
📥 AgentC → Planner (最終結果):
────────────────────────────────────────────────────────────
{
"jsonrpc": "2.0",
"id": "79cd2946-44cf-44ee-be7e-052f0ca562d5",
"result": {
"task_id": "79cd2946-44cf-44ee-be7e-052f0ca562d5",
"status": "completed",
"result": {
"answer": "現在の日時は 2025/11/21金曜日 13:34:08 です。",
"raw": {
"iso": "2025-11-21T04:34:08.832Z",
"timestamp": 1763699648832,
"timezone": "Asia/Tokyo"
},
"timestamp": "2025-11-21T04:34:08.832Z"
}
}
}
────────────────────────────────────────────────────────────
============================================================
🎯 AgentC からの回答:
============================================================
現在の日時は 2025/11/21金曜日 13:34:08 です。
📊 詳細: {"iso":"2025-11-21T04:34:08.832Z","timestamp":1763699648832,"timezone":"Asia/Tokyo"}
============================================================
⏳ 次の質問まで2秒待機...
============================================================
📝 ユーザーの質問: "A2Aプロトコルとは何ですか?簡潔に説明してください。"
============================================================
🧠 Planner: どのAgentに依頼すべきか判断中...
📤 Planner → さくらAI Engine (Agent選択):
────────────────────────────────────────────────────────────
{
"system": "あなたはタスクを適切なAgentに振り分けるPlannerです。\n以下のAgentが利用可能です:\n\n- AGENT_B: SakuraAIAgent - さくらのAI Engine を使って質問に答えるエージェント [スキル: answerQuestion: LLMを使って質問に答えます, generateText: 指示に基づいてテキストを生成します]\n- AGENT_C: TimeAgent ...",
"user": "質問: A2Aプロトコルとは何ですか?簡潔に説明してください。\n\nどのAgentに依頼すべきですか?「AGENT_B」または「AGENT_C」のみで答えてください。"
}
────────────────────────────────────────────────────────────
📥 さくらAI Engine → Planner (判断結果):
────────────────────────────────────────────────────────────
{
"decision": "AGENT_B"
}
────────────────────────────────────────────────────────────
🎯 Planner判断: AGENT_B (質問応答Agent) を選択
📤 Planner → AgentB (タスク作成):
────────────────────────────────────────────────────────────
{
"jsonrpc": "2.0",
"method": "tasks.create",
"params": {
"instruction": "A2Aプロトコルとは何ですか?簡潔に説明してください。"
}
}
────────────────────────────────────────────────────────────
📥 AgentB → Planner (タスク作成応答):
────────────────────────────────────────────────────────────
{
"jsonrpc": "2.0",
"id": "82fd5e52-885d-437f-a6b1-23fbbdc6d0b9",
"result": {
"task_id": "82fd5e52-885d-437f-a6b1-23fbbdc6d0b9",
"status": "submitted"
}
}
────────────────────────────────────────────────────────────
✓ タスクを作成しました [82fd5e52-885d-437f-a6b1-23fbbdc6d0b9]
⏳ タスクの完了を待機中...
📋 [1/30] 状態: submitted
⚙️ [2/30] 状態: working
⚙️ [3/30] 状態: working
⚙️ [4/30] 状態: working
✅ [5/30] 状態: completed
✓ AgentB: タスク完了を確認
📥 AgentB → Planner (最終結果):
────────────────────────────────────────────────────────────
{
"jsonrpc": "2.0",
"id": "82fd5e52-885d-437f-a6b1-23fbbdc6d0b9",
"result": {
"task_id": "82fd5e52-885d-437f-a6b1-23fbbdc6d0b9",
"status": "completed",
"result": {
"answer": "**A2A(Application‑to‑Application)プロトコル**は、**アプリケーション同士が直接データや機能をやり取りするための通信規約**です 。主な特徴は次のとおりです。\n\n| 特徴 | 内容 |\n|------|------|\n| **目的** | 異なるシステム・サービス間で、業務ロジックやデータをシームレスに連携させる |\n| **通信形態** | HTTP/HTTPS、WebSocket、gRPC、MQTT など、既存のトランスポート層上で実装されることが多い |\n| **データ形式** | JSON、XML、Protocol Buffers など、軽量で可読性のあるフォーマットが利用される |\n| **認証・認可** | OAuth、JWT、APIキーなどを組み合わせて安全性を確保 |\n| **利用例** | - マイクロサービス間の API 呼び出し<br>- IoT デバイスとクラウドアプリのデータ送受信<br>- 業務システム間のリアルタイム連携 |\n| **メリット** | - 開発者が直接 API を呼び出せるため実装がシンプル<br>- スケーラビリティと柔軟性が高い<br>- 標準化されたプロトコルで他システムとの互換性が保たれる |\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-21T04:34:16.340Z"
}
}
}
────────────────────────────────────────────────────────────
============================================================
🎯 AgentB からの回答:
============================================================
**A2A(Application‑to‑Application)プロトコル**は、**アプリケーション同士が直接データや機能をやり取りするための通信規約**です。主な特徴は次のとおりです。
| 特徴 | 内容 |
|------|------|
| **目的** | 異なるシステム・サービス間で、業務ロジックやデータをシームレスに連携させる |
| **通信形態** | HTTP/HTTPS、WebSocket、gRPC、MQTT など、既存のトランスポート層上で実装されることが多い |
| **データ形式** | JSON、XML、Protocol Buffers など、軽量で可読性のあるフォーマットが利用される |
| **認証・認可** | OAuth、JWT、APIキーなどを組み合わせて安全性を確保 |
| **利用例** | - マイクロサービス間の API 呼び出し<br>- IoT デバイスとクラウドアプリのデータ送受信<br>- 業務システム間のリアルタイム連携 |
| **メリット** | - 開発者が直接 API を呼び出せるため実装がシンプル<br>- スケーラビリティと柔軟性が高い<br>- 標準化されたプロトコルで他システム との互換性が保たれる |
| **デメリット** | - ネットワーク遅延や障害に対する耐性を設計で考慮する必要がある<br>- セキュリティ対策を怠ると情報漏洩リスクがある |
要するに、A2Aプロトコルは「**アプリケーション同士が相互に機能やデータをやり取りするための標準化された通信手段**
📊 モデル: gpt-oss-120b
============================================================
⏳ 次の質問まで2秒待機...
============================================================
📝 ユーザーの質問: "現在の日時を教えてください"
============================================================
🧠 Planner: どのAgentに依頼すべきか判断中...
📤 Planner → さくらAI Engine (Agent選択):
────────────────────────────────────────────────────────────
{
"system": "あなたはタスクを適切なAgentに振り分けるPlannerです。\n以下のAgentが利用可能です:\n\n- AGENT_B: SakuraAIAgent - さくらのAI Engine を使って質問に答えるエージェント [スキル: answerQuestion: LLMを使って質問に答えます, generateText: 指示に基づいてテキストを生成します]\n- AGENT_C: TimeAgent ...",
"user": "質問: 現在の日時を教えてください\n\nどのAgentに依頼すべきですか?「AGENT_B」または「AGENT_C」のみで答えてください。"
}
────────────────────────────────────────────────────────────
📥 さくらAI Engine → Planner (判断結果):
────────────────────────────────────────────────────────────
{
"decision": "AGENT_C"
}
────────────────────────────────────────────────────────────
🎯 Planner判断: AGENT_C (時刻Agent) を選択
📤 Planner → AgentC (タスク作成):
────────────────────────────────────────────────────────────
{
"jsonrpc": "2.0",
"method": "tasks.create",
"params": {
"instruction": "現在の日時を教えてください"
}
}
────────────────────────────────────────────────────────────
📥 AgentC → Planner (タスク作成応答):
────────────────────────────────────────────────────────────
{
"jsonrpc": "2.0",
"id": "bdfb047f-5a70-4374-9468-1b771955a6b8",
"result": {
"task_id": "bdfb047f-5a70-4374-9468-1b771955a6b8",
"status": "submitted"
}
}
────────────────────────────────────────────────────────────
✓ タスクを作成しました [bdfb047f-5a70-4374-9468-1b771955a6b8]
⏳ タスクの完了を待機中...
📋 [1/30] 状態: submitted
✅ [2/30] 状態: completed
✓ AgentC: タスク完了を確認
📥 AgentC → Planner (最終結果):
────────────────────────────────────────────────────────────
{
"jsonrpc": "2.0",
"id": "bdfb047f-5a70-4374-9468-1b771955a6b8",
"result": {
"task_id": "bdfb047f-5a70-4374-9468-1b771955a6b8",
"status": "completed",
"result": {
"answer": "現在の日時は 2025/11/21金曜日 13:34:19 です。",
"raw": {
"iso": "2025-11-21T04:34:19.971Z",
"timestamp": 1763699659971,
"timezone": "Asia/Tokyo"
},
"timestamp": "2025-11-21T04:34:19.971Z"
}
}
}
────────────────────────────────────────────────────────────
============================================================
🎯 AgentC からの回答:
============================================================
現在の日時は 2025/11/21金曜日 13:34:19 です。
📊 詳細: {"iso":"2025-11-21T04:34:19.971Z","timestamp":1763699659971,"timezone":"Asia/Tokyo"}
============================================================
✅ すべての質問が完了しました!
実行解説
まずPlannerは以下の通り2つのWorkerを認識します。
📋 Worker Agentの情報を収集中...
📄 AgentB のカード情報を取得: SakuraAIAgent
📄 AgentC のカード情報を取得: TimeAgent
🤖 利用可能なWorker Agent:
- AGENT_B: SakuraAIAgent - さくらのAI Engineを使って質問に答えるエージェント
- AGENT_C: TimeAgent - 現在時刻を返すエージェント。時間に関する質問に答えます。
その後入力されたプロンプトに応じてAI Engine側にAgentの選択を依頼しています。
📤 Planner → さくらAI Engine (Agent選択):
────────────────────────────────────────────────────────────
{
"system": "あなたはタスクを適切なAgentに振り分けるPlannerです。\n以下のAgentが利用可能です:\n\n- AGENT_B: SakuraAIAgent - さくらのAI Engine を使って質問に答えるエージェント [スキル: answerQuestion: LLMを使って質問に答えます, generateText: 指示に基づいてテキストを生成します]\n- AGENT_C: TimeAgent ...",
"user": "質問: 今何時ですか?\n\nどのAgentに依頼すべきですか?「AGENT_B」または「AGENT_C」のみで答えてください。"
}
────────────────────────────────────────────────────────────
📥 さくらAI Engine → Planner (判断結果):
────────────────────────────────────────────────────────────
{
"decision": "AGENT_C"
}
────────────────────────────────────────────────────────────
あとは前回と同じようにAgentの実行状態をPlannerがポーリングしながら見守り、レスポンスを出力させます。
Discussion