Skip to Content
メモリAI SDK useChatフック

例: Mastra ServerでのAI SDK useChatフック

この例では、Mastra Serverデプロイメントを使用する際に、MastraのメモリをVercel AI SDKのuseChatフックと統合する方法を示します。重要なのは、MastraClient SDKを介してNext.jsアプリをMastra Serverに接続することです。

アーキテクチャ概要

Mastra Serverを使用する場合、2つのオプションがあります:

オプション1: APIルート経由(本番環境推奨)

  1. ReactフロントエンドがUIの状態管理にuseChatフックを使用
  2. APIルートがMastraClientを使用してMastra Serverと通信
  3. Mastra Serverがエージェントの実行、メモリ、ツールを処理

メリット:

  • Mastra ServerのURLとAPIキーをバックエンドで安全に保持
  • 追加の認証・認可ロジックを追加可能
  • リクエストの変換とレスポンス処理が可能
  • マルチテナントアプリケーションに適している

オプション2: 直接接続(開発・プロトタイピングに適している)

  1. ReactフロントエンドがUIの状態管理にuseChatフックを使用
  2. useChatフックがMastra Serverのストリームエンドポイントに直接接続
  3. 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サーバー内で直接実行されます
  • エージェントの設定と依存関係をローカルで管理します
  • 開発には簡単ですが、より多くのセットアップが必要です

トラブルシューティング

よくある問題:

  1. 接続が拒否される: Mastra Serverが実行中でアクセス可能であることを確認してください
  2. 認証エラー: APIキーの設定を確認してください
  3. メッセージの重複: 最新のメッセージのみを送信していることを確認してください
  4. スレッド履歴の欠落: threadresourceを含むメモリ設定が正しく渡されていることを確認してください
  5. CORSエラー(直接接続): フロントエンドのオリジンからのリクエストを許可するようにMastra Serverを設定してください

関連