Vercel AI SDKの決定版入門ガイドへようこそ。人工知能がデジタルランドスケープを急速に変革している世界において、AIをウェブアプリケーションに統合する能力は、ニッチな専門分野から現代の開発者にとっての核となる能力へと変化しました。このガイドは、好奇心旺盛な初心者から有能なAIアプリケーション開発者になることを目指して設計されています。
長い間、強力な大規模言語モデル(LLM)と使いやすいウェブインターフェースの間のギャップを埋めることは、複雑な作業でした。開発者は、ばらばらのプロバイダーAPIと格闘し、複雑な状態を管理し、レスポンスのストリーミングのような機能を手動で実装する必要がありました。Vercel AI SDKは、これらの問題を解決するために作成されました。これは、AIを活用したエクスペリエンスを構築する複雑さの上に、統一されたエレガントな抽象化レイヤーを提供するTypeScriptファーストのツールキットです。
これは単なるクイックスタートではありません。このチュートリアルを通して、Next.jsとGoogleのGeminiモデルを使用して、ゼロから完全で機能豊富なAIチャットボットを構築します。「Hello World」の簡単な例をはるかに超えた内容を学びます。学ぶ内容は以下の通りです。
- 「なぜ」:現代のAIアプリケーションのコアコンセプトとアーキテクチャパターンについての深い理解。
- 「どのように」:プロジェクトのセットアップ、サーバーサイドロジックの記述、洗練されたインタラクティブなフロントエンドの構築のための詳細なステップバイステッププロセス。
- 高度な機能:リアルタイム情報にアクセスするための「ツール」でチャットボットを強化する方法、および複雑な多段階インタラクションをオーケストレーションする方法。
- 本番環境対応の実践:ローディング状態の処理、エラーの適切な管理、および実際のアプリケーションのためのコード構造化の方法。
この包括的なガイドの終わりまでに、動作する高度なチャットボットだけでなく、Vercel AI SDKを使用して独自の強力なAIを活用したアプリケーションを自信を持って構築するために必要な深い概念的知識も習得できます。
開発チームが最大の生産性で協力するための統合されたオールインワンプラットフォームをお探しですか?
Apidogはあなたのすべての要望に応え、Postmanをはるかに手頃な価格で置き換えます!
第1章:基盤とセットアップ
すべての偉大な構造には強固な基盤が必要です。この章では、開発環境をセットアップし、必要なツールをインストールし、APIキーを整理します。また、各選択の背後にある「なぜ」を理解するために少し時間を取ります。
前提条件
コードを一行も書く前に、ツールボックスが準備できていることを確認しましょう。
- Node.js(バージョン18以降):Vercel AI SDKやNext.jsのようなモダンなJavaScriptフレームワークは、Node.jsの最新バージョンで利用可能な機能に依存しています。ターミナルで
node -v
を実行してバージョンを確認できます。インストールされていない場合は、公式Node.jsウェブサイトからダウンロードできます。 - Google AI APIキー:このキーは、Googleの強力なGeminiファミリーモデルを使用するための認証されたパスです。Vercel AI SDKはプロバイダーに依存しませんが、このガイドではGeminiに焦点を当てます。
- Google AI Studioにアクセスします。
- Googleアカウントでサインインします。
- 「Get API key」をクリックし、「Create API key in new project」をクリックします。
- 生成されたキーをコピーし、今は安全な場所に保管してください。このキーはパスワードのように扱ってください。絶対に公開しないでください。
ステップ1:Next.jsプロジェクトの初期化
本番環境対応のアプリケーションを構築するための最高のReactフレームワークであるNext.jsを使用します。そのApp Routerパラダイムは、AIアプリケーションのサーバー中心的な性質と完全に統合されます。
ターミナルを開き、このコマンドを実行して新しいプロジェクトを作成します。
npx create-next-app@latest vercel-ai-tutorial
インストーラーはいくつかの質問をします。シームレスに進めるために、これらの設定を使用してください。
- Would you like to use TypeScript? Yes (TypeScriptは型安全なAIインタラクションに不可欠です)
- Would you like to use ESLint? Yes (コード品質のために)
- Would you like to use Tailwind CSS? Yes (UIのスタイリングを迅速に行うために)
- Would you like to use
src/
directory? Yes (コードを整理するための一般的な慣習です) - Would you like to use App Router? Yes (これはこのガイドに不可欠です)
- Would you like to customize the default import alias? No (デフォルトで問題ありません)
インストールが完了したら、新しく作成したプロジェクトディレクトリに移動します。
cd vercel-ai-tutorial
ステップ2:Vercel AI SDKのインストール
次に、AI SDKパッケージをプロジェクトに追加しましょう。
npm install ai @ai-sdk/react @ai-sdk/google zod
これらの各パッケージが何をするのかを分解してみましょう。
ai
:これがSDKの心臓部です。streamText
やgenerateObject
のような、LLMプロバイダーとの直接通信を処理するコアでフレームワークに依存しない関数が含まれています。@ai-sdk/react
:このパッケージは、インタラクティブなUIの構築を容易にするReactフック(特にuseChat
)を提供します。状態管理、ストリーミング、API通信の複雑さを抽象化します。@ai-sdk/google
:これはプロバイダーパッケージです。コアのai
パッケージがGoogleのAIモデルと通信できるようにする特定のアダプターです。OpenAIを使用したい場合は、代わりに@ai-sdk/openai
をインストールします。zod
:強力なスキーマ宣言および検証ライブラリです。AI SDKの厳密な一部ではありませんが、ツール呼び出しのような高度な機能のためにデータの構造を定義し、AIの出力が予測可能で型安全であることを保証するための不可欠なパートナーです。
ステップ3:APIキーの保護
アプリケーションコードにAPIキーをハードコードしないでください。これは主要なセキュリティリスクです。プロフェッショナルな標準は、環境変数を使用することです。Next.jsには、.env.local
ファイルによるこのための組み込みサポートがあります。
プロジェクトのルートにファイルを作成します。
touch .env.local
次に、この新しいファイルを開き、Google AIキーを追加します。
# .env.local
# This file is for local development and should NOT be committed to git.
GOOGLE_GENERATIVE_AI_API_KEY=YOUR_GOOGLE_AI_API_KEY
YOUR_GOOGLE_AI_API_KEY
を以前コピーしたキーに置き換えてください。Next.jsはこのファイルを自動的にロードし、キーをサーバーで利用可能にします。これはまさに私たちが必要としている場所です。
第2章:チャットボットのバックボーンの構築
プロジェクトのセットアップが完了したので、アプリケーションのコアコンポーネント、つまりAIと通信するサーバーサイドAPIエンドポイントと、ユーザーがインタラクションするクライアントサイドUIを構築する時が来ました。
AIアプリのクライアント・サーバーアーキテクチャ
私たちのチャットボットには、主に2つの部分があります。
- サーバーサイドAPIルート(
/api/chat/route.ts
):これはサーバー上で実行される安全な環境です。その主な仕事は、ユーザーのブラウザからチャット履歴を受け取り、秘密のAPIキーを追加し、リクエストをGoogle AIサービスに転送し、レスポンスをユーザーにストリーミングすることです。このロジックをサーバーに保持することはセキュリティにとって重要です。これにより、APIキーが公開されることがなくなります。 - クライアントサイドUI(
page.tsx
):これはユーザーのブラウザで実行されるReactコンポーネントです。チャット履歴のレンダリング、ユーザー入力のキャプチャ、およびその入力をAPIルートに送信する役割を担います。
この分離は、安全でパフォーマンスの高いウェブアプリケーションを構築するための基本です。
ステップ4:APIルートハンドラーの作成
サーバーサイドエンドポイントを作成しましょう。src/app
ディレクトリに、新しいフォルダapi
を作成し、その中にさらにchat
フォルダを作成します。最後に、chat
フォルダの中にroute.ts
という名前のファイルを作成します。
最終的なパスはsrc/app/api/chat/route.ts
になるはずです。
このファイルに以下のコードを記述します。
// src/app/api/chat/route.ts
import { google } from '@ai-sdk/google';
import { streamText } from 'ai';
// Vercel固有の設定:ストリーミングレスポンスを最大30秒許可する
export const maxDuration = 30;
// メインのAPIルートハンドラー
export async function POST(req: Request) {
try {
// リクエストボディから`messages`配列を抽出する
const { messages } = await req.json();
// 会話履歴を使用してAIプロバイダーを呼び出す
const result = await streamText({
model: google('models/gemini-1.5-pro-latest'),
// `messages`配列は会話のコンテキストをモデルに提供する
messages,
});
// ストリーミングレスポンスで応答する
return result.toDataStreamResponse();
} catch (error) {
// 潜在的なエラーを処理することは良い習慣です
if (error instanceof Error) {
return new Response(JSON.stringify({ error: error.message }), { status: 500 });
}
return new Response(JSON.stringify({ error: 'An unknown error occurred' }), { status: 500 });
}
}
この重要なファイルを分解してみましょう。
export const maxDuration = 30;
:これはVercel固有の設定です。サーバーレス関数にはデフォルトのタイムアウトがあります。AIの応答が生成を開始するまでに時間がかかることがあるため、リクエストが途中で終了するのを防ぐためにタイムアウトを30秒に延長しています。export async function POST(req: Request)
:Next.js App Routerでは、route.ts
ファイルでHTTPメソッド(POST
など)と同じ名前のasync関数をエクスポートすると、APIエンドポイントが作成されます。const { messages } = await req.json();
:フロントエンドはリクエストでJSONオブジェクトを送信し、そこからmessages
配列を分割代入しています。この配列は会話の完全な履歴であり、LLMが文脈を理解した応答を提供するために不可欠です。const result = await streamText(...)
:これがVercel AI SDKへのコアコールです。使用したいmodel
とmessages
履歴を提供します。SDKはバックグラウンドでGoogle APIへの認証済みリクエストを処理します。return result.toDataStreamResponse();
:これは強力なヘルパー関数です。streamText
から返されるReadableStream
を取得し、適切なヘッダーとフォーマットを持つResponse
オブジェクトでラップします。これにより、クライアントサイドのフックがストリームを非常に簡単に消費できるようになります。try...catch
:API呼び出し中の潜在的なエラーを適切に処理するために、ロジックをtry...catch
ブロックでラップし、クライアントに明確なエラーメッセージを返します。
ステップ5:ユーザーインターフェースの作成
さて、楽しい部分です:UIの構築です。@ai-sdk/react
パッケージのおかげで、これは驚くほどシンプルです。メインページファイルであるsrc/app/page.tsx
を開き、その内容全体を以下のコードに置き換えてください。
// src/app/page.tsx
'use client';
import { useChat } from '@ai-sdk/react';
import { useRef, useEffect } from 'react';
export default function Chat() {
const { messages, input, handleInputChange, handleSubmit, isLoading, error } = useChat();
// メッセージのスクロール可能なコンテナへのref
const messagesContainerRef = useRef<HTMLDivElement>(null);
// メッセージが変更されるたびにメッセージコンテナの最下部にスクロールするeffect
useEffect(() => {
if (messagesContainerRef.current) {
messagesContainerRef.current.scrollTop = messagesContainerRef.current.scrollHeight;
}
}, [messages]);
return (
<div className="flex flex-col h-screen bg-gray-50">
{/* メッセージコンテナ */}
<div ref={messagesContainerRef} className="flex-1 overflow-y-auto p-8 space-y-4">
{messages.map(m => (
<div
key={m.id}
className={`flex gap-3 ${m.role === 'user' ? 'justify-end' : 'justify-start'}`}
>
{/* ユーザーのアバターを表示 */}
{m.role === 'user' && (
<div className="w-10 h-10 rounded-full bg-blue-500 flex items-center justify-center text-white font-bold">U</div>
)}
{/* メッセージバブル */}
<div
className={`max-w-xl p-3 rounded-2xl shadow-md whitespace-pre-wrap ${
m.role === 'user'
? 'bg-blue-500 text-white rounded-br-none'
: 'bg-white text-black rounded-bl-none'
}`}
>
<span className="font-bold block">{m.role === 'user' ? 'You' : 'AI Assistant'}</span>
{m.content}
</div>
{/* AIのアバターを表示 */}
{m.role !== 'user' && (
<div className="w-10 h-10 rounded-full bg-gray-700 flex items-center justify-center text-white font-bold">AI</div>
)}
</div>
))}
</div>
{/* 入力フォーム */}
<div className="p-4 bg-white border-t">
<form onSubmit={handleSubmit} className="flex items-center gap-4 max-w-4xl mx-auto">
<input
className="flex-1 p-3 border rounded-full focus:outline-none focus:ring-2 focus:ring-blue-500"
value={input}
placeholder="Ask me anything..." // 何でも聞いてください...
onChange={handleInputChange}
disabled={isLoading}
/>
<button
type="submit"
className="px-6 py-3 bg-blue-500 text-white rounded-full font-semibold hover:bg-blue-600 disabled:bg-blue-300 disabled:cursor-not-allowed"
disabled={isLoading}
>
Send // 送信
</button>
</form>
{error && (
<p className="text-red-500 mt-2 text-center">{error.message}</p>
)}
</div>
</div>
);
}
これはかなりの量のコードですが、そのほとんどはTailwind CSSで洗練されたUIを作成するためのものです。ロジックに焦点を当てましょう。
'use client';
:これは不可欠です。このコンポーネントをクライアントコンポーネントとしてマークし、ブラウザで実行され、状態とエフェクトを使用できるようにします。const { ... } = useChat();
:この一行がAI SDK UIライブラリの魔法です。必要なすべての状態と機能を提供します。messages
:チャットメッセージの配列。自動的に同期されます。input
、handleInputChange
、handleSubmit
:入力フォームの状態とハンドラーです。handleSubmit
はメッセージを自動的にパッケージ化し、/api/chat
エンドポイントを呼び出します。isLoading
:AIが応答を生成している間はtrue
になるブール値です。待機中にフォームを無効にするためにこれを使用します。error
:API呼び出しが失敗した場合にデータが格納されるエラーオブジェクトです。これをユーザーに表示します。useRef
とuseEffect
:これは、新しいメッセージが追加されるたびにチャットビューが自動的に最下部にスクロールし、最新のメッセージが常に表示されるようにするための標準的なReactパターンです。
ステップ6:アプリケーションの実行
これで、完全で構造化されたAIチャットボットを構築しました。起動してみましょう!
npm run dev
ブラウザでhttp://localhost:3000
にアクセスしてください。洗練されたチャットインターフェースが表示されるはずです。何か質問してみてください。メッセージが即座に表示され、AIの応答がトークンごとにストリームされてくるのがわかります。
第3章:高度な機能 - チャットボットにスーパーパワーを与える
私たちのチャットボットは賢いですが、その知識は学習データに限定されています。ライブ情報にアクセスしたり、現実世界でアクションを実行したりすることはできません。この章では、これらの制限を克服するために「ツール」を与えます。
ツールとは何ですか?
ツールとは、LLMが実行を選択できる関数です。ツールをモデルに説明し、ユーザーのクエリに答えるためにツールが必要だとモデルが判断した場合、テキスト生成を一時停止し、代わりに特別な「ツール呼び出し」オブジェクトを出力します。その後、コードはモデルによって提供された引数で関数を実行し、結果がモデルに送り返されます。モデルはこの新しい情報を使用して、最終的により正確な応答を生成します。
チャットボットに2つのツールを付与しましょう。
- 場所の現在の天気を取得するツール。
- 華氏を摂氏に変換するツール。
これにより、ボットは「ロンドンの天気は摂氏でどうですか?」のような質問に答えることができるようになります。これは複数のステップと外部データを必要とするタスクです。
ステップ7:ツールをサポートするようにAPIをアップグレードする
サーバーのstreamText
呼び出しでツールを定義する必要があります。src/app/api/chat/route.ts
を開き、新しいtools
定義を含むように変更します。
// src/app/api/chat/route.ts
import { google } from '@ai-sdk/google';
import { streamText, tool } from 'ai';
import { z } from 'zod';
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages } = await req.json();
const result = await streamText({
model: google('models/gemini-1.5-pro-latest'),
messages,
// モデルが使用できるツールを定義する
tools: {
getWeather: tool({
description: '指定された場所の現在の天気を取得します。常に華氏で温度を返します。', // Get the current weather for a specific location. Always returns temperature in Fahrenheit.
parameters: z.object({
location: z.string().describe('都市と州、例:San Francisco, CA'), // The city and state, e.g., San Francisco, CA
}),
execute: async ({ location }) => {
// 実際のアプリでは、実際の天気APIからフェッチします
console.log(`${location}の天気を取得しています`); // Fetching weather for ${location}
return {
temperature: Math.floor(Math.random() * (100 - 30 + 1) + 30),
high: Math.floor(Math.random() * (100 - 80 + 1) + 80),
low: Math.floor(Math.random() * (50 - 30 + 1) + 30),
conditions: ['晴れ', '曇り', '雨'][Math.floor(Math.random() * 3)], // ['Sunny', 'Cloudy', 'Rainy']
};
},
}),
convertFahrenheitToCelsius: tool({
description: '華氏から摂氏に温度を変換します。', // Convert a temperature from Fahrenheit to Celsius.
parameters: z.object({
temperature: z.number().describe('華氏での温度'), // The temperature in Fahrenheit
}),
execute: async ({ temperature }) => {
console.log(`${temperature}°Fを摂氏に変換しています`); // Converting ${temperature}°F to Celsius
return {
celsius: Math.round((temperature - 32) * (5 / 9)),
};
},
}),
},
});
return result.toDataStreamResponse();
}
tools
オブジェクトを分析してみましょう。
- 各キー(
getWeather
、convertFahrenheitToCelsius
)はツールの名前です。 description
:これはモデルにとって最も重要な部分です。この説明を読んで、ツールが何をするのか、いつ使用すべきかを理解します。明確かつ具体的に記述してください。parameters
:zod
を使用して関数のシグネチャを定義します。これにより、モデルに提供する必要のある引数が正確に伝わります。z.string().describe(...)
は、期待されるフォーマットについてモデルにヒントを与えます。execute
:これは、ツールが呼び出されたときに実行される実際のサーバーサイド関数です。ここでは、ランダムなデータでAPI呼び出しをシミュレートしていますが、これを実際の天気サービスへのfetch
呼び出しに簡単に置き換えることができます。
ステップ8:UIでの多段階ツール呼び出しを有効にする
サーバーでツールを定義するだけでは十分ではありません。デフォルトでは、モデルがツールを呼び出すと、会話は停止します。そのツール呼び出しの結果を自動的にモデルに送り返して、推論を続け、最終的な回答を策定できるように、useChat
フックに指示する必要があります。
これは信じられないほどシンプルです。src/app/page.tsx
で、useChat
フックの初期化を更新します。
// src/app/page.tsx
// ...
export default function Chat() {
const { messages, input, handleInputChange, handleSubmit, isLoading, error } = useChat({
// ツールの結果を自動的にモデルに送り返すようにフックに指示する
experimental_sendExtraToolMessages: true,
});
// ... rest of the component
}
これで完了です。experimental_sendExtraToolMessages: true
プロパティが多段階のツール使用フローをアクティブにします。
ステップ9:ツール呼び出しのためのより良いUI
現在のUIはm.content
のみを表示します。ツールが呼び出された場合、興味深い情報はメッセージオブジェクトの別のプロパティにあります。ツール呼び出しをきれいにレンダリングするための専用コンポーネントを作成しましょう。
まず、src/app/page.tsx
のメインメッセージループを更新して、これらの呼び出しをレンダリングするようにします。
// src/app/page.tsx
// ... Chatコンポーネントのreturnステートメント内
<div ref={messagesContainerRef} className="flex-1 overflow-y-auto p-8 space-y-4">
{messages.map(m => (
<div
key={m.id}
className={`flex gap-3 ${m.role === 'user' ? 'justify-end' : 'justify-start'}`}
>
{/* ... アバター ... */}
<div
className={`max-w-xl p-3 rounded-2xl shadow-md whitespace-pre-wrap ${
m.role === 'user'
? 'bg-blue-500 text-white rounded-br-none'
: 'bg-white text-black rounded-bl-none'
}`}
>
<span className="font-bold block">{m.role === 'user' ? 'You' : 'AI Assistant'}</span>
{/* ツール呼び出しをレンダリング */}
{m.toolInvocations?.map(tool => (
<div key={tool.toolCallId} className="my-2 p-2 bg-gray-100 rounded text-sm text-gray-700">
<p className="font-semibold">Tool Call: `{tool.toolName}`</p> {/* ツール呼び出し: `{tool.toolName}` */}
<pre className="mt-1 p-1 bg-gray-200 rounded text-xs">
{JSON.stringify(tool.args, null, 2)}
</pre>
</div>
))}
{m.content}
</div>
{/* ... アバター ... */}
</div>
))}
{isLoading && messages[messages.length - 1]?.role === 'assistant' && (
<div className="flex justify-start p-8 space-x-3">
<div className="w-10 h-10 rounded-full bg-gray-700 flex items-center justify-center text-white font-bold">AI</div>
<div className="p-3 rounded-2xl shadow-md bg-white">
<div className="typing-indicator">
<span></span><span></span><span></span>
</div>
</div>
</div>
)}
</div>
// ...
また、アシスタントが考えている間に表示されるシンプルなタイピングインジケーターも追加しました。これには少しCSSを追加する必要があります。src/app/globals.css
ファイルに以下を追加してください。
/* src/app/globals.css */
.typing-indicator span {
height: 8px;
width: 8px;
background-color: #9E9EA1;
border-radius: 50%;
display: inline-block;
animation: a 1.2s infinite ease-in-out;
}
.typing-indicator span:nth-child(1) { animation-delay: -0.4s; }
.typing-indicator span:nth-child(2) { animation-delay: -0.2s; }
@keyframes a {
0%, 60%, 100% { transform: scale(0.2); }
30% { transform: scale(1); }
}
さあ、もう一度アプリケーションを実行してください。「ニューヨークの天気は摂氏でどうですか?」と質問してください。UIで興味深い一連のイベントが展開されるのがわかります。
- モデルはまず
getWeather
ツールを呼び出します。UIにレンダリングされたツール呼び出しが表示されます。 - 結果(華氏でのランダムな温度)がモデルに送り返されます。
- モデルは摂氏が必要であることを知っており、最初のツールの結果の温度を入力として使用して、
convertFahrenheitToCelsius
ツールを呼び出します。 - 最後に、摂氏の温度を手に入れて、元の質問に答える自然言語の応答を生成します。
これがAIエージェントを構築する力であり、Vercel AI SDKはこの複雑なオーケストレーションを驚くほど簡単にします。
第4章:これからどこへ行くか?
高度なAIパワードチャットボットを構築することに成功しました。白紙の状態から、レスポンスをストリーミングし、ローディング状態とエラー状態を処理し、ツールを活用して外部データと多段階でインタラクションできる機能豊富なアプリケーションになりました。
このガイドは強力な基盤を提供しましたが、これは始まりにすぎません。Vercel AI SDKにはさらに多くの機能があります。今後の探求のためのいくつかの道筋を以下に示します。
- Generative UI:テキストとデータのみをストリーミングしました。React Server Componentsを使用すると、AI SDKはAIに完全に形成されたインタラクティブなReactコンポーネントを生成およびストリーミングさせることができます。天気を尋ねて、テキストだけでなく、美しくインタラクティブな天気ウィジェットが返ってくるのを想像してみてください。これは巨大な可能性を秘めた最先端の機能です。
- Retrieval-Augmented Generation (RAG):独自のプライベートドキュメントに基づいて推論できるチャットボットを構築します。PDF、一連のMarkdownファイル、または会社の内部ナレッジベースに関する質問に答えるチャットボットを作成できます。
- 他のプロバイダーを探求する:私たちが構築したアーキテクチャは非常にモジュール化されています。GoogleのモデルをOpenAIやAnthropicのモデルと交換してみてください。APIルートのコードを一行変更するだけで済むことが多く、特定のユースケースに最適なモデルを実験して見つけることができます。
- Vercel AI Playground:AI Playgroundは、プロンプトをテストし、異なるモデルの出力、パフォーマンス、コストを並べて比較するための非常に貴重なツールです。
ウェブ開発の未来は、インテリジェントでインタラクティブ、そしてパーソナライズされています。Vercel AI SDKを使用することで、あなたはこの革命の最前線に立つためのツールと知識を今や持っています。ハッピービルディング!
開発チームが最大の生産性で協力するための統合されたオールインワンプラットフォームをお探しですか?
Apidogはあなたのすべての要望に応え、Postmanをはるかに手頃な価格で置き換えます!