Vercel AI SDK 6 : AI SDK UI – チャットボット

useChat フックは、チャットボットアプリケーション用の会話型ユーザーインターフェイスの作成を簡単にします。AI プロバイダーからのチャットメッセージのストリーミングを可能にし、チャット状態を管理し、新しいメッセージを受け取ると同時に UI を自動的に更新します。

Vercel AI SDK 6 : AI SDK UI – チャットボット

作成 : Masashi Okumura (@classcat.com)
作成日時 : 02/09/2026
バージョン : ai@6.0.77

* 本記事は ai-sdk.dev/docs の以下のページを参考にしています :

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

 

Vercel AI SDK 6.x : AI SDK UI – チャットボット

useChat フックは、チャットボットアプリケーション用の会話型ユーザーインターフェイスの作成を簡単にします。AI プロバイダーからのチャットメッセージのストリーミングを可能にし、チャット状態を管理し、新しいメッセージを受け取ると同時に UI を自動的に更新します。

要約すると、useChat フックは以下の機能を提供します :

  • メッセージ・ストリーミング : AI プロバイダーからのすべてのメッセージはチャット UI にリアルタイムでストリーミングされます。

  • 状態管理 : フックは、入力、メッセージ、ステータス、エラー等の状態を管理します。

  • シームレスな統合 : チャット AI を任意のデザインやレイアウトに、最小限の労力で簡単に統合できます。

このガイドでは、useChat フックを使用して、リアルタイムのメッセージストリーミングを備えたチャットアプリケーションを作成する方法を学習します。チャットボットでツールを使用する方法を学習するには chatbot with tools ガイドを確認してください。まずは次のサンプルから始めましょう。

 

サンプル

app/page.tsx

'use client';

import { useChat } from '@ai-sdk/react';
import { DefaultChatTransport } from 'ai';
import { useState } from 'react';

export default function Page() {
  const { messages, sendMessage, status } = useChat({
    transport: new DefaultChatTransport({
      api: '/api/chat',
    }),
  });
  const [input, setInput] = useState('');

  return (
    <>
      {messages.map(message => (
        <div key={message.id}>
          {message.role === 'user' ? 'User: ' : 'AI: '}
          {message.parts.map((part, index) =>
            part.type === 'text' ? <span key={index}>{part.text}</span> : null,
          )}
        </div>
      ))}

      <form
        onSubmit={e => {
          e.preventDefault();
          if (input.trim()) {
            sendMessage({ text: input });
            setInput('');
          }
        }}
      >
        <input
          value={input}
          onChange={e => setInput(e.target.value)}
          disabled={status !== 'ready'}
          placeholder="Say something..."
        />
        <button type="submit" disabled={status !== 'ready'}>
          Submit
        </button>
      </form>
    </>
  );
}

Provider app/api/chat/route.ts

import { convertToModelMessages, streamText, UIMessage } from 'ai';
import { openai } from "@ai-sdk/openai";

// Allow streaming responses up to 30 seconds
export const maxDuration = 30;

export async function POST(req: Request) {
  const { messages }: { messages: UIMessage[] } = await req.json();

  const result = streamText({
    model: openai("gpt-4o-mini"),
    system: 'You are a helpful assistant.',
    messages: await convertToModelMessages(messages),
  });

  return result.toUIMessageStreamResponse();
}

ℹ️ UI メッセージは、メッセージ部分を含む新しい parts プロパティを持ちます。contents プロパティではなく、parts プロパティを使用してメッセージをレンダリングすることを勧めます。parts プロパティは、テキスト、ツール呼び出し、ツールの結果を含む、様々なメッセージ型をサポートし、より柔軟で複雑なチャット UI を可能にします。

Page コンポーネントでは、ユーザが sendMessage を使用してメッセージを送信するたびに、useChat フックは AI プロバイダーのエンドポイントにリクエストします。メッセージはリアルタイムにストリーミングとして返され、チャット UI に表示されます。

これは、ユーザは AI レスポンス全体が受信されるのを待つ必要なく、利用可能になり次第、確認できるようなシームレスなチャットエクスペリエンスを可能にします。

 

カスタマイズされた UI

useChat はまた、コードを通してチャットメッセージの状態を管理し、ステータスを表示し、ユーザインタラクションによるトリガーなしにメッセージを更新する方法も提供します。

 

ステータス

useChat フックは status を返します。以下の可能な値があります :

  • submitted: メッセージは API に送信され、応答ストリームの開始を待機しています。

  • streaming: レスポンスは API からアクティブにストリーミングされていて、データのチャンクを受信しています。

  • ready: 完全なレスポンスが受信され、処理されました; 新しいユーザメッセージを送信できます。

  • error: API リクエスト中にエラーが発生し、正常な補完を妨げました。

status を例えば、以下の目的で使用できます :

  • チャットボットがユーザのメッセージを処理している間、ローディングスピナーを表示する。

  • 現在のメッセージを中止するための “Stop” ボタンを表示する。

  • 送信 (submit) ボタンを無効にする。

