AI

Vercel AI SDKの使い方|Next.js実装とLangChain連携のサンプル集【2026年最新】

Vercel AI SDK(npmパッケージ名ai)は、モデルの呼び出しからチャットUIのストリーミングまでを1本のライブラリでつなぐTypeScript製ツールキットです。この記事は実装サンプル集として、テキスト生成・Next.jsチャット・ツール実行・LangChain連携までを、そのまま貼って動くコードでまとめました。「v6とは何か」「v7への移行」といった概要はVercel AI SDK v6とは?新機能とv7との違い・移行で解説しているので、本記事では手を動かす部分に絞ります。2026年7月時点の最新はai v7系(7.0.30)で、npm install aiを実行するとv7が入ります。

まとめ|この記事で分かること

  • 導入: npm install ai @ai-sdk/react @ai-sdk/openai。モデルはopenai('gpt-5.6')のように生成し、プロバイダを差し替えても呼び出し側は変えない。
  • 生成: 一発ならgenerateText、逐次表示ならstreamText、構造化出力はgenerateTextOutput.objectgenerateObjectはv7で非推奨)。
  • Next.jsチャット: サーバーはstreamText(...).toUIMessageStreamResponse()、クライアントはuseChatDefaultChatTransportmessage.partsを描画。
  • ツール実行: toolで関数を定義し、stopWhen: stepCountIs(n)でループを制御。定型のエージェントはToolLoopAgentクラスにまとめられる。
  • LangChain連携: @ai-sdk/langchaintoUIMessageStreamで、LangGraphエージェントの出力をそのままAI SDKのチャットUIへ流せる(旧LangChainAdapterは廃止)。

以降は、セットアップ→生成→チャットUI→ツール→LangChain連携の順に、コードを追いながら組み立てます。

セットアップ|インストールとモデルプロバイダ設定

必要なのはコア(ai)、Reactフック(@ai-sdk/react)、モデルプロバイダ(例: @ai-sdk/openai)の3点です。構造化出力にZodを使うので合わせて入れておきます。

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

APIキーは環境変数に置きます。Next.jsならプロジェクト直下の.env.localに記述すれば、サーバー側のprocess.env.OPENAI_API_KEYから自動で読まれます。

# .env.local
OPENAI_API_KEY=sk-xxxxxxxx

バージョンには注意が必要です。npm install aiで入るのは最新のv7系(7.0.30)ですが、v6系(GAは2025年12月22日)向けに書かれた社内コードを動かすならnpm install ai@6とメジャーを固定してください。無指定だと2026年6月25日リリースのv7が入り、後述の非推奨APIなどで挙動が変わります。

モデルプロバイダの切り替え

プロバイダは@ai-sdk/openai@ai-sdk/anthropic@ai-sdk/googleなどが個別パッケージで提供され、いずれもmodelオブジェクトを返します。生成関数はこのオブジェクトを受け取るだけなので、モデルを1か所に閉じ込めておけば差し替えが1行で済みます。

// lib/model.ts
import { openai } from '@ai-sdk/openai';
// import { anthropic } from '@ai-sdk/anthropic';
// import { google } from '@ai-sdk/google';

// プロバイダを差し替えても呼び出し側のコードは変えない
export const model = openai('gpt-5.6');

OpenAIとAnthropicを併用してタスクごとに使い分ける、といった構成も呼び出し側を触らずに切り替えられます。これがAI SDK Core(ai本体のコア層)が担う、プロバイダ非依存の生成インターフェースです。

テキスト生成の基本|generateText・streamText・Output.object

一発で受け取る(generateText)

結果をまとめて1回で受け取るならgenerateTextです。戻り値のtextに生成文字列が入ります。バッチ処理や、UIに逐次表示する必要がない用途に向きます。

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

const { text } = await generateText({
  model: openai('gpt-5.6'),
  prompt: 'TypeScriptを採用する理由を3行で説明して',
});

console.log(text);

逐次表示する(streamText)

チャットのように生成を待たせず表示したいときはstreamTextを使い、textStreamfor awaitで回します。streamText自体はawaitせず、返ってきたオブジェクトのストリームを消費する点がポイントです。

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

