Skip to main content

在你的 SvelteKit 项目中集成 Mastra

🌐 Integrate Mastra in your SvelteKit project

在本指南中,你将使用 Mastra 构建一个调用工具的 AI 代理,然后通过从路由中直接导入和调用该代理,将其连接到 SvelteKit。

🌐 In this guide, you'll build a tool-calling AI agent using Mastra, then connect it to SvelteKit by importing and calling the agent directly from your routes.

你将使用 AI SDK UI 来创建一个美观、互动的聊天体验。

🌐 You'll use AI SDK UI to create a beautiful, interactive chat experience.

在你开始之前
Direct link to 在你开始之前

🌐 Before you begin

  • 你需要从支持的模型提供商获取一个 API 密钥。如果你没有偏好,可以使用OpenAI
  • 安装 Node.js v22.13.0 或更高版本

创建一个新的 SvelteKit 应用(可选)
Direct link to 创建一个新的 SvelteKit 应用(可选)

🌐 Create a new SvelteKit app (optional)

如果你已经有一个使用 Tailwind 的 SvelteKit 应用,可以直接跳到下一步。

🌐 If you already have a SvelteKit app using Tailwind, skip to the next step.

运行以下命令以创建一个新的 SvelteKit 应用

🌐 Run the following command to create a new SvelteKit app:

npx sv create mastra-svelte --template minimal --types ts --add tailwindcss="plugins:forms" --install npm

这会创建一个名为 mastra-svelte 的项目,但你可以用任何你想要的名字替换它。Tailwind 后来被添加用于样式设计。

🌐 This creates a project called mastra-svelte, but you can replace it with any name you want. Tailwind was added for styling purposes later on.

初始化 Mastra
Direct link to 初始化 Mastra

🌐 Initialize Mastra

导航到你的 SvelteKit 项目:

🌐 Navigate to your SvelteKit project:

cd mastra-svelte

运行 mastra init。出现提示时,选择一个提供商(例如 OpenAI)并输入你的密钥:

🌐 Run mastra init. When prompted, choose a provider (e.g. OpenAI) and enter your key:

npx mastra@latest init

这将创建一个包含示例天气代理和以下文件的 src/mastra 文件夹:

🌐 This creates a src/mastra folder with an example weather agent and the following files:

  • index.ts - Mastra 配置,包括内存
  • tools/weather-tool.ts - 一个用于获取给定位置天气的工具
  • agents/weather-agent.ts——一个使用该工具的天气代理和提示

在接下来的步骤中,你将从 SvelteKit 路由中调用 weather-agent.ts

🌐 You'll call weather-agent.ts from your SvelteKit routes in the next steps.

安装 AI SDK 界面
Direct link to 安装 AI SDK 界面

🌐 Install AI SDK UI

安装 AI SDK 界面以及 Mastra 适配器:

🌐 Install AI SDK UI along with the Mastra adapter:

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

创建聊天路线
Direct link to 创建聊天路线

🌐 Create a chat route

创建 src/routes/api/chat/+server.ts

🌐 Create src/routes/api/chat/+server.ts:

src/routes/api/chat/+server.ts
import type { RequestHandler } from "./$types";
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: RequestHandler = 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: RequestHandler = 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 格式流式返回代理的响应,而 GET 路由则从内存中获取消息历史,以便在客户端重新加载时能够恢复 UI。

🌐 The POST route accepts a prompt and streams the agent's response back in AI SDK format, while the GET route fetches message history from memory so the UI can be hydrated when the client reloads.

为了调用 GET 处理程序,你需要创建一个 src/routes/+page.ts 文件。它的 load() 函数会与 +page.svelte 一起运行。

🌐 In order for the GET handler to be called, you need to create a src/routes/+page.ts file. Its load() function runs alongside +page.svelte.

src/routes/+page.ts
import type { UIDataTypes, UIMessage, UITools } from "ai";
import type { PageLoad } from "./$types";

export const load: PageLoad = async ({ fetch }) => {
const response = await fetch("/api/chat");
const initialMessages = (await response.json()) as UIMessage<
unknown,
UIDataTypes,
UITools
>[];
return { initialMessages };
};

添加聊天界面
Direct link to 添加聊天界面

🌐 Add the chat UI

src/routes/+page.svelte 替换为以下内容。

🌐 Replace src/routes/+page.svelte with the following.

src/routes/+page.svelte
<script lang="ts">
import { Chat } from '@ai-sdk/svelte';
import { DefaultChatTransport, type ToolUIPart } from 'ai';

let input = $state('');
const { data } = $props();
let messages = $derived(data.initialMessages)

const chat = new Chat({
transport: new DefaultChatTransport({
api: '/api/chat'
}),
get messages() {
return messages;
}
});

function handleSubmit(event: SubmitEvent) {
event.preventDefault();
chat.sendMessage({ text: input });
input = '';
}

