Vercel AI SDK 7入門——Node.js 22とAgent APIでAI機能を作り直す
Vercel AI SDK 7のNode.js 22必須化、ESM専用化、Agent APIを踏まえ、Next.js + FirestoreでAI機能を移行する実践手順を解説します。

はじめに
AI SDKを使ったNext.jsアプリで、こんな不安はありませんか?
- AI呼び出しのコードが増え、どの機能がどのモデルを使っているか追いにくい
- ツール呼び出しや利用量ログを後付けで足していて、Route Handlerが太っている
- Node.jsやパッケージ更新のタイミングで、CIだけ落ちるのが怖い
2026年6月25日、Vercelは AI SDK 7 を発表しました。公式ブログとChangelogでは、Node.js 22以上、ESM専用化、Zod v4対応、Agent API、ツール実行まわりの型改善、telemetryとtimeoutの再設計などが紹介されています。
本記事では、架空の業務支援SaaS「TaskLedger」を題材に、Next.js App Router + Firestore + GitHub ActionsでAI機能をAI SDK 7向けに整理するハンズオンを行います。
参考にした一次情報は次の通りです。
何を優先して見るべきか
AI SDK 7は「新機能が増えた」だけではなく、実行環境と設計単位を見直すリリースです。Web系エンジニアが最初に確認すべき点を整理します。
| 観点 | 確認すること | TaskLedgerでの方針 |
|---|---|---|
| Node.js | Node.js 22以上で動かす | CIと本番ランタイムを22へ寄せる |
| モジュール | ESM importを使う | require("ai") の自作スクリプトをなくす |
| スキーマ | Zod v4対応を確認する | zod/v4 importへ寄せる |
| エージェント | Agent APIの責務を分ける | まずRoute Handlerを薄くし、将来Agent化できる形にする |
| 監査 | telemetryとFirestoreログを分ける | 技術メトリクスと業務監査ログを混ぜない |
ポイントは、AI SDKの新APIを一気に全面採用することではありません。まず「AI呼び出しを業務コードから分離し、ログを残し、Node.js 22でCIが通る」状態を作るのが実務的です。
事前準備: Node.js 22と依存関係をそろえる
AI SDK 7では、まず実行環境をそろえるところから始めます。Next.jsアプリ側では、AI SDK本体、Firebase Admin SDK、スキーマ定義用のZodを使います。
yarn add ai@latest firebase-admin zod
GitHub ActionsではNode.js 22を明示します。既存プロジェクトがNode.js 20や18で動いている場合、AI機能だけでなくビルド全体への影響を見たいので、PR上で yarn build まで回します。
name: build
on:
pull_request:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: yarn
- run: yarn install --frozen-lockfile
- run: yarn build
ローカルでもCIでも同じNode.jsを使うため、package.json にエンジン条件を置いておくとレビュー時に意図が伝わります。
{
"engines": {
"node": ">=22"
}
}
ハンズオン1: FirestoreにAI実行台帳を作る
TaskLedgerでは、20〜30件のGitHub Issue相当のタスクをAIで棚卸しし、実装順とリスクを提案してもらいます。ただし、AIの返答だけを保存しても運用には足りません。誰が、どのプロジェクトで、どのタスク集合を対象に、どのモデル設定で実行したかを残します。
Firestoreには aiRuns コレクションを作る想定です。
| フィールド | 型 | 用途 |
|---|---|---|
projectId | string | 対象プロジェクト |
requestedBy | string | 実行ユーザーID |
taskIds | string[] | 対象タスク |
model | string | 利用モデルID |
status | string | running / done / failed |
summary | string | AIの要約結果 |
createdAt | timestamp | 実行開始 |
finishedAt | timestamp | 実行終了 |
プロンプト全文をFirestoreに保存するかは慎重に決めます。個人情報や顧客固有の文脈が混ざる場合、入力全文ではなく、対象ID、件数、ハッシュ、出力要約だけを保存する方が扱いやすいです。
ハンズオン2: AI呼び出しをサービス関数に分離する
次に、AI SDK 7を使う処理をRoute Handlerから切り出します。ここでは generateText を使い、Firestoreから読んだタスク情報を短く整形して渡します。モデルIDは環境変数から読み、実際に使うIDはVercel AI Gatewayや各プロバイダの公式画面で管理する前提です。
// src/lib/ai/summarizeTasks.ts
import { generateText } from "ai";
type TaskInput = {
taskId: string;
title: string;
status: "todo" | "doing" | "blocked" | "done";
risk: "low" | "medium" | "high";
};
type SummarizeTasksInput = {
projectName: string;
tasks: TaskInput[];
};
export async function summarizeTasks(input: SummarizeTasksInput) {
const model = process.env.AI_MODEL_ID;
if (!model) {
throw new Error("AI_MODEL_ID is not configured");
}
const taskList = input.tasks
.map((task) => {
return [
`ID: ${task.taskId}`,
`Title: ${task.title}`,
`Status: ${task.status}`,
`Risk: ${task.risk}`,
].join("\n");
})
.join("\n\n---\n\n");
const result = await generateText({
model,
system:
"You are a senior TypeScript project reviewer. Reply in Japanese. Focus on implementation order, blockers, and verification.",
prompt: [
`Project: ${input.projectName}`,
"次のタスクを、Next.js App Router、Firestore、GitHub Actionsで実装する前提で整理してください。",
"出力は「優先順位」「先に解消する依存関係」「CIで確認すること」の3セクションにしてください。",
taskList,
].join("\n\n"),
});
return {
text: result.text,
usage: result.usage,
finishReason: result.finishReason,
};
}
この形にしておくと、後からAgent APIを使う場合でも、Route Handler全体を書き換えずにAI実行部分だけを差し替えられます。AI SDK 7の公式情報では、ToolLoopAgent、WorkflowAgent、HumanInTheLoopAgentのように用途別のAgent APIが用意されています。最初から複雑なエージェントに寄せるより、まず「入力整形」「AI実行」「監査ログ」を分けるのが安全です。
ハンズオン3: Route Handlerで権限確認とログ保存を行う
Route Handlerでは、AIを呼ぶ前に必ず業務認可を確認します。AI SDKやモデルは、Firestore上のプロジェクト権限を知りません。
// src/app/api/projects/[projectId]/ai-summary/route.ts
import { FieldValue } from "firebase-admin/firestore";
import { adminDb } from "@/lib/firebase/admin";
import { summarizeTasks } from "@/lib/ai/summarizeTasks";
import { requireUser } from "@/lib/auth/requireUser";
import { assertCanReadProject } from "@/lib/projects/permissions";
type Params = {
params: Promise<{ projectId: string }>;
};
type RequestBody = {
taskIds: string[];
};
export async function POST(request: Request, { params }: Params) {
const user = await requireUser(request);
const { projectId } = await params;
const body = (await request.json()) as RequestBody;
await assertCanReadProject(user.uid, projectId);
const taskIds = body.taskIds.slice(0, 30);
if (taskIds.length === 0) {
return Response.json({ error: "taskIds is required" }, { status: 400 });
}
const tasksSnapshot = await adminDb
.collection("tasks")
.where("projectId", "==", projectId)
.where("taskId", "in", taskIds)
.get();
const tasks = tasksSnapshot.docs.map((doc) => {
const data = doc.data() as {
taskId: string;
title: string;
status: "todo" | "doing" | "blocked" | "done";
risk: "low" | "medium" | "high";
};
return data;
});
const runRef = adminDb.collection("aiRuns").doc();
await runRef.set({
projectId,
requestedBy: user.uid,
taskIds,
model: process.env.AI_MODEL_ID ?? null,
status: "running",
createdAt: FieldValue.serverTimestamp(),
});
try {
const result = await summarizeTasks({
projectName: "TaskLedger",
tasks,
});
await runRef.update({
status: "done",
summary: result.text,
usage: result.usage ?? null,
finishReason: result.finishReason ?? null,
finishedAt: FieldValue.serverTimestamp(),
});
return Response.json({ runId: runRef.id, summary: result.text });
} catch (error) {
await runRef.update({
status: "failed",
errorMessage: error instanceof Error ? error.message : "Unknown error",
finishedAt: FieldValue.serverTimestamp(),
});
return Response.json({ error: "AI summary failed" }, { status: 500 });
}
}
Firestoreの in クエリには件数上限があります。大量のタスクを扱う場合は、30件前後のバッチに分け、最初にブロッカーだけを抽出するなど、AIに渡す情報量を制御します。
ハンズオン4: 30タスクを小さな波に分ける
AI SDK 7のAgent APIは魅力的ですが、実務ではタスクの分け方が先です。TaskLedgerでは、30件のタスクを次のように扱います。
AIには「全部を実装して」ではなく、「Waveごとのリスクを整理して」と頼む方が精度が上がります。AI SDK 7のtimeoutやtelemetryの改善は、こうした実行単位を運用で追うときに効いてきます。Firestoreの aiRuns は業務監査、VercelやOpenTelemetry側のtelemetryは技術メトリクス、と役割を分けると後から調査しやすくなります。
移行時の注意点
AI SDK 7へ上げるときは、次の順番で確認すると事故が少ないです。
- CIをNode.js 22へ上げ、
yarn buildが通るか確認する require("ai")を使う自作スクリプトをESM importへ直す- Zodを使っている箇所をZod v4対応で確認する
- AI呼び出しをRoute Handlerからサービス関数へ分離する
- Firestoreに実行台帳を残し、失敗時の再実行単位を決める
- Agent APIは、まず1つの業務フローで小さく試す
特に注意したいのは、AI SDKのtelemetryとFirestoreログを混同しないことです。telemetryはレイテンシや実行状況を見るための技術ログ、Firestoreの aiRuns は「誰が何を実行したか」を追うための業務ログです。片方に寄せると、セキュリティレビューや障害調査で情報が足りなくなります。
まとめ
Vercel AI SDK 7は、Next.jsでAI機能を作るチームにとって、環境と設計を見直す良いタイミングです。
- Node.js 22以上とESM importを前提にCIを整える
- AI呼び出しをサービス関数へ分離し、Agent APIへ移行しやすくする
- Firestoreに
aiRunsを作り、業務監査ログを残す - 20〜30件のタスクは小さなWaveに分け、AIに依存関係とリスク整理を任せる
- telemetry、timeout、Agent APIは、実行単位が整理されてから導入すると効果が出やすい
まずは既存のAI Route Handlerを1つ選び、Node.js 22のCI上でビルドし、Firestoreに実行台帳を残すところから始めるのがおすすめです。AI SDK 7の新機能は、その土台ができてから順に取り込むと、実装も運用も安定します。