Vercel AI SDK 聊天运行时
Vercel AI SDK 的 useChat hook 与 streamText 函数,实现 Next.js 全栈流式 AI 聊天的标准模式。
#type / concept
#status / evergreen
#tech / dev / frontend
#tech / ai
[!info] related notes
- assistant-ui Runtime: Runtime 架构与后端集成
- SSE 消费: 前端 SSE 消费
- Function Calling: Function Calling 流式累积
Vercel AI SDK 聊天运行时
Vercel AI SDK 是 Next.js 生态中最流行的 AI 聊天全栈方案。
streamText处理后端流式生成,useChat处理前端状态管理。
核心组件
| 组件 | 职责 |
|---|---|
streamText | 后端:调用 LLM 并流式返回 |
useChat | 前端:管理消息状态、发送、流式接收 |
toDataStreamResponse | 后端:将流转为标准 Response |
tool | 定义工具 schema 和执行函数 |
后端 API Route
// app/api/chat/route.ts
import { openai } from "@ai-sdk/openai";
import { streamText, tool } from "ai";
import { z } from "zod";
export async function POST(req: Request) {
const { messages, system } = await req.json();
const result = streamText({
model: openai("gpt-4o"),
system: "你是一个专业的健康助手。",
messages,
maxSteps: 5, // 允许多轮工具调用
tools: {
getWeather: tool({
description: "获取指定城市的天气",
parameters: z.object({
city: z.string().describe("城市名称"),
}),
execute: async ({ city }) => {
const weather = await fetchWeather(city);
return weather;
},
}),
},
});
return result.toDataStreamResponse();
}
前端 useChat
// app/chat/page.tsx
"use client";
import { useChat } from "ai/react";
function ChatPage() {
const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
api: "/api/chat",
maxSteps: 5, // 与后端一致
});
return (
<div className="flex flex-col h-screen">
<div className="flex-1 overflow-y-auto p-4 space-y-4">
{messages.map((m) => (
<div key={m.id} className={m.role === "user" ? "text-right" : ""}>
<div className="inline-block rounded-xl p-3 max-w-[80%]">
{m.content}
</div>
{/* 工具调用展示 */}
{m.toolInvocations?.map((t) => (
<div key={t.toolCallId} className="text-sm text-muted-foreground">
🔧 {t.toolName}: {JSON.stringify(t.args)}
</div>
))}
</div>
))}
</div>
<form onSubmit={handleSubmit} className="p-4 border-t flex gap-2">
<input
value={input}
onChange={handleInputChange}
placeholder="输入消息..."
className="flex-1 rounded-xl border p-3"
/>
<button type="submit" disabled={isLoading}>发送</button>
</form>
</div>
);
}
与 assistant-ui 集成
// 使用 assistant-ui 的 useChatRuntime 桥接
import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
function AssistantUIChat() {
const runtime = useChatRuntime({
api: "/api/chat",
maxSteps: 5,
});
return (
<AssistantRuntimeProvider runtime={runtime}>
<Thread /> {/* assistant-ui 预构建组件 */}
</AssistantRuntimeProvider>
);
}
useChatRuntime 内部调用 useChat,将返回的状态映射到 assistant-ui 的 Runtime 接口。
多模型支持
// 不同模型只需换 provider
import { openai } from "@ai-sdk/openai";
import { anthropic } from "@ai-sdk/anthropic";
import { google } from "@ai-sdk/google";
const result = streamText({
model: openai("gpt-4o"), // OpenAI
// model: anthropic("claude-sonnet-4-20250514"), // Anthropic
// model: google("gemini-2.0-flash"), // Google
messages,
});
工具调用流式累积
// 流式接收工具调用时,参数是逐步累积的
// message.toolInvocations[].state 变化:
// "call" → 部分参数
// "result" → 完整结果
const result = streamText({
model: openai("gpt-4o"),
messages,
tools: {
extractInfo: tool({
parameters: z.object({
bodyPart: z.string(),
symptomType: z.string(),
}),
execute: async (args) => {
// args 在 state="result" 时才完整
return processSymptom(args);
},
}),
},
});
优雅之处:
streamText+toDataStreamResponse一行完成流式 API RoutemaxSteps自动处理多轮工具调用循环,无需手动编排zodschema 同时用于类型推断和运行时验证- 从
useChat迁移到useChatRuntime零成本——底层是同一个 hook