AI 工具 | | 约 33 分钟 | 12,823 字

Vercel AI SDK:全栈 AI 应用开发

用 Vercel AI SDK 构建流式 AI 应用,支持多模型切换

Vercel AI SDK 是什么

Vercel AI SDK 是一个用于构建 AI 应用的 TypeScript 工具包。它的核心价值在于:让前端开发者能轻松构建流式 AI 应用,不需要深入了解 LLM API 的底层细节。

和 LangChain、LlamaIndex 这些 Python 框架不同,Vercel AI SDK 是 TypeScript 优先的,天然适合 Next.js、React、Svelte 等前端框架。如果你是全栈 Web 开发者,这可能是你最顺手的 AI 开发工具。

核心特性

特性说明
流式响应原生支持 SSE 流式传输
多模型支持统一的 Provider 接口
React HooksuseChat、useCompletion 等
工具调用支持 Function Calling
结构化输出Zod Schema 验证
中间件请求/响应拦截
框架无关支持 Next.js、Nuxt、SvelteKit 等

安装与配置

安装

# 核心包
npm install ai

# 模型提供商(按需安装)
npm install @ai-sdk/openai
npm install @ai-sdk/anthropic
npm install @ai-sdk/google

配置环境变量

# .env.local
OPENAI_API_KEY=your-openai-key
ANTHROPIC_API_KEY=your-anthropic-key

Provider 系统

Vercel AI SDK 通过 Provider 系统统一了不同模型的调用接口。

创建模型实例

import { openai } from '@ai-sdk/openai';
import { anthropic } from '@ai-sdk/anthropic';
import { google } from '@ai-sdk/google';

// OpenAI
const gpt4o = openai('gpt-4o');
const gpt4oMini = openai('gpt-4o-mini');

// Anthropic
const claude = anthropic('claude-sonnet-4-20250514');

// Google
const gemini = google('gemini-1.5-pro');

切换模型

因为接口统一,切换模型只需要改一行代码:

import { generateText } from 'ai';

// 用 OpenAI
const result1 = await generateText({
  model: openai('gpt-4o-mini'),
  prompt: '什么是 TypeScript?'
});

// 切换到 Anthropic,其他代码不变
const result2 = await generateText({
  model: anthropic('claude-sonnet-4-20250514'),
  prompt: '什么是 TypeScript?'
});

核心 API

generateText — 生成文本

import { generateText } from 'ai';
import { openai } from '@ai-sdk/openai';

const { text, usage } = await generateText({
  model: openai('gpt-4o-mini'),
  system: '你是一个 TypeScript 专家,用中文回答。',
  prompt: '解释 TypeScript 的泛型',
});

console.log(text);
console.log(`Token 使用: ${usage.totalTokens}`);

streamText — 流式生成

import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';

const result = streamText({
  model: openai('gpt-4o-mini'),
  system: '你是一个技术博客作者。',
  prompt: '写一篇关于 WebAssembly 的介绍',
});

// 流式输出
for await (const chunk of result.textStream) {
  process.stdout.write(chunk);
}

generateObject — 结构化输出

import { generateObject } from 'ai';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';

const { object } = await generateObject({
  model: openai('gpt-4o-mini'),
  schema: z.object({
    name: z.string().describe('项目名称'),
    description: z.string().describe('项目描述'),
    techStack: z.array(z.string()).describe('技术栈'),
    difficulty: z.enum(['easy', 'medium', 'hard']).describe('难度'),
  }),
  prompt: '推荐一个适合初学者的全栈项目',
});

console.log(object);
// {
//   name: "Todo App",
//   description: "一个简单的待办事项应用...",
//   techStack: ["Next.js", "TypeScript", "Prisma", "SQLite"],
//   difficulty: "easy"
// }

React Hooks

这是 Vercel AI SDK 最强大的部分——为 React 提供的开箱即用的 Hooks。

useChat — 聊天界面

// app/api/chat/route.ts (Next.js API Route)
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';

export async function POST(req: Request) {
  const { messages } = await req.json();

  const result = streamText({
    model: openai('gpt-4o-mini'),
    system: '你是一个友好的助手,用中文回答。',
    messages,
  });

  return result.toDataStreamResponse();
}
// components/Chat.tsx
'use client';

import { useChat } from 'ai/react';

