Vercel AI SDK の使い方:初心者向けガイド

Rebecca Kovács

Rebecca Kovács

10 6月 2025

Vercel AI SDK の使い方:初心者向けガイド

Vercel AI SDKの決定版入門ガイドへようこそ。人工知能がデジタルランドスケープを急速に変革している世界において、AIをウェブアプリケーションに統合する能力は、ニッチな専門分野から現代の開発者にとっての核となる能力へと変化しました。このガイドは、好奇心旺盛な初心者から有能なAIアプリケーション開発者になることを目指して設計されています。

長い間、強力な大規模言語モデル(LLM)と使いやすいウェブインターフェースの間のギャップを埋めることは、複雑な作業でした。開発者は、ばらばらのプロバイダーAPIと格闘し、複雑な状態を管理し、レスポンスのストリーミングのような機能を手動で実装する必要がありました。Vercel AI SDKは、これらの問題を解決するために作成されました。これは、AIを活用したエクスペリエンスを構築する複雑さの上に、統一されたエレガントな抽象化レイヤーを提供するTypeScriptファーストのツールキットです。

これは単なるクイックスタートではありません。このチュートリアルを通して、Next.jsとGoogleのGeminiモデルを使用して、ゼロから完全で機能豊富なAIチャットボットを構築します。「Hello World」の簡単な例をはるかに超えた内容を学びます。学ぶ内容は以下の通りです。

この包括的なガイドの終わりまでに、動作する高度なチャットボットだけでなく、Vercel AI SDKを使用して独自の強力なAIを活用したアプリケーションを自信を持って構築するために必要な深い概念的知識も習得できます。

💡
素晴らしいAPIテストツールをお探しですか?美しいAPIドキュメントを生成できます。

開発チームが最大の生産性で協力するための統合されたオールインワンプラットフォームをお探しですか?

Apidogはあなたのすべての要望に応え、Postmanをはるかに手頃な価格で置き換えます
button

第1章:基盤とセットアップ

すべての偉大な構造には強固な基盤が必要です。この章では、開発環境をセットアップし、必要なツールをインストールし、APIキーを整理します。また、各選択の背後にある「なぜ」を理解するために少し時間を取ります。

前提条件

コードを一行も書く前に、ツールボックスが準備できていることを確認しましょう。

  1. Google AI Studioにアクセスします。
  2. Googleアカウントでサインインします。
  3. Get API key」をクリックし、「Create API key in new project」をクリックします。
  4. 生成されたキーをコピーし、今は安全な場所に保管してください。このキーはパスワードのように扱ってください。絶対に公開しないでください。

ステップ1:Next.jsプロジェクトの初期化

本番環境対応のアプリケーションを構築するための最高のReactフレームワークであるNext.jsを使用します。そのApp Routerパラダイムは、AIアプリケーションのサーバー中心的な性質と完全に統合されます。

ターミナルを開き、このコマンドを実行して新しいプロジェクトを作成します。

npx create-next-app@latest vercel-ai-tutorial

インストーラーはいくつかの質問をします。シームレスに進めるために、これらの設定を使用してください。

インストールが完了したら、新しく作成したプロジェクトディレクトリに移動します。

cd vercel-ai-tutorial

ステップ2:Vercel AI SDKのインストール

次に、AI SDKパッケージをプロジェクトに追加しましょう。

npm install ai @ai-sdk/react @ai-sdk/google zod

これらの各パッケージが何をするのかを分解してみましょう。

ステップ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つの部分があります。

  1. サーバーサイドAPIルート(/api/chat/route.ts):これはサーバー上で実行される安全な環境です。その主な仕事は、ユーザーのブラウザからチャット履歴を受け取り、秘密のAPIキーを追加し、リクエストをGoogle AIサービスに転送し、レスポンスをユーザーにストリーミングすることです。このロジックをサーバーに保持することはセキュリティにとって重要です。これにより、APIキーが公開されることがなくなります。
  2. クライアントサイド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 });
  }
}

この重要なファイルを分解してみましょう。

ステップ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を作成するためのものです。ロジックに焦点を当てましょう。

ステップ6:アプリケーションの実行

これで、完全で構造化されたAIチャットボットを構築しました。起動してみましょう!

npm run dev

ブラウザでhttp://localhost:3000にアクセスしてください。洗練されたチャットインターフェースが表示されるはずです。何か質問してみてください。メッセージが即座に表示され、AIの応答がトークンごとにストリームされてくるのがわかります。

第3章:高度な機能 - チャットボットにスーパーパワーを与える

私たちのチャットボットは賢いですが、その知識は学習データに限定されています。ライブ情報にアクセスしたり、現実世界でアクションを実行したりすることはできません。この章では、これらの制限を克服するために「ツール」を与えます。

ツールとは何ですか?

ツールとは、LLMが実行を選択できる関数です。ツールをモデルに説明し、ユーザーのクエリに答えるためにツールが必要だとモデルが判断した場合、テキスト生成を一時停止し、代わりに特別な「ツール呼び出し」オブジェクトを出力します。その後、コードはモデルによって提供された引数で関数を実行し、結果がモデルに送り返されます。モデルはこの新しい情報を使用して、最終的により正確な応答を生成します。

チャットボットに2つのツールを付与しましょう。

  1. 場所の現在の天気を取得するツール。
  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オブジェクトを分析してみましょう。

ステップ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で興味深い一連のイベントが展開されるのがわかります。

  1. モデルはまずgetWeatherツールを呼び出します。UIにレンダリングされたツール呼び出しが表示されます。
  2. 結果(華氏でのランダムな温度)がモデルに送り返されます。
  3. モデルは摂氏が必要であることを知っており、最初のツールの結果の温度を入力として使用して、convertFahrenheitToCelsiusツールを呼び出します。
  4. 最後に、摂氏の温度を手に入れて、元の質問に答える自然言語の応答を生成します。

これがAIエージェントを構築する力であり、Vercel AI SDKはこの複雑なオーケストレーションを驚くほど簡単にします。

第4章:これからどこへ行くか?

高度なAIパワードチャットボットを構築することに成功しました。白紙の状態から、レスポンスをストリーミングし、ローディング状態とエラー状態を処理し、ツールを活用して外部データと多段階でインタラクションできる機能豊富なアプリケーションになりました。

このガイドは強力な基盤を提供しましたが、これは始まりにすぎません。Vercel AI SDKにはさらに多くの機能があります。今後の探求のためのいくつかの道筋を以下に示します。

ウェブ開発の未来は、インテリジェントでインタラクティブ、そしてパーソナライズされています。Vercel AI SDKを使用することで、あなたはこの革命の最前線に立つためのツールと知識を今や持っています。ハッピービルディング!

💡
素晴らしいAPIテストツールをお探しですか?美しいAPIドキュメントを生成できます。

開発チームが最大の生産性で協力するための統合されたオールインワンプラットフォームをお探しですか?

Apidogはあなたのすべての要望に応え、Postmanをはるかに手頃な価格で置き換えます
button

ApidogでAPIデザイン中心のアプローチを取る

APIの開発と利用をよりシンプルなことにする方法を発見できる