Vercel AI SDK 聊天运行时

Vercel AI SDK 的 useChat hook 与 streamText 函数,实现 Next.js 全栈流式 AI 聊天的标准模式。

#type / concept #status / evergreen #tech / dev / frontend #tech / ai

[!info] related notes

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 Route
  • maxSteps 自动处理多轮工具调用循环,无需手动编排
  • zod schema 同时用于类型推断和运行时验证
  • useChat 迁移到 useChatRuntime 零成本——底层是同一个 hook

创建于 2026/6/25 更新于 2026/6/25