vercel-ai-sdk について
Vercel AI SDKは、Reactフックとサーバーユーティリティを使ってストリーミングチャットを構築するためのSDKです。ツール呼び出し、添付ファイル、複数段階の処理、型付きメッセージに対応します。
主な機能
- 01Reactチャットフック
- 02サーバー側ストリーミング機能
- 03実行状態付きツール呼び出し
- 04ファイルとカスタムデータのメッセージパーツ
- 05型付きメッセージとツール
活用シーン
- 01ストリーミングチャット画面の構築
- 02ツール呼び出しの進行状況と結果の表示
- 03チャットでのファイル添付処理
- 04サーバー側・クライアント側ツールの実装
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ existential-birds/ beagle --skill vercel-ai-sdkインストール後、Claude Code で/vercel-ai-sdkを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/messages.mdstreaming.mdtools.mduse-chat.md詳細
Vercel AI SDK
Vercel AI SDK は、ツール呼び出し、ファイル添付、複数ステップの推論に対応したストリーミングチャットインターフェースを構築するための React フックとサーバーユーティリティを提供します。
クイックリファレンス
useChat の基本設定
import { useChat } from '@ai-sdk/react';
const { messages, status, sendMessage, stop, regenerate } = useChat({
id: 'chat-id',
messages: initialMessages,
onFinish: ({ message, messages, isAbort, isError }) => {
console.log('Chat finished');
},
onError: (error) => {
console.error('Chat error:', error);
}
});
// Send a message
sendMessage({ text: 'Hello', metadata: { createdAt: Date.now() } });
// Send with files
sendMessage({
text: 'Analyze this',
files: fileList // FileList or FileUIPart[]
});
ChatStatus の状態
status フィールドは、チャットの現在の状態を示します。
ready:チャットはアイドル状態で、新しいメッセージを受け付ける準備ができていますsubmitted:メッセージを API に送信済みで、レスポンスストリームの開始を待っていますstreaming:API からレスポンスをストリーミング中ですerror:リクエスト中にエラーが発生しました
メッセージ構造
メッセージでは、パーツベースの構造を持つ UIMessage 型を使用します。
interface UIMessage {
id: string;
role: 'system' | 'user' | 'assistant';
metadata?: unknown;
parts: Array<UIMessagePart>; // text, file, tool-*, reasoning, etc.
}
パーツの型には、次のものがあります。
text:ストリーミング状態を含むテキストコンテンツfile:ファイル添付(画像、ドキュメント)tool-{toolName}:状態遷移を伴うツール呼び出しreasoning:AI の推論トレースdata-{typeName}:カスタムデータパーツ
サーバーサイドのストリーミング
import { streamText } from 'ai';
import { convertToModelMessages } from 'ai';
const result = streamText({
model: openai('gpt-4'),
messages: convertToModelMessages(uiMessages),
tools: {
getWeather: tool({
description: 'Get weather',
inputSchema: z.object({ city: z.string() }),
execute: async ({ city }) => {
return { temperature: 72, weather: 'sunny' };
}
})
}
});
return result.toUIMessageStreamResponse({
originalMessages: uiMessages,
onFinish: ({ messages }) => {
// Save to database
}
});
ツール処理のパターン
クライアントサイドでのツール実行:
const { addToolOutput } = useChat({
onToolCall: async ({ toolCall }) => {
if (toolCall.toolName === 'getLocation') {
addToolOutput({
tool: 'getLocation',
toolCallId: toolCall.toolCallId,
output: 'San Francisco'
});
}
}
});
ツールの状態のレンダリング:
{message.parts.map(part => {
if (part.type === 'tool-getWeather') {
switch (part.state) {
case 'input-streaming':
return <pre>{JSON.stringify(part.input, null, 2)}</pre>;
case 'input-available':
return <div>Getting weather for {part.input.city}...</div>;
case 'output-available':
return <div>Weather: {part.output.weather}</div>;
case 'output-error':
return <div>Error: {part.errorText}</div>;
}
}
})}
リファレンスファイル
各項目の詳細なドキュメント:
- use-chat.md:useChat API の完全なリファレンス
- messages.md:UIMessage の構造とパーツの型
- streaming.md:サーバーサイドのストリーミング実装
- tools.md:ツール定義と実行のパターン
よく使われるパターン
エラー処理
const { error, clearError } = useChat({
onError: (error) => {
toast.error(error.message);
}
});
// Clear error and reset to ready state
if (error) {
clearError();
}
メッセージの再生成
const { regenerate } = useChat();
// Regenerate last assistant message
await regenerate();
// Regenerate specific message
await regenerate({ messageId: 'msg-123' });
カスタムトランスポート
import { DefaultChatTransport } from 'ai';
const { messages } = useChat({
transport: new DefaultChatTransport({
api: '/api/chat',
prepareSendMessagesRequest: ({ id, messages, trigger, messageId }) => ({
body: {
chatId: id,
lastMessage: messages[messages.length - 1],
trigger,
messageId
}
})
})
});
パフォーマンスの最適化
// Throttle UI updates to reduce re-renders
const chat = useChat({
experimental_throttle: 100 // Update max once per 100ms
});
メッセージの自動送信
import { lastAssistantMessageIsCompleteWithToolCalls } from 'ai';
const chat = useChat({
sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls
// Automatically resend when all tool calls have outputs
});
型安全性
SDK はツールとメッセージの完全な型推論を提供します。
import { InferUITools, UIMessage } from 'ai';
const tools = {
getWeather: tool({
inputSchema: z.object({ city: z.string() }),
execute: async ({ city }) => ({ weather: 'sunny' })
})
};
type MyMessage = UIMessage<
{ createdAt: number }, // Metadata type
UIDataTypes,
InferUITools<typeof tools> // Tool types
>;
const { messages } = useChat<MyMessage>();
重要な概念
パーツベースのアーキテクチャ
メッセージでは、単一の content フィールドではなく、パーツ配列を使用します。これにより、次のことが可能になります。
- 他のパーツを維持しながらテキストをストリーミングする
- 独立した状態遷移を持つツール呼び出し
- テキストにファイル添付やカスタムデータを混在させる
ツールの状態遷移
ツールパーツは、次の状態に進みます。
input-streaming:ツールの入力をストリーミング中(任意)input-available:ツールの入力が完了approval-requested:ユーザーの承認待ち(任意)approval-responded:ユーザーが承認または拒否済み(任意)output-available:ツールの実行が完了output-error:ツールの実行に失敗output-denied:ユーザーが承認を拒否
ストリーミングプロトコル
SDK では、UIMessageChunk 型とともに Server-Sent Events(SSE)を使用します。
text-start、text-delta、text-endtool-input-available、tool-output-availablereasoning-start、reasoning-delta、reasoning-endstart、finish、abort
クライアントサイドのツールとサーバーサイドのツール
サーバーサイドのツールには execute 関数があり、API ルート上で実行されます。
クライアントサイドのツールでは execute を省略し、onToolCall と addToolOutput を通じて処理します。
ゲート
次の順序で進めます。コードまたは UI で合格条件が満たされるまでは、その手順は未完了として扱ってください(「動くはず」では不十分です)。
- ストリーミングルート — 合格条件: チャットハンドラーが
convertToModelMessages→streamText(またはアプリで標準化している SDK のパターン) →toUIMessageStreamResponse(または同等のストリームレスポンス)をつないでいます。 不合格条件: UI メッセージストリームの契約に準拠せず、プレーンな JSON 文字列をレスポンスとして返しています。 - クライアント ↔ ルート — 合格条件:
useChatのid/DefaultChatTransportのapi(およびprepareSendMessagesRequestの body)が、ルートパスとサーバーが読み取る body に一致しています。 不合格条件: クライアントが、ハンドラーが想定するものとは異なるパスまたは形式に POST しています。 - ツールのクローズドループ — 合格条件:
tools内のすべてのツールにサーバー側のexecuteがあるか、または同じtoolCallIdを使うonToolCallとaddToolOutputがあり、UI が表示するtool-*の状態を UI で処理しています。 不合格条件: ツール名がtoolsに存在するものの、ハンドラーがないか、レンダラーに必要な状態がありません。 - 永続化(該当する場合) — 合格条件: 保存前にサーバーが
validateUIMessages(またはより厳格な検証)を実行しています。 不合格条件: 検証されていないクライアントのペイロードがストレージに書き込まれています。
ベストプラクティス
error状態を必ず処理し、ユーザーにフィードバックを提供します- 更新頻度が高い場合は
experimental_throttleを使用します statusに基づいて適切なローディング状態を実装します- カスタムメタデータとツールを使ってメッセージに型を指定します
- 複数ターンにわたるツールのワークフローには
sendAutomaticallyWhenを使用します - UX を向上させるため、UI ですべてのツール状態を処理します
- 長時間実行されるリクエストをユーザーがキャンセルできるよう、
stop()を使用します - サーバーで
validateUIMessagesを使ってメッセージを検証します