エージェント間でルーティングする¶
試験運用
エージェントルーティングは試験運用の機能であり、今後のリリースで変更される可能性があります。 フィードバックを お待ちしています。
異なるタスク向けのエージェントを構築する場合、各呼び出しをどのエージェントが
処理するかを実行時に選択するルーティング関数を定義できます。RoutedAgent は
この機能を提供し、エラー時のエージェントフォールバック、A/B テスト、計画モード、
入力の複雑さに基づく自動ルーティングを可能にします。選択されたエージェントが
出力を生成する前に失敗した場合、ルーティング関数はエラーコンテキスト付きで再度
呼び出され、フォールバックを選択できます。
RoutedAgent は、SequentialAgent や ParallelAgent のような
ワークフローエージェントとは異なります。ワークフロー
エージェントは複数のエージェントを固定パターンでオーケストレーションします。また、
LLM がどのエージェントへ引き継ぐかを決める
LLM 主導の委譲とも
異なります。RoutedAgent では、開発者が明示的なルーティング関数を書き、呼び出し
ごとに 1 つの エージェントを選択します。モデルレベルのルーティングについては
モデルルーティングを参照してください。
ルーティングの仕組み¶
RoutedAgent と RoutedLlm はどちらも、選択と
フェイルオーバーを処理する共通のルーティングユーティリティを基盤にしています。
ルーター関数は、利用可能なエージェントのマップと現在のコンテキストを受け取り、 実行するエージェントのキーを返します。同期関数でも非同期関数でもかまいません。
agents パラメータには、明示的なキーを持つ Record<string, BaseAgent>、または
エージェント配列を渡せます。配列を渡した場合は、各エージェントの name プロパティが
キーとして使用されます。
フェイルオーバーの動作:
- ルーターは最初の選択のために、まず
errorContextなしで呼び出されます。 - 選択されたエージェントが イベントを 1 つも yield する前に エラーを投げた
場合、ルーターは
failedKeysとlastErrorを含むerrorContext付きで再度 呼び出されます。 - 選択されたエージェントが イベントを yield した後に エラーを投げた場合、部分的な 結果がすでに出力されているため、エラーは再試行されず直接伝播します。
- すでに試行されたキーは再選択できません。ルーターが以前に失敗したキーを返した場合、 エラーが伝播します。
- ルーターが
undefinedを返した場合、ルーティングは停止し、最後のエラーが投げられます。
基本的な使用方法¶
複数のエージェントを作成し、キーを返すルーター関数を定義して、それらを
RoutedAgent でラップします。次の例では、呼び出し間で変化する外部設定値に基づいて
2 つのエージェント間でルーティングします。
import { LlmAgent, RoutedAgent, InMemoryRunner } from '@google/adk';
const agentA = new LlmAgent({
name: 'agent_a',
model: 'gemini-flash-latest',
instruction: 'You are Agent A. Always identify yourself as Agent A.',
});
const agentB = new LlmAgent({
name: 'agent_b',
model: 'gemini-flash-latest',
instruction: 'You are Agent B. Always identify yourself as Agent B.',
});
// External configuration that can change at runtime
const config = { selectedAgent: 'agent_a' };
const routedAgent = new RoutedAgent({
name: 'my_routed_agent',
agents: { agent_a: agentA, agent_b: agentB },
router: () => config.selectedAgent,
});
const runner = new InMemoryRunner({
agent: routedAgent,
appName: 'my_app',
});
const session = await runner.sessionService.createSession({
appName: 'my_app',
userId: 'user_1',
});
const run = runner.runAsync({
userId: 'user_1',
sessionId: session.id,
newMessage: { role: 'user', parts: [{ text: 'Who are you?' }] },
});
for await (const event of run) {
if (event.content?.parts?.[0]?.text) {
console.log(event.content.parts[0].text);
}
}
次の呼び出しの前に config.selectedAgent を 'agent_b' に変更すると、別の
エージェントへルーティングされます。
エラー時のフォールバック¶
エージェントが失敗すると、ルーターは errorContext 付きで再度呼び出され、
フォールバックを選択できます。フェイルオーバーは、エージェントがイベントを yield する
前に失敗した場合にのみ適用されます(ルーティングの仕組みを
参照)。次の例では、失敗したエージェントを再選択しないように
errorContext.failedKeys を確認します。
import {
BaseAgent,
InvocationContext,
LlmAgent,
RoutedAgent,
} from '@google/adk';
const primaryAgent = new LlmAgent({
name: 'primary',
model: 'gemini-flash-latest',
instruction: 'You are the primary agent.',
});
const fallbackAgent = new LlmAgent({
name: 'fallback',
model: 'gemini-pro-latest',
instruction: 'You are the fallback agent.',
});
const router = (
agents: Readonly<Record<string, BaseAgent>>,
context: InvocationContext,
// errorContext is provided when a previously selected agent fails
errorContext?: { failedKeys: ReadonlySet<string>; lastError: unknown },
) => {
if (!errorContext) {
return 'primary'; // Try primary first
}
if (errorContext.failedKeys.has('primary')) {
return 'fallback'; // Fall back if primary failed
}
return undefined; // No more options, propagate the error
};
const routedAgent = new RoutedAgent({
name: 'my_routed_agent',
agents: { primary: primaryAgent, fallback: fallbackAgent },
router,
});
計画モード¶
ルーターは任意の外部状態を読み取り、異なる指示、モデル、ツールを持つエージェントの 中から選択できます。これにより、エージェントが動作を動的に切り替える計画モードを 実装できます。たとえば、基本エージェントには読み書きツールを持たせ、計画エージェントは 読み取り専用アクセスに制限し、分析のためにより強力なモデルを使わせることができます。
次の例では、別の RoutedAgent 構成を示します。runner の完全な設定については
基本的な使用方法を参照してください。
import {
FunctionTool,
LlmAgent,
RoutedAgent,
} from '@google/adk';
import { z } from 'zod';
const readFileTool = new FunctionTool({
name: 'read_file',
description: 'Reads content from a file.',
parameters: z.object({ filePath: z.string() }),
execute: (args) => ({ content: `Contents of ${args.filePath}` }),
});
const writeFileTool = new FunctionTool({
name: 'write_file',
description: 'Writes content to a file.',
parameters: z.object({ filePath: z.string(), content: z.string() }),
execute: (args) => ({ result: `Wrote to ${args.filePath}` }),
});
const basicAgent = new LlmAgent({
name: 'basic',
model: 'gemini-flash-latest',
instruction: 'You are a basic assistant. Use tools to help the user.',
tools: [readFileTool, writeFileTool],
});
const planningAgent = new LlmAgent({
name: 'planning',
model: 'gemini-flash-latest',
instruction: 'You are a planning expert. Analyze carefully. You can only read files.',
tools: [readFileTool],
});
// Toggle this to switch between basic and planning agents
let planningMode = false;
const routedAgent = new RoutedAgent({
name: 'my_routed_agent',
agents: { basic: basicAgent, planning: planningAgent },
router: () => (planningMode ? 'planning' : 'basic'),
});
呼び出し前に planningMode = true を設定すると、制限されたツールセットと異なる
指示を持つ計画エージェントへルーティングされます。
複雑さに基づく自動ルーティング¶
ルーター関数は軽量な分類モデルを呼び出して入力を分類し、その結果に応じて異なる エージェントへルーティングできます。ルーターは非同期にできるため、エージェントを 選択する前に内部で LLM 呼び出しを実行できます。
次の例では、別の RoutedAgent 構成を示します。runner の完全な設定については
基本的な使用方法を参照してください。
import {
BaseAgent,
Gemini,
InvocationContext,
LlmAgent,
RoutedAgent,
} from '@google/adk';
const simpleAgent = new LlmAgent({
name: 'simple',
model: 'gemini-flash-latest',
instruction: 'You are a simple assistant for basic questions.',
});
const complexAgent = new LlmAgent({
name: 'complex',
model: 'gemini-pro-latest',
instruction: 'You are an expert assistant for complex analysis.',
});
// Lightweight model to classify input complexity
const classifierModel = new Gemini({ model: 'gemini-flash-latest' });
const router = async (
agents: Readonly<Record<string, BaseAgent>>,
context: InvocationContext,
) => {
// Extract the user's input text
const text = context.userContent?.parts?.[0]?.text || '';
if (!text) return 'simple';
const prompt =
`Classify this request as 'simple' or 'complex'. ` +
`Reply with ONLY that word.\nRequest: "${text}"`;
const generator = classifierModel.generateContentAsync({
contents: [{ role: 'user', parts: [{ text: prompt }] }],
toolsDict: {},
liveConnectConfig: {},
});
let classification = '';
for await (const resp of generator) {
if (resp.content?.parts?.[0]?.text) {
classification += resp.content.parts[0].text;
}
}
return classification.toLowerCase().includes('complex')
? 'complex'
: 'simple';
};
const routedAgent = new RoutedAgent({
name: 'my_routed_agent',
agents: { simple: simpleAgent, complex: complexAgent },
router,
});