export function Chat() {
  const { messages, input, handleInputChange, handleSubmit, isLoading } =
    useChat();

  return (
    <div className="flex flex-col h-screen max-w-2xl mx-auto p-4">
      {/* 消息列表 */}
      <div className="flex-1 overflow-y-auto space-y-4">
        {messages.map((message) => (
          <div
            key={message.id}
            className={`p-3 rounded-lg ${
              message.role === 'user'
                ? 'bg-blue-100 ml-auto max-w-[80%]'
                : 'bg-gray-100 mr-auto max-w-[80%]'
            }`}
          >
            <p className="text-sm font-medium">
              {message.role === 'user' ? '你' : 'AI'}
            </p>
            <p>{message.content}</p>
          </div>
        ))}
      </div>

      {/* 输入框 */}
      <form onSubmit={handleSubmit} className="flex gap-2 mt-4">
        <input
          value={input}
          onChange={handleInputChange}
          placeholder="输入消息..."
          className="flex-1 p-2 border rounded-lg"
          disabled={isLoading}
        />
        <button
          type="submit"
          disabled={isLoading}
          className="px-4 py-2 bg-blue-500 text-white rounded-lg disabled:opacity-50"
        >
          {isLoading ? '思考中...' : '发送'}
        </button>
      </form>
    </div>
  );
}

useChat 的高级用法

const {
  messages,
  input,
  handleInputChange,
  handleSubmit,
  isLoading,
  error,
  reload,     // 重新生成最后一条回复
  stop,       // 停止生成
  append,     // 手动添加消息
  setMessages // 设置消息列表
} = useChat({
  api: '/api/chat',
  initialMessages: [],
  onFinish: (message) => {
    console.log('生成完成:', message);
  },
  onError: (error) => {
    console.error('出错了:', error);
  },
});

useCompletion — 文本补全

'use client';

import { useCompletion } from 'ai/react';

export function CodeGenerator() {
  const { completion, input, handleInputChange, handleSubmit, isLoading } =
    useCompletion({
      api: '/api/completion',
    });

  return (
    <div className="p-4">
      <form onSubmit={handleSubmit}>
        <textarea
          value={input}
          onChange={handleInputChange}
          placeholder="描述你想要的代码..."
          className="w-full p-2 border rounded"
          rows={3}
        />
        <button
          type="submit"
          disabled={isLoading}
          className="mt-2 px-4 py-2 bg-green-500 text-white rounded"
        >
          生成代码
        </button>
      </form>

      {completion && (
        <pre className="mt-4 p-4 bg-gray-900 text-green-400 rounded overflow-x-auto">
          <code>{completion}</code>
        </pre>
      )}
    </div>
  );
}

工具调用(Tool Calling)

让 AI 调用你定义的函数:

定义工具

// 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: '获取指定城市的天气信息',
        parameters: z.object({
          city: z.string().describe('城市名称'),
        }),
        execute: async ({ city }) => {
          // 调用天气 API
          const weather = await fetchWeather(city);
          return {
            city,
            temperature: weather.temp,
            condition: weather.condition,
          };
        },
      }),

      searchDocs: tool({
        description: '搜索项目文档',
        parameters: z.object({
          query: z.string().describe('搜索关键词'),
        }),
        execute: async ({ query }) => {
          const results = await searchDocuments(query);
          return results;
        },
      }),
    },
  });

  return result.toDataStreamResponse();
}

多步工具调用

AI 可以连续调用多个工具来完成复杂任务:

const result = streamText({
  model: openai('gpt-4o'),
  messages,
  tools: { getWeather, searchDocs, calculateRoute },
  maxSteps: 5,  // 最多执行 5 步
});

Next.js 集成

完整的 Chat 应用

项目结构:

app/
  api/
    chat/
      route.ts      # API 路由
  page.tsx           # 页面
  components/
    Chat.tsx         # 聊天组件
    MessageList.tsx  # 消息列表
    ChatInput.tsx    # 输入框

API 路由

// app/api/chat/route.ts
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';

export const runtime = 'edge'; // 使用 Edge Runtime 获得更好的流式性能

export async function POST(req: Request) {
  const { messages } = await req.json();

  const result = streamText({
    model: openai('gpt-4o-mini'),
    system: `你是一个编程助手。
    - 用中文回答
    - 代码示例使用 TypeScript
    - 回答要简洁实用`,
    messages,
    maxTokens: 2000,
    temperature: 0.7,
  });

  return result.toDataStreamResponse();
}

带 Markdown 渲染的聊天组件

// components/Chat.tsx
'use client';

import { useChat } from 'ai/react';
import ReactMarkdown from 'react-markdown';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism';

