【第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
:user
とassistant
のやり取りの全履歴を保持する配列です。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アプリケーション構築で学んだ最適化テクニック
コメント