Vercel AI SDK 入門 - TypeScript で生成 AI アプリを最速で作るための実践ガイド

2026.04.10

公式ドキュメント

この記事の要点

• Vercel AI SDKは主要AIプロバイダを統一インターフェイスで扱えるTypeScriptライブラリ
• ストリーミング・Tool Calling・構造化出力・RSC連携を一括提供
• Next.jsとの深い統合でフルスタックAIアプリを最速で構築可能

Vercel AI SDK は、Vercel が公開する TypeScript 向けの生成 AI 開発ライブラリです。OpenAI / Anthropic / Google / Mistral / xAI など主要プロバイダを統一インターフェイスで扱え、ストリーミング・Tool Calling・構造化出力・React Server Components 連携など、現代的な AI アプリ構築に必要な要素をひとまとめに提供してくれます。本記事では Vercel AI SDK の主要機能を、Next.js のサンプルとともに体系的に紹介します。

概要

flowchart TB
    subgraph SDK["Vercel AI SDK"]
        Core["@ai-sdk/* core"] --> Providers
        Core --> APIs
        subgraph Providers["プロバイダ"]
            P1["@ai-sdk/openai"]
            P2["@ai-sdk/anthropic"]
            P3["@ai-sdk/google"]
            P4["@ai-sdk/mistral"]
        end
        subgraph APIs["主要 API"]
            A1["generateText"]
            A2["streamText"]
            A3["generateObject"]
            A4["streamObject"]
            A5["tool"]
        end
        UI["ai/react / ai/rsc"] --> Core
    end

Vercel AI SDK は「コア」「プロバイダ」「UI バインディング」の 3 層構成になっており、フロントエンドからサーバーアクション、API ルートまで一貫した型で扱えるのが特長です。

主要機能

1. プロバイダ非依存の generateText / streamText

import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";

const { text } = await generateText({
  model: openai("gpt-4o-mini"),
  prompt: "TypeScript の型安全性のメリットを 3 つ挙げて",
});

console.log(text);

プロバイダを差し替えるだけで別モデルに切り替えられます。

import { anthropic } from "@ai-sdk/anthropic";

const { text } = await generateText({
  model: anthropic("claude-3-5-sonnet-latest"),
  prompt: "TypeScript の型安全性のメリットを 3 つ挙げて",
});

2. ストリーミング応答

import { streamText } from "ai";
import { openai } from "@ai-sdk/openai";

const result = await streamText({
  model: openai("gpt-4o-mini"),
  prompt: "Promise と async/await の違いを説明して",
});

for await (const chunk of result.textStream) {
  process.stdout.write(chunk);
}

3. 構造化出力 (generateObject)

import { generateObject } from "ai";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";

const { object } = await generateObject({
  model: openai("gpt-4o-mini"),
  schema: z.object({
    title: z.string(),
    tags: z.array(z.string()).max(5),
    summary: z.string().max(140),
  }),
  prompt: "TypeScript 入門記事のメタ情報を生成して",
});

console.log(object.title, object.tags);

zod スキーマを渡すだけで、型推論付きでオブジェクトを取得できます。

4. Tool Calling

import { generateText, tool } from "ai";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";

const result = await generateText({
  model: openai("gpt-4o-mini"),
  tools: {
    getWeather: tool({
      description: "指定都市の天気を取得",
      parameters: z.object({ city: z.string() }),
      execute: async ({ city }) => {
        // 実際は外部 API を呼ぶ
        return { city, tempC: 21, condition: "sunny" };
      },
    }),
  },
  prompt: "東京の天気を教えて",
});

console.log(result.toolResults);

5. React UI ヘルパー

"use client";
import { useChat } from "ai/react";