const result = streamText({
  model: openai('gpt-5.6'),
  prompt: '自己紹介文を書いて',
});

// textStream は非同期イテラブル。届いたトークンから順に出力する
for await (const delta of result.textStream) {
  process.stdout.write(delta);
}

構造化データで受け取る(Output.object)

JSONなど決まった形で受け取りたい場合、v7ではgenerateTextoutputを渡し、ZodスキーマをOutput.objectで包みます。結果はoutputプロパティにスキーマ通りの型で入ります。v6まで定番だったgenerateObjectstreamObjectはv7で非推奨(@deprecated)になり、generateTextstreamTextoutput指定に統合されました。古い記事のコピペは非推奨警告の対象になるので、新規実装はこちらで書きます。

import { generateText, Output } from 'ai';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';

const { output } = await generateText({
  model: openai('gpt-5.6'),
  output: Output.object({
    schema: z.object({
      title: z.string(),
      tags: z.array(z.string()),
    }),
  }),
  prompt: 'この記事のタイトルとタグ候補を生成して',
});

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

Next.jsチャットUIの実装|App RouterとuseChat

サーバー側ルートハンドラ

App Routerではapp/api/chat/route.tsにPOSTハンドラを置きます。クライアントから届くのはUI用のUIMessage形式なので、convertToModelMessagesでモデル用に変換してからstreamTextへ渡し、toUIMessageStreamResponse()でそのままレスポンスにします。convertToModelMessagesはPromiseを返すためawaitが要る点に注意してください。

// app/api/chat/route.ts
import { streamText, convertToModelMessages, type UIMessage } from 'ai';
import { openai } from '@ai-sdk/openai';

export async function POST(req: Request) {
  const { messages }: { messages: UIMessage[] } = await req.json();

  const result = streamText({
    model: openai('gpt-5.6'),
    messages: await convertToModelMessages(messages),
  });

  return result.toUIMessageStreamResponse();
}

クライアント側(useChat)

クライアントは@ai-sdk/reactuseChatフックを使います。接続先はnew DefaultChatTransport({ api: '/api/chat' })で指定します。v5以降、メッセージ本文は文字列ではなくmessage.partsの配列になっており、type'text'のパートを描画します。送信はsendMessage({ text })statusで送信中かどうかを判定できます。

// app/page.tsx
'use client';
import { useChat } from '@ai-sdk/react';
import { DefaultChatTransport } from 'ai';
import { useState } from 'react';

export default function Chat() {
  const [input, setInput] = useState('');
  const { messages, sendMessage, status } = useChat({
    transport: new DefaultChatTransport({ api: '/api/chat' }),
  });

  return (
    <div>
      {messages.map((m) => (
        <div key={m.id}>
          <strong>{m.role}: </strong>
          {m.parts.map((part, i) =>
            part.type === 'text' ? <span key={i}>{part.text}</span> : null,
          )}
        </div>
      ))}

      <form
        onSubmit={(e) => {
          e.preventDefault();
          sendMessage({ text: input });
          setInput('');
        }}
      >
        <input value={input} onChange={(e) => setInput(e.target.value)} />
        <button disabled={status !== 'ready'}>送信</button>
      </form>
    </div>
  );
}

これでNext.jsでAI SDKを動かすチャットの最小構成が完成です。useChatにはregenerate(再生成)やstop(中断)、後述のツール承認用addToolApprovalResponseも揃っています。

ツール呼び出しとエージェント|tool・stopWhen・ToolLoopAgent

ツールを定義する(tool)

モデルに外部処理を呼ばせるにはtoolで関数を定義します。入力はinputSchemaにZodで型を与え(v5でパラメータ名がparametersからinputSchemaへ変わっています)、executeに実処理を書きます。

import { tool } from 'ai';
import { z } from 'zod';

export const weatherTool = tool({
  description: '指定した都市の現在の天気を返す',
  inputSchema: z.object({
    city: z.string().describe('都市名(例: 東京)'),
  }),
  execute: async ({ city }) => {
    // 実際は外部APIを呼ぶ。ここではダミー値
    return { city, tempC: 21, condition: 'くもり' };
  },
});

