关于 vercel-ai-sdk
面向使用 Vercel AI SDK v5 的开发者,提供实现聊天、文本生成、工具调用及 MCP 集成的编码指南。重点说明 v5 API 模式、依赖管理,以及反复构建和测试验证。
主要功能
- 01聊天与流式响应实现
- 02工具调用与嵌入生成
- 03v4 到 v5 迁移指导
- 04依赖安装与包管理器检查
- 05构建、Lint 和测试排错
适用场景
- 01实现 useChat 聊天界面
- 02创建生成或流式响应 API
- 03为语义搜索生成嵌入
- 04将项目迁移至 AI SDK v5
MCP 相关
指南涵盖集成 MCP 服务端的实现。未说明该技能本身提供 MCP 服务。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ wsimmonds/ claude-nextjs-skills --skill vercel-ai-sdk安装后在 Claude Code 中输入/vercel-ai-sdk确认已加载。
详细介绍
Vercel AI SDK v5 实现指南
何时使用此技能
在以下情况下使用此技能:
- 使用
useChathook 实现 AI 聊天界面 - 创建用于生成或流式传输 AI 响应的 API 路由
- 添加工具调用 / 函数调用功能
- 为语义搜索或 RAG 生成文本嵌入
- 从 AI SDK v4 迁移到 v5
- 集成 Model Context Protocol (MCP) 服务
- 处理流式响应或消息持久化
结构化实现工作流
了解任务需求 - 确定所需的 AI 功能(聊天、生成、工具、嵌入) - 确定采用客户端(useChat)还是服务端(API 路由)实现 - 检查是否需要流式或非流式响应 - 确认模型提供方(OpenAI、Anthropic 等) 如有疑问,确认当前 API 模式 - 如果不确定 API 模式,请使用 WebFetch 检查 https://ai-sdk.dev/docs/ - 确认该提供方的模型指定格式 - 验证复杂功能的函数签名 使用正确的 v5 模式进行实现 - 使用基于字符串的模型指定方式('provider/model-id') - 对于聊天:使用 sendMessage(不要使用 append),并采用基于 parts 的消息 - 对于工具:必须从 'ai' 导入并使用 tool() 辅助函数,必须使用 inputSchema(不要使用 parameters),必须使用 zod - 对于流式传输:使用 toUIMessageStreamResponse() 或 toTextStreamResponse() - 对于嵌入:使用 provider.textEmbeddingModel() 确保 TypeScript 类型正确 - 检查是否从 'ai' 包正确导入 - 验证消息类型(useChat 使用 UIMessage) - 确保工具参数类型能够正确推断 - 为异步函数添加显式类型 使用正确的包管理器安装缺失的依赖项 - **关键:首先检测项目使用的包管理器** * 检查锁文件:pnpm-lock.yaml → 使用 pnpm,package-lock.json → 使用 npm,yarn.lock → 使用 yarn,bun.lockb → 使用 bun * 如果存在 pnpm-lock.yaml,必须使用 pnpm(不要使用 npm!) - 检查所有导入的包是否都已安装 - 如果构建失败并提示 "Module not found",从错误信息中确定包名 - 将该包添加到 package.json 的 dependencies 中 - 使用正确的包管理器安装: * 如果存在 pnpm-lock.yaml:`pnpm install [package]` 或 `pnpm add [package]` * 如果存在 package-lock.json:`npm install [package]` * 如果存在 yarn.lock:`yarn add [package]` * 如果存在 bun.lockb:`bun install [package]` 或 `bun add [package]` - 重新运行构建,以验证安装是否成功 **绝不要使用错误的包管理器!** - 如果项目使用 pnpm,却使用 npm,会在 pnpm-lock.yaml 旁创建 package-lock.json - 这会导致依赖版本不匹配并使构建失败 - 务必检查现有锁文件,并使用对应的包管理器 绝不要将 "Module not found" 错误视为环境问题
你必须使用正确的包管理器安装所需的包
常用所需包:
- ai(核心 AI SDK)
- @ai-sdk/openai(OpenAI 提供方)
- @ai-sdk/anthropic(Anthropic 提供方)
- @modelcontextprotocol/sdk(MCP 集成)
- zod(用于工具模式)
</critical>
运行构建,并修复所有错误,直到构建通过
- 运行:npm run build(或 bun run build)
- 如果构建失败,请仔细阅读完整的错误消息
- 常见修复方法:
* “Module not found” → 安装相应的包(返回步骤 5)
* 类型错误 → 修复 TypeScript 类型
* 配置错误 → 检查 next.config.js/ts
* 框架错误 → 调查错误原因,尝试不同的解决方法
- 应用修复,然后重新运行构建
- 重复上述步骤,直到构建通过
绝不要以“构建失败,但代码是正确的”为由停止
未经调试,绝不要归咎于“环境”或“框架错误”
持续迭代,直到构建通过
如果遇到框架错误,就调查原因、尝试变通方案并进行调试
在构建通过之前绝不要放弃
运行测试,并调试所有失败,直到测试通过
- 运行:npm run test(或 bun run test)
- 如果测试失败,请仔细阅读完整的错误消息
- 调试失败原因:
* 检查测试的预期结果与你提供的内容是否一致
* 查看测试断言
* 确认实现符合要求
* 检查是否存在类型不匹配或缺少属性
- 修复实现
- 重新运行测试
- 重复上述步骤,直到所有测试通过
绝不要在未调试的情况下接受测试失败
未经调查,绝不要将失败归咎于“测试框架错误”
每个测试失败都在告诉你一些信息——认真对待
持续调试,直到所有测试通过
测试失败不可接受——这意味着实现尚未完成
确认所有检查均已通过
- 运行:npm run build && npm run lint && npm run test
- 三项都必须通过:✅ 构建、✅ Lint、✅ 测试
- 如果任何一项失败,就返回并修复
- 所有检查都通过之前,不要认为任务已完成
只有满足以下条件,任务才算完成:
- ✅ 构建通过
- ✅ Lint 通过
- ✅ 测试通过
“代码是正确的”还不够
必须达到全部通过的状态
这就是成为自主 Agent 的意义
</critical>
⚠️ 自主 Agent 心态
你不只是在编写代码——你要自主完成任务。
这意味着:
- ✅ 编写正确的实现
- ✅ 安装所有必需的依赖
- ✅ 运行构建并修复所有错误
- ✅ 运行测试并调试所有失败
- ✅ 持续迭代,直到所有检查都通过
- ✅ 绝不找借口或放弃
应避免的常见失败模式
❌ 错误:“代码是正确的,但没有安装这个包——这是环境问题” ✅ 正确:“构建因缺少包而失败——现在用 npm install [package] 安装它”
❌ 错误:“测试通过了,但构建失败了——这不关我的事” ✅ 正确:“构建失败了——现在开始调试并修复错误”
❌ 错误:“框架有问题,修不了” ✅ 正确:“检测到框架错误——调查问题、尝试变通方案并持续调试,直到找到解决办法”
❌ 错误:“实现已完成”(但测试仍未通过) ✅ 正确:“测试失败了——持续调试和修复,直到全部通过”
依赖安装工作流
遇到“Module not found”错误时:
-
首先检测包管理器——检查锁文件:
ls -la | grep -E "lock" # Look for: pnpm-lock.yaml, package-lock.json, yarn.lock, bun.lockb -
根据导入语句识别包
Error: Cannot find module '@ai-sdk/openai' Import: import { openai } from '@ai-sdk/openai' Package needed: @ai-sdk/openai -
使用正确的包管理器安装
# If pnpm-lock.yaml exists (MOST COMMON for Next.js evals): pnpm install @ai-sdk/openai # or pnpm add @ai-sdk/openai # If package-lock.json exists: npm install @ai-sdk/openai # If yarn.lock exists: yarn add @ai-sdk/openai # If bun.lockb exists: bun install @ai-sdk/openai -
重新运行构建以验证
npm run build # or pnpm run build, yarn build, bun run build -
修复出现的任何新错误
⚠️ 严重警告: 使用错误的包管理器(例如,项目使用 pnpm,却使用 npm)会:
- 创建第二个相互冲突的锁文件
- 安装不同版本的依赖项
- 导致依赖项版本不匹配
- 引发诸如“Cannot read properties of null”之类难以排查的错误,从而导致构建失败
构建错误调试工作流
构建失败时:
- 阅读完整的错误消息——不要粗略浏览
- 找出根本原因:
- 找不到模块 → 安装包
- 类型错误 → 修复类型
- 配置错误 → 检查配置文件
- Next.js 错误 → 查找资料并尝试不同方法
- 应用修复
- 重新运行构建
- 重复以上步骤,直到构建通过
测试失败调试工作流
测试失败时:
- 阅读完整的测试错误——弄清楚预期结果
- 比较预期行为与实际行为
- 根据测试断言检查实现
- 在代码中修复问题
- 重新运行测试
- 重复以上步骤,直到所有测试通过
成功标准
只有满足以下条件,任务才算完成:
- ✅ 构建通过(
npm run build成功) - ✅ Lint 通过(
npm run lint成功) - ✅ 测试通过(
npm run test成功)
绝不要停留在“代码正确”——务必达到全部通过状态!
⚠️ 严重提示:工具调用 API——必须使用 tool() 辅助函数
实现工具调用时,必须使用 'ai' 包中的 tool() 辅助函数。
❌ 错误——普通对象(会导致构建错误)
// DO NOT DO THIS - This pattern is INCORRECT
import { z } from 'zod';
tools: {
myTool: {
description: 'My tool',
parameters: z.object({...}), // ❌ WRONG - "parameters" doesn't exist in v5
execute: async ({...}) => {...},
}
}
这会导致以下错误: Type '{ description: string; parameters: ... }' is not assignable to type '{ inputSchema: FlexibleSchema<any>; ... }'
✅ 正确——使用 tool() 辅助函数(必需)
// ALWAYS DO THIS - This is the ONLY correct pattern
import { tool } from 'ai'; // ⚠️ MUST import tool
import { z } from 'zod';
tools: {
myTool: tool({ // ⚠️ MUST wrap with tool()
description: 'My tool',
inputSchema: z.object({...}), // ⚠️ MUST use "inputSchema" (not "parameters")
execute: async ({...}) => {...},
}),
}
工具调用检查清单
实现任何工具前,请确认:
- 从 'ai' 包导入了
tool:import { tool } from 'ai'; - 使用
tool({ ... })包装工具定义 - 使用
inputSchema属性(不是parameters) - 使用 zod schema:
z.object({ ... }) - 使用异步回调定义
execute函数 - 为工具添加
description字符串
⚠️ 严重提示:v4 到 v5 的常见破坏性变更
1. useChat Hook 变更
❌ 错误(v4 模式):
const { messages, input, setInput, append } = useChat();
// Sending message
append({ content: text, role: 'user' });
✅ 正确(v5 模式):
const { messages, sendMessage } = useChat();
const [input, setInput] = useState('');
// Sending message
sendMessage({ text: input });
2. 消息结构
❌ 错误(v4 简单内容):
<div>{message.content}</div>
✅ 正确(基于 v5 parts 的结构):
<div>
{message.parts.map((part, index) =>
part.type === 'text' ? <span key={index}>{part.text}</span> : null
)}
</div>
3. 模型指定方式
✅ 首选:基于字符串(v5 推荐):
import { generateText } from 'ai';
const result = await generateText({
model: 'openai/gpt-4o', // String format
prompt: 'Hello',
});
✅ 也可使用:基于函数(兼容旧版):
import { openai } from '@ai-sdk/openai';
import { generateText } from 'ai';
const result = await generateText({
model: openai('gpt-4o'), // Function format
prompt: 'Hello',
});
核心 API 参考
1. generateText——非流式文本生成
**用途:**为非交互式场景生成文本(电子邮件草稿、摘要、带工具的 Agent)。
签名:
import { generateText } from 'ai';
const result = await generateText({
model: 'openai/gpt-4o', // String format: 'provider/model-id'
prompt: 'Your prompt here', // User input
system: 'Optional system message', // Optional system instructions
tools?: { ... }, // Optional tool calling
maxSteps?: 5, // For multi-step tool calling
});
返回值:
{
text: string; // Generated text output
toolCalls: ToolCall[]; // Tool invocations made
finishReason: string; // Why generation stopped
usage: TokenUsage; // Token consumption
response: RawResponse; // Raw provider response
warnings: Warning[]; // Provider-specific alerts
}
示例:
// app/api/generate/route.ts
import { generateText } from 'ai';
export async function GET() {
const result = await generateText({
model: 'anthropic/claude-4-sonnet',
prompt: 'Why is the sky blue?',
});
return Response.json({ text: result.text });
}
2. streamText——流式文本生成
**用途:**为交互式聊天应用流式传输响应。
签名:
import { streamText } from 'ai';
const result = streamText({
model: 'openai/gpt-4o',
prompt: 'Your prompt here',
system: 'Optional system message',
messages?: ModelMessage[], // For chat history
tools?: { ... },
onFinish?: async (result) => { ... },
onError?: async (error) => { ... },
});
返回方法:
// For chat applications with useChat hook
result.toUIMessageStreamResponse();
// For simple text streaming
result.toTextStreamResponse();
示例——聊天 API 路由:
// app/api/chat/route.ts
import { streamText, convertToModelMessages } from 'ai';
import type { UIMessage } from 'ai';
export async function POST(req: Request) {
const { messages }: { messages: UIMessage[] } = await req.json();
const result = streamText({
model: 'openai/gpt-4o',
system: 'You are a helpful assistant.',
messages: convertToModelMessages(messages),
});
return result.toUIMessageStreamResponse();
}
3. useChat Hook——客户端聊天界面
**用途:**构建支持流式传输的交互式聊天 UI。
签名:
import { useChat } from 'ai/react';
const {
messages, // Array of UIMessage with parts-based structure
sendMessage, // Function to send messages (replaces append)
status, // 'submitted' | 'streaming' | 'ready' | 'error'
stop, // Abort current streaming
regenerate, // Reprocess last message
setMessages, // Manually modify history
error, // Error object if request fails
reload, // Retry after error
} = useChat({
api: '/api/chat', // API endpoint
onFinish?: (message) => { ... },
onError?: (error) => { ... },
});
完整示例:
'use client';
import { useChat } from 'ai/react';
import { useState } from 'react';
export default function ChatPage() {
const { messages, sendMessage, status } = useChat();
const [input, setInput] = useState('');
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!input.trim()) return;
sendMessage({ text: input });
setInput('');
};
return (
<div>
<div>
{messages.map((message) => (
<div key={message.id}>
<strong>{message.role}:</strong>
{message.parts.map((part, index) =>
part.type === 'text' ? (
<span key={index}>{part.text}</span>
) : null
)}
</div>
))}
</div>
<form onSubmit={handleSubmit}>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Type a message..."
disabled={status === 'streaming'}
/>
<button type="submit" disabled={status === 'streaming'}>
Send
</button>
</form>
</div>
);
}
4. 工具调用 / 函数调用
**用途:**使 AI 模型能够使用结构化参数调用函数。
定义工具:
import { tool } from 'ai';
import { z } from 'zod';
const weatherTool = tool({
description: 'Get the weather in a location',
inputSchema: z.object({
location: z.string().describe('The location to get the weather for'),
unit: z.enum(['C', 'F']).describe('Temperature unit'),
}),
execute: async ({ location, unit }) => {
// Fetch or mock weather data
return {
location,
temperature: 24,
unit,
condition: 'Sunny',
};
},
});
将工具与 generateText/streamText 搭配使用:
// app/api/chat/route.ts
import { streamText, convertToModelMessages, tool } from 'ai';
import { z } from 'zod';
import type { UIMessage } from 'ai';
export async function POST(req: Request) {
const { messages }: { messages: UIMessage[] } = await req.json();
const result = streamText({
model: 'openai/gpt-4o',
messages: convertToModelMessages(messages),
tools: {
getWeather: tool({
description: 'Get the weather for a location',
inputSchema: z.object({
city: z.string().describe('The city to get the weather for'),
unit: z.enum(['C', 'F']).describe('The unit to display the temperature in'),
}),
execute: async ({ city, unit }) => {
// Mock response
return `It is currently 24°${unit} and Sunny in ${city}!`;
},
}),
},
});
return result.toUIMessageStreamResponse();
}
多步工具调用:
const result = await generateText({
model: 'openai/gpt-4o',
tools: {
weather: weatherTool,
search: searchTool,
},
prompt: 'What is the weather in San Francisco and find hotels there?',
maxSteps: 5, // Allow up to 5 tool call steps
});
5. 文本嵌入
**用途:**将文本转换为数值向量,用于语义搜索、RAG 或相似度计算。
签名:
import { embed } from 'ai';
import { openai } from '@ai-sdk/openai';
const result = await embed({
model: openai.textEmbeddingModel('text-embedding-3-small'),
value: 'Text to embed',
});
返回值:
{
embedding: number[]; // Numerical array representing the text
usage: { tokens: number }; // Token consumption
response: RawResponse; // Raw provider response
}
示例——嵌入 API 路由:
// app/api/embed/route.ts
import { embed } from 'ai';
import { openai } from '@ai-sdk/openai';
export async function GET() {
const { embedding, usage } = await embed({
model: openai.textEmbeddingModel('text-embedding-3-small'),
value: 'sunny day at the beach',
});
return Response.json({ embedding, usage });
}
批量嵌入:
import { embedMany } from 'ai';
const { embeddings, usage } = await embedMany({
model: openai.textEmbeddingModel('text-embedding-3-small'),
values: [
'sunny day at the beach',
'rainy afternoon in the city',
'snowy mountain landscape',
],
});
6. 消息工具
convertToModelMessages:
将 useChat 中的 UI 消息转换为 AI 函数使用的 ModelMessage 对象。
import { convertToModelMessages } from 'ai';
import type { UIMessage } from 'ai';
export async function POST(req: Request) {
const { messages }: { messages: UIMessage[] } = await req.json();
const result = streamText({
model: 'openai/gpt-4o',
messages: convertToModelMessages(messages), // Convert for model
});
return result.toUIMessageStreamResponse();
}
7. 模型上下文协议(MCP)集成
用途: 连接外部 MCP 服务,以动态访问工具。
示例:
// app/api/chat/route.ts
import { experimental_createMCPClient, streamText } from 'ai';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
export async function POST(req: Request) {
const { prompt }: { prompt: string } = await req.json();
try {
// Connect to MCP server
const httpTransport = new StreamableHTTPClientTransport(
new URL('http://localhost:3000/mcp')
);
const httpClient = await experimental_createMCPClient({
transport: httpTransport,
});
// Fetch tools from MCP server
const tools = await httpClient.tools();
const response = streamText({
model: 'openai/gpt-4o',
tools,
prompt,
onFinish: async () => {
await httpClient.close(); // Clean up
},
onError: async () => {
await httpClient.close(); // Clean up on error
},
});
return response.toTextStreamResponse();
} catch (error) {
return new Response('Internal Server Error', { status: 500 });
}
}
要点:
- 使用
experimental_createMCPClient(注意:这是实验性 API) - 始终在
onFinish和onError中关闭客户端 - 使用
httpClient.tools()动态获取工具 - 需要
@modelcontextprotocol/sdk包
模型指定模式
基于字符串(v5 推荐)
// Format: 'provider/model-id'
model: 'openai/gpt-4o'
model: 'anthropic/claude-4-sonnet'
model: 'google/gemini-2.0-flash'
基于函数(兼容旧版)
import { openai } from '@ai-sdk/openai';
import { anthropic } from '@ai-sdk/anthropic';
model: openai('gpt-4o')
model: anthropic('claude-4-sonnet')
嵌入模型
import { openai } from '@ai-sdk/openai';
// Text embeddings use a different method
openai.textEmbeddingModel('text-embedding-3-small')
openai.textEmbeddingModel('text-embedding-3-large')
TypeScript 最佳实践
类型导入
import type {
UIMessage, // Message type from useChat
ModelMessage, // Message type for model functions
ToolCall, // Tool call information
TokenUsage, // Token consumption data
} from 'ai';
强类型工具
import { tool } from 'ai';
import { z } from 'zod';
// Tool helper infers execute parameter types
const myTool = tool({
description: 'My tool',
inputSchema: z.object({
param1: z.string(),
param2: z.number(),
}),
execute: async ({ param1, param2 }) => {
// param1 is inferred as string
// param2 is inferred as number
return { result: 'success' };
},
});
API 路由类型
// app/api/chat/route.ts
import type { UIMessage } from 'ai';
export async function POST(req: Request): Promise<Response> {
const { messages }: { messages: UIMessage[] } = await req.json();
// ... implementation
}
常见模式
模式 1:简单聊天应用
客户端(app/page.tsx):
'use client';
import { useChat } from 'ai/react';
import { useState } from 'react';
export default function Chat() {
const { messages, sendMessage, status } = useChat();
const [input, setInput] = useState('');
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 === 'streaming'}>Send</button>
</form>
</div>
);
}
服务端(app/api/chat/route.ts):
import { streamText, convertToModelMessages } from 'ai';
import type { UIMessage } from 'ai';
export async function POST(req: Request) {
const { messages }: { messages: UIMessage[] } = await req.json();
const result = streamText({
model: 'openai/gpt-4o',
system: 'You are a helpful assistant.',
messages: convertToModelMessages(messages),
});
return result.toUIMessageStreamResponse();
}
模式 2:使用工具的聊天
带工具调用的服务端:
import { streamText, convertToModelMessages, tool } from 'ai';
import { z } from 'zod';
import type { UIMessage } from 'ai';
export async function POST(req: Request) {
const { messages }: { messages: UIMessage[] } = await req.json();
const result = streamText({
model: 'openai/gpt-4o',
messages: convertToModelMessages(messages),
tools: {
getWeather: tool({
description: 'Get weather for a city',
inputSchema: z.object({
city: z.string(),
}),
execute: async ({ city }) => {
// API call or mock data
return { city, temp: 72, condition: 'Sunny' };
},
}),
searchWeb: tool({
description: 'Search the web',
inputSchema: z.object({
query: z.string(),
}),
execute: async ({ query }) => {
// Search implementation
return { results: ['...'] };
},
}),
},
});
return result.toUIMessageStreamResponse();
}
模式 3:非交互式生成
// app/api/summarize/route.ts
import { generateText } from 'ai';
export async function POST(req: Request) {
const { text } = await req.json();
const result = await generateText({
model: 'anthropic/claude-4-sonnet',
system: 'You are a summarization expert.',
prompt: `Summarize this text:\n\n${text}`,
});
return Response.json({ summary: result.text });
}
模式 4:使用嵌入进行语义搜索
// app/api/search/route.ts
import { embed } from 'ai';
import { openai } from '@ai-sdk/openai';
export async function POST(req: Request) {
const { query } = await req.json();
// Generate embedding for search query
const { embedding } = await embed({
model: openai.textEmbeddingModel('text-embedding-3-small'),
value: query,
});
// Use embedding for similarity search in vector database
// const results = await vectorDB.search(embedding);
return Response.json({ embedding, results: [] });
}
常见问题与解决方案
问题 1:工具未使用 tool() 辅助函数 — ⚠️ 严重
这是最常见也最严重的错误。请务必使用 tool() 辅助函数!
// ❌ WRONG - Plain object (WILL CAUSE BUILD FAILURE)
import { z } from 'zod';
tools: {
myTool: {
description: 'My tool',
parameters: z.object({ // ❌ Wrong property name
city: z.string(),
}),
execute: async ({ city }) => { ... },
},
}
// Build error: Type '{ description: string; parameters: ... }' is not assignable
// ✅ CORRECT - Use tool() helper (REQUIRED)
import { tool } from 'ai'; // ⚠️ MUST import tool
import { z } from 'zod';
tools: {
myTool: tool({ // ⚠️ MUST use tool() wrapper
description: 'My tool',
inputSchema: z.object({ // ⚠️ Use inputSchema (not parameters)
city: z.string(),
}),
execute: async ({ city }) => { ... },
}),
}
问题 2:在 v5 中使用 v4 的 useChat API
// ❌ WRONG - v4 pattern
const { input, setInput, append } = useChat();
append({ content: 'Hello', role: 'user' });
// ✅ CORRECT - v5 pattern
const { sendMessage } = useChat();
const [input, setInput] = useState('');
sendMessage({ text: 'Hello' });
问题 3:访问 message.content 而不是 message.parts
// ❌ WRONG - v4 pattern
<div>{message.content}</div>
// ✅ CORRECT - v5 parts-based
<div>
{message.parts.map((part, i) =>
part.type === 'text' ? <span key={i}>{part.text}</span> : null
)}
</div>
问题 4:未为模型转换 UIMessages
// ❌ WRONG - passing UIMessages directly
const result = streamText({
model: 'openai/gpt-4o',
messages: messages, // UIMessage[] - type error
});
// ✅ CORRECT - convert to ModelMessage[]
const result = streamText({
model: 'openai/gpt-4o',
messages: convertToModelMessages(messages),
});
问题 5:忘记清理 MCP 客户端
// ❌ WRONG - no cleanup
const httpClient = await experimental_createMCPClient({
transport: httpTransport,
});
const tools = await httpClient.tools();
const response = streamText({ model, tools, prompt });
return response.toTextStreamResponse();
// ✅ CORRECT - cleanup in callbacks
const response = streamText({
model,
tools,
prompt,
onFinish: async () => {
await httpClient.close();
},
onError: async () => {
await httpClient.close();
},
});
问题 6:使用了错误的响应方法
// ❌ WRONG - using text stream for useChat
return result.toTextStreamResponse(); // Won't work with useChat hook
// ✅ CORRECT - use UI message stream for useChat
return result.toUIMessageStreamResponse();
// ✅ ALSO CORRECT - text stream for non-chat scenarios
// For simple text streaming (not using useChat hook)
return result.toTextStreamResponse();
问题 7:使用了错误的嵌入模型方法
// ❌ WRONG - using regular model method
const { embedding } = await embed({
model: openai('text-embedding-3-small'), // Wrong method
value: 'text',
});
// ✅ CORRECT - use textEmbeddingModel
const { embedding } = await embed({
model: openai.textEmbeddingModel('text-embedding-3-small'),
value: 'text',
});
迁移检查清单(v4 → v5)
从 v4 迁移到 v5 时,请更新以下内容:
- 在 useChat 中将
append替换为sendMessage - 从 useChat 的解构赋值中移除
input、setInput、handleInputChange - 为 input 添加本地状态管理:
const [input, setInput] = useState('') - 将消息渲染从
message.content更新为message.parts.map(...) - 更新 sendMessage 调用,使用
{ text: input }结构 - 确认 API 路由中使用了
convertToModelMessages - 确认使用了
toUIMessageStreamResponse()(而非 v4 流式方法) - 更新工具定义,使用带有
inputSchema的tool()辅助函数 - 考虑使用基于字符串的模型指定方式(
'provider/model-id') - 更新 TypeScript 类型(
UIMessage、ModelMessage)
决策指南
实现 AI SDK 功能时,请思考:
-
这是客户端还是服务端功能?
- 客户端:使用
useChathook - 服务端:使用
generateText或streamText
- 客户端:使用
-
需要流式还是非流式?
- 流式聊天:
streamText+toUIMessageStreamResponse() - 非流式:
generateText - 简单文本流:
streamText+toTextStreamResponse()
- 流式聊天:
-
需要调用工具吗?
- 需要:使用
tool()辅助函数和inputSchema(zod)定义工具 - 将工具对象传给
generateText或streamText
- 需要:使用
-
使用的消息格式正确吗?
- 客户端(useChat):返回带有
parts属性的UIMessage[] - 服务端:使用
convertToModelMessages()转换为ModelMessage[] - 使用
message.parts.map(...)渲染消息
- 客户端(useChat):返回带有
-
模型指定方式正确吗?
- 优先使用字符串格式:
'openai/gpt-4o' - 函数格式也可用:
openai('gpt-4o') - 嵌入:
openai.textEmbeddingModel('text-embedding-3-small')
- 优先使用字符串格式:
-
需要嵌入吗?
- 单个值使用
embed - 批量值使用
embedMany - 使用
textEmbeddingModel()方法
- 单个值使用
快速参考
| 任务 | 函数 | 关键参数 |
|---|---|---|
| 生成文本 | generateText() | model、prompt、system、tools |
| 流式生成文本 | streamText() | model、messages、tools、onFinish |
| 聊天 UI | useChat() | api、onFinish、onError |
| 工具调用 | tool() | description、inputSchema、execute |
| 文本嵌入 | embed() | model、value |
| 批量嵌入 | embedMany() | model、values |
| 消息转换 | convertToModelMessages() | messages(UIMessage[]) |
| MCP 集成 | experimental_createMCPClient() | transport |
更多资源
如有疑问,请查阅官方文档:
- 主要文档:https://ai-sdk.dev/docs
- API 参考:https://ai-sdk.dev/docs/reference
- 示例:https://ai-sdk.dev/examples
请记住: AI SDK v5 使用基于字符串的模型指定方式和基于 parts 的消息,使用 sendMessage 替代 append,并要求在 API 路由中使用 convertToModelMessages。