export function Chat() {
  const { messages, input, handleInputChange, handleSubmit, isLoading } =
    useChat();

  return (
    <div className="max-w-3xl mx-auto p-4">
      <div className="space-y-4 mb-4">
        {messages.map((m) => (
          <div key={m.id} className="flex gap-3">
            <div className="font-bold w-16 shrink-0">
              {m.role === 'user' ? '你' : 'AI'}
            </div>
            <div className="prose prose-sm max-w-none">
              <ReactMarkdown
                components={{
                  code({ className, children, ...props }) {
                    const match = /language-(\w+)/.exec(className || '');
                    return match ? (
                      <SyntaxHighlighter
                        style={oneDark}
                        language={match[1]}
                        PreTag="div"
                      >
                        {String(children).replace(/\n$/, '')}
                      </SyntaxHighlighter>
                    ) : (
                      <code className={className} {...props}>
                        {children}
                      </code>
                    );
                  },
                }}
              >
                {m.content}
              </ReactMarkdown>
            </div>
          </div>
        ))}
      </div>

      <form onSubmit={handleSubmit} className="flex gap-2">
        <input
          value={input}
          onChange={handleInputChange}
          placeholder="问我任何编程问题..."
          className="flex-1 p-3 border rounded-lg"
        />
        <button
          type="submit"
          disabled={isLoading}
          className="px-6 py-3 bg-black text-white rounded-lg"
        >
          发送
        </button>
      </form>
    </div>
  );
}

中间件

Vercel AI SDK 支持中间件来拦截和修改请求/响应:

import { streamText, experimental_wrapLanguageModel } from 'ai';
import { openai } from '@ai-sdk/openai';

// 创建带日志的模型
const modelWithLogging = experimental_wrapLanguageModel({
  model: openai('gpt-4o-mini'),
  middleware: {
    wrapGenerate: async ({ doGenerate, params }) => {
      console.log('请求参数:', JSON.stringify(params, null, 2));
      const result = await doGenerate();
      console.log('Token 使用:', result.usage);
      return result;
    },
  },
});

const result = await streamText({
  model: modelWithLogging,
  prompt: 'Hello!',
});

实用模式

1. 模型回退

import { generateText } from 'ai';
import { openai } from '@ai-sdk/openai';
import { anthropic } from '@ai-sdk/anthropic';

async function generateWithFallback(prompt: string) {
  try {
    return await generateText({
      model: openai('gpt-4o-mini'),
      prompt,
    });
  } catch (error) {
    console.warn('OpenAI 失败,切换到 Anthropic');
    return await generateText({
      model: anthropic('claude-sonnet-4-20250514'),
      prompt,
    });
  }
}

2. 速率限制

// 简单的速率限制
const rateLimiter = new Map<string, number[]>();

function checkRateLimit(userId: string, limit = 10, window = 60000) {
  const now = Date.now();
  const timestamps = rateLimiter.get(userId) || [];
  const recent = timestamps.filter(t => now - t < window);

  if (recent.length >= limit) {
    throw new Error('请求过于频繁,请稍后再试');
  }

  recent.push(now);
  rateLimiter.set(userId, recent);
}

3. 对话历史持久化

// 使用数据库存储对话历史
import { prisma } from '@/lib/prisma';

export async function POST(req: Request) {
  const { messages, conversationId } = await req.json();

  // 保存用户消息
  await prisma.message.create({
    data: {
      conversationId,
      role: 'user',
      content: messages[messages.length - 1].content,
    },
  });

  const result = streamText({
    model: openai('gpt-4o-mini'),
    messages,
    onFinish: async ({ text }) => {
      // 保存 AI 回复
      await prisma.message.create({
        data: {
          conversationId,
          role: 'assistant',
          content: text,
        },
      });
    },
  });

  return result.toDataStreamResponse();
}

部署

Vercel 部署

Vercel AI SDK 和 Vercel 平台天然集成:

# 安装 Vercel CLI
npm i -g vercel

# 部署
vercel

# 设置环境变量
vercel env add OPENAI_API_KEY

其他平台

Vercel AI SDK 不绑定 Vercel 平台,可以部署到任何支持 Node.js 的环境:

  • AWS Lambda
  • Google Cloud Functions
  • Cloudflare Workers(使用 Edge Runtime)
  • 自托管 Node.js 服务器

好的 AI 应用不只是调用 API,而是把 AI 能力无缝融入用户体验。Vercel AI SDK 让这件事变得简单。

评论

加载中...

相关文章

分享:

评论

加载中...