マルチステップ実行の停止条件(stopWhen)

ツールを渡すと、モデルは「ツール呼び出し→結果を受けて再推論」を繰り返します。この反復を制御するのがstopWhenで、stepCountIs(5)なら最大5ステップで打ち切ります。stopWhenを指定しないと1ステップで止まりツール結果を使った最終回答が出ないので、ツール利用時はほぼ必須です。stepCountIsはv7で内部的にisStepCountへ改名されましたが、stepCountIsも別名として残っているため既存コードはそのまま動きます。

import { streamText, stepCountIs } from 'ai';
import { openai } from '@ai-sdk/openai';
import { weatherTool } from './weather-tool';

const result = streamText({
  model: openai('gpt-5.6'),
  tools: { weather: weatherTool },
  // ツール実行→再推論のループを最大5ステップで打ち切る
  stopWhen: stepCountIs(5),
  prompt: '東京と大阪、暖かいのはどっち?',
});

ToolLoopAgentクラスにまとめる

モデル・指示・ツール・停止条件を毎回書くのが煩雑なら、ToolLoopAgentクラス(旧Experimental_Agentの後継)に定義をまとめられます。.generate()で一括生成、.stream()でストリーミングと、生成関数と同じ使い分けができます。stopWhenを省いたときの既定はstepCountIs(20)です。クラスの設計思想やツール承認フローの詳細はv6とは?の解説記事にまとめています。

import { ToolLoopAgent, stepCountIs } from 'ai';
import { openai } from '@ai-sdk/openai';
import { weatherTool } from './weather-tool';

const agent = new ToolLoopAgent({
  model: openai('gpt-5.6'),
  instructions: 'あなたは天気アシスタント。ツールで調べてから答える。',
  tools: { weather: weatherTool },
  stopWhen: stepCountIs(5), // 省略時の既定は stepCountIs(20)
});

const { text } = await agent.generate({ prompt: '東京の天気は?' });

LangChainとVercel AI SDKの連携|@ai-sdk/langchain

どちらを使うか|役割分担で考える

「LangChainとVercel AI SDKのどちらを使うべきか」は二者択一ではありません。ロジックはLangChain(LangGraph)、UIのストリーミングはVercel AI SDKと役割を分けて併用するのが、両者の強みを活かす構成です。複数ステップの分岐・状態管理・ツールオーケストレーションはLangGraphのグラフで組み、その出力を@ai-sdk/langchainアダプタでAI SDKのチャットUIへ流します。逆に、単純な1〜2ステップのチャットや構造化出力だけなら、LangChainを挟まずAI SDK単体で書いた方が依存も学習コストも軽く済みます。エージェントの分岐が浅いうちからLangGraphを持ち込むのは過剰です。LangChainとLangGraphの役割差はLangChainとLangGraphの違い|v1.0で逆転した関係と使い分けで整理しています。

LangGraphの出力をチャットUIへ流す

連携には専用パッケージ@ai-sdk/langchain(2026年7月時点3.0.30)を使います。@langchain/coreが必須のpeer依存です。LangChain v1系(langchain 1.5.3)のcreateAgentでエージェントを作り、そのストリームをtoUIMessageStreamでAI SDKの形式へ変換してcreateUIMessageStreamResponseで返します。クライアント側は前掲のuseChatの接続先を/api/langchainに変えるだけで、UIコードは共通のまま使えます。

npm install @ai-sdk/langchain @langchain/core langchain @langchain/openai
// app/api/langchain/route.ts
import { createAgent } from 'langchain';
import { toBaseMessages, toUIMessageStream } from '@ai-sdk/langchain';
import { createUIMessageStreamResponse, type UIMessage } from 'ai';

// LangGraph側でロジックを組む(ツール・分岐・状態)
const agent = createAgent({
  model: 'openai:gpt-5.6',
  tools: [],
});

export async function POST(req: Request) {
  const { messages }: { messages: UIMessage[] } = await req.json();

  // AI SDKのUIMessage → LangChainのBaseMessage
  const lcMessages = await toBaseMessages(messages);

  const stream = await agent.stream(
    { messages: lcMessages },
    { streamMode: ['values', 'messages'] },
  );

  // LangGraphストリーム → AI SDKのUIメッセージストリーム
  return createUIMessageStreamResponse({
    stream: toUIMessageStream(stream),
  });
}

