例: Mastra ServerでのAI SDK useChat
フック
この例では、Mastra Serverデプロイメントを使用する際に、MastraのメモリをVercel AI SDKのuseChat
フックと統合する方法を示します。重要なのは、MastraClient SDKを介してNext.jsアプリをMastra Serverに接続することです。
アーキテクチャ概要
Mastra Serverを使用する場合、2つのオプションがあります:
オプション1: APIルート経由(本番環境推奨)
- ReactフロントエンドがUIの状態管理に
useChat
フックを使用 - APIルートが
MastraClient
を使用してMastra Serverと通信 - Mastra Serverがエージェントの実行、メモリ、ツールを処理
メリット:
- Mastra ServerのURLとAPIキーをバックエンドで安全に保持
- 追加の認証・認可ロジックを追加可能
- リクエストの変換とレスポンス処理が可能
- マルチテナントアプリケーションに適している
オプション2: 直接接続(開発・プロトタイピングに適している)
- ReactフロントエンドがUIの状態管理に
useChat
フックを使用 useChat
フックがMastra Serverのストリームエンドポイントに直接接続- Mastra Serverがエージェントの実行、メモリ、ツールを処理
useChat
でのメッセージ重複の防止
useChat
のデフォルトの動作では、各リクエストでチャット履歴全体を送信します。MastraのメモリはthreadId
に基づいて履歴を自動的に取得するため、クライアントから完全な履歴を送信すると、コンテキストウィンドウとストレージでメッセージが重複してしまいます。
解決策: useChat
を設定して、メモリ設定(スレッドとリソース識別子)と共に最新のメッセージのみを送信するようにします。
components/Chat.tsx
import { useChat } from "ai/react";
export function Chat({ thread, resource }) {
const { messages, input, handleInputChange, handleSubmit } = useChat({
api: "/api/chat", // バックエンドエンドポイント
// 最新のメッセージとカスタムIDのみを渡す
experimental_prepareRequestBody: (request) => {
// メッセージ配列が空でないことを確認し、最後のメッセージを取得
const lastMessage = request.messages.length > 0 ? request.messages[request.messages.length - 1] : null;
// APIルート用の構造化されたボディを返す
return {
message: lastMessage, // 最新のメッセージコンテンツ/ロールのみを送信
memory: {
thread,
resource,
},
};
},
// オプション: バックエンドから履歴を読み込む場合の初期メッセージ
// initialMessages: loadedMessages,
});
// ... チャットUIコンポーネントの残り
return (
<div>
{/* メッセージをレンダリング */}
<form onSubmit={handleSubmit}>
<input value={input} onChange={handleInputChange} placeholder="メッセージを送信..." />
<button type="submit">送信</button>
</form>
</div>
);
}
app/api/chat/route.ts
import { MastraClient } from "@mastra/client-js";
import { CoreMessage } from "@mastra/core";
// Mastraクライアントを初期化してサーバーに接続
const mastraClient = new MastraClient({
baseUrl: process.env.MASTRA_SERVER_URL || "http://localhost:4111",
// オプション: サーバーが認証を必要とする場合はAPIキーを追加
headers: {
"x-api-key": process.env.MASTRA_API_KEY,
},
});
export async function POST(request: Request) {
// experimental_prepareRequestBodyによって構造化されたデータを取得
const { message, memory }: { message: CoreMessage | null; memory: { thread: string; resource: string } } = await request.json();
// メッセージがnullの場合を処理(例:初期読み込みやエラー)
if (!message || !message.content) {
return new Response("Missing message content", { status: 400 });
}
// Mastra Serverからエージェントを取得
const agent = mastraClient.getAgent("ChatAgent"); // エージェントのIDを使用
// メモリコンテキストでレスポンスをストリーミング
const response = await agent.stream({
messages: [{ role: message.role || "user", content: message.content }],
memory,
});
// ストリーミングレスポンスをフロントエンドに返す
return new Response(response.body);
}
代替案: 直接サーバールート
チャット用のカスタムルートハンドラーでMastra Serverをデプロイした場合、直接接続することもできます:
components/Chat.tsx
import { useChat } from "ai/react";
export function Chat({ thread, resource, agentId = "ChatAgent" }) {
const { messages, input, handleInputChange, handleSubmit } = useChat({
// Mastra Serverのストリームエンドポイントに直接接続
api: `${process.env.NEXT_PUBLIC_MASTRA_SERVER_URL}/api/agents/${agentId}/stream`,
experimental_prepareRequestBody: (request) => {
const lastMessage = request.messages.length > 0 ? request.messages[request.messages.length - 1] : null;
// Mastra Serverは単一のメッセージではなく、完全なメッセージ配列を期待します
return {
messages: lastMessage ? [lastMessage] : [],
memory: {
thread,
resource,
},
};
},
headers: {
// 必要に応じて認証を含める
"x-api-key": process.env.NEXT_PUBLIC_MASTRA_API_KEY,
},
});
// ... コンポーネントの残り
}
環境変数
環境変数を設定してください:
.env.local
MASTRA_SERVER_URL=http://localhost:4111 # Your Mastra Server URL
MASTRA_API_KEY=your-api-key # If authentication is enabled
# For direct client connection (if using the alternative approach)
NEXT_PUBLIC_MASTRA_SERVER_URL=http://localhost:4111
NEXT_PUBLIC_MASTRA_API_KEY=your-api-key
詳細については、メッセージの永続化に関するAI SDKドキュメント を参照してください。
基本的なスレッド管理UI
このページはuseChat
に焦点を当てていますが、スレッドを管理するためのUI(一覧表示、作成、選択)も構築できます。これには通常、Mastraのメモリ機能であるmemory.getThreadsByResourceId()
やmemory.createThread()
と連携するバックエンドAPIエンドポイントが必要です。
components/ThreadList.tsx
import React, { useState, useEffect } from 'react';
// API関数が存在すると仮定: fetchThreads, createNewThread
async function fetchThreads(userId: string): Promise<{ id: string; title: string }[]> { /* ... */ }
async function createNewThread(userId: string): Promise<{ id: string; title: string }> { /* ... */ }
function ThreadList({ userId, currentThreadId, onSelectThread }) {
const [threads, setThreads] = useState([]);
// ... ローディングとエラー状態 ...
useEffect(() => {
// userIdのスレッドを取得
}, [userId]);
const handleCreateThread = async () => {
// createNewThread APIを呼び出し、状態を更新し、新しいスレッドを選択
};
// ... スレッドのリストと新しい会話ボタンを含むUIをレンダリング ...
return (
<div>
<h2>会話</h2>
<button onClick={handleCreateThread}>新しい会話</button>
<ul>
{threads.map(thread => (
<li key={thread.id}>
<button onClick={() => onSelectThread(thread.id)} disabled={thread.id === currentThreadId}>
{thread.title || `チャット ${thread.id.substring(0, 8)}...`}
</button>
</li>
))}
</ul>
</div>
);
}
// 親チャットコンポーネントでの使用例
function ChatApp() {
const userId = "user_123";
const [currentThreadId, setCurrentThreadId] = useState<string | null>(null);
return (
<div style={{ display: 'flex' }}>
<ThreadList
userId={userId}
currentThreadId={currentThreadId}
onSelectThread={setCurrentThreadId}
/>
<div style={{ flexGrow: 1 }}>
{currentThreadId ? (
<Chat thread={currentThreadId} resource={userId} agentId="your-agent-id" /> // あなたのuseChatコンポーネント
) : (
<div>会話を選択するか開始してください。</div>
)}
</div>
</div>
);
}
主な違い: Mastra Server vs 直接統合
Mastra Serverを使用する場合:
- Next.jsアプリはMastra Serverのクライアントとして動作します
- サーバーとの通信には
MastraClient
を使用します - エージェントの設定、メモリ、ツールはサーバー上で管理されます
- 本番環境のデプロイメントやマルチクライアントシナリオに適しています
直接統合を使用する場合(AI SDKドキュメントから):
- エージェントはNext.jsサーバー内で直接実行されます
- エージェントの設定と依存関係をローカルで管理します
- 開発には簡単ですが、より多くのセットアップが必要です
トラブルシューティング
よくある問題:
- 接続が拒否される: Mastra Serverが実行中でアクセス可能であることを確認してください
- 認証エラー: APIキーの設定を確認してください
- メッセージの重複: 最新のメッセージのみを送信していることを確認してください
- スレッド履歴の欠落:
thread
とresource
を含むメモリ設定が正しく渡されていることを確認してください - CORSエラー(直接接続): フロントエンドのオリジンからのリクエストを許可するようにMastra Serverを設定してください
関連
- MastraClient概要: Mastra Client SDKについて詳しく学ぶ
- Mastra Server: Mastra Serverのデプロイと設定方法
- メモリ概要: メモリリソースとスレッドの基本概念
- AI SDK統合: 一般的なuseChatドキュメント