简单介绍

TS AI Agent框架Mastra,提供记忆功能框架、执行Workflow、RAG等功能。

Agent使用System Prompts

import {createOpenAI} from "@ai-sdk/openai";
import {Agent} from '@mastra/core/agent';
import {createTool} from '@mastra/core/tools';
import { z } from 'zod';

//创建提示词,规定LLM回答的形式
const instructions = `You are a helpful cat expert assistant. When need random contents, you should always include random contents.

Your main responsibilities:
1. Answer questions about random words
2. Use the randomContentsGenerate tool to provide random contents
3. Incorporate the random contents naturally into your responses

Always use the randomContentsGenerate tool at least once in your responses to ensure accuracy.`;

//创建一个函数,用于生成一串内容
const randomWordsGenerateFunc = () => {
return 'Lorem, ipsum dolor sit amet consectetur adipisicing elit. Totam, alias illum consectetur ullam in aperiam eaque itaque accusamus quod animi rerum accusantium quaerat dolorum voluptatibus fugit deleniti perferendis recusandae ex?'
}

//创建一个Tool,设置id和描述,执行工具返回的结果就是上面的函数的结果
const randomContentsGenerate = createTool({
id: 'Get random contents',
inputSchema: z.object({}),
description: 'Generate random contents',
execute: async () => {
console.log('using tool to generate random contents...')
return {
randomContents: randomWordsGenerateFunc(),
}
}
})


//因为我使用的是第三方的代理API,所以使用createOpenAI来创建model,如果使用官方API的话,可以直接使用openai函数
const llmProvider = createOpenAI({
apiKey: process.env.OPENAI_API_KEY,
baseURL: process.env.OPENAI_BASE_URL,
})


const llm = llmProvider('gpt-4o-2024-11-20') //指定模型名称

//创建一个Agent,指定名称、传入提示词、传入模型和Tools
const loremAgent = new Agent({
name: 'lorem',
instructions: instructions,
model: llm,
tools: {
randomContentsGenerate
}
})

export default loremAgent

这样就编写好了一个Agent,将其传给项目index.ts下的mastra对象,即可启动项目后,在playground中使用

import { Mastra } from '@mastra/core';
import loremAgent from "./agents/prompts";
import weatherAgent from "./agents/weather";
import weatherWorkflow from './agents/weather_work_flow'

export const mastra = new Mastra(
{
agents: {
loremAgent,
weatherAgent
},
workflows: {
weatherWorkflow
}
}
)

当然也可以单独执行,不使用框架的playground

const result = await loremAgent.generate("Give me some random contents");
console.log(result.text);

使用异步联网的Tool

import { Agent } from "@mastra/core/agent";
import { createTool } from "@mastra/core/tools";
import {createOpenAI} from "@ai-sdk/openai";
import { z } from "zod";

//定义API接口请求返回结果数据结构
interface WeatherResponse {
current: {
time: string;
temperature_2m: number;
apparent_temperature: number;
relative_humidity_2m: number;
wind_speed_10m: number;
wind_gusts_10m: number;
weather_code: number;
};
}

//创建一个Tool,用于获取对应的城市的天气数据
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 }) => {
//调用接口获取数据,接口参数包装在context中
return await getWeather(context.location);
},
});

