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

例:AI SDK useChat フック

Mastraのメモリを、Vercel AI SDKのuseChatフックを使ってReactのようなフロントエンドフレームワークと統合する際は、メッセージ履歴の重複を避けるために慎重な取り扱いが必要です。この例では、推奨されるパターンを示します。

useChatを使用したメッセージの重複防止

useChatのデフォルト動作では、リクエストごとにチャット履歴全体を送信します。MastraのメモリはthreadIdに基づいて自動的に履歴を取得するため、クライアントから完全な履歴を送信すると、コンテキストウィンドウとストレージにメッセージが重複して表示されます。

解決策: useChatを設定して、最新のメッセージのみthreadIdresourceIdと一緒に送信するようにします。

// components/Chat.tsx (React Example) import { useChat } from "ai/react"; export function Chat({ threadId, resourceId }) { const { messages, input, handleInputChange, handleSubmit } = useChat({ api: "/api/chat", // Your backend endpoint // Pass only the latest message and custom IDs experimental_prepareRequestBody: (request) => { // Ensure messages array is not empty and get the last message const lastMessage = request.messages.length > 0 ? request.messages[request.messages.length - 1] : null; // Return the structured body for your API route return { message: lastMessage, // Send only the most recent message content/role threadId, resourceId, }; }, // Optional: Initial messages if loading history from backend // initialMessages: loadedMessages, }); // ... rest of your chat UI component return ( <div> {/* Render messages */} <form onSubmit={handleSubmit}> <input value={input} onChange={handleInputChange} placeholder="Send a message..." /> <button type="submit">Send</button> </form> </div> ); } // app/api/chat/route.ts (Next.js Example) import { Agent } from "@mastra/core/agent"; import { Memory } from "@mastra/memory"; import { LibSQLStore } from "@mastra/libsql"; import { openai } from "@ai-sdk/openai"; import { CoreMessage } from "@mastra/core"; const agent = new Agent({ name: "ChatAgent", instructions: "You are a helpful assistant.", model: openai("gpt-4o"), memory: new Memory({ storage: new LibSQLStore({ url: "file:../mastra.db", // Or your database URL }), }); }); export async function POST(request: Request) { // Get data structured by experimental_prepareRequestBody const { message, threadId, resourceId }: { message: CoreMessage | null; threadId: string; resourceId: string } = await request.json(); // Handle cases where message might be null (e.g., initial load or error) if (!message || !message.content) { // Return an appropriate response or error return new Response("Missing message content", { status: 400 }); } // Process with memory using the single message content const stream = await agent.stream(message.content, { threadId, resourceId, // Pass other message properties if needed, e.g., role // messageOptions: { role: message.role } }); // Return the streaming response return stream.toDataStreamResponse(); }

詳細については、メッセージ永続化に関するAI SDKのドキュメントを参照してください。

基本的なスレッド管理UI

このページでは主に useChat に焦点を当てていますが、スレッドの管理(一覧表示、作成、選択)用のUIも構築できます。これには通常、Mastraのメモリ機能である memory.getThreadsByResourceId()memory.createThread() などと連携するバックエンドAPIエンドポイントが関与します。

// Conceptual React component for a thread list import React, { useState, useEffect } from 'react'; // Assume API functions exist: 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([]); // ... loading and error states ... useEffect(() => { // Fetch threads for userId }, [userId]); const handleCreateThread = async () => { // Call createNewThread API, update state, select new thread }; // ... render UI with list of threads and New Conversation button ... return ( <div> <h2>Conversations</h2> <button onClick={handleCreateThread}>New Conversation</button> <ul> {threads.map(thread => ( <li key={thread.id}> <button onClick={() => onSelectThread(thread.id)} disabled={thread.id === currentThreadId}> {thread.title || `Chat ${thread.id.substring(0, 8)}...`} </button> </li> ))} </ul> </div> ); } // Example Usage in a Parent Chat Component 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 threadId={currentThreadId} resourceId={userId} /> // Your useChat component ) : ( <div>Select or start a conversation.</div> )} </div> </div> ); }

関連