PR

【第3回】フロントエンド編:Next.jsとVercel AI SDKで作るモダンなチャットUI

【第3回】フロントエンド編:Next.jsとVercel AI SDKで作るモダンなチャットUI

はじめに:神体験UIは「SDK」で手に入れる

第2回では、PDFの内容を学習し、質問に答えるバックエンドAPIを構築しました。しかし、ユーザーが直接触れるのはフロントエンドです。特にLLMアプリでは、回答が生成されるまでの待ち時間や、その表示方法がユーザー体験を大きく左右します。

「ChatGPTのように、回答がリアルタイムで流れてくるように表示したい…でも実装が大変そう…」

その悩みを、Vercel AI SDKが解決します。このSDKを使えば、わずか数行のコードで、複雑なストリーミング処理を含むモダンなチャットUIを構築できてしまうのです。

今回は、Next.jsとVercel AI SDKを使い、私たちが作ったバックエンドAPIと連携する、快適なフロントエンドを構築します。

1. プロジェクトのセットアップ

まず、Next.jsのプロジェクトをセットアップし、必要なライブラリをインストールします。

# 1. Next.jsプロジェクトを作成
npx create-next-app@latest llm-chatbot-ui --typescript --tailwind --eslint
# 2. プロジェクトディレクトリに移動
cd llm-chatbot-ui
# 3. Vercel AI SDKをインストール
npm install ai

2. APIルートの作成

フロントエンドから直接バックエンドAPI(第2回で作成したFastAPIサーバー)を叩くのではなく、Next.jsのAPIルートをプロキシとして経由させます。これにより、CORSの問題を回避し、APIキーなどの機密情報をフロントエンドから隠蔽できます。

app/api/chat/route.ts というファイルを作成します。

// app/api/chat/route.ts
import { StreamingTextResponse } from 'ai';
// バックエンドAPIのURL
const FASTAPI_URL = 'http://127.0.0.1:8000/ask/';
export async function POST(req: Request) {
  const { messages } = await req.json();
  // 最後のメッセージを質問として取得
  const lastMessage = messages[messages.length - 1];
  const question = lastMessage.content;
  // FastAPIに送信するためのフォームデータを作成
  const formData = new FormData();
  formData.append('question', question);
  const response = await fetch(FASTAPI_URL, {
    method: 'POST',
    body: formData,
  });
  // FastAPIからのレスポンスをストリーミングで返す
  // この実装はバックエンドがストリーミングをサポートしている必要があります。
  // 今回は簡略化のため、ストリーミングではないレスポンスを想定します。
  const data = await response.json();
  const answer = data.answer || 'Sorry, I could not get an answer.';
  // StreamingTextResponseを使ってテキストをストリームとして返す
  const stream = new ReadableStream({
    start(controller) {
      controller.enqueue(new TextEncoder().encode(answer));
      controller.close();
    },
  });
  return new StreamingTextResponse(stream);
}

注: 上記は簡略版です。実際のRAGバックエンドがストリーミングを返す場合は、fetchのレスポンスボディを直接StreamingTextResponseに渡すことができます。

3. チャットUIの構築

いよいよUIの構築です。Vercel AI SDKが提供するuseChatフックの魔法をご覧ください。

app/page.tsx を以下のように編集します。

// app/page.tsx
'use client';
import { useChat } from 'ai/react';
export default function Chat() {
  const { messages, input, handleInputChange, handleSubmit } = useChat({
    // ここでNext.jsのAPIルートを指定
    api: '/api/chat',
  });
  return (
    <div className="flex flex-col w-full max-w-md py-24 mx-auto stretch">
      {messages.map(m => (
        <div key={m.id} className="whitespace-pre-wrap">
          <strong>{m.role === 'user' ? 'User: ' : 'AI: '}</strong>
          {m.content}
        </div>
      ))}
      <form onSubmit={handleSubmit}>
        <input
          className="fixed bottom-0 w-full max-w-md p-2 mb-8 border border-gray-300 rounded shadow-xl"
          value={input}
          placeholder="Say something..."
          onChange={handleInputChange}
        />
      </form>
    </div>
  );
}

4. 解説:useChatフックの魔法

たったこれだけのコードで、なぜチャットUIが機能するのでしょうか?

  • useChat(): Vercel AI SDKが提供するカスタムフックです。
  • messages: userassistantのやり取りの全履歴を保持する配列です。UIはこれをmapで表示するだけです。
  • input, handleInputChange: フォームの入力値とそのハンドラです。input要素にそのまま渡せます。
  • handleSubmit: フォームが送信されたときの処理です。この関数が内部で/api/chatへのリクエストを行い、messages配列を更新し、ストリーミングレスポンスを処理してくれます。

開発者は、UIの状態管理やAPI通信の複雑なロジックを一切書く必要がありません。ただuseChatフックを呼び出し、返された値と関数を適切な場所に配置するだけです。

まとめ:フロントエンド開発の革命

今回は、Next.jsとVercel AI SDKを使い、驚くほどシンプルなコードで高機能なチャットUIを構築しました。これにより、私たちのLLMアプリケーションは、ユーザーが直接触れる「顔」を持つことができました。

  • Next.jsのAPIルートをプロキシとして利用した
  • useChatフックで、UIの状態管理とAPI通信を抽象化した
  • 複雑な実装なしに、ストリーミング表示の基盤を構築した

バックエンドとフロントエンドが揃った今、次はいよいよアプリケーションに「知性」を与えるステップです。

次回予告:【第4回】エージェント編:Function Callingで外部APIと連携する自律型AI

単なるQAボットから、外部ツールを使いこなす「エージェント」へと進化させます。お楽しみに!


関連記事
【第2回】バックエンド編:PythonとLangChainで構築するRAG API実践入門
Next.js 14実践開発ガイド:企業レベルのWebアプリケーション構築で学んだ最適化テクニック

コメント

タイトルとURLをコピーしました