mcpField
V
vercel-ai-sdk

vercel-ai-sdk

github.com/existential-birds開発85101 回インストール12 週間前に更新
リポジトリ

vercel-ai-sdk について

Vercel AI SDKは、Reactフックとサーバーユーティリティを使ってストリーミングチャットを構築するためのSDKです。ツール呼び出し、添付ファイル、複数段階の処理、型付きメッセージに対応します。

主な機能

  1. 01Reactチャットフック
  2. 02サーバー側ストリーミング機能
  3. 03実行状態付きツール呼び出し
  4. 04ファイルとカスタムデータのメッセージパーツ
  5. 05型付きメッセージとツール

活用シーン

  1. 01ストリーミングチャット画面の構築
  2. 02ツール呼び出しの進行状況と結果の表示
  3. 03チャットでのファイル添付処理
  4. 04サーバー側・クライアント側ツールの実装

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/existential-birds/beagle --skill vercel-ai-sdk

インストール後、Claude Code で/vercel-ai-sdkを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
messages.md
streaming.md
tools.md
use-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 フィールドではなく、パーツ配列を使用します。これにより、次のことが可能になります。

  • 他のパーツを維持しながらテキストをストリーミングする
  • 独立した状態遷移を持つツール呼び出し
  • テキストにファイル添付やカスタムデータを混在させる
ツールの状態遷移

ツールパーツは、次の状態に進みます。

  1. input-streaming:ツールの入力をストリーミング中(任意)
  2. input-available:ツールの入力が完了
  3. approval-requested:ユーザーの承認待ち(任意)
  4. approval-responded:ユーザーが承認または拒否済み(任意)
  5. output-available:ツールの実行が完了
  6. output-error:ツールの実行に失敗
  7. output-denied:ユーザーが承認を拒否
ストリーミングプロトコル

SDK では、UIMessageChunk 型とともに Server-Sent Events(SSE)を使用します。

  • text-start、text-delta、text-end
  • tool-input-available、tool-output-available
  • reasoning-start、reasoning-delta、reasoning-end
  • start、finish、abort
クライアントサイドのツールとサーバーサイドのツール

サーバーサイドのツールには execute 関数があり、API ルート上で実行されます。

クライアントサイドのツールでは execute を省略し、onToolCall と addToolOutput を通じて処理します。

ゲート

次の順序で進めます。コードまたは UI で合格条件が満たされるまでは、その手順は未完了として扱ってください(「動くはず」では不十分です)。

  1. ストリーミングルート — 合格条件: チャットハンドラーが convertToModelMessages → streamText(またはアプリで標準化している SDK のパターン) → toUIMessageStreamResponse(または同等のストリームレスポンス)をつないでいます。 不合格条件: UI メッセージストリームの契約に準拠せず、プレーンな JSON 文字列をレスポンスとして返しています。
  2. クライアント ↔ ルート — 合格条件: useChat の id/DefaultChatTransport の api(および prepareSendMessagesRequest の body)が、ルートパスとサーバーが読み取る body に一致しています。 不合格条件: クライアントが、ハンドラーが想定するものとは異なるパスまたは形式に POST しています。
  3. ツールのクローズドループ — 合格条件: tools 内のすべてのツールにサーバー側の execute があるか、または同じ toolCallId を使う onToolCall と addToolOutput があり、UI が表示する tool-* の状態を UI で処理しています。 不合格条件: ツール名が tools に存在するものの、ハンドラーがないか、レンダラーに必要な状態がありません。
  4. 永続化(該当する場合) — 合格条件: 保存前にサーバーが validateUIMessages(またはより厳格な検証)を実行しています。 不合格条件: 検証されていないクライアントのペイロードがストレージに書き込まれています。

ベストプラクティス

  1. error 状態を必ず処理し、ユーザーにフィードバックを提供します
  2. 更新頻度が高い場合は experimental_throttle を使用します
  3. status に基づいて適切なローディング状態を実装します
  4. カスタムメタデータとツールを使ってメッセージに型を指定します
  5. 複数ターンにわたるツールのワークフローには sendAutomaticallyWhen を使用します
  6. UX を向上させるため、UI ですべてのツール状態を処理します
  7. 長時間実行されるリクエストをユーザーがキャンセルできるよう、stop() を使用します
  8. サーバーで validateUIMessages を使ってメッセージを検証します

コメント