//具体获取天气数据的处理函数
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();

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}&current=temperature_2m,apparent_temperature,relative_humidity_2m,wind_speed_10m,wind_gusts_10m,weather_code`;

const response = await fetch(weatherUrl);
const data: WeatherResponse = await response.json();

//将接口中的数据格式化成对应需要的输出结构
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,
};
};

//weather code 对应的天气状况
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",
56: "Light freezing drizzle",
57: "Dense freezing drizzle",
61: "Slight rain",
63: "Moderate rain",
65: "Heavy rain",
66: "Light freezing rain",
67: "Heavy freezing rain",
71: "Slight snow fall",
73: "Moderate snow fall",
75: "Heavy snow fall",
77: "Snow grains",
80: "Slight rain showers",
81: "Moderate rain showers",
82: "Violent rain showers",
85: "Slight snow showers",
86: "Heavy snow showers",
95: "Thunderstorm",
96: "Thunderstorm with slight hail",
99: "Thunderstorm with heavy hail",
};
return conditions[code] || "Unknown";
}


//创建大模型model
const llmProvider = createOpenAI({
apiKey: process.env.OPENAI_API_KEY,
baseURL: process.env.OPENAI_BASE_URL,
})

const llm = llmProvider('gpt-4o-2024-11-20')


//创建一个Agent,设置prompts和对应的模型、Tools等
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
- Include relevant details like humidity, wind conditions, and precipitation
- Keep responses concise but informative
Use the weatherTool to fetch current weather data.`,
model: llm,
tools: { weatherTool },
});

export default weatherAgent

将Agent添加到Workflow中去

Mastra中的工作流可以编排具有分支、并行执行、资源暂停等功能的复杂操作序列,将多个功能集成在一起完成更加复杂的需求。

import { Agent } from "@mastra/core/agent";
import { Step, Workflow } from "@mastra/core/workflows";
import { z } from "zod";
import { createOpenAI } from "@ai-sdk/openai";


//创建一个Step,Workflow由多个Step组成
const fetchWeather = new Step({
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"),
}),
execute: async ({ context }) => {
//从上一个Step获取结果,传入step id,第一个step id为'trigger'
const triggerData = context?.getStepResult<{
city: string;
}>("trigger");

if (!triggerData) {
throw new Error("Trigger data not found");
}

const geocodingUrl = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(triggerData.city)}&count=1`;

const geocodingResponse = await fetch(geocodingUrl);
const geocodingData = await geocodingResponse.json();

if (!geocodingData.results?.[0]) {
throw new Error(`Location '${triggerData.city}' not found`);
}

const { latitude, longitude, name } = geocodingData.results[0];

const weatherUrl = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&daily=temperature_2m_max,temperature_2m_min,precipitation_probability_max,weather_code&timezone=auto`;

const response = await fetch(weatherUrl);
const data = await response.json();

//返回一个对应结构的对象数组
return data.daily.time.map((date: string, index: number) => ({
date,
maxTemp: data.daily.temperature_2m_max[index],
minTemp: data.daily.temperature_2m_min[index],
precipitationChance: data.daily.precipitation_probability_max[index],
condition: getWeatherCondition(data.daily.weather_code[index]),
location: name,
}));
},
});

//获取天气数据步骤返回的数据结构类型
const forecastSchema = z.array(
z.object({
date: z.string(),
maxTemp: z.number(),
minTemp: z.number(),
precipitationChance: z.number(),
condition: z.string(),
location: z.string(),
}),
);

//创建大模型provider 并指定对应的模型
const llmProvider = createOpenAI({
apiKey: process.env.OPENAI_API_KEY,
baseURL: process.env.OPENAI_BASE_URL,
})

const llm = llmProvider('gpt-4o-2024-11-20')

//创建一个对天气情况进行分析并指定计划的Agent
const agent = new Agent({
name: "Weather Agent",
//设置提示词
instructions: `
你是一位擅长根据天气情况制定计划的本地活动和旅行专家。分析天气数据并提供切实可行的活动建议。
对于预报中的每一天,请按照以下方式精确构建您的回答:
📅 [星期, 日, 月, 年]
═══════════════════════════
🌡️ 天气总结
• 状况: [简要说明]
• 温度: [X°C/Y°F to A°C/B°F]
• 降水: [X% 概率]
🌅 早晨活动
户外:
• [活动名] - [简要描述,包括具体位置/路线]
最好的时间段: [指定时间区间]
注意事项: [相关天气考虑]
🌞 下午活动
户外:
• [活动名] - [简要描述,包括具体位置/路线]
最好的时间段: [指定时间区间]
注意事项: [相关天气考虑]
🏠 室内替代方案
• [活动名] - [简要描述,包括具体场地]
使用该替代方案的情况:[触发此替代方案的天气条件]
⚠️ 特别注意事项
• [任何相关的天气警告、紫外线指数、风况等。]
指南:
- 建议每天进行 2-3 次特定时间的户外活动
- 包括 1-2 个室内备用选项
- 降水量超过 50% 时,以室内活动为主
- 所有活动必须针对具体地点
- 包括具体场地、路线或地点
- 根据温度考虑活动强度
- 保持描述简洁但信息丰富
保持这种精确的格式以保持一致性,使用所示的表情符号和部分标题。
`,
model: llm,
});


