跳到主要内容

在 Astro 项目中集成 Mastra

本指南将介绍如何使用 Mastra 构建一个可调用 Tool 的 AI Agent,然后通过路由直接导入并调用该 Agent,将其连接到 Astro。

你将使用 AI SDK UIAI Elements 创建美观的交互式聊天体验。

备注

虽然本指南展示了如何将 Astro 与 React 和完整服务器端渲染(SSR)配合使用,但 Astro 与 Mastra 还有许多其他用法。你可以按文件选择启用 SSR,也可以使用 Svelte、Vue、Solid 或 Preact 等其他框架。你可以用 Astro 构建聊天界面,并在 Astro 中原生调用端点

开始之前
开始之前的直接链接

  • 你需要受支持的模型 Provider所提供的 API 密钥。如果没有偏好,请使用 OpenAI
  • 安装 Node.js v22.13.0 或更高版本

创建新的 Astro 应用(可选)
创建新的 Astro 应用(可选)的直接链接

如果已有 Astro 应用,请跳到下一步。Astro 应用应按以下方式设置:

  • 使用 SSR(在 astro.config.mjs 中设置 output: "server"
  • 使用 React 集成
  • 已安装 Tailwind

为支持按需渲染,本指南使用 Node.js adapter,但任何受支持的 server adapter 都可以使用。

npm create astro@latest mastra-astro -- --add node --add react --add tailwind --install --skip-houston --template minimal --git

这会创建名为 mastra-astro 的项目,你也可以将其替换为任意名称。

使用 cd 进入项目,然后编辑 astro.config.mjs 文件,将 output 设置设为 "server"

// @ts-check
import { defineConfig } from 'astro/config'

import node from '@astrojs/node'
import react from '@astrojs/react'
import tailwindcss from '@tailwindcss/vite'

// https://astro.build/config
export default defineConfig({
output: 'server',
adapter: node({
mode: 'standalone',
}),

integrations: [react()],

vite: {
plugins: [tailwindcss()],
},
})

编辑 tsconfig.json 以解析路径:

tsconfig.json
{
"compilerOptions": {
// ...
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
},
// ...
},
}

初始化 Mastra
初始化 Mastra的直接链接

运行 mastra init。出现提示时,选择 Provider(例如 OpenAI)并输入密钥:

npx mastra@latest init

这会创建一个 src/mastra 文件夹,其中包含示例天气 Agent 和以下文件:

  • index.ts - Mastra 配置,包括 memory
  • tools/weather-tool.ts - 获取指定位置天气的 Tool
  • agents/weather-agent.ts- 使用该 Tool 的天气 Agent 及其提示词

接下来,你会从 Astro 路由调用 weather-agent.ts

安装 AI SDK UI 和 AI Elements
安装 AI SDK UI 和 AI Elements的直接链接

安装 AI SDK UI 和 Mastra adapter:

npm install @mastra/ai-sdk@latest @ai-sdk/react ai

接下来初始化 AI Elements。出现提示时请选择默认选项:

npx ai-elements@latest

这会将完整的 AI Elements UI 组件库下载到 @/components/ai-elements 文件夹。

创建聊天路由
创建聊天路由的直接链接

创建 src/pages/api/chat.ts

src/pages/api/chat.ts
import type { APIRoute } from 'astro'
import { handleChatStream } from '@mastra/ai-sdk'
import { toAISdkV5Messages } from '@mastra/ai-sdk/ui'
import { createUIMessageStreamResponse } from 'ai'
import { mastra } from '@/mastra'

const THREAD_ID = 'example-user-id'
const RESOURCE_ID = 'weather-chat'

export const POST: APIRoute = async ({ request }) => {
const params = await request.json()
const stream = await handleChatStream({
mastra,
agentId: 'weather-agent',
params: {
...params,
memory: {
...params.memory,
thread: THREAD_ID,
resource: RESOURCE_ID,
},
},
})

return createUIMessageStreamResponse({ stream })
}

export const GET: APIRoute = async () => {
const memory = await mastra.getAgentById('weather-agent').getMemory()
let response = null

try {
response = await memory?.recall({
threadId: THREAD_ID,
resourceId: RESOURCE_ID,
})
} catch {
console.log('No previous messages found.')
}

const uiMessages = toAISdkV5Messages(response?.messages || [])

return Response.json(uiMessages)
}