export default function Chat() {
  const { messages, input, handleInputChange, handleSubmit } = useChat({
    api: "/api/chat",
  });

  return (
    <div>
      <ul>
        {messages.map((m) => (
          <li key={m.id}>
            <strong>{m.role}:</strong> {m.content}
          </li>
        ))}
      </ul>
      <form onSubmit={handleSubmit}>
        <input value={input} onChange={handleInputChange} placeholder="メッセージ" />
        <button type="submit">送信</button>
      </form>
    </div>
  );
}
// app/api/chat/route.ts
import { streamText } from "ai";
import { openai } from "@ai-sdk/openai";

export async function POST(req: Request) {
  const { messages } = await req.json();
  const result = await streamText({
    model: openai("gpt-4o-mini"),
    messages,
  });
  return result.toDataStreamResponse();
}

ポイント: generateText/streamTextの切り替えだけでバッチ処理とリアルタイムストリーミングを使い分けられます。

実践サンプル: RAG API

import { streamText, embed } from "ai";
import { openai } from "@ai-sdk/openai";

async function search(query: string) {
  const { embedding } = await embed({
    model: openai.embedding("text-embedding-3-small"),
    value: query,
  });
  // pgvector / Qdrant 等を呼ぶ想定
  return await vectorStore.search(embedding, { limit: 5 });
}

export async function POST(req: Request) {
  const { question } = await req.json();
  const docs = await search(question);
  const context = docs.map((d) => `- ${d.text}`).join("\n");

  const result = await streamText({
    model: openai("gpt-4o-mini"),
    system: "与えられたコンテキストのみを根拠に答え、引用元を必ず示すこと。",
    prompt: `# Context\n${context}\n\n# Question\n${question}`,
  });

  return result.toDataStreamResponse();
}

比較表 (アプローチ比較)

項目Vercel AI SDKLangChain.js自前ラッパ
学習コスト中〜高
型安全性高 (zod 連携)自分次第
プロバイダ抽象化ありあり自前
UI バインディングあり (React/Svelte 等)なし自前
ストリーミング一級サポート可能自前

実践メモ: providerのmodel IDを環境変数で管理すると、OpenAI→Anthropicなどプロバイダ切り替えがコード変更なしで可能です。

ベストプラクティス

1. zod スキーマ駆動

generateObject は出力検証を兼ねるため、API レスポンスやフォーム生成など「型が大事」な場面で積極的に使う。

2. プロバイダ差し替え可能に書く

model を環境変数で切り替えられるよう、薄いファクトリ関数を用意しておく。

export function getModel() {
  const name = process.env.AI_MODEL ?? "openai:gpt-4o-mini";
  const [provider, model] = name.split(":");
  if (provider === "anthropic") return anthropic(model);
  if (provider === "mistral") return mistral(model);
  return openai(model);
}

3. ストリーミングを基本に

UX が大きく改善するため、特別な理由がなければ streamText を採用。

4. Tool 実行の安全策

execute 内で外部 API を叩く際は、引数のバリデーション・タイムアウト・リトライを必ず実装。

5. ロギング

入力・出力・トークン数・所要時間をトレースに残し、コストと品質を継続評価する。

注意: ストリーミングレスポンスではエラーハンドリングのタイミングが異なります。try-catchだけでなくonErrorコールバックも併用しましょう。

注意点・落とし穴

  • プロバイダ差: モデルによって Tool Calling のネスト深度や JSON 出力の安定度が異なります。
  • コスト: ストリーミング中でもトークン課金は発生します。長文応答にはリミットを設定。
  • エッジランタイム: 一部 Node API は使えないため、依存ライブラリの互換性を確認。
  • 依存バージョン: SDK と各プロバイダパッケージのバージョン整合に注意。
  • PII 取り扱い: 機微情報をそのまま外部 API に送らない設計を徹底。

導入手順

  1. npm install ai @ai-sdk/openai zod
  2. OPENAI_API_KEY.env.local に設定
  3. サーバー側で streamText を使う API ルートを作成
  4. クライアント側で useChat を使い UI を組む
  5. zod スキーマを使った構造化出力を導入
  6. ログ・トレース・コスト計測を組み込む
  7. プロバイダ差し替え用の薄い抽象化を入れる