ユーザーのメッセージはtoBaseMessagesでLangChainのBaseMessageへ、グラフの出力はtoUIMessageStreamでAI SDKのUIMessageChunkへと、境界の変換をアダプタが吸収します。ツールの途中経過を出したいときはstreamMode'tools'を加えると、進行中のツール出力がpreliminaryとして届きます。LangGraph側のツール実装はLangGraphのTool Callingとは|仕組みとToolNodeでの実装が参考になります。

旧LangChainAdapterからの移行

ネット上の古い記事にあるLangChainAdapter.toDataStreamResponseは、ai本体(v3/v4)に入っていた旧APIで、現在は削除されています。連携機能は@ai-sdk/langchainパッケージへ切り出され、toUIMessageStreamcreateUIMessageStreamResponseの組み合わせに置き換わりました。import { LangChainAdapter } from 'ai'が見つからないエラーは、この移行が原因です。

// 旧: ai v3/v4(現在は動かない)
import { LangChainAdapter } from 'ai';
const stream = await chain.stream(input);
return LangChainAdapter.toDataStreamResponse(stream);

// 新: @ai-sdk/langchain(v5系以降)
import { toUIMessageStream } from '@ai-sdk/langchain';
import { createUIMessageStreamResponse } from 'ai';
return createUIMessageStreamResponse({
  stream: toUIMessageStream(await chain.stream(input)),
});

LangChain JS自体もv1.0でcreate_agent中心の構成に変わっています。移行の全体像はLangChain v1.0とは|create_agent・ミドルウェアを実装例で解説を参照してください。

つまずきやすいポイントと対策

  • バージョン混在: npm install aiはv7を入れる。v6前提のコードはai@6にピン留めする。
  • generateObjectの非推奨警告: v7ではgenerateTextOutput.objectに置き換える。
  • convertToModelMessagesのawait漏れ: Promiseを返すためawaitが必要。忘れるとルートハンドラで型エラーになる。
  • ツールが1回で止まる: stopWhen: stepCountIs(n)を付け忘れると、ツール結果を使った最終回答まで到達しない。
  • LangChainAdapterが無い: ai本体から削除済み。@ai-sdk/langchainを導入しtoUIMessageStreamを使う。
  • message.contentを直接参照: v5以降はmessage.partsの配列。type: 'text'のパートを描画する。

よくある質問

Vercel AI SDKの最新バージョンは?

2026年7月時点の最新はai v7系で、npmでの最新版は7.0.30です。npm install aiを実行するとv7が入ります。v7.0.0のリリースは2026年6月25日、その前のv6.0.0のGAは2025年12月22日でした。

LangChainとVercel AI SDKは併用できますか?

できます。公式の@ai-sdk/langchainパッケージを使い、LangChain/LangGraphのストリームをtoUIMessageStreamでAI SDKの形式に変換すれば、useChatのチャットUIにそのまま流せます。LangGraphでロジックを、AI SDKでUIストリーミングを担当させる構成が定番です。

v6とv7で使い方は変わりますか?

基本のgenerateTextstreamTextuseChatの形は共通ですが、generateObjectの非推奨化や関数名のリネームなど差分があります。v6→v7の破壊的変更と移行手順はVercel AI SDK v6とv7の違い・移行の解説にまとめています。

useChatでツール実行に承認を挟むには?

ツール定義にneedsApproval(真偽値または関数)を付けると、実行前に承認待ちになります。クライアント側はuseChatが返すaddToolApprovalResponse{ id, approved }を返して実行を続行・拒否できます。

LangChainAdapterが見つからないのはなぜ?

ai本体にあったLangChainAdapter.toDataStreamResponseは削除され、連携機能は@ai-sdk/langchainパッケージへ移りました。現在はtoUIMessageStreamcreateUIMessageStreamResponseを組み合わせて使います。

関連記事

資料請求

RELATED POSTS 関連記事