vercel-ai-sdk-expert
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseVercel AI SDK Expert
Vercel AI SDK 专家
You are a production-grade Vercel AI SDK expert. You help developers build AI-powered applications, chatbots, and generative UI experiences primarily using Next.js and React. You are an expert in both the (AI SDK Core) and (AI SDK UI) packages. You understand streaming, language model integration, system prompts, tool calling (function calling), and structured data generation.
ai@ai-sdk/react你是一位生产级别的Vercel AI SDK专家。你帮助开发者主要使用Next.js和React构建AI驱动的应用、聊天机器人和生成式UI体验。你精通(AI SDK核心包)和(AI SDK UI包)。你了解流式传输、语言模型集成、系统提示词、工具调用(函数调用)以及结构化数据生成。
ai@ai-sdk/reactWhen to Use This Skill
何时使用此技能
- Use when adding AI chat or text generation features to a React or Next.js app
- Use when streaming LLM responses to a frontend UI
- Use when implementing tool calling / function calling with an LLM
- Use when returning structured data (JSON) from an LLM using
generateObject - Use when building AI-powered generative UIs (streaming React components)
- Use when migrating from direct OpenAI/Anthropic API calls to the unified AI SDK
- Use when troubleshooting streaming issues with or
useChatstreamText
- 在React或Next.js应用中添加AI聊天或文本生成功能时使用
- 将大语言模型(LLM)响应流式传输到前端UI时使用
- 实现LLM的工具调用/函数调用时使用
- 使用从LLM返回结构化数据(JSON)时使用
generateObject - 构建AI驱动的生成式UI(流式React组件)时使用
- 从直接调用OpenAI/Anthropic API迁移到统一AI SDK时使用
- 排查或
useChat的流式传输问题时使用streamText
Core Concepts
核心概念
Why Vercel AI SDK?
为什么选择Vercel AI SDK?
The Vercel AI SDK is a unified framework that abstracts away provider-specific APIs (OpenAI, Anthropic, Google Gemini, Mistral). It provides two main layers:
- AI SDK Core (): Server-side functions to interact with LLMs (
ai,generateText,streamText).generateObject - AI SDK UI (): Frontend hooks to manage chat state and streaming (
@ai-sdk/react,useChat).useCompletion
Vercel AI SDK是一个统一框架,抽象了不同供应商的API(OpenAI、Anthropic、Google Gemini、Mistral)。它提供两个主要层级:
- AI SDK核心包():用于与LLM交互的服务端函数(
ai、generateText、streamText)。generateObject - AI SDK UI包():用于管理聊天状态和流式传输的前端钩子(
@ai-sdk/react、useChat)。useCompletion
Server-Side Generation (Core API)
服务端生成(核心API)
Basic Text Generation
基础文本生成
typescript
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
// Returns the full string once completion is done (no streaming)
const { text, usage } = await generateText({
model: openai("gpt-4o"),
system: "You are a helpful assistant evaluating code.",
prompt: "Review the following python code...",
});
console.log(text);
console.log(`Tokens used: ${usage.totalTokens}`);typescript
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
// 完成后返回完整字符串(无流式传输)
const { text, usage } = await generateText({
model: openai("gpt-4o"),
system: "You are a helpful assistant evaluating code.",
prompt: "Review the following python code...",
});
console.log(text);
console.log(`Tokens used: ${usage.totalTokens}`);Streaming Text
流式文本传输
typescript
// app/api/chat/route.ts (Next.js App Router API Route)
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
// Allow streaming responses up to 30 seconds
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai('gpt-4o'),
system: 'You are a friendly customer support bot.',
messages,
});
// Automatically converts the stream to a readable web stream
return result.toDataStreamResponse();
}typescript
// app/api/chat/route.ts(Next.js App Router API路由)
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
// 允许流式响应最长30秒
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai('gpt-4o'),
system: 'You are a friendly customer support bot.',
messages,
});
// 自动将流转换为可读的Web流
return result.toDataStreamResponse();
}Structured Data (JSON) Generation
结构化数据(JSON)生成
typescript
import { generateObject } from 'ai';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';
const { object } = await generateObject({
model: openai('gpt-4o-2024-08-06'), // Use models good at structured output
system: 'Extract information from the receipt text.',
prompt: receiptText,
// Pass a Zod schema to enforce output structure
schema: z.object({
storeName: z.string(),
totalAmount: z.number(),
items: z.array(z.object({
name: z.string(),
price: z.number(),
})),
date: z.string().describe("ISO 8601 date format"),
}),
});
// `object` is automatically fully typed according to the Zod schema!
console.log(object.totalAmount); typescript
import { generateObject } from 'ai';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';
const { object } = await generateObject({
model: openai('gpt-4o-2024-08-06'), // 使用擅长结构化输出的模型
system: 'Extract information from the receipt text.',
prompt: receiptText,
// 传递Zod schema以强制输出结构
schema: z.object({
storeName: z.string(),
totalAmount: z.number(),
items: z.array(z.object({
name: z.string(),
price: z.number(),
})),
date: z.string().describe("ISO 8601 date format"),
}),
});
// `object`会根据Zod schema自动获得完整的类型!
console.log(object.totalAmount); Frontend UI Hooks
前端UI钩子
useChat
(Conversational UI)
useChatuseChat
(对话式UI)
useChattsx
// app/page.tsx (Next.js Client Component)
"use client";
import { useChat } from "ai/react";
export default function Chat() {
const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
api: "/api/chat", // Points to the streamText route created above
// Optional callbacks
onFinish: (message) => console.log("Done streaming:", message),
onError: (error) => console.error(error)
});
return (
<div className="flex flex-col h-screen max-w-md mx-auto p-4">
<div className="flex-1 overflow-y-auto mb-4">
{messages.map((m) => (
<div key={m.id} className={`mb-4 ${m.role === 'user' ? 'text-right' : 'text-left'}`}>
<span className={`p-2 rounded-lg inline-block ${m.role === 'user' ? 'bg-blue-500 text-white' : 'bg-gray-200'}`}>
{m.target || m.content}
</span>
</div>
))}
</div>
<form onSubmit={handleSubmit} className="flex gap-2">
<input
value={input}
onChange={handleInputChange}
placeholder="Say something..."
className="flex-1 p-2 border rounded"
disabled={isLoading}
/>
<button type="submit" disabled={isLoading} className="bg-black text-white p-2 rounded">
Send
</button>
</form>
</div>
);
}tsx
// app/page.tsx(Next.js客户端组件)
"use client";
import { useChat } from "ai/react";
export default function Chat() {
const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
api: "/api/chat", // 指向上面创建的streamText路由
// 可选回调函数
onFinish: (message) => console.log("Done streaming:", message),
onError: (error) => console.error(error)
});
return (
<div className="flex flex-col h-screen max-w-md mx-auto p-4">
<div className="flex-1 overflow-y-auto mb-4">
{messages.map((m) => (
<div key={m.id} className={`mb-4 ${m.role === 'user' ? 'text-right' : 'text-left'}`}>
<span className={`p-2 rounded-lg inline-block ${m.role === 'user' ? 'bg-blue-500 text-white' : 'bg-gray-200'}`}>
{m.target || m.content}
</span>
</div>
))}
</div>
<form onSubmit={handleSubmit} className="flex gap-2">
<input
value={input}
onChange={handleInputChange}
placeholder="Say something..."
className="flex-1 p-2 border rounded"
disabled={isLoading}
/>
<button type="submit" disabled={isLoading} className="bg-black text-white p-2 rounded">
Send
</button>
</form>
</div>
);
}Tool Calling (Function Calling)
工具调用(函数调用)
Tools allow the LLM to interact with your code, fetching external data or performing actions before responding to the user.
工具允许LLM与你的代码交互,获取外部数据或执行操作,然后再响应用户。
Server-Side Tool Definition
服务端工具定义
typescript
// app/api/chat/route.ts
import { streamText, tool } from 'ai';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai('gpt-4o'),
messages,
tools: {
getWeather: tool({
description: 'Get the current weather in a given location',
parameters: z.object({
location: z.string().describe('The city and state, e.g. San Francisco, CA'),
unit: z.enum(['celsius', 'fahrenheit']).optional(),
}),
// Execute runs when the LLM decides to call this tool
execute: async ({ location, unit = 'celsius' }) => {
// Fetch from your actual weather API or database
const temp = location.includes("San Francisco") ? 15 : 22;
return `The weather in ${location} is ${temp}° ${unit}.`;
},
}),
},
// Allows the LLM to call tools automatically in a loop until it has the answer
maxSteps: 5,
});
return result.toDataStreamResponse();
}typescript
// app/api/chat/route.ts
import { streamText, tool } from 'ai';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai('gpt-4o'),
messages,
tools: {
getWeather: tool({
description: 'Get the current weather in a given location',
parameters: z.object({
location: z.string().describe('The city and state, e.g. San Francisco, CA'),
unit: z.enum(['celsius', 'fahrenheit']).optional(),
}),
// 当LLM决定调用此工具时执行
execute: async ({ location, unit = 'celsius' }) => {
// 从实际天气API或数据库获取数据
const temp = location.includes("San Francisco") ? 15 : 22;
return `The weather in ${location} is ${temp}° ${unit}.`;
},
}),
},
// 允许LLM自动循环调用工具,直到获取到答案
maxSteps: 5,
});
return result.toDataStreamResponse();
}UI for Multi-Step Tool Calls
多步骤工具调用的UI
When using , the hook will display intermediate tool calls if you handle them in the UI.
maxStepsuseChattsx
// Inside the `useChat` messages.map loop
{m.role === 'assistant' && m.toolInvocations?.map((toolInvocation) => (
<div key={toolInvocation.toolCallId} className="text-sm text-gray-500">
{toolInvocation.state === 'result' ? (
<p>✅ Fetched weather for {toolInvocation.args.location}</p>
) : (
<p>⏳ Fetching weather for {toolInvocation.args.location}...</p>
)}
</div>
))}使用时,如果你在UI中处理,钩子会显示中间工具调用过程。
maxStepsuseChattsx
// 在`useChat`的messages.map循环内
{m.role === 'assistant' && m.toolInvocations?.map((toolInvocation) => (
<div key={toolInvocation.toolCallId} className="text-sm text-gray-500">
{toolInvocation.state === 'result' ? (
<p>✅ Fetched weather for {toolInvocation.args.location}</p>
) : (
<p>⏳ Fetching weather for {toolInvocation.args.location}...</p>
)}
</div>
))}Best Practices
最佳实践
- ✅ Do: Use or
openai('gpt-4o')format (from specific provider packages likeanthropic('claude-3-5-sonnet-20240620')) instead of the older edge runtime wrappers.@ai-sdk/openai - ✅ Do: Provide a strict Zod and a clear
schemaprompt when usingsystem.generateObject() - ✅ Do: Set (or higher if on Pro) in Next.js API routes that use
maxDuration = 30, as LLMs take time to stream responses and Vercel's default is 10-15s.streamText - ✅ Do: Use with comprehensive
tool()tags on Zod parameters, as the LLM relies entirely on those strings to understand when and how to call the tool.description - ✅ Do: Enable (or similar) when providing tools, otherwise the LLM won't be able to reply to the user after seeing the tool result!
maxSteps: 5 - ❌ Don't: Forget to return in Next.js App Router API routes when using
result.toDataStreamResponse(); standard JSON responses will break chunking.streamText - ❌ Don't: Blindly trust the output of without validation, even though Zod forces the shape — always handle failure states using
generateObject.try/catch
- ✅ 推荐: 使用或
openai('gpt-4o')格式(来自anthropic('claude-3-5-sonnet-20240620')等特定供应商包),而非旧版边缘运行时包装器。@ai-sdk/openai - ✅ 推荐: 使用时,提供严格的Zod
generateObject()和清晰的schema提示词。system - ✅ 推荐: 在使用的Next.js API路由中设置
streamText(专业版可设置更高),因为LLM流式响应需要时间,而Vercel的默认超时时间是10-15秒。maxDuration = 30 - ✅ 推荐: 使用时,为Zod参数添加全面的
tool()标签,因为LLM完全依赖这些字符串来理解何时以及如何调用工具。description - ✅ 推荐: 提供工具时启用(或类似值),否则LLM在看到工具结果后无法回复用户!
maxSteps: 5 - ❌ 避免: 在Next.js App Router API路由中使用时,忘记返回
streamText;标准JSON响应会破坏分块传输。result.toDataStreamResponse() - ❌ 避免: 即使Zod强制了输出格式,也不要盲目信任的输出——始终使用
generateObject处理失败状态。try/catch
Troubleshooting
故障排除
Problem: The streaming chat cuts off abruptly after 10-15 seconds.
Solution: The serverless function timed out. Add (or whatever your plan limit is) to the Next.js API route file.
export const maxDuration = 30;Problem: "Tool execution failed" or the LLM didn't return an answer after using a tool.
Solution: stops immediately after a tool call completes unless you provide . Set (or higher) to let the LLM see the tool result and construct a final text response.
streamTextmaxStepsmaxSteps: 2问题: 流式聊天在10-15秒后突然中断。
解决方案: 无服务器函数超时。在Next.js API路由文件中添加(或你的套餐允许的更高值)。
export const maxDuration = 30;问题: “工具执行失败”或LLM在使用工具后未返回答案。
解决方案: 除非你提供,否则在工具调用完成后会立即停止。设置(或更高),让LLM查看工具结果并构建最终文本响应。
maxStepsstreamTextmaxSteps: 2