//创建一个Step用于生成具体的计划
const planActivities = new Step({
id: "plan-activities",
description: "Suggests activities based on weather conditions",
//设置输入参数的类型
inputSchema: forecastSchema,
execute: async ({ context, mastra }) => {
//从上一步中获取结果
const forecast =
context?.getStepResult<z.infer<typeof forecastSchema>>("fetch-weather");

if (!forecast) {
throw new Error("Forecast data not found");
}

//根据传入的天气数据生成完整的prompt
const prompt = `Based on the following weather forecast for ${forecast[0].location}, suggest appropriate activities:
${JSON.stringify(forecast, null, 2)}
`;

//让llm生成活动计划返回
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,
};
},
});

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 weatherWorkflow = new Workflow({
name: "weather-workflow",
//设置触发参数
triggerSchema: z.object({
city: z.string().describe("The city to get the weather for"),
}),
})
//设置Step
.step(fetchWeather)
//然后传给planActivities
.then(planActivities);

//提交workflow
weatherWorkflow.commit();

export default weatherWorkflow

结果

{
"activePaths": {
"plan-activities": {
"status": "completed",
"stepPath": [
"fetch-weather",
"plan-activities"
]
}
},
"runId": "a10ea022-a27c-42d8-b8d9-18a0279ec3a1",
"timestamp": 1747650000155,
"results": {
"fetch-weather": {
"status": "success",
"output": [
{
"date": "2025-05-19",
"maxTemp": 31.9,
"minTemp": 18.8,
"precipitationChance": 0,
"condition": "Overcast",
"location": "Nantong"
},
{
"date": "2025-05-20",
"maxTemp": 31.2,
"minTemp": 21.2,
"precipitationChance": 48,
"condition": "Overcast",
"location": "Nantong"
},
{
"date": "2025-05-21",
"maxTemp": 30.9,
"minTemp": 24.2,
"precipitationChance": 73,
"condition": "Thunderstorm",
"location": "Nantong"
},
{
"date": "2025-05-22",
"maxTemp": 26.4,
"minTemp": 19.8,
"precipitationChance": 9,
"condition": "Overcast",
"location": "Nantong"
},
{
"date": "2025-05-23",
"maxTemp": 19.7,
"minTemp": 17.8,
"precipitationChance": 73,
"condition": "Moderate rain",
"location": "Nantong"
},
{
"date": "2025-05-24",
"maxTemp": 24.1,
"minTemp": 16.5,
"precipitationChance": 36,
"condition": "Overcast",
"location": "Nantong"
},
{
"date": "2025-05-25",
"maxTemp": 26,
"minTemp": 13.3,
"precipitationChance": 3,
"condition": "Mainly clear",
"location": "Nantong"
}
]
},
"plan-activities": {
"status": "success",
"output": {
"activities": "Here’s a detailed breakdown of activities for Nantong based on the provided weather forecast: \n\n---\n\n📅 **[星期一, 19, 五月, 2025]** \n═══════════════════════════ \n🌡️ **天气总结** \n• 状况: 多云 \n• 温度: 18.8°C/65.8°F to 31.9°C/89.4°F \n• 降水: 0% 概率 \n\n🌅 **早晨活动** \n户外: \n• **五山公园晨间步行** - 在五山风景区中放松,欣赏花木和湖光美景。 \n 最好的时间段: 7:00 - 9:00 \n 注意事项: 温度较低,适合清晨活动,请随身带水。 \n\n🌞 **下午活动** \n户外: \n• **濠河环河骑行** - 环绕濠河进行自行车骑行,感受古典桥梁与水景的结合。 \n 最好的时间段: 15:00 - 17:30 \n 注意事项: 全天无降水,携带帽子防晒,注意气温可能较高。 \n\n🏠 **室内替代方案** \n• **南通博物苑** - 探索中国首座博物馆,了解丰富的历史与文化展品。 \n 使用该替代方案的情况: 温度过高或个别时间段阳光较强。 \n\n⚠️ **特别注意事项** \n• 白天最高气温接近32°C,请保持补水并避免长时间曝晒。 \n\n---\n\n📅 **[星期二, 20, 五月, 2025]** \n═══════════════════════════ \n🌡️ **天气总结** \n• 状况: 多云 \n• 温度: .2°C/70.2°F to 31.2°C/88.2°F \n• 降水: 48% 概率 \n\n🌅 **早晨活动** \n户外: \n• **蛎岈山徒步** - 探索蛎岈山的自然景观,适合轻度徒步和拍照。 \n 最好的时间段: 8:00 - 10:30 \n 注意事项: 检查降水可能性,尽量提前出发。 \n\n🌞 **下午活动** \n户外: \n• **城市滨江公园漫步** - 沿长江旁的大草坪和亲水平台随意漫步。 \n 最好的时间段: 16:00 - 18:00 \n 注意事项: 下午降雨概率接近50%,随身带折叠伞。 \n\n🏠 **室内替代方案** \n• **1895 南通大学艺术博物馆** - 欣赏艺术展览,感受本地文化氛围。 \n 使用该替代方案的情况: 如果下午降水。 \n\n⚠️ **特别注意事项** \n• 降水概率较高,建议保持灵活行程,随时准备转移至室内活动。 \n\n---\n\n📅 **[星期三, 21, 五月, 2025]** \n═══════════════════════════ \n🌡️ **天气总结** \n• 状况: 雷暴 \n• 温度: 24.2°C/75.6°F to 30.9°C/87.6°F \n• 降水: 73% 概率 \n\n🌅 **早晨活动** \n户外: \n• **暂不建议户外活动** - 由于早间降水和雷暴风险,建议选择室内活动。 \n\n🌞 **下午活动** \n户外: \n• **暂不建议户外活动** - 全天降水概率较高,雷暴状况可能持续。 \n\n🏠 **室内替代方案** \n• **南通伶工博物馆** - 了解中国戏曲历史,参与多媒体互动展览。 \n 使用该替代方案的情况: 大部分时间雷暴和降水。 \n• **金鹰国际购物中心** - 购物或享用美食,适合晴天活动取消时的备用方案。 \n 使用该替代方案的情况: 全天降水或雷暴。 \n\n⚠️ **特别注意事项** \n• 雷暴天气较危险,避免外出,高温湿度可能有所增加,请保持增强通风。 \n\n---\n\n📅 **[星期四, 22, 五月, 2025]** \n═══════════════════════════ \n🌡️ **天气总结** \n• 状况: 多云 \n• 温度: 19.8°C/67.6°F to 26.4°C/79.5°F \n• 降水: 9% 概率 \n\n🌅 **早晨活动** \n户外: \n• **紫琅湖晨练** - 开启一天的慢跑或散步,这里风景宜人。 \n 最好的时间段: 7:30 - 9:30 \n 注意事项: 天气凉爽宜人,容易出汗,请携带毛巾。 \n\n🌞 **下午活动** \n户外: \n• **南通洲际绿博园参观** - 欣赏千姿百态的花卉展,漫步在花园之间。 \n 最好的时间段: 14:30 - 17:00 \n 注意事项: 温度适中,无降水,非常适合户外花园活动。 \n\n🏠 **室内替代方案** \n• **南通国际会展中心** - 看看是否有当日展览或活动。 \n 使用该替代方案的情况: 如果下午出现阴雨或风大。 \n\n⚠️ **特别注意事项** \n• 体感气候舒适,无特别预警。 \n\n---\n\n📅 **[星期五, 23, 五月, 2025]** \n═══════════════════════════ \n🌡️ **天气总结** \n• 状况: 中雨 \n• 温度: 17.8°C/64°F to 19.7°C/67.5°F \n• 降水: 73% 概率 \n\n🌅 **早晨活动** \n户外: \n• **不建议户外活动** - 雨量较大,请选择室内方案。 \n\n🌞 **下午活动** \n户外: \n• **不建议户外活动** - 降水持续,请改至室内活动。 \n\n🏠 **室内替代方案** \n• **南通文化艺术中心** - 欣赏当日的展览或表演,尤其适合阴雨天气的下午。 \n 使用该替代方案的情况: 全天降水。 \n• **图书馆安静阅读** - 在南通市图书馆度过一个静谧的阅读时间。 \n 使用该替代方案的情况: 在雨势强烈时为最佳选择。 \n\n⚠️ **特别注意事项** \n• 全天湿冷明显,请穿着防水的衣物和鞋子。 \n\n---\n\n📅 **[星期六, 24, 五月, 2025]** \n═══════════════════════════ \n🌡️ **天气总结** \n• 状况: 多云 \n• 温度: 16.5°C/61.7°F to 24.1°C/75.4°F \n• 降水: 36% 概率 \n\n🌅 **早晨活动** \n户外: \n• **军山探幽** - 爬山远足,山顶俯瞰长江景色壮丽。 \n 最好的时间段: 8:00 - 11:00 \n 注意事项: 检查降雨可能性,随身携带轻便雨衣。 \n\n🌞 **下午活动** \n户外: \n• **濠河古城游** - 游览南通古建筑与沿河景观,适合慢节奏的行程。 \n 最好的时间段: 15:00 - 17:30 \n 注意事项: 下午降水概率稍低,但仍需注意天气变化。 \n\n🏠 **室内替代方案** \n• **南通规划馆** - 了解南通的城市发展历史与未来蓝图。 \n 使用该替代方案的情况: 突发降雨时。 \n\n⚠️ **特别注意事项** \n• 温度适中,请佩戴舒适的鞋以适应户外行走。 \n\n---\n\n📅 **[星期日, 25, 五月, 2025]** \n═══════════════════════════ \n🌡️ **天气总结** \n• 状况: 晴转多云 \n• 温度: 13.3°C/56°F to 26°C/78.8°F \n• 降水: 3% 概率 \n\n🌅 **早晨活动** \n户外: \n• **桃园晨光漫步** - 早晨采摘水果或者在绿道上散步。 \n 最好的时间段: 7:00 - 9:30 \n 注意事项: 天气晴朗,适合低强度户外活动。 \n\n🌞 **下午活动** \n户外: \n• **南通狼山风景区攀登** - 进行轻度攀登,欣赏山顶寺庙与江面风光。 \n 最好的时间段: 15:00 - 17:30 \n 注意事项: 确保攀登时携带水。 \n\n🏠 **室内替代方案** \n• **书店与咖啡厅慢生活** - 在南通热门书店享受阅读与咖啡相伴的午后。 \n 使用该替代方案的情况: 感觉较冷或者夕阳时光过于强烈。 \n\n⚠️ **特别注意事项** \n• 早晚温差大,请穿着适合早晚时段的保暖衣物。 \n\n--- \n\n以上是每日活动指南,根据天气灵活调整安排,祝您在南通有愉快的几天!"
}
}
}
}