app/page.tsx

'use client';

import { useChat } from '@ai-sdk/react';
import { DefaultChatTransport } from 'ai';
import { useState } from 'react';

export default function Page() {
  const { messages, sendMessage, status, stop } = useChat({
    transport: new DefaultChatTransport({
      api: '/api/chat',
    }),
  });
  const [input, setInput] = useState('');

  return (
    <>
      {messages.map(message => (
        <div key={message.id}>
          {message.role === 'user' ? 'User: ' : 'AI: '}
          {message.parts.map((part, index) =>
            part.type === 'text' ? {part.text} : null,
          )}
        </div>
      ))}

      {(status === 'submitted' || status === 'streaming') && (
        <div>
          {status === 'submitted' && }
          <button type="button" onClick={() => stop()}>
            Stop
          </button>
        </div>
      )}

      <form
        onSubmit={e => {
          e.preventDefault();
          if (input.trim()) {
            sendMessage({ text: input });
            setInput('');
          }
        }}
      >
        <input
          value={input}
          onChange={e => setInput(e.target.value)}
          disabled={status !== 'ready'}
          placeholder="Say something..."
        />
        <button type="submit" disabled={status !== 'ready'}>
          Submit
        </button>
      </form>
    </>
  );
}

 

メッセージの変更

幾つかの既存のメッセージを直接変更したい場合があります。例えば、各メッセージに削除ボタンを追加して、ユーザがチャット履歴からそれらを削除することを可能にします。

setMessages 関数はこれらのタスクを実現するのに役立ちます :

const { messages, setMessages } = useChat()

const handleDelete = (id) => {
  setMessages(messages.filter(message => message.id !== id))
}

return <>
  {messages.map(message => (
    <div key={message.id}>
      {message.role === 'user' ? 'User: ' : 'AI: '}
      {message.parts.map((part, index) => (
        part.type === 'text' ? (
          <span key={index}>{part.text}</span>
        ) : null
      ))}
      <button onClick={() => handleDelete(message.id)}>Delete</button>
    </div>
  ))}
  ...

You can think of messages and setMessages as a pair of state and setState in React.

 

キャンセルと再生成

AI プロバイダーから応答メッセージがストリーミングされている最中に、それを中止することも一般的なユースケースです。useChat フックから返される stop 関数を呼び出すことでこれを実現できます。

const { stop, status } = useChat()

return <>
  <button onClick={stop} disabled={!(status === 'streaming' || status === 'submitted')}>Stop</button>
  ...

ユーザが “Stop” ボタンをクリックすると、fetch (取得) リクエストが中止されます。これにより不要なリソースの消費を回避し、チャットボット・アプリケーションの UX を改良できます。

同様に、useChat フックにより返される regenerate 関数を呼び出して、AI プロバイダーに最後のメッセージの再処理をリクエストすることもできます :

const { regenerate, status } = useChat();

return (
  <>
    <button
      onClick={regenerate}
      disabled={!(status === 'ready' || status === 'error')}
    >
      Regenerate
    </button>
    ...
  </>
);

ユーザが “Regenerate” ボタンをクリックすると、AI プロバイダーは最後のメッセージを再生成して、それに応じて現在のものを置き換えます。

 

UI 更新のスロットル

ℹ️ This feature is currently only available for React.

デフォルトでは、useChat フックは、新しいチャンクを受信するたびにレンダリングをトリガーします。experimental_throttle オプションを使用して、UI の更新を調整 (throttle) できます。

page.tsx

const { messages, ... } = useChat({
  // Throttle the messages and data updates to 50ms:
  experimental_throttle: 50
})

 

イベント・コールバック

useChat は、チャットボットのライフサイクルの様々なステージを処理するために使用できる、オプションのイベントコールバックを提供しています :

  • onFinish: アシスタントのレスポンスが完了したときに呼び出されます。このイベントは、応答メッセージ、すべてのメッセージ、そして中止 (abort)、切断 (disconnect) とエラーのフラグも含みます。

  • onError: 取得リクエスト中にエラーが発生したときに呼び出されます。

  • onData: データ部 (data part) が受信されるたびに呼び出されます。

これらのコールバックは、ログ記録、分析、カスタム UI の更新のような追加のアクションをトリガーするために使用できます。

import { UIMessage } from 'ai';

const {
  /* ... */
} = useChat({
  onFinish: ({ message, messages, isAbort, isDisconnect, isError }) => {
    // use information to e.g. update other UI states
  },
  onError: error => {
    console.error('An error occurred:', error);
  },
  onData: data => {
    console.log('Received data part from server:', data);
  },
});

onData コールバックでエラーを投げることで、処理を中止できることに注目してください。これは onError コールバックのトリガーとなり、メッセージがチャット UI に追加されることを停止します。これは、AI プロバイダーからの予期せぬレスポンスを処理する際に役立ちます。

 

以上