});
```
+ ### ワークフローを作成する
+
+ `weather-workflow.ts` ファイルを作成します:
+
+ ```bash copy
+ mkdir -p src/mastra/workflows && touch src/mastra/workflows/weather-workflow.ts
+ ```
+
+ 次のコードを追加します:
+
+ ```ts filename="src/mastra/workflows/weather-workflow.ts" showLineNumbers copy
+ import { openai } from "@ai-sdk/openai";
+ import { Agent } from "@mastra/core/agent";
+ import { createStep, createWorkflow } from "@mastra/core/workflows";
+ import { z } from "zod";
+
+ const llm = openai("gpt-4o-mini");
+
+ const agent = new Agent({
+ name: "Weather Agent",
+ model: llm,
+ instructions: `
+ あなたは天候に基づいた計画を得意とする、地域のアクティビティと旅行のエキスパートです。天気データを分析し、実用的なアクティビティの提案を行ってください。
+
+ 予報の各日について、必ず以下のフォーマットで回答してください:
+
+ 📅 [曜日, 月 日, 年]
+ ═══════════════════════════
+
+ 🌡️ 天気概要
+ • 状況: [簡単な説明]
+ • 気温: [X°C/Y°F から A°C/B°F]
+ • 降水確率: [X%の確率]
+
+ 🌅 午前のアクティビティ
+ 屋外:
+ • [アクティビティ名] - [特定の場所やルートを含む簡単な説明]
+ 最適な時間帯: [具体的な時間帯]
+ 注意: [関連する天候の注意点]
+
+ 🌞 午後のアクティビティ
+ 屋外:
+ • [アクティビティ名] - [特定の場所やルートを含む簡単な説明]
+ 最適な時間帯: [具体的な時間帯]
+ 注意: [関連する天候の注意点]
+
+ 🏠 屋内の代替案
+ • [アクティビティ名] - [特定の施設を含む簡単な説明]
+ 最適: [この代替案を推奨する天候条件]
+
+ ⚠️ 特別な注意事項
+ • [関連する天候警報、UV指数、風の状況など]
+
+ ガイドライン:
+ - 1日につき2~3件の時間指定の屋外アクティビティを提案すること
+ - 1~2件の屋内バックアップ案を含めること
+ - 降水確率が50%を超える場合は屋内アクティビティを優先すること
+ - すべてのアクティビティはその場所に特化したものであること
+ - 具体的な施設、トレイル、場所を含めること
+ - 気温に応じてアクティビティの強度を考慮すること
+ - 説明は簡潔かつ有益にすること
+
+ 一貫性のため、必ずこのフォーマット(絵文字やセクション見出しを含む)を守ってください。
+ `,
+ });
+
+ const forecastSchema = z.object({
+ date: z.string(),
+ maxTemp: z.number(),
+ minTemp: z.number(),
+ precipitationChance: z.number(),
+ condition: z.string(),
+ location: z.string(),
+ });
+
+ function getWeatherCondition(code: number): string {
+ const conditions: Record<number, string> = {
+ 0: 'Clear sky',
+ 1: 'Mainly clear',
+ 2: 'Partly cloudy',
+ 3: 'Overcast',
+ 45: 'Foggy',
+ 48: 'Depositing rime fog',
+ 51: 'Light drizzle',
+ 53: 'Moderate drizzle',
+ 55: 'Dense drizzle',
+ 61: 'Slight rain',
+ 63: 'Moderate rain',
+ 65: 'Heavy rain',
+ 71: 'Slight snow fall',
+ 73: 'Moderate snow fall',
+ 75: 'Heavy snow fall',
+ 95: 'Thunderstorm',
+ };
+ return conditions[code] || 'Unknown';
+ }
+
+ const fetchWeather = createStep({
+ id: 'fetch-weather',
+ description: 'Fetches weather forecast for a given city',
+ inputSchema: z.object({
+ city: z.string().describe('The city to get the weather for'),
+ }),
+ outputSchema: forecastSchema,
+ execute: async ({ inputData }) => {
+ if (!inputData) {
+ throw new Error('Input data not found');
+ }
+
+ const geocodingUrl = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(inputData.city)}&count=1`;
+ const geocodingResponse = await fetch(geocodingUrl);
+ const geocodingData = (await geocodingResponse.json()) as {
+ results: { latitude: number; longitude: number; name: string }[];
+ };
+
+ if (!geocodingData.results?.[0]) {
+ throw new Error(`Location '${inputData.city}' not found`);
+ }
+
+ const { latitude, longitude, name } = geocodingData.results[0];
+
+ const weatherUrl = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}¤t=precipitation,weathercode&timezone=auto,&hourly=precipitation_probability,temperature_2m`;
+ const response = await fetch(weatherUrl);
+ const data = (await response.json()) as {
+ current: {
+ time: string;
+ precipitation: number;
+ weathercode: number;
+ };
+ hourly: {
+ precipitation_probability: number[];
+ temperature_2m: number[];
+ };
+ };
+
+ const forecast = {
+ date: new Date().toISOString(),
+ maxTemp: Math.max(...data.hourly.temperature_2m),
+ minTemp: Math.min(...data.hourly.temperature_2m),
+ condition: getWeatherCondition(data.current.weathercode),
+ precipitationChance: data.hourly.precipitation_probability.reduce((acc, curr) => Math.max(acc, curr), 0),
+ location: inputData.city,
+ };
+
+ return forecast;
+ },
+ });
+
+ const planActivities = createStep({
+ id: 'plan-activities',
+ description: '天候状況に基づいてアクティビティを提案する',
+ inputSchema: forecastSchema,
+ outputSchema: z.object({
+ activities: z.string(),
+ }),
+ execute: async ({ inputData }) => {
+ const forecast = inputData;
+
+ if (!forecast) {
+ throw new Error('天気予報データが見つかりません');
+ }
+
+ const prompt = `${forecast.location}の以下の天気予報に基づいて、適切なアクティビティを提案してください:
+ ${JSON.stringify(forecast, null, 2)}
+ `;
+
+ const response = await agent.stream([
+ {
+ role: 'user',
+ content: prompt,
+ },
+ ]);
+
+ let activitiesText = '';
+
+ for await (const chunk of response.textStream) {
+ process.stdout.write(chunk);
+ activitiesText += chunk;
+ }
+
+ return {
+ activities: activitiesText,
+ };
+ },
+ });
+
+ const weatherWorkflow = createWorkflow({
+ id: 'weather-workflow',
+ inputSchema: z.object({
+ city: z.string().describe('天気を取得する都市'),
+ }),
+ outputSchema: z.object({
+ activities: z.string(),
+ }),
+ })
+ .then(fetchWeather)
+ .then(planActivities);
+
+ weatherWorkflow.commit();
+
+ export { weatherWorkflow };
+ ```
+
### エージェントの登録
- 最後に、`src/mastra/index.ts`にMastraのエントリーポイントを作成し、エージェントを登録します:
+ Mastraのエントリーポイントを作成し、エージェントを登録します:
+
+ ```bash copy
+ touch src/mastra/index.ts
+ ```
+
+ 以下のコードを追加します:
```ts filename="src/mastra/index.ts" showLineNumbers copy
import { Mastra } from "@mastra/core";
-
- import { weatherAgent } from "./agents/weather";
+ import { createLogger } from "@mastra/core/logger";
+ import { LibSQLStore } from "@mastra/libsql";
+ import { weatherWorkflow } from "./agents/weather-workflow";