const STATE_TO_LABEL_MAP: Record<ToolUIPart["state"], string> = {
"input-streaming": "Pending",
"input-available": "Running",
// @ts-expect-error - Only available in AI SDK v6
"approval-requested": "Awaiting Approval",
"approval-responded": "Responded",
"output-available": "Completed",
"output-error": "Error",
"output-denied": "Denied",
};
</script>

<main class="max-w-3xl mx-auto p-6 size-full h-screen">
<div class="flex flex-col h-full">
<div class="flex-1 min-h-0 overflow-y-auto" data-name="conversation">
<div data-name="conversation-content" class="flex flex-col gap-8">
{#each chat.messages as message, messageIndex (messageIndex)}
<div>
{#each message.parts as part, partIndex (partIndex)}
{#if part.type === 'text'}
<div data-name="message" class={[message.role === 'user' && 'ml-auto justify-end', 'group flex w-full max-w-[95%] flex-col gap-2', message.role === 'user' ? 'is-user' : 'is-assistant']}>
<div data-name="message-content" class={["is-user:dark flex w-fit max-w-full min-w-0 flex-col gap-2 overflow-hidden text-sm",
"group-[.is-user]:ml-auto group-[.is-user]:rounded-lg group-[.is-user]:bg-blue-100 group-[.is-user]:px-4 group-[.is-user]:py-3 group-[.is-user]:text-foreground",
"group-[.is-assistant]:text-foreground"]}>
<div data-name="message-response" class="size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0">
{part.text}
</div>
</div>
</div>
{:else if part.type.startsWith('tool-')}
<div data-name="tool" class="not-prose mb-6 w-full rounded-lg border border-gray-300 shadow">
<details data-name="tool-header" class="w-full p-3 hover:cursor-pointer">
<summary class="font-medium text-sm">{(part as ToolUIPart).type.split("-").slice(1).join("-")} - {STATE_TO_LABEL_MAP[(part as ToolUIPart).state ?? 'output-available']}</summary>
<div data-name="tool-content" class="">
<div data-name="tool-input" class="space-y-2 overflow-hidden py-4">
<div class="font-medium text-muted-foreground text-xs uppercase tracking-wide">Parameters</div>
<pre class="w-full overflow-x-auto rounded-md border border-gray-300 bg-gray-50 p-3 text-sm"><code>{JSON.stringify((part as ToolUIPart).input, null, 2)}</code></pre>
</div>
<div data-name="tool-output" class="space-y-2 overflow-hidden py-4">
<div class="font-medium text-muted-foreground text-xs uppercase tracking-wide">{(part as ToolUIPart).errorText ? 'Error' : 'Result'}</div>
<pre class="w-full overflow-x-auto rounded-md border border-gray-300 bg-gray-50 p-3 text-sm"><code>{JSON.stringify((part as ToolUIPart).output, null, 2)}</code></pre>
{#if (part as ToolUIPart).errorText}
<div data-name="tool-error" class="text-red-600">
{(part as ToolUIPart).errorText}
</div>
{/if}
</div>
</div>
</details>
</div>
{/if}
{/each}
</div>
{/each}
</div>
</div>
<form class="w-full grid grid-cols-[1fr_auto] gap-6 shrink-0 pt-4" onsubmit={handleSubmit} data-name="prompt-input">
<input name="chat-input" class="rounded-lg border border-gray-300 shadow h-10" placeholder="City name" bind:value={input} />
<button class="bg-blue-600 text-white shadow-lg border border-blue-400 px-4 whitespace-nowrap rounded-lg text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]" type="submit">Send</button>
</form>
</div>
</main>

此页面将 Chat 连接到 api/chat 端点,将提示发送到该端点,并将响应分块返回。

🌐 This page connects Chat to the api/chat endpoint, sending prompts there and streaming the response back in chunks.

它使用自定义消息和工具组件来呈现响应文本。

🌐 It renders the response text using custom message and tool components.

测试你的代理
Direct link to 测试你的代理

🌐 Test your agent

  1. 使用 npm run dev 运行你的 SvelteKit 应用
  2. http://localhost:5173 打开聊天
  3. 试着询问天气。如果你的 API 密钥设置正确,你将会收到回复

下一步
Direct link to 下一步

🌐 Next steps

恭喜你使用 SvelteKit 构建了你的 Mastra 代理!🎉

🌐 Congratulations on building your Mastra agent with SvelteKit! 🎉

从这里,你可以使用你自己的工具和逻辑来扩展项目:

🌐 From here, you can extend the project with your own tools and logic:

  • 了解更多关于代理的信息
  • 给你的代理分配自己的工具
  • 给你的代理添加类似人类的内存

当你准备好时,了解更多关于 Mastra 如何与 AI SDK UI 和 SvelteKit 集成,以及如何在任何地方部署你的代理的信息:

🌐 When you're ready, read more about how Mastra integrates with AI SDK UI and SvelteKit, and how to deploy your agent anywhere: