TypeScript AI SDK開發:2026年Vercel AI SDK構建生產級AI應用的7個關鍵模式
AI應用開發,為什麼總是這麼痛苦?
大模型API呼叫本身不難,但構建生產級AI應用卻處處是坑:流式輸出在前端展示總是閃爍跳躍,Server Components和AI流式響應的整合讓人抓狂,Edge Runtime的限制導致一半依賴不能用,Tool Calling的錯誤處理幾乎沒有最佳實踐……更別提部署到生產環境後的延遲、穩定性和成本問題。
Vercel AI SDK 在2026年已經演進到4.x版本,提供了從流式渲染到工具呼叫的完整解決方案。本文總結7個關鍵模式,幫你從Demo級別躍升到生產級別。
Vercel AI SDK 核心架構
| 模組 | 職責 | 關鍵API |
|---|---|---|
| AI Core | 統一多模型呼叫介面 | generateText(), streamText(), generateObject() |
| AI SDK UI | 前端流式渲染Hook | useChat(), useCompletion(), useObject() |
| AI SDK RSC | Server Components整合 | streamUI(), createAI() |
| Tool Calling | 函數呼叫與工具編排 | tools, execute, maxSteps |
核心依賴版本:
{
"ai": "^4.2.0",
"@ai-sdk/openai": "^1.3.0",
"@ai-sdk/anthropic": "^1.2.0",
"next": "^15.2.0",
"react": "^19.0.0",
"zod": "^3.24.0"
}
問題深入分析:AI應用的7個核心挑戰
| 挑戰 | 傳統方案 | AI SDK方案 | 優勢 |
|---|---|---|---|
| 流式渲染 | 手動SSE解析 | useChat() Hook |
自動重連、狀態管理 |
| 型別安全 | any型別 | Zod Schema | 編譯期驗證 |
| Server整合 | API Route + useEffect | RSC + streamUI() |
零客戶端JS |
| Edge部署 | Node.js Runtime | Edge Runtime | 全球低延遲 |
| 工具呼叫 | 手動JSON解析 | tools + execute |
自動編排 |
| 錯誤恢復 | try/catch | maxSteps + 重試 |
自動重試鏈 |
| 成本控制 | 無 | Token計數 + 快取 | 精確計費 |
模式1:流式聊天UI
// app/api/chat/route.ts
import { openai } from '@ai-sdk/openai';
import { streamText } from 'ai';
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai('gpt-4o'),
system: '你是一個專業的技術助手,回答要簡潔準確。',
messages,
maxTokens: 4096,
temperature: 0.7,
});
return result.toDataStreamResponse();
}
// app/chat/page.tsx
'use client';
import { useChat } from '@ai-sdk/react';
export default function ChatPage() {
const { messages, input, handleInputChange, handleSubmit, isLoading, error, reload } = useChat({
api: '/api/chat',
onError: (err) => console.error('Chat error:', err),
});
return (
<div className="flex flex-col h-screen max-w-3xl mx-auto p-4">
<div className="flex-1 overflow-y-auto space-y-4 mb-4">
{messages.map((message) => (
<div
key={message.id}
className={`p-4 rounded-lg ${
message.role === 'user'
? 'bg-blue-500 text-white ml-auto max-w-[80%]'
: 'bg-gray-100 dark:bg-gray-800 mr-auto max-w-[80%]'
}`}
>
<div className="whitespace-pre-wrap">{message.content}</div>
</div>
))}
{isLoading && <div className="animate-pulse p-4">思考中...</div>}
</div>
{error && (
<div className="mb-2 p-2 bg-red-100 text-red-700 rounded text-sm">
錯誤: {error.message}
<button onClick={() => reload()} className="ml-2 underline">重試</button>
</div>
)}
<form onSubmit={handleSubmit} className="flex gap-2">
<input
value={input}
onChange={handleInputChange}
placeholder="輸入你的問題..."
className="flex-1 p-3 border rounded-lg"
disabled={isLoading}
/>
<button type="submit" disabled={isLoading || !input.trim()}
className="px-6 py-3 bg-blue-500 text-white rounded-lg disabled:opacity-50">
發送
</button>
</form>
</div>
);
}
模式2:Server Components流式渲染
// app/api/generate/route.ts
import { openai } from '@ai-sdk/openai';
import { streamUI } from 'ai/rsc';
import { z } from 'zod';
async function WeatherComponent({ city }: { city: string }) {
const weatherData = {
city,
temperature: 22 + Math.floor(Math.random() * 10),
condition: ['晴天', '多雲', '小雨'][Math.floor(Math.random() * 3)],
humidity: 45 + Math.floor(Math.random() * 30),
};
return (
<div className="p-4 bg-gradient-to-r from-blue-50 to-cyan-50 rounded-lg border">
<h3 className="font-bold text-lg">{weatherData.city} 天氣</h3>
<div className="flex gap-4 mt-2">
<span className="text-3xl">{weatherData.temperature}°C</span>
<div>
<div>{weatherData.condition}</div>
<div className="text-sm text-gray-500">濕度 {weatherData.humidity}%</div>
</div>
</div>
</div>
);
}
export async function POST(req: Request) {
const { prompt } = await req.json();
const result = streamUI({
model: openai('gpt-4o'),
prompt,
tools: {
showWeather: {
description: '顯示指定城市的天氣資訊',
parameters: z.object({ city: z.string().describe('城市名稱') }),
generate: async ({ city }) => <WeatherComponent city={city} />,
},
},
});
return result.toUIStreamResponse();
}
模式3:Tool Calling工具呼叫
// app/api/agent/route.ts
import { openai } from '@ai-sdk/openai';
import { streamText } from 'ai';
import { z } from 'zod';
const tools = {
searchProducts: {
description: '搜尋商品資料庫',
parameters: z.object({
query: z.string().describe('搜尋關鍵詞'),
category: z.string().optional().describe('商品分類'),
maxPrice: z.number().optional().describe('最高價格'),
}),
execute: async ({ query, category, maxPrice }) => {
const products = [
{ id: 1, name: '機械鍵盤 Pro', price: 599, category: '外設' },
{ id: 2, name: '4K顯示器', price: 2999, category: '顯示器' },
{ id: 3, name: '無線滑鼠', price: 199, category: '外設' },
];
let results = products.filter(p => p.name.includes(query) || p.category === category);
if (maxPrice) results = results.filter(p => p.price <= maxPrice);
return results;
},
},
calculateTotal: {
description: '計算訂單總價',
parameters: z.object({
items: z.array(z.object({ productId: z.number(), quantity: z.number(), unitPrice: z.number() })),
discount: z.number().optional().describe('折扣百分比'),
}),
execute: async ({ items, discount = 0 }) => {
const subtotal = items.reduce((sum, item) => sum + item.unitPrice * item.quantity, 0);
const total = subtotal - subtotal * (discount / 100);
return { subtotal: subtotal.toFixed(2), discount: (subtotal * discount / 100).toFixed(2), total: total.toFixed(2) };
},
},
};
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai('gpt-4o'),
system: '你是一個電商助手,幫助用戶搜尋商品、計算價格和建立訂單。',
messages,
tools,
maxSteps: 5,
});
return result.toDataStreamResponse();
}
模式4:結構化輸出
import { z } from 'zod';
export const analysisSchema = z.object({
summary: z.string().describe('文章摘要'),
sentiment: z.enum(['positive', 'neutral', 'negative']).describe('情感傾向'),
keywords: z.array(z.string()).describe('關鍵詞列表'),
categories: z.array(z.object({ name: z.string(), confidence: z.number().min(0).max(1) })),
entities: z.array(z.object({
name: z.string(),
type: z.enum(['person', 'organization', 'location', 'product', 'event']),
mentions: z.number(),
})),
});
模式5:Edge Runtime部署
// app/api/edge-chat/route.ts
import { openai } from '@ai-sdk/openai';
import { streamText } from 'ai';
export const runtime = 'edge';
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai('gpt-4o-mini'),
system: '簡潔回答用戶問題。',
messages,
maxTokens: 2048,
});
return result.toDataStreamResponse();
}
模式6:多模型切換
// lib/models.ts
import { openai } from '@ai-sdk/openai';
import { anthropic } from '@ai-sdk/anthropic';
import { google } from '@ai-sdk/google';
export const models = {
'gpt-4o': openai('gpt-4o'),
'gpt-4o-mini': openai('gpt-4o-mini'),
'claude-sonnet-4-20250514': anthropic('claude-sonnet-4-20250514'),
'gemini-2.0-flash': google('gemini-2.0-flash'),
} as const;
export type ModelId = keyof typeof models;
export function getModel(modelId: ModelId) { return models[modelId]; }
模式7:中介軟體與鑑權
// middleware.ts
import { NextRequest, NextResponse } from 'next/server';
const rateLimitMap = new Map<string, { count: number; resetAt: number }>();
export function middleware(req: NextRequest) {
if (req.nextUrl.pathname.startsWith('/api/')) {
const ip = req.headers.get('x-forwarded-for') || 'unknown';
const now = Date.now();
const record = rateLimitMap.get(ip);
if (!record || now > record.resetAt) {
rateLimitMap.set(ip, { count: 1, resetAt: now + 60000 });
} else if (record.count >= 20) {
return NextResponse.json({ error: '請求過於頻繁' }, { status: 429 });
} else {
record.count++;
}
}
return NextResponse.next();
}
export const config = { matcher: '/api/:path*' };
避坑指南
坑1:useChat流式輸出閃爍
React 19的併發渲染可能導致流式輸出時UI閃爍跳躍。
解決方案:使用 onFinish 回調做最終狀態同步,使用 whitespace-pre-wrap,使用 key={message.id}。
坑2:Edge Runtime依賴不相容
許多npm包使用Node.js API,在Edge Runtime下無法運行。
解決方案:檢查依賴是否Edge相容,不相容的依賴降級到Node.js Runtime。
坑3:Tool Calling無限迴圈
maxSteps 設置過大,工具呼叫可能陷入迴圈。
解決方案:設置合理的 maxSteps(推薦3-5),在工具 execute 中添加終止條件。
坑4:streamUI組件序列化失敗
RSC流式傳輸的組件必須可序列化,閉包和不可序列化的props會報錯。
解決方案:組件中不要使用閉包捕獲外部變數,所有資料通過props傳遞。
坑5:generateObject Schema不匹配
模型輸出不符合Zod Schema定義,導致解析失敗。
解決方案:Schema欄位添加詳細的 describe() 說明,使用 enum 替代自由文字欄位。
報錯排查
| 序號 | 報錯信息 | 原因 | 解決方法 |
|---|---|---|---|
| 1 | AI_APICallError: 429 Rate limit exceeded |
API呼叫頻率超限 | 添加指數退避重試 |
| 2 | AI_InvalidPromptError: messages must not be empty |
messages陣列為空 | 發送前校驗 |
| 3 | AI_NoOutputSpecifiedError |
streamText未指定輸出方式 | 呼叫 toDataStreamResponse() |
| 4 | AI_ToolExecutionError |
工具execute函數拋出異常 | 在execute中添加try/catch |
| 5 | AI_JSONParseError |
模型輸出非有效JSON | 使用 generateObject 替代 |
| 6 | EdgeRuntime: Dynamic require of "fs" |
Edge中使用了Node.js API | 切換Runtime |
| 7 | Hydration mismatch |
SSR和客戶端渲染不一致 | 確保流式組件不依賴客戶端狀態 |
| 8 | AI_InvalidArgumentError: schema validation failed |
Zod Schema驗證失敗 | 檢查Schema定義 |
| 9 | AI_ReadonlyStreamError: stream already consumed |
流被重複消費 | 確保每個流只讀取一次 |
| 10 | Maximum call stack size exceeded |
maxSteps導致無限遞迴 | 降低maxSteps |
進階優化
1. 流式響應快取
import { cache } from 'react';
export const generateAnalysis = cache(async (text: string) => {
const { object } = await generateObject({
model: openai('gpt-4o-mini'),
schema: analysisSchema,
prompt: text,
});
return object;
});
2. Token用量追蹤
const result = streamText({
model: openai('gpt-4o'),
messages,
onFinish: ({ usage }) => {
console.log(`Tokens: prompt=${usage.promptTokens}, completion=${usage.completionTokens}`);
},
});
3. 流式UI載入骨架
const result = streamUI({
model: openai('gpt-4o'),
prompt,
loading: (
<div className="animate-pulse space-y-3 p-4">
<div className="h-4 bg-gray-200 rounded w-3/4" />
<div className="h-32 bg-gray-200 rounded" />
</div>
),
});
對比分析
| 維度 | Vercel AI SDK | LangChain.js | OpenAI SDK | LlamaIndex.ts |
|---|---|---|---|---|
| 流式UI | 原生React Hook | 需手動整合 | 需手動整合 | 需手動整合 |
| Server Components | 原生支持 | 不支持 | 不支持 | 不支持 |
| 多模型統一 | Provider抽象 | Callback抽象 | 僅OpenAI | Provider抽象 |
| Tool Calling | 聲明式 | 鏈式 | 手動 | 聲明式 |
| 結構化輸出 | Zod Schema | Zod/Dynamic | JSON Mode | Zod Schema |
| Edge Runtime | 原生支持 | 部分相容 | 相容 | 部分相容 |
| 包體積 | ~30KB | ~200KB+ | ~50KB | ~150KB+ |
| 學習曲線 | 低 | 高 | 低 | 中 |
| Next.js整合 | 深度整合 | 需適配 | 需適配 | 需適配 |
總結:Vercel AI SDK 是2026年構建Next.js AI應用的最佳選擇。7個關鍵模式涵蓋了從流式渲染、Server Components整合、Tool Calling到Edge部署的完整鏈路。核心優勢在於與React/Next.js的深度整合——
useChatHook讓流式聊天只需幾行代碼,streamUI讓AI直接生成React組件,Zod Schema讓結構化輸出型別安全。建議從模式1開始,逐步引入Tool Calling和RSC,最後最佳化Edge部署和快取策略。
線上工具推薦
- JSON格式化:/zh-TW/json/format — 格式化AI模型響應和Tool Calling參數
- Base64編解碼:/zh-TW/encode/base64 — 處理API金鑰和Token編碼
- Curl轉代碼:/zh-TW/dev/curl-to-code — 將AI API除錯curl轉為TypeScript代碼
本站提供瀏覽器本地工具,免註冊即可試用 →