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、構造化出力はgenerateText+Output.object(generateObjectはv7で非推奨)。 - Next.jsチャット: サーバーは
streamText(...).toUIMessageStreamResponse()、クライアントはuseChat+DefaultChatTransportでmessage.partsを描画。 - ツール実行:
toolで関数を定義し、stopWhen: stepCountIs(n)でループを制御。定型のエージェントはToolLoopAgentクラスにまとめられる。 - LangChain連携:
@ai-sdk/langchainのtoUIMessageStreamで、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を使い、textStreamをfor 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ではgenerateTextにoutputを渡し、ZodスキーマをOutput.objectで包みます。結果はoutputプロパティにスキーマ通りの型で入ります。v6まで定番だったgenerateObject/streamObjectはv7で非推奨(@deprecated)になり、generateText/streamTextのoutput指定に統合されました。古い記事のコピペは非推奨警告の対象になるので、新規実装はこちらで書きます。
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/reactのuseChatフックを使います。接続先は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パッケージへ切り出され、toUIMessageStream+createUIMessageStreamResponseの組み合わせに置き換わりました。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では
generateText+Output.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で使い方は変わりますか?
基本のgenerateText/streamText/useChatの形は共通ですが、generateObjectの非推奨化や関数名のリネームなど差分があります。v6→v7の破壊的変更と移行手順はVercel AI SDK v6とv7の違い・移行の解説にまとめています。
useChatでツール実行に承認を挟むには?
ツール定義にneedsApproval(真偽値または関数)を付けると、実行前に承認待ちになります。クライアント側はuseChatが返すaddToolApprovalResponseで{ id, approved }を返して実行を続行・拒否できます。
LangChainAdapterが見つからないのはなぜ?
ai本体にあったLangChainAdapter.toDataStreamResponseは削除され、連携機能は@ai-sdk/langchainパッケージへ移りました。現在はtoUIMessageStreamとcreateUIMessageStreamResponseを組み合わせて使います。