MastraでAIエージェントとチャットしてみる
背景
弊社は全社的にAI活用が進んでいます。
私のチームでも週次でAI活用について共有する機会があります。
これに便乗してAIエージェントがどのようになっているのか概要を掴む為に今回TS製のAIエージェントフレーワークを触ってみます。
Mastraとは
オープンソースのTypeScriptエージェントフレームワーク
AIアプリケーションや機能を構築するために必要なプリミティブを提供するように設計されています
プリミティブとは
複雑な構造を形作る際の構成要素として使われる、単純あるいは基本的な構造や要素
Mastraの機能は様々ありますが今回はAIエージェントとチャットする機能について試します。
localでAIエージェントとchatする
以下に沿ってlocalでAIエージェントとchatします。
LLMプロバイダーからAPIキーを取得
プロジェクトを作成する
Open-Meteoのforecast エンドポイントを使用して天気レポートを生成する天気エージェントを構築します
npx create-mastra@latest
LLMプロバイダーにはOpenAIがrecommendedになっていました。
OpenAIのapi keyの入力に失敗したので、以下の指示の通りに.envファイルにapi keyを貼り付けました。
Add your OPENAI_API_KEY as an environment variable
in your .env file
今回はインタラクティブにインストールしましたが、手動でインストールも可能です。
localサーバーを立ち上げる
npm run dev
http://localhost:4111/
に遷移します。
チャットでメッセージを送る
「Weather Agent」をクリックします。
ここでチャットでメッセージを送ってみます。
なぜロンドンの天気を回答できたのか処理を追います。
処理の概要
まず以下のinstructions
でエージェントのコンテキストの定義をしているからです。
export const weatherAgent = new Agent({
name: 'Weather Agent',
instructions: `
You are a helpful weather assistant that provides accurate weather information.
Your primary function is to help users get weather details for specific locations. When responding:
- Always ask for a location if none is provided
- If the location name isn’t in English, please translate it
- If giving a location with multiple parts (e.g. "New York, NY"), use the most relevant part (e.g. "New York")
- Include relevant details like humidity, wind conditions, and precipitation
- Keep responses concise but informative
Use the weatherTool to fetch current weather data.
`,
instructions
の説明
referenceで検索
In Mastra, "instructions" are a key part of an agent's configuration. They define the agent's role, personality, task-specific guidance, constraints, and context for handling user requests.
You can retrieve an agent's instructions using the getInstructions() method. This method returns the instructions as a string, and if the instructions are defined as a function, it will resolve them (optionally using a RuntimeContext for dynamic behavior).
ソースに戻って instructions
に渡された文を訳すとこのエージェントは以下の役割があることがわかりました。
あなたは正確な気象情報を提供するお天気アシスタントです。
あなたの主な役割は、ユーザーが特定の場所の天気の詳細を得るのを助けることです。
応答するとき
- 場所が提供されていない場合は、常に場所を尋ねる
- 場所名が英語でない場合は翻訳してください。
- 複数の部分からなる場所(例:「New York, NY」)を指定する場合は、最も関連性の高い部分(例:「New York」)を使用してください。
- 湿度、風の状態、降水量など、関連する詳細を含める。
- 回答は簡潔に、しかし有益に保つ
weatherToolを使用して現在の気象データを取得する。
試しに「どこかの天気を教えて」と送ります。
確かに場所を答えるまで聞かれそうなことがわかりました。
次にそのコンテキストからlocation
を取得しています。
export const weatherTool = createTool({
id: 'get-weather',
description: 'Get current weather for a location',
inputSchema: z.object({
location: z.string().describe('City name'),
}),
outputSchema: z.object({
temperature: z.number(),
feelsLike: z.number(),
humidity: z.number(),
windSpeed: z.number(),
windGust: z.number(),
conditions: z.string(),
location: z.string(),
}),
execute: async ({ context }) => {
return await getWeather(context.location);
},
});
そしてそれをgetWeather
の引数に渡しています。
location
から緯度経度( latitude
,longitude
)を取得して天気の情報を取得しています。
const getWeather = async (location: string) => {
const geocodingUrl = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(location)}&count=1`;
const geocodingResponse = await fetch(geocodingUrl);
const geocodingData = (await geocodingResponse.json()) as GeocodingResponse;
if (!geocodingData.results?.[0]) {
throw new Error(`Location '${location}' not found`);
}
const { latitude, longitude, name } = geocodingData.results[0];
const weatherUrl = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}¤t=temperature_2m,apparent_temperature,relative_humidity_2m,wind_speed_10m,wind_gusts_10m,weather_code`;
const response = await fetch(weatherUrl);
const data = (await response.json()) as WeatherResponse;
return {
temperature: data.current.temperature_2m,
feelsLike: data.current.apparent_temperature,
humidity: data.current.relative_humidity_2m,
windSpeed: data.current.wind_speed_10m,
windGust: data.current.wind_gusts_10m,
conditions: getWeatherCondition(data.current.weather_code),
location: name,
};
};
最後に
- AIエージェントの開発を少し体験できて、貴重な機会でした。
- 複雑な文章をLLMで整理し、それをアプリケーションの処理に渡すことで、柔軟な対応が可能になることが理解できました。
作ったリポジトリは以下です。
Discussion