パフォーマンス観点

  • ストリーミング遅延 (TTFB / TTFT) を計測
  • レスポンス長の上限設定でコスト暴走を防ぐ
  • キャッシュ可能な部分 (システムプロンプト等) を活用
  • エッジランタイムでユーザー近接実行

FAQ

Q1. Next.js 専用ですか?

A. いいえ。Node.js / Bun / Deno / SvelteKit / Remix など TypeScript が動く環境で利用できます。

Q2. LangChain との違いは?

A. Vercel AI SDK はよりシンプルで型に強く、UI バインディングを含む点が特徴。エージェントフレームワーク全体を組むなら LangChain も検討対象です。

Q3. RAG はどう作る?

A. embed / embedMany でベクトル化し、好きなベクター DB と組み合わせます。検索結果をプロンプトに渡すだけで良いシンプルな構成です。

Q4. オンプレモデルは使えますか?

A. OpenAI 互換エンドポイントを提供する vLLM / Ollama などをカスタムプロバイダとしてラップすることで利用可能です。

Q5. 型安全性はどこまで担保される?

A. zod スキーマ + generateObject で、応答オブジェクトの型推論まで TypeScript で受けられます。

まとめ

Vercel AI SDK は「TypeScript で生成 AI アプリを書く」際のデファクトに近い存在です。プロバイダ抽象化、ストリーミング、構造化出力、Tool Calling、UI バインディングを統一インターフェイスで扱えるため、PoC から本番まで一貫したコードベースを保てます。まずは streamTextuseChat の組み合わせで小さなチャットを作り、そこから RAG・エージェント・構造化出力へと段階的に拡張していくのがおすすめです。

マルチステップ Tool Calling

エージェントのように、LLM がツールを呼び、その結果を踏まえてさらに思考する流れも generateTextmaxSteps (または同等オプション) で表現できます。

import { generateText, tool } from "ai";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";

const result = await generateText({
  model: openai("gpt-4o-mini"),
  maxSteps: 5,
  tools: {
    searchDocs: tool({
      description: "社内ドキュメントを検索",
      parameters: z.object({ query: z.string() }),
      execute: async ({ query }) => {
        return await searchVectorStore(query);
      },
    }),
    createTicket: tool({
      description: "サポートチケットを作成",
      parameters: z.object({
        title: z.string(),
        body: z.string(),
        priority: z.enum(["low", "mid", "high"]),
      }),
      execute: async (input) => {
        return await ticketApi.create(input);
      },
    }),
  },
  prompt: "ログインできないという問い合わせを受けた。原因候補を調べてチケットを作って",
});

console.log(result.text);

React Server Components との統合

Vercel AI SDK には RSC 向けのストリーミング UI 構築機能 (ai/rsc) があり、サーバー側で生成途中の React コンポーネントを段階的にクライアントへ送ることができます。これを使うと、ストリーミングテキストだけでなく、ストリーミング UI が実現できます。

// app/actions.ts
"use server";
import { createStreamableUI } from "ai/rsc";
import { streamText } from "ai";
import { openai } from "@ai-sdk/openai";

export async function ask(question: string) {
  const ui = createStreamableUI(<div>考え中...</div>);

  (async () => {
    const result = await streamText({
      model: openai("gpt-4o-mini"),
      prompt: question,
    });
    let text = "";
    for await (const chunk of result.textStream) {
      text += chunk;
      ui.update(<article>{text}</article>);
    }
    ui.done();
  })();

  return ui.value;
}

エラーハンドリングとリトライ

LLM API はネットワーク・レート制限・タイムアウトでエラーになることがあります。指数バックオフを挟んだリトライ層を作っておくと安心です。

async function withRetry<T>(fn: () => Promise<T>, retries = 3): Promise<T> {
  let lastErr: unknown;
  for (let i = 0; i < retries; i++) {
    try {
      return await fn();
    } catch (e) {
      lastErr = e;
      const delay = 500 * 2 ** i + Math.random() * 200;
      await new Promise((r) => setTimeout(r, delay));
    }
  }
  throw lastErr;
}

