别再手写胶水代码了!Mastra 如何让 AI Agent 开发从”苦力活”变成”创意艺术”
为什么值得关注 / 项目简介
在 AI 应用开发领域,我们经常会遇到这样的困境:想要快速构建一个能够调用外部工具、保持对话记忆、与多种大语言模型交互的智能代理系统,却发现光是集成各种 SDK、对接 API、处理异常情况就要花费大量时间。这就像是明明想要盖一栋漂亮的房子,却要先从烧砖和搅拌水泥开始。
Mastra 的出现正是为了解决这个痛点。 作为一款专为 TypeScript/JavaScript 开发者设计的 AI 应用框架,Mastra 提供了一站式解决方案,让你能够从繁琐的基础设施工作中解放出来,专注于业务逻辑和用户体验的构建。
想象一下这样的开发体验:你只需要几行配置代码,就能让 AI 代理具备搜索互联网、执行代码、查询数据库的能力;你不需要深入了解每个 API 的认证机制,Mastra 已经帮你封装好了;你想要追踪代理的思考过程和工具调用记录,Mastra 内置的可观测性工具让你一目了然。这就是 Mastra 带给开发者的核心价值。
作为一个开源项目,Mastra 由专业团队维护,社区活跃度高,文档详尽完善。无论是初创公司快速验证 AI 产品概念,还是大型企业构建生产级 AI 系统,Mastra 都能提供足够的技术支撑。更重要的是,它的模块化设计意味着你可以只使用需要的部分,而不必引入整个框架带来的复杂度。
接下来的内容中,我们将从环境搭建开始,逐步深入到核心功能,最后通过一个完整的实战项目让你掌握使用 Mastra 构建 AI 应用的全部技能。无论你是 TypeScript 新手还是资深开发者,都能从中获得有价值的知识。
环境搭建 / Getting Started
系统要求与依赖
在开始之前,让我们确认你的开发环境满足基本要求。Mastra 主要面向 Node.js 环境开发,因此你需要确保本地安装了合适版本的 Node.js 和包管理工具。
Mastra 的技术栈要求如下:Node.js 版本需要 18.0 或更高版本,推荐使用 20.x LTS 版本以获得最佳稳定性和性能;包管理器推荐使用 npm、pnpm 或 yarn 中的任意一种;TypeScript 版本建议 5.0 或更高,以充分利用框架提供的类型安全特性。此外,由于 AI 应用通常涉及大量异步操作和网络请求,确保你的开发环境网络连接稳定,能够正常访问各大 AI 服务提供商的 API。
在正式开始之前,还需要准备各 AI 服务商的 API 密钥。以 OpenAI 为例,你需要在其官方网站注册账号并创建 API Key。其他支持的模型服务商如 Anthropic、Google、AWS Bedrock 等也都有各自的注册和认证流程。建议将这些密钥存储在环境变量中,而不是直接写在代码里,这样既能保证安全性,也方便在不同环境间切换。
项目初始化步骤
现在开始创建第一个 Mastra 项目。打开终端,进入你想要存放项目的目录,执行以下命令来初始化项目:
# 创建项目目录并进入
mkdir my-mastra-app
cd my-mastra-app
# 初始化 npm 项目
npm init -y
# 安装 Mastra 核心依赖
npm install @mastra/core
# 安装 TypeScript 相关依赖(如果你选择使用 TypeScript)
npm install -D typescript @types/node ts-node
# 安装你自己需要使用的 AI 模型提供商 SDK
npm install @mastra/model-openai @mastra/model-anthropic
安装完成后,创建项目的基础文件结构。一个典型的 Mastra 项目会包含以下几个核心目录和文件:src 目录用于存放源代码,mastra.config.ts 作为配置文件定义各种资源和代理,env 文件存储环境变量,dist 目录存放编译后的 JavaScript 文件。
接下来创建 TypeScript 配置文件 tsconfig.json,这将为你的代码提供类型检查和编译配置:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"lib": ["ES2022"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
然后创建环境变量文件 .env,在其中配置你的 API 密钥:
# OpenAI API 配置
OPENAI_API_KEY=sk-your-openai-api-key-here
# 如果你还需要使用其他模型服务
ANTHROPIC_API_KEY=sk-ant-your-anthropic-api-key-here
# 可选:配置日志级别和调试选项
LOG_LEVEL=info
DEBUG=mastra:*
现在创建一个最简单的 Mastra 配置文件 src/mastra.ts,来体验框架的基本结构:
import { Mastra } from '@mastra/core';
// 初始化 Mastra 实例
export const mastra = new Mastra({
// 在这里配置代理、工具和其他资源
agents: {
// 我们将在这里添加智能代理
},
tools: {
// 我们将在这里添加工具
},
vectors: {
// 我们将在这里配置向量数据库
}
});
完成以上步骤后,你的项目基础结构已经就绪。接下来我们将深入探讨 Mastra 的核心概念和功能特性。
核心概念解析 / Core Concepts
代理(Agent)体系
在深入学习 Mastra 之前,理解其核心概念至关重要。Mastra 的设计哲学围绕几个关键抽象展开,理解这些概念将帮助你更好地利用框架的能力。
代理是 Mastra 中最重要的概念之一。 简单来说,代理就是一个能够接收用户输入、使用语言模型进行推理、调用工具获取信息、然后返回响应的智能实体。你可以把它想象成一个拥有特定技能的员工,它能够理解自然语言指令,使用适当的工具完成任务,并记住对话的上下文。
在 Mastra 中,代理由多个组件构成。语言模型是代理的”大脑”,负责理解和生成自然语言;工具集定义了代理能够执行的操作;记忆系统让代理能够跨对话记住重要信息;提示模板则决定了代理的行为风格和角色定位。这种模块化的设计让你可以根据需求灵活组合不同的组件。
创建一个基本代理的代码结构如下:
import { Agent } from '@mastra/core/models/agent';
import { openai } from '@mastra/model-openai';
// 创建代理实例
const myAgent = new Agent({
name: '我的第一个代理',
description: '一个能够回答问题的简单助手',
// 指定使用的语言模型
model: openai.chat('gpt-4'),
// 定义系统提示,决定代理的角色和行为
instructions: `你是一个乐于助人的AI助手。你的特点是:
1. 回答简洁明了,避免冗长
2. 在不确定时诚实承认
3. 适当使用emoji让回复更生动`,
// 关联工具(后续章节详细讲解)
tools: [],
});
代理的行为通过几个关键阶段来理解。首先是理解阶段,代理接收用户输入,使用语言模型解析意图;然后是规划阶段,代理决定是否需要调用工具以及调用哪些工具;接着是执行阶段,代理执行选定的工具并收集结果;最后是响应阶段,代理综合所有信息生成最终回复。这种循环机制让代理能够处理复杂的多步骤任务。
工具系统(Tools)
工具是扩展代理能力的关键机制。没有工具的代理就像一个只有书本知识而没有实践能力的人——它能回答理论问题,但无法真正完成任务。工具让代理能够与外部世界交互,执行代码、查询数据、发送消息、操作文件等。
Mastra 提供了两种创建工具的方式:原生工具和动态工具。原生工具通过继承或装饰器模式定义,具有完整的类型安全和验证能力;动态工具则更加灵活,可以通过函数直接定义,适合快速原型开发。
让我们看一个创建天气查询工具的完整示例:
import { tool } from '@mastra/core';
// 定义工具的输入模式(使用 JSON Schema)
const weatherInputSchema = {
type: 'object',
properties: {
city: {
type: 'string',
description: '城市名称,如"北京"、"上海"',
},
units: {
type: 'string',
enum: ['celsius', 'fahrenheit'],
description: '温度单位',
default: 'celsius',
},
},
required: ['city'],
};
// 创建天气查询工具
const getWeather = tool({
name: 'getWeather',
description: '获取指定城市的当前天气信息',
// 定义输入参数的模式
inputSchema: weatherInputSchema,
// 定义工具的执行逻辑
execute: async ({ city, units = 'celsius' }) => {
// 这里是实际调用天气 API 的逻辑
// 为了演示,我们返回一个模拟数据
const mockWeatherData = {
city,
temperature: 22,
condition: '晴朗',
humidity: 45,
windSpeed: 12,
};
// 根据单位转换温度
if (units === 'fahrenheit') {
mockWeatherData.temperature = Math.round(
(mockWeatherData.temperature * 9) / 5 + 32
);
}
return mockWeatherData;
},
});
// 导出工具供代理使用
export { getWeather };
工具的 inputSchema 是非常重要的部分,它不仅用于验证输入参数,还在代理决定是否调用工具时发挥作用。当代理需要决定是否使用某个工具时,它会根据工具的描述和输入模式来判断。因此,编写清晰、准确的描述能够让代理更智能地选择工具。
记忆系统(Memory)
人类智能的一个关键特征是能够记住之前的对话内容和重要信息。同样,让 AI 代理拥有记忆能力是构建真正有用应用的前提。Mastra 提供了强大的记忆系统,支持多种存储后端和检索策略。
记忆系统在 Mastra 中分为几个层次。第一层是工作记忆,存储当前对话的内容,随着对话结束而清除;第二层是长期记忆,可以跨对话持久化存储重要信息;第三层是向量记忆,将信息转换为向量存储,支持语义相似性搜索。
一个典型的记忆配置如下:
import { Memory, VectorStore } from '@mastra/core';
import { PineconeStore } from '@mastra/vector-pinecone';
// 配置向量存储用于语义搜索
const vectorStore = new PineconeStore({
indexName: 'my-mastra-index',
apiKey: process.env.PINECONE_API_KEY,
});
// 创建记忆系统实例
const memory = new Memory({
// 存储最近的对话历史
recentMessages: 10,
// 配置向量存储用于长期记忆
vectorStore: vectorStore,
// 配置记忆的保留策略
options: {
lastMessages: 20, // 保留最近 20 条消息
semanticRecall: true, // 启用语义回忆
threads: 5, // 每个对话线程保留的消息数
}
});
记忆系统的语义搜索功能特别强大。当用户提出一个问题时,代理不仅能够查看最近的对话上下文,还能在长期记忆中搜索相关的历史信息。例如,用户问”上次我们讨论的那个方案进展如何”,代理能够通过语义搜索找到之前相关的讨论内容,而不需要用户再次描述整个背景。
实战教程 / Step-by-Step Tutorial
第一个完整项目:智能旅行助手
现在让我们通过一个完整的实战项目来巩固所学知识。我们将构建一个智能旅行助手代理,它能够:
- 查询不同城市的天气情况
- 推荐旅行目的地的景点
- 制定简单的旅行计划
- 记住用户的偏好设置
这个项目将涉及代理创建、工具集成、记忆系统使用等核心功能,是你掌握 Mastra 的绝佳练手项目。
首先创建项目结构和基础配置文件。项目目录结构如下:
my-travel-assistant/
├── src/
│ ├── index.ts # 应用入口
│ ├── mastra.ts # Mastra 配置文件
│ ├── agents/
│ │ └── travelAgent.ts # 旅行助手代理定义
│ ├── tools/
│ │ ├── weather.ts # 天气查询工具
│ │ ├── attractions.ts # 景点推荐工具
│ │ └── planner.ts # 行程规划工具
│ └── memory/
│ └── setup.ts # 记忆系统配置
├── .env
├── tsconfig.json
└── package.json
让我们从创建天气查询工具开始,这个工具将使用模拟数据(实际项目中可以接入真实的天气 API):
// src/tools/weather.ts
import { tool } from '@mastra/core';
// 定义天气查询工具
export const getWeatherTool = tool({
name: 'getWeather',
description: `获取指定城市的当前天气信息。这个工具对于计划旅行行程非常有用,
因为天气状况会直接影响活动安排。当用户询问某地的天气或计划某项户外活动时,
请优先使用这个工具。`,
inputSchema: {
type: 'object',
properties: {
city: {
type: 'string',
description: '城市名称,需要查询天气的目标城市',
},
date: {
type: 'string',
description: '日期,格式为 YYYY-MM-DD,默认为今天',
},
},
required: ['city'],
},
execute: async ({ city, date }) => {
// 在实际项目中,这里会调用真实的天气 API
// 为了演示目的,我们使用模拟数据
// 模拟天气数据
const weatherDatabase = {
'北京': { temp: 18, condition: '晴', humidity: 35, wind: '东南风3级' },
'上海': { temp: 22, condition: '多云', humidity: 55, wind: '东风2级' },
'东京': { temp: 20, condition: '小雨', humidity: 70, wind: '东北风2级' },
'巴黎': { temp: 15, condition: '阴', humidity: 60, wind: '西风3级' },
'纽约': { temp: 12, condition: '晴', humidity: 40, wind: '西北风2级' },
'伦敦': { temp: 11, condition: '阵雨', humidity: 75, wind: '西南风4级' },
};
// 标准化城市名称
const normalizedCity = city.trim();
const weather = weatherDatabase[normalizedCity];
if (!weather) {
return {
success: false,
message: `抱歉,暂时没有 ${city} 的天气数据。建议您访问天气预报网站查询。`,
};
}
// 格式化日期显示
const displayDate = date || new Date().toISOString().split('T')[0];
return {
success: true,
city: normalizedCity,
date: displayDate,
temperature: weather.temp,
condition: weather.condition,
humidity: weather.humidity,
wind: weather.wind,
advice: weather.condition.includes('雨')
? '建议携带雨具'
: '适合户外活动',
};
},
});
// 工具使用示例的辅助函数
export function getWeatherExample() {
return `
调用示例:
{
"city": "北京",
"date": "2024-04-15"
}
返回示例:
{
"success": true,
"city": "北京",
"date": "2024-04-15",
"temperature": 18,
"condition": "晴",
"humidity": 35,
"wind": "东南风3级",
"advice": "适合户外活动"
}
`;
}
接下来创建景点推荐工具,这个工具能够根据城市推荐热门旅游景点:
// src/tools/attractions.ts
import { tool } from '@mastra/core';
// 定义景点推荐工具
export const recommendAttractionsTool = tool({
name: 'recommendAttractions',
description: `根据用户提供的城市推荐热门旅游景点和活动。这个工具会返回景点的
基本信息、开放时间、门票价格范围和推荐理由。当你需要帮用户规划旅行行程或
了解某个城市的著名景点时使用此工具。`,
inputSchema: {
type: 'object',
properties: {
city: {
type: 'string',
description: '目标城市名称',
},
interests: {
type: 'array',
items: { type: 'string' },
description: '用户兴趣偏好,如 ["历史", "美食", "自然风光"]',
},
budget: {
type: 'string',
enum: ['经济', '适中', '豪华'],
description: '预算等级',
},
},
required: ['city'],
},
execute: async ({ city, interests = [], budget = '适中' }) => {
// 模拟景点数据库
const attractionsDatabase = {
'北京': [
{
name: '故宫',
category: '历史',
rating: 4.8,
priceRange: '60元',
duration: '3-4小时',
description: '明清两代的皇家宫殿,世界上现存规模最大的宫殿建筑群',
},
{
name: '长城(八达岭)',
category: '历史',
rating: 4.7,
priceRange: '45元',
duration: '半天',
description: '中国古代的军事防御工程,世界七大奇迹之一',
},
{
name: '颐和园',
category: '自然',
rating: 4.6,
priceRange: '30元',
duration: '2-3小时',
description: '中国现存最大的皇家园林,风景优美',
},
],
'东京': [
{
name: '浅草寺',
category: '文化',
rating: 4.5,
priceRange: '免费',
duration: '1-2小时',
description: '东京最古老的寺庙,具有浓厚的江户风情',
},
{
name: '东京迪士尼乐园',
category: '娱乐',
rating: 4.7,
priceRange: '8200日元',
duration: '全天',
description: '亚洲最受欢迎的主题乐园之一',
},
{
name: '涩谷十字路口',
category: '都市',
rating: 4.4,
priceRange: '免费',
duration: '1小时',
description: '世界上最繁忙的交叉路口之一,感受东京的繁华',
},
],
'巴黎': [
{
name: '埃菲尔铁塔',
category: '地标',
rating: 4.6,
priceRange: '26欧元',
duration: '2-3小时',
description: '巴黎的标志性建筑,登顶可俯瞰全城',
},
{
name: '卢浮宫',
category: '艺术',
rating: 4.8,
priceRange: '17欧元',
duration: '3-4小时',
description: '世界最大的艺术博物馆,收藏了《蒙娜丽莎》等名作',
},
{
name: '塞纳河游船',
category: '休闲',
rating: 4.5,
priceRange: '15欧元',
duration: '1小时',
description: '沿塞纳河欣赏巴黎两岸的标志性建筑',
},
],
};
const normalizedCity = city.trim();
let attractions = attractionsDatabase[normalizedCity];
if (!attractions) {
return {
success: false,
message: `抱歉,暂时没有 ${city} 的景点数据。`,
suggestions: ['北京', '东京', '巴黎'],
};
}
// 根据兴趣过滤
if (interests.length > 0) {
attractions = attractions.filter(attr =>
interests.some(interest =>
attr.category.includes(interest)
)
);
}
// 根据预算调整推荐
const budgetLabels = {
'经济': '免费或低价',
'适中': '中等价位',
'豪华': '高端体验',
};
return {
success: true,
city: normalizedCity,
recommendations: attractions,
filterInfo: {
interests: interests.length > 0 ? interests : '全部',
budget: budgetLabels[budget],
},
totalCount: attractions.length,
};
},
});
// 批量获取景点信息的工具
export const getAttractionsDetailsTool = tool({
name: 'getAttractionsDetails',
description: `获取指定景点的详细信息,包括最佳游览时间、游览建议、交通指南等。
在用户确定要参观某个景点后,使用此工具获取更详细的信息以便做出行程安排。`,
inputSchema: {
type: 'object',
properties: {
attractionName: {
type: 'string',
description: '景点名称',
},
city: {
type: 'string',
description: '所在城市',
},
},
required: ['attractionName'],
},
execute: async ({ attractionName, city }) => {
// 模拟详细数据
const detailsDatabase = {
'故宫': {
bestTime: '建议早上8:30开门时进入,避开人流高峰',
tips: [
'提前在故宫官网预约门票',
'建议租用讲解器或请导游',
'参观顺序:中轴线 → 西六宫 → 东六宫',
'神武门出去后可直接登上景山公园俯瞰故宫全景',
],
transport: '地铁1号线天安门东或天安门西站下车',
note: '故宫每周一闭馆(法定节假日除外)',
},
};
const details = detailsDatabase[attractionName];
if (!details) {
return {
success: false,
message: `暂无 ${attractionName} 的详细信息`,
};
}
return {
success: true,
attraction: attractionName,
city: city || '北京',
...details,
};
},
});
现在创建行程规划工具,这个工具能够根据天气和景点信息生成旅行计划:
// src/tools/planner.ts
import { tool } from '@mastra/core';
// 定义行程规划工具
export const planTripTool = tool({
name: 'planTrip',
description: `根据用户的需求和偏好生成旅行计划。这个工具会综合考虑天气条件、
景点开放时间、个人兴趣等因素,生成合理的行程安排。当用户询问如何安排行程
或需要具体的旅行计划时使用此工具。`,
inputSchema: {
type: 'object',
properties: {
destination: {
type: 'string',
description: '目的地城市',
},
startDate: {
type: 'string',
description: '出发日期,格式 YYYY-MM-DD',
},
endDate: {
type: 'string',
description: '结束日期,格式 YYYY-MM-DD',
},
interests: {
type: 'array',
items: { type: 'string' },
description: '兴趣偏好数组',
},
travelers: {
type: 'number',
description: '旅行人数',
default: 1,
},
specialRequirements: {
type: 'string',
description: '特殊要求,如行动不便、携带儿童等',
},
},
required: ['destination', 'startDate', 'endDate'],
},
execute: async ({
destination,
startDate,
endDate,
interests = [],
travelers = 1,
specialRequirements = '',
}) => {
// 计算行程天数
const start = new Date(startDate);
const end = new Date(endDate);
const days = Math.ceil((end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24)) + 1;
// 生成每日行程模板
const generateDailyPlan = (dayNumber, theme, activities) => ({
day: dayNumber,
date: new Date(start.getTime() + (dayNumber - 1) * 24 * 60 * 60 * 1000)
.toISOString().split('T')[0],
theme,
activities,
notes: [],
});
// 根据天数生成行程
let itinerary = [];
if (days === 1) {
itinerary = [
generateDailyPlan(1, '精华一日游', [
{ time: '09:00-12:00', activity: '上午游览核心景点' },
{ time: '12:00-14:00', activity: '午餐,品尝当地特色美食' },
{ time: '14:00-17:00', activity: '下午探索文化场所' },
{ time: '18:00-20:00', activity: '晚间体验当地夜生活' },
]),
];
} else if (days === 2) {
itinerary = [
generateDailyPlan(1, '初识城市', [
{ time: '09:00-12:00', activity: '游览城市地标景点' },
{ time: '12:00-14:00', activity: '特色餐厅午餐' },
{ time: '14:00-18:00', activity: '探索历史文化遗产' },
{ time: '19:00-21:00', activity: '观赏城市夜景' },
]),
generateDailyPlan(2, '深度体验', [
{ time: '09:00-12:00', activity: '根据兴趣选择的主题游览' },
{ time: '12:00-14:00', activity: '当地市场或美食街探索' },
{ time: '14:00-17:00', activity: '户外活动或博物馆参观' },
{ time: '晚上', activity: '自由活动或休息' },
]),
];
} else {
// 多日行程
const baseThemes = ['地标打卡', '文化探索', '自然风光', '休闲放松', '美食购物'];
for (let i = 1; i <= days; i++) {
const theme = baseThemes[(i - 1) % baseThemes.length];
itinerary.push(generateDailyPlan(i, theme, [
{ time: '09:00-12:00', activity: `上午:${theme}相关活动` },
{ time: '12:00-14:00', activity: '午餐休息时间' },
{ time: '14:00-18:00', activity: `下午:延伸探索` },
{ time: '19:00-21:00', activity: '晚间活动或休息' },
]));
}
}
// 生成预算估算
const budgetEstimate = {
accommodation: days * 300 * travelers, // 每晚每人间价
food: days * 150 * travelers, // 每日餐饮
transportation: days * 50, // 市内交通
attractions: days * 100 * travelers, // 景点门票
total: (days * 300 * travelers) +
(days * 150 * travelers) +
(days * 50) +
(days * 100 * travelers),
};
// 准备注意事项
const tips = [];
if (interests.includes('美食')) {
tips.push('记得尝试当地的特色美食和街头小吃');
}
if (travelers > 2) {
tips.push('建议提前预订热门餐厅和大景点门票');
}
if (specialRequirements) {
tips.push(`特殊需求:${specialRequirements}`);
}
tips.push('建议购买旅游保险');
tips.push('随身携带常用药品和防晒用品');
return {
success: true,
summary: {
destination,
startDate,
endDate,
duration: days,
travelers,
interests: interests.length > 0 ? interests : ['综合'],
},
itinerary,
budget: budgetEstimate,
tips,
preparationChecklist: [
'✅ 护照/身份证',
'✅ 机票/酒店预订确认',
'✅ 现金和信用卡',
'✅ 手机和充电器',
'✅ 常用药品',
'✅ 根据天气准备衣物',
],
};
},
});
// 添加到旅行计划的工具
export const addToPlanTool = tool({
name: 'addToPlan',
description: `将特定的活动或景点添加到已有的旅行计划中。这个工具可以帮助
用户调整和修改行程安排。当用户想要在计划中添加或修改具体活动时使用。`,
inputSchema: {
type: 'object',
properties: {
day: {
type: 'number',
description: '要添加活动的日期序号',
},
time: {
type: 'string',
description: '时间,如 "14:00-16:00"',
},
activity: {
type: 'string',
description: '活动名称',
},
location: {
type: 'string',
description: '活动地点',
},
note: {
type: 'string',
description: '备注信息',
},
},
required: ['day', 'activity'],
},
execute: async ({ day, time, activity, location, note }) => {
return {
success: true,
message: `已将"${activity}"添加到第${day}天行程`,
addedItem: {
day,
time: time || '待定',
activity,
location: location || '待确认',
note: note || '',
},
reminder: '记得提前预订和确认开放时间',
};
},
});
现在创建代理定义文件,将所有工具组合在一起:
// src/agents/travelAgent.ts
import { Agent } from '@mastra/core/models/agent';
import { openai } from '@mastra/model-openai';
import { getWeatherTool } from '../tools/weather';
import { recommendAttractionsTool, getAttractionsDetailsTool } from '../tools/attractions';
import { planTripTool, addToPlanTool } from '../tools/planner';
import { memory } from '../memory/setup';
// 定义旅行助手的系统提示
const TRAVEL_ASSISTANT_PROMPT = `你是一个专业、热情的旅行助手,名叫"小旅"。你的目标是帮助用户规划完美的旅行体验。
=== 核心能力 ===
1. 天气查询:你可以查询全球主要城市的天气预报,帮助用户选择最佳出行时间
2. 景点推荐:根据用户的兴趣偏好推荐适合的旅游景点
3. 行程规划:帮助用户制定合理的旅行计划,包括每日活动安排
4. 预算估算:提供旅行费用的大致估算
=== 服务风格 ===
- 热情友好,像朋友一样与用户交流
- 提供实用、具体的建议,避免泛泛而谈
- 适当使用 emoji 让对话更加生动
- 记住用户的偏好设置,提供个性化服务
=== 回答原则 ===
1. 先理解用户的需求,再提供帮助
2. 建议要有依据,如当地季节特点、天气情况等
3. 诚实告知不确定的信息,不要编造数据
4. 主动提醒用户注意事项和可能的风险
=== 使用工具的时机 ===
- 当用户询问天气时 → 使用 getWeather 工具
- 当用户询问景点或寻求推荐时 → 使用 recommendAttractions 工具
- 当用户需要某个景点的详细信息时 → 使用 getAttractionsDetails 工具
- 当用户请求制定行程计划时 → 使用 planTrip 工具
- 当用户想要添加活动到计划时 → 使用 addToPlan 工具
请始终以帮助用户获得最佳旅行体验为出发点来回答问题。`;
// 创建旅行助手代理
export const travelAgent = new Agent({
name: 'travel-assistant',
description: '专业旅行助手,帮助用户规划行程、查询天气、推荐景点',
// 配置语言模型
model: openai.chat('gpt-4', {
// 模型参数配置
temperature: 0.7, // 控制创造性,0.7 是比较平衡的值
maxTokens: 2000, // 最大输出 token 数
}),
// 系统提示
instructions: TRAVEL_ASSISTANT_PROMPT,
// 关联工具
tools: [
getWeatherTool,
recommendAttractionsTool,
getAttractionsDetailsTool,
planTripTool,
addToPlanTool,
],
// 配置记忆系统
memory: memory,
// 配置对话样式
styles: {
// 角色设定
role: 'travel consultant',
// 默认语言
language: '中文',
// 响应格式
responseFormat: 'natural', // natural | structured
},
});
// 导出代理的快捷方法
export async function askTravelAssistant(question: string, threadId?: string) {
return await travelAgent.generateResponse(question, {
threadId: threadId || `thread-${Date.now()}`,
});
}
创建记忆系统的配置文件:
// src/memory/setup.ts
import { Memory } from '@mastra/core/memory';
import { SQLiteStore } from '@mastra/core/storage-sqlite';
// 配置 SQLite 存储用于消息历史
const messageStore = new SQLiteStore({
// 数据库文件路径
dbPath: './data/memory.db',
});
// 创建记忆系统实例
export const memory = new Memory({
// 配置消息存储
storage: messageStore,
// 配置记忆保留策略
options: {
// 保留最近的 N 条消息
lastMessages: 20,
// 保留最近 N 天的对话
rememberDays: 30,
// 是否启用语义搜索
semanticRecall: true,
// 语义搜索返回的相关记忆数量
topK: 5,
// 线程配置
threads: {
// 每个线程保留的最大消息数
maxMessages: 50,
},
},
});
// 辅助函数:清除对话历史
export async function clearConversationHistory(threadId: string) {
await memory.clearThread(threadId);
return { success: true, message: '对话历史已清除' };
}
// 辅助函数:获取对话摘要
export async function getConversationSummary(threadId: string) {
const history = await memory.getThreadMessages(threadId);
return {
threadId,
messageCount: history.messages.length,
lastMessage: history.messages[history.messages.length - 1],
summary: `该对话共有 ${history.messages.length} 条消息`,
};
}
最后,创建主入口文件,整合所有组件:
// src/index.ts
import { mastra } from './mastra';
import { travelAgent, askTravelAssistant } from './agents/travelAgent';
import { clearConversationHistory } from './memory/setup';
// 主程序入口
async function main() {
console.log('🧳 欢迎使用智能旅行助手!\n');
console.log('---');
console.log('开始与旅行助手对话...\n');
// 示例对话 1:查询天气
console.log('👤 用户: 我想去北京旅游,下周天气怎么样?');
const response1 = await askTravelAssistant(
'我想去北京旅游,下周天气预报怎么样?有什么建议吗?'
);
console.log(`🤖 小旅: ${response1.text}\n`);
// 示例对话 2:推荐景点
console.log('👤 用户: 北京有什么好玩的地方?我对历史文化比较感兴趣。');
const response2 = await askTravelAssistant(
'北京有什么好玩的地方?我对历史文化比较感兴趣。'
);
console.log(`🤖 小旅: ${response2.text}\n`);
// 示例对话 3:制定行程
console.log('👤 用户: 我计划4月15日到4月18日去北京,帮我制定一个行程计划。');
const response3 = await travelAgent.generateResponse(
'我计划4月15日到4月18日去北京旅行,一共2个人,帮我制定一个行程计划,需要包含预算估算。'
);
console.log(`🤖 小旅: ${response3.text}\n`);
// 示例对话 4:连续对话(体现记忆功能)
console.log('👤 用户: 那边的美食有什么推荐的吗?');
const response4 = await askTravelAssistant(
'那北京有什么特色美食推荐吗?我想尝尝地道的北京菜。'
);
console.log(`🤖 小旅: ${response4.text}\n`);
console.log('---');
console.log('对话结束!');
}
// 交互式对话模式
async function interactiveMode() {
const readline = await import('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
console.log('🧳 智能旅行助手已启动!');
console.log('输入您的旅行问题,按回车发送。输入 "quit" 退出。\n');
const question = (query: string): Promise<string> => {
return new Promise(resolve => {
rl.question(query, resolve);
});
};
let threadId = `travel-${Date.now()}`;
while (true) {
const input = await question('👤 您: ');
if (input.toLowerCase() === 'quit') {
console.log('再见!祝您旅途愉快!✈️');
rl.close();
break;
}
if (input.trim() === '') {
continue;
}
try {
const response = await askTravelAssistant(input, threadId);
console.log(`🤖 小旅: ${response.text}\n`);
} catch (error) {
console.error('抱歉,发生了错误:', error);
}
}
}
// 导出主要功能供外部使用
export {
main,
interactiveMode,
askTravelAssistant,
travelAgent,
};
// 根据运行模式执行不同的入口
if (require.main === module) {
const args = process.argv.slice(2);
if (args.includes('--interactive') || args.includes('-i')) {
interactiveMode();
} else {
main();
}
}
运行项目前,确保环境变量配置正确,然后执行以下命令:
# 编译 TypeScript 代码
npx tsc
# 运行编译后的 JavaScript
node dist/index.js
# 或者使用 ts-node 直接运行 TypeScript
npx ts-node src/index.ts
# 进入交互式对话模式
npx ts-node src/index.ts --interactive
通过这个实战项目,你应该已经掌握了 Mastra 的核心用法。接下来让我们探讨更多应用场景和进阶技巧。
常见使用场景 / Common Use Cases
智能客服系统
智能客服是企业级 AI 应用中最常见的场景之一。Mastra 的代理系统非常适合构建能够处理客户咨询、解答产品问题、甚至执行简单操作的客服机器人。
一个典型的客服代理需要具备以下能力:准确理解客户意图、从知识库中检索相关信息、生成专业且友好的回复、识别需要人工介入的复杂问题。Mastra 提供了构建这类系统所需的一切组件,包括对话管理、工具集成、记忆系统和可观测性支持。
以下是一个智能客服代理的配置示例:
// 构建客服代理的基础配置
const customerServiceAgent = new Agent({
name: 'customer-service',
description: '在线客服助手,处理客户咨询和常见问题',
model: openai.chat('gpt-4', {
temperature: 0.3, // 客服场景需要较低的随机性
}),
instructions: `
你是一个专业耐心的在线客服。你的职责是:
1. 解答客户关于产品和服务的问题
2. 帮助客户解决常见的技术问题
3. 处理订单相关查询
4. 收集客户反馈和建议
服务原则:
- 始终保持礼貌和耐心
- 不确定的问题不要猜测,告知客户需要核实
- 涉及退款、投诉等敏感问题,引导至人工客服
- 保护客户隐私,不询问不必要的个人信息
`,
tools: [
// 产品信息查询工具
productQueryTool,
// 订单管理工具
orderManagementTool,
// 知识库检索工具
knowledgeBaseTool,
// 创建工单工具
createTicketTool,
],
});
内容创作助手
AI 在内容创作领域有着广泛应用,从文章写作到代码生成都可以借助语言模型完成。Mastra 的工具系统让你能够为创作助手配备各种专业能力,如资料搜索、图片生成、格式转换等。
一个完善的内容创作助手可能包含以下功能模块:主题研究和资料收集、文章大纲生成、初稿写作、语法和风格优化、SEO 优化建议、发布格式转换。每个功能都可以通过工具来实现,代理负责协调各个工具的使用顺序和内容流转。
// 内容创作助手示例
const contentCreatorAgent = new Agent({
name: 'content-creator',
description: 'AI 内容创作助手,支持文章写作和优化',
model: openai.chat('gpt-4', {
temperature: 0.7, // 创作场景需要适度创造性
}),
instructions: `
你是一个专业的内容创作者,擅长撰写高质量的文章和营销内容。
你的工作流程:
1. 理解创作需求和目标受众
2. 收集相关资料和素材
3. 制定内容大纲
4. 撰写初稿
5. 优化完善
质量标准:
- 内容要有价值,能真正帮助读者
- 逻辑清晰,结构合理
- 语言流畅,表达准确
- 根据用途调整风格(专业/轻松/口语化)
`,
tools: [
webSearchTool, // 资料搜索
imageGenerator, // 配图生成
seoAnalyzer, // SEO 分析
grammarChecker, // 语法检查
contentTranslator, // 多语言翻译
],
});
数据分析助手
将 AI 与数据分析结合是另一个强大的应用场景。数据分析助手能够帮助用户理解数据结构、执行查询、生成报告、发现趋势和异常。这在商业智能、市场研究和运营监控等领域都有重要价值。
数据分析助手需要能够执行 SQL 查询、调用数据分析 API、生成图表、处理文件数据等。Mastra 的动态工具功能特别适合这类场景,因为它允许你根据数据结构动态生成查询工具。
// 数据分析助手示例
const dataAnalystAgent = new Agent({
name: 'data-analyst',
description: '智能数据分析助手,帮助理解和处理数据',
model: openai.chat('gpt-4', {
temperature: 0.2, // 分析场景需要较高的准确性
}),
instructions: `
你是一个专业的数据分析师,擅长从数据中提取洞察和建议。
分析原则:
1. 数据优先,用数据说话
2. 解释分析方法和假设
3. 区分事实和推断
4. 提供可操作的建议
5. 考虑数据的局限性和偏差
报告格式:
- 摘要(关键发现)
- 详细分析
- 数据可视化建议
- 结论和建议
`,
tools: [
sqlExecutor, // SQL 执行器
chartGenerator, // 图表生成
anomalyDetector, // 异常检测
trendAnalyzer, // 趋势分析
reportGenerator, // 报告生成
],
});
进阶技巧与最佳实践 / Tips and Best Practices
提示工程技巧
编写高质量的系统提示是充分发挥 AI 代理能力的关键。一个好的提示应该清晰地定义角色、提供具体的指导原则、说明工具的使用时机,并给出示例帮助模型理解期望的行为。
结构化提示的组成要素:
const structuredPrompt = `
=== 角色定义 ===
[明确描述代理的身份、专业领域和行为特征]
=== 核心能力 ===
[列出代理的主要功能和专长]
=== 行为准则 ===
[具体的行为规范,包括该做什么、不该做什么]
=== 交互风格 ===
[描述期望的沟通方式,如正式/非正式、语言风格等]
=== 工具使用指南 ===
[何时使用哪种工具,以及使用的注意事项]
=== 边界条件 ===
[明确代理的能力边界和限制]
=== 示例对话 ===
[提供几个典型的用户-代理交互示例]
`.trim();
温度参数的选择指南: 温度参数控制输出的随机性,取值范围通常是 0 到 1 之间。温度越低,输出越确定和保守;温度越高,输出越有创意但也可能更加不可预测。对于不同场景,建议使用不同的温度值:事实问答使用 0.1-0.3,客服对话使用 0.3-0.5,创意写作使用 0.7-0.9,头脑风暴使用 0.9-1.0。
工具设计最佳实践
工具是代理能力的延伸,精心设计的工具能够大大增强代理的实用性。以下是工具设计的最佳实践:
提供清晰的描述:
const wellDesignedTool = tool({
name: 'searchProducts',
description: `在商品数据库中搜索产品。这个工具支持按名称、品牌、
类别、价格范围等条件搜索。当用户想要查找特定商品、比较价格、
或者了解某类产品的供应情况时使用此工具。
注意:
- 返回结果按相关性排序
- 最多返回 20 个结果
- 价格单位为人民币元`,
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: '搜索关键词,支持产品名称、品牌或类别',
},
maxPrice: {
type: 'number',
description: '最高价格限制,单位元',
},
category: {
type: 'string',
description: '产品类别',
},
},
required: ['query'],
},
execute: async ({ query, maxPrice, category }) => {
// 实际搜索逻辑
// ...
},
});
处理错误和边界情况:
const robustTool = tool({
name: 'getUserInfo',
description: '获取用户的基本信息',
inputSchema: {
type: 'object',
properties: {
userId: {
type: 'string',
description: '用户ID',
},
},
required: ['userId'],
},
execute: async ({ userId }) => {
try {
// 执行查询
const userInfo = await database.query(userId);
// 验证结果
if (!userInfo) {
return {
success: false,
error: 'USER_NOT_FOUND',
message: '未找到指定的用户',
};
}
return {
success: true,
data: userInfo,
};
} catch (error) {
// 优雅地处理异常
console.error('数据库查询失败:', error);
return {
success: false,
error: 'DATABASE_ERROR',
message: '查询失败,请稍后重试',
retryable: true, // 标记是否可重试
};
}
},
});
性能优化建议
在生产环境中,性能优化非常重要。以下是一些实用的性能优化建议:
模型选择策略:
// 根据任务复杂度选择不同规模的模型
const getAppropriateModel = (taskComplexity: 'low' | 'medium' | 'high') => {
const modelMap = {
low: 'gpt-3.5-turbo', // 简单问答、格式转换
medium: 'gpt-4', // 一般推理、写作、分析
high: 'gpt-4-32k', // 复杂推理、长文档处理
};
return openai.chat(modelMap[taskComplexity]);
};
// 在代理配置中使用
const agent = new Agent({
model: getAppropriateModel('medium'),
// ...
});
异步处理和并发控制:
// 批量处理工具调用
async function batchExecute(tools: Tool[], inputs: any[]) {
// 使用 Promise.all 并发执行,但限制并发数
const concurrencyLimit = 5;
const results = [];
for (let i = 0; i < inputs.length; i += concurrencyLimit) {
const batch = inputs.slice(i, i + concurrencyLimit);
const batchResults = await Promise.all(
batch.map(input => executeTool(tools, input))
);
results.push(...batchResults);
}
return results;
}
安全最佳实践
AI 应用涉及敏感数据和用户交互,安全性不容忽视。
敏感信息的处理:
// 永远不要在日志中记录敏感信息
const sensitiveDataHandler = {
// 敏感字段列表
sensitiveFields: ['password', 'apiKey', 'creditCard', 'ssn'],
// 脱敏函数
sanitize: (data: Record<string, any>) => {
const sanitized = { ...data };
for (const field of sensitiveDataHandler.sensitiveFields) {
if (sanitized[field]) {
sanitized[field] = '***REDACTED***';
}
}
return sanitized;
},
// 安全日志记录
safeLog: (message: string, data?: Record<string, any>) => {
const safeData = data ? sensitiveDataHandler.sanitize(data) : undefined;
console.log(message, safeData);
},
};
输入验证和过滤:
import { z } from 'zod';
// 使用 Zod 定义输入 schema 并自动验证
const validatedTool = tool({
name: 'processUserInput',
description: '处理用户输入',
inputSchema: z.object({
userId: z.string().min(1).max(100),
action: z.enum(['view', 'edit', 'delete']),
content: z.string().max(10000).optional(),
}),
execute: async (validatedInput) => {
// validatedInput 已经被 Zod 验证
// 可以安全地使用
const { userId, action, content } = validatedInput;
// ...
},
});
项目部署与生产环境 / Deployment
开发环境与生产环境的差异
在将 Mastra 应用部署到生产环境之前,需要了解开发和生产环境的关键差异,并做好相应准备。
环境配置分离:
// config/environment.ts
interface EnvironmentConfig {
nodeEnv: 'development' | 'production' | 'staging';
apiKeys: Record<string, string>;
rateLimits: {
requestsPerMinute: number;
requestsPerHour: number;
};
logging: {
level: 'debug' | 'info' | 'warn' | 'error';
destination: 'console' | 'file' | 'remote';
};
}
// 根据环境加载配置
export function loadConfig(): EnvironmentConfig {
const env = process.env.NODE_ENV || 'development';
return {
nodeEnv: env,
apiKeys: {
openai: process.env.OPENAI_API_KEY || '',
// ... 其他 API 密钥
},
rateLimits: {
requestsPerMinute: env === 'production' ? 60 : 1000,
requestsPerHour: env === 'production' ? 1000 : 10000,
},
logging: {
level: env === 'production' ? 'warn' : 'debug',
destination: env === 'production' ? 'remote' : 'console',
},
};
}
Docker 部署
容器化部署是现代应用的标准方式,以下是 Mastra 应用的 Dockerfile 配置:
# Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
# 复制 package 文件
COPY package*.json ./
# 安装依赖
RUN npm ci
# 复制源代码
COPY . .
# 编译 TypeScript
RUN npm run build
# 生产镜像
FROM node:20-alpine AS production
WORKDIR /app
# 创建非 root 用户
RUN addgroup -g 1001 -S nodejs && \
adduser -S mastra -u 1001
# 复制 package 和构建产物
COPY package*.json ./
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
# 设置环境变量
ENV NODE_ENV=production
ENV PORT=3000
# 切换到非 root 用户
USER mastra
# 暴露端口
EXPOSE 3000
# 启动命令
CMD ["node", "dist/index.js"]
对应的 docker-compose.yml 配置:
# docker-compose.yml
version: '3.8'
services:
mastra-app:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- OPENAI_API_KEY=${OPENAI_API_KEY}
- DATABASE_URL=${DATABASE_URL}
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
# 如果需要数据库
postgres:
image: postgres:15-alpine
volumes:
- postgres-data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=mastra
- POSTGRES_USER=${DB_USER}
- POSTGRES_PASSWORD=${DB_PASSWORD}
restart: unless-stopped
volumes:
postgres-data:
监控和可观测性
生产环境必须配备完善的监控和日志系统:
// src/observability/monitoring.ts
import { mastra } from '../mastra';
// 配置监控
mastra.setTelemetry({
enabled: true,
serviceName: 'travel-assistant',
exporters: [
// 发送到日志服务
{
type: 'otlp',
endpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
},
],
});
// 请求追踪中间件
export async function traceRequest(
request: Request,
handler: (req: Request) => Promise<Response>
) {
const traceId = generateTraceId();
try {
// 开始追踪
const span = startSpan('http-request', {
attributes: {
'http.method': request.method,
'http.url': request.url,
'trace.id': traceId,
},
});
const response = await handler(request);
// 记录成功
span.setStatus({ code: 0 });
span.end();
return response;
} catch (error) {
// 记录错误
console.error('请求失败', {
traceId,
error: error.message,
stack: error.stack,
});
throw error;
}
}
// 健康检查端点
export async function healthCheck() {
const checks = {
uptime: process.uptime(),
memory: process.memoryUsage(),
timestamp: Date.now(),
};
return {
status: 'healthy',
checks,
};
}
总结与资源链接 / Conclusion
核心要点回顾
通过本文的学习,你应该已经掌握了以下关键知识和技能:
Mastra 的核心理念: 它是一个专为 TypeScript/JavaScript 开发者设计的 AI 应用框架,通过模块化的设计让构建智能代理变得简单高效。从代理创建到工具集成,从记忆系统到部署监控,Mastra 提供了一站式解决方案。
代理系统的组成: 代理由语言模型、系统提示、工具集和记忆系统四部分组成。语言模型是决策核心,系统提示定义行为风格,工具集扩展执行能力,记忆系统保留上下文信息。
工具开发的要点: 工具是代理与外部世界交互的桥梁。设计工具时需要提供清晰的描述、准确的输入模式、完善错误处理和适当的结果格式化。好的工具描述能帮助代理更智能地决定何时调用。
实战项目经验: 通过构建智能旅行助手的完整项目,你学会了如何组织项目结构、创建多种类型的工具、配置代理、整合记忆系统,以及如何运行和测试应用。
生产部署考虑: 将应用部署到生产环境需要关注环境配置分离、Docker 容器化、监控可观测性和安全防护等方面。
延伸学习资源
官方文档和社区:
- Mastra 官方文档:https://mastra.ai/docs
- GitHub 仓库:https://github.com/mastra-ai/mastra
- Discord 社区:加入与其他开发者交流经验
- 示例项目集:官方仓库中的 examples 目录包含更多参考实现
相关技术栈:
- LangChain.js:如果你是 JavaScript 开发者,LangChain.js 提供了另一种构建 LLM 应用的方式
- LlamaIndex:专注于知识检索和问答场景
- AutoGPT:了解自主代理的设计思路
- LangFlow:可视化的 AI 流程设计工具
推荐阅读:
- Prompt Engineering Guide:学习高级提示技术
- Building LLM-Powered Applications:O’Reilly 出品的实践指南
- Practical AI Ethics:AI 应用的伦理考量
未来展望
AI 应用开发领域正在快速发展,Mastra 作为新兴框架也在持续演进。在未来,我们可以期待更多模型提供商的支持、更强大的工具生态系统、更完善的调试和监控功能,以及与企业级系统的深度集成。
作为开发者,保持学习的热情非常重要。AI 技术日新月异,新的工具和方法不断涌现。建议你多动手实践,参与开源社区,持续关注领域动态,这样才能在变革中保持竞争力。
最后,希望这篇教程能够帮助你在 AI 应用开发的道路上迈出坚实的一步。无论你是想构建智能客服、内容创作助手,还是数据分析平台,Mastra 都能成为你得力的工具。祝你开发愉快,期待看到你的精彩作品!
评论区