Mastra で作る AIエージェント(5) 軍師を気軽に交換
Mastra で作るAI エージェント というシリーズの第5回です。
前回までは、AIエージェントの構成を「三国志」になぞらえて以下のように把握しました。
- フロントに立つリーダーの劉備=エージェント:何をやるにしても軍師に相談
- 天才軍師・諸葛孔明=LLM:劉備に何かと助言するが、決して自分が直接前面に出ない
- 将軍たち=ツール:劉備に呼ばれて定型作業を遂行、RAG/API/MCPなどなど

ひととおり、「チーム劉備」の組成の仕方が分かったところで、今回からしばらくは、チームの組成以外の話をまとめてご紹介します。今回はモデルルーティングです。
軍師を交換してみたい
AIエージェントの頭脳を担うのは、言わずと知れたLLMであり、我々の三国志の例では「軍師・孔明」なのですが、「軍師は孔明でなきゃいかんのか? 司馬仲達や周瑜に取り替えたいのだが?」ということがありえます。現実世界では、例えば「OpenAIのgptだけではなく、AnthropicのClaudeやGoogleのGeminiも使いたい」という感じでしょうか。もちろん、交換可能です。

各社バラバラなAPIインタフェース
ただ、問題なのは、OpenAIとAnthropicとGoogleとで会社が違うものですから、各社のAPIの使い方が微妙に違うのです。Mastra を使わずに素で各社のAPIを使おうとするとこうなります。
OpenAI API を使う場合
import OpenAI from 'openai';
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
async function chatWithOpenAI(userMessage: string) {
const response = await openai.chat.completions.create({
model: 'gpt-4',
messages: [
{ role: 'system', content: 'あなたは親切なアシスタントです。' },
{ role: 'user', content: userMessage }
],
temperature: 0.7,
max_tokens: 1000,
});
return response.choices[0].message.content;
}
// 使用例
const result = await chatWithOpenAI('こんにちは!');
console.log(result);
Anthropic Claude API を使う場合
import Anthropic from '@anthropic-ai/sdk';
const anthropic = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY,
});
async function chatWithClaude(userMessage: string) {
const response = await anthropic.messages.create({
model: 'claude-3-5-sonnet-20241022',
max_tokens: 1000,
temperature: 0.7,
system: 'あなたは親切なアシスタントです。',
messages: [
{ role: 'user', content: userMessage }
],
});
return response.content[0].type === 'text'
? response.content[0].text
: '';
}
// 使用例
const result = await chatWithClaude('こんにちは!');
console.log(result);
Google Gemini API を使う場合
import { GoogleGenerativeAI } from '@google/generative-ai';
const genAI = new GoogleGenerativeAI(process.env.GOOGLE_API_KEY!);
async function chatWithGemini(userMessage: string) {
const model = genAI.getGenerativeModel({
model: 'gemini-pro',
generationConfig: {
temperature: 0.7,
maxOutputTokens: 1000,
},
});
const chat = model.startChat({
history: [
{
role: 'user',
parts: [{ text: 'あなたは親切なアシスタントです。' }],
},
{
role: 'model',
parts: [{ text: '承知しました。何でもお聞きください。' }],
},
],
});
const result = await chat.sendMessage(userMessage);
return result.response.text();
}
// 使用例
const result = await chatWithGemini('こんにちは!');
console.log(result);
これらのコードを見ると、以下の違いがあることがわかります:
-
初期化方法が異なる
- OpenAI:
new OpenAI({ apiKey }) - Claude:
new Anthropic({ apiKey }) - Gemini:
new GoogleGenerativeAI(apiKey)→getGenerativeModel()
- OpenAI:
-
メッセージの構造が異なる
- OpenAI:
messages配列にroleとcontent - Claude:
messages配列だがsystemは別パラメータ - Gemini:
chat.startChat()でhistoryを設定、parts配列
- OpenAI:
-
レスポンスの取得方法が異なる
- OpenAI:
response.choices[0].message.content - Claude:
response.content[0].text - Gemini:
result.response.text()
- OpenAI:
-
パラメータ名が異なる
- OpenAI:
max_tokens - Claude:
max_tokens - Gemini:
maxOutputTokens
- OpenAI:
ということで、「孔明から仲達にチェンジ!」と思うと、先ほどの20~30行を書き換える必要が出てきてしまいます。
1か所変更すればOK
Mastraを使えば、以下のように1か所変更するだけで切り替わります。これをモデルルーティングと呼びます。
export const weatherAgent = new Agent({
id: 'weather-agent',
name: 'Weather Agent',
instructions: `(プロンプト)`,
model: openai('gpt-5-mini'), /* <=========ここだけ変更すればOK */
tools: { weatherTool },
});
もっとも、これは Mastra 自体が内製しているのではなく、内部で業界標準の Vercel AI SDK というライブラリを使用して実現しています。
AIエージェントフレームワークの代表的な機能の一つ
モデルルーティングは、まさにMastraを含む「AIエージェントフレームワーク」の代表的な機能の一つです。「劉備チーム」のLLMに「司馬仲達」というのもなかなかショッキングですが、よいモデルが出てきたらサッと切り替えられる手軽さも重要です。
Discussion