const { text } = await withRetry(() =>
  generateText({ model: openai("gpt-4o-mini"), prompt: "Hello" }),
);

監視とトークン計測

generateText / streamText は usage 情報 (入出力トークン数) を返してくれます。ログに必ず残し、コスト・回帰検知に役立てましょう。

const result = await generateText({
  model: openai("gpt-4o-mini"),
  prompt: "Hello",
});

console.log({
  promptTokens: result.usage.promptTokens,
  completionTokens: result.usage.completionTokens,
  totalTokens: result.usage.totalTokens,
  finishReason: result.finishReason,
});

評価セットによる回帰テスト

import { generateObject } from "ai";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";

const cases = [
  { input: "TypeScript の特徴は?", mustContain: ["型"] },
  { input: "React の useState は?", mustContain: ["状態"] },
];

for (const c of cases) {
  const { object } = await generateObject({
    model: openai("gpt-4o-mini"),
    schema: z.object({ answer: z.string() }),
    prompt: c.input,
  });
  const ok = c.mustContain.every((k) => object.answer.includes(k));
  console.log(c.input, ok ? "PASS" : "FAIL");
}

エッジ環境での運用

Vercel Edge Functions / Cloudflare Workers などのエッジランタイムでも Vercel AI SDK は動作します。ユーザーに地理的に近いリージョンで実行することで TTFB を短縮でき、生成 AI 体験が滑らかになります。エッジ環境では Node 固有 API の利用に制限があるため、依存ライブラリの互換性を確認しましょう。

プロバイダ別の小ネタ

OpenAI

  • gpt-4o-mini はコスト効率が高く、PoC や軽量タスクに向く
  • 構造化出力は zod 経由で安定

Anthropic

  • 長文要約・コードレビューに強い
  • システムプロンプトをしっかり書くほど性能を発揮

Google

  • マルチモーダル入力 (画像) との組み合わせがしやすい
  • 長コンテキスト処理に強み

Mistral

  • 欧州データ主権要件に向く
  • Codestral とのモデル切替で用途分担

キャッシュ戦略

完全に同一なプロンプトは KV キャッシュ的な仕組みでコスト削減できる場合があります。アプリ側でも「同じ入力の結果」を Redis などにキャッシュしておけば、定型応答系のレイテンシとコストを大幅に下げられます。

import { createClient } from "redis";

const redis = createClient({ url: process.env.REDIS_URL });
await redis.connect();

async function cachedAsk(prompt: string) {
  const key = `ai:${prompt}`;
  const hit = await redis.get(key);
  if (hit) return hit;

  const { text } = await generateText({
    model: openai("gpt-4o-mini"),
    prompt,
  });
  await redis.set(key, text, { EX: 60 * 60 });
  return text;
}

テスト戦略

  • 単体テストではプロバイダ呼び出しをモック
  • 統合テストでは固定プロンプト + 期待文字列の包含チェック
  • 評価セットによる回帰テストを CI に組み込み
  • LLM as a Judge: モデル自身に出力品質を採点させる手法も併用可

既存アプリへの段階導入のコツ

  1. まずは 1 つの機能 (例: 要約ボタン) から導入
  2. サーバーサイドで generateText を試し、動作確認
  3. UX を改善するため streamText + useChat に置き換え
  4. 構造化が必要な箇所を generateObject に移行
  5. 共通ロジック (リトライ・ロギング・PII マスキング) をラップ
  6. プロバイダ抽象化レイヤーを追加し、モデル切替を容易に
  7. 評価セットを CI に組み込み、回帰検知を自動化

参考リソース

この技術を体系的に学びたいですか?

未来学では東証プライム上場企業のITエンジニアが24時間サポート。月額24,800円から、退会金0円のオンラインIT塾です。

メールで無料相談する
← 一覧に戻る