POST 路由接受提示词,并以 AI SDK 格式流式返回 Agent 响应;GET 路由则从 memory 获取消息历史,以便客户端重新加载时恢复 UI 状态。

创建聊天组件
创建聊天组件的直接链接

创建 src/components/chat.tsx

src/components/chat.tsx
import '@/styles/global.css'
import { useEffect, useState } from 'react'
import { DefaultChatTransport, type ToolUIPart } from 'ai'
import { useChat } from '@ai-sdk/react'

import {
PromptInput,
PromptInputBody,
PromptInputTextarea,
} from '@/components/ai-elements/prompt-input'

import {
Conversation,
ConversationContent,
ConversationScrollButton,
} from '@/components/ai-elements/conversation'

import { Message, MessageContent, MessageResponse } from '@/components/ai-elements/message'

import { Tool, ToolHeader, ToolContent, ToolInput, ToolOutput } from '@/components/ai-elements/tool'

function Chat() {
const [input, setInput] = useState<string>('')

const { messages, setMessages, sendMessage, status } = useChat({
transport: new DefaultChatTransport({
api: '/api/chat',
}),
})

useEffect(() => {
const fetchMessages = async () => {
const res = await fetch('/api/chat')
const data = await res.json()
setMessages([...data])
}
fetchMessages()
}, [setMessages])

const handleSubmit = async () => {
if (!input.trim()) return

sendMessage({ text: input })
setInput('')
}

return (
<div className="relative size-full h-screen w-full p-6">
<div className="flex h-full flex-col">
<Conversation className="h-full">
<ConversationContent>
{messages.map(message => (
<div key={message.id}>
{message.parts?.map((part, i) => {
if (part.type === 'text') {
return (
<Message key={`${message.id}-${i}`} from={message.role}>
<MessageContent>
<MessageResponse>{part.text}</MessageResponse>
</MessageContent>
</Message>
)
}

if (part.type?.startsWith('tool-')) {
return (
<Tool key={`${message.id}-${i}`}>
<ToolHeader
type={(part as ToolUIPart).type}
state={(part as ToolUIPart).state || 'output-available'}
className="cursor-pointer"
/>
<ToolContent>
<ToolInput input={(part as ToolUIPart).input || {}} />
<ToolOutput
output={(part as ToolUIPart).output}
errorText={(part as ToolUIPart).errorText}
/>
</ToolContent>
</Tool>
)
}

return null
})}
</div>
))}
<ConversationScrollButton />
</ConversationContent>
</Conversation>

<PromptInput onSubmit={handleSubmit} className="mt-20">
<PromptInputBody>
<PromptInputTextarea
onChange={e => setInput(e.target.value)}
className="md:leading-10"
value={input}
placeholder="Type your message..."
disabled={status !== 'ready'}
/>
</PromptInputBody>
</PromptInput>
</div>
</div>
)
}

export default Chat

此组件将 useChat() 连接到 api/chat 端点,把提示词发送到该端点,并分块流式返回响应。

它使用 <MessageResponse> 组件渲染响应文本,并使用 <Tool> 组件显示所有 Tool 调用。

渲染聊天组件
渲染聊天组件的直接链接

最后一步是在索引页面渲染聊天组件。编辑 src/pages/index.astro

src/pages/index.astro
---
import Chat from '@/components/chat';
---

<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content="{Astro.generator}" />
<title>Astro</title>
</head>
<body>
<Chat client:load />
</body>
</html>

导入 Chat 组件,并使用 client:load 指令将其添加到 body,使它在客户端运行。

测试 Agent
测试 Agent的直接链接

  1. 使用 npm run dev 运行 Astro 应用
  2. 打开聊天页面:http://localhost:4321
  3. 尝试询问天气。如果 API 密钥配置正确,你会收到响应

后续步骤
后续步骤的直接链接

恭喜你使用 Astro 构建了 Mastra Agent!🎉

接下来,你可以使用自己的 Tool 和逻辑扩展项目:

  • 进一步了解 Agent
  • 为 Agent 添加自己的 Tool
  • 为 Agent 添加类似人类的 memory

准备就绪后,可进一步了解 Mastra 与 AI SDK UI 的集成方式,以及如何将 Agent 部署到任意位置: