🚀 Vercel AI SDK 使用指南:Nuxt 3 + Element Plus 实现第一个AI 聊天应用

前言

Vercel AI SDK 是目前前端圈最火的 AI 开发库之一,它提供了一套统一的接口来对接各种大模型(OpenAI, Anthropic, Mistral 等)。但是,官方文档主要以 OpenAI 或 Vercel AI Gateway 为例,对于国内开发者来说,网络连接和支付往往是最大的痛点。

今天这篇教程,我们将使用 Nuxt 3 结合 Vercel AI SDK ,并接入国内强大的 DeepSeek(深度求索) API,手把手带你开发一个丝滑的 AI 聊天应用。

为什么选择这套技术栈?

  • Nuxt 3: Vue 3 的最佳全栈框架,开发体验极佳。
  • Vercel AI SDK (Core + Vue): 处理流式传输(Streaming)和状态管理的神器,省去自己写 WebSocket 或 SSE 的麻烦。
  • DeepSeek: 国内顶尖大模型,API 完全兼容 OpenAI 格式,价格亲民且无需复杂的网络配置。

准备工作

  1. Node.js 环境: 建议 v18 或更高版本。
  2. DeepSeek API Key : 去 DeepSeek 开放平台 申请一个 API Key。(你也可以用 Moonshot/Kimi、阿里通义千问等支持 OpenAI 兼容协议的国内模型,配置方法类似)。

第一步:初始化 Nuxt 项目

打开终端,使用 nuxi 创建一个新的 Nuxt 项目。

bash 复制代码
npx nuxi@latest init my-ai-app
cd my-ai-app

安装依赖(建议使用 pnpm 或 npm):

bash 复制代码
npm install

第二步:安装 AI SDK 和相关依赖

我们需要安装 ai 核心库、@ai-sdk/core 核心模块、Vue 适配库以及 OpenAI 适配器。同时安装 zod 用于定义数据结构。

bash 复制代码
npm install ai @ai-sdk/core @ai-sdk/vue @ai-sdk/openai zod

注意 :这里我们虽然用的是 DeepSeek,但因为通过 OpenAI 协议调用,所以安装 @ai-sdk/openai 是正确的做法。


第三步:配置环境变量

在项目根目录下创建一个 .env 文件,填入你的 DeepSeek API Key。

env 复制代码
# .env 文件
NUXT_DEEPSEEK_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

接下来,为了让 Nuxt 能读取到这个变量,我们需要修改 nuxt.config.ts

typescript 复制代码
// nuxt.config.ts
export default defineNuxtConfig({
  compatibilityDate: '2024-11-01',
  devtools: { enabled: true },
  
  // 将环境变量暴露给服务端运行时
  runtimeConfig: {
    deepseekApiKey: process.env.NUXT_DEEPSEEK_API_KEY,
  },
})

第四步:创建后端 API 路由 (核心)

这是最关键的一步。我们需要在 Nuxt 的服务端创建一个 API 接口,用来处理前端发来的消息,并调用 DeepSeek 模型,最后将结果以**流(Stream)**的形式返回给前端。

创建文件 server/api/chat.ts

typescript 复制代码
// server/api/chat.ts
import { createOpenAI } from '@ai-sdk/openai';
import { streamText } from 'ai';

export default defineLazyEventHandler(async () => {
  const config = useRuntimeConfig();
  const apiKey = config.deepseekApiKey;

  if (!apiKey) throw new Error('未找到 DeepSeek API Key');

  // 1. 初始化 OpenAI 客户端,但在 baseURL 中指向 DeepSeek 的地址
  const deepseek = createOpenAI({
    apiKey: apiKey,
    baseURL: 'https://api.deepseek.com', // DeepSeek 的官方 API 地址
  });

  return defineEventHandler(async (event) => {
    // 2. 解析前端传来的请求体
    const { messages } = await readBody(event);

    // 3. 调用模型
    const result = streamText({
      // 使用 deepseek-chat 模型
      model: deepseek('deepseek-chat'),
      // 直接使用 messages(AI SDK v4+ 已内置转换)
      messages,
      // 可选:设置系统提示词
      system: '你是一个乐于助人的中文 AI 助手。',
    });

    // 4. 将流式响应返回给前端
    return result.toDataStreamResponse();
  });
});

代码解析:

  • createOpenAI: 我们利用这个工厂函数创建了一个自定义的 provider,将 baseURL 指向 DeepSeek,从而完美复用 OpenAI 的 SDK 逻辑。
  • streamText: 这是 AI SDK 的核心函数,负责向模型发起流式请求。
  • toDataStreamResponse(): 将 AI 的回复通过 HTTP Response Stream 实时推流给前端,实现"打字机"效果。

第五步:构建前端 UI

现在回到前端,使用 @ai-sdk/vue 提供的 useChat 钩子,它帮我们封装了发送消息、加载状态、自动追加消息列表等所有逻辑。

修改 app.vue (或者 pages/index.vue):

vue 复制代码
<script setup lang="ts">
import { useChat } from '@ai-sdk/vue';

// 使用 useChat 钩子
// 它会自动调用 /api/chat 接口
const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat();
</script>

<template>
  <div class="container">
    <header>
      <h1>DeepSeek AI 助手</h1>
    </header>

    <div class="chat-box">
      <div v-for="m in messages" :key="m.id" class="message" :class="m.role">
        <div class="role-label">{{ m.role === 'user' ? '我' : 'DeepSeek' }}:</div>
        <div class="content">{{ m.content }}</div>
      </div>
      
      <div v-if="isLoading && messages[messages.length - 1]?.role === 'user'" class="loading">
        思考中...
      </div>
    </div>

    <form @submit="handleSubmit" class="input-area">
      <input
        class="input-box"
        :value="input"
        placeholder="输入你想问的问题..."
        @change="handleInputChange"
      />
      <button type="submit" :disabled="isLoading">发送</button>
    </form>
  </div>
</template>

<style scoped>
/* 简单的样式美化 */
.container {
  max-width: 800px;
  margin: 0 auto;
  padding: 20px;
  font-family: sans-serif;
  height: 100vh;
  display: flex;
  flex-direction: column;
}

header {
  text-align: center;
  margin-bottom: 20px;
  border-bottom: 1px solid #eee;
}

.chat-box {
  flex: 1;
  overflow-y: auto;
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 8px;
  margin-bottom: 20px;
  background-color: #f9f9f9;
}

.message {
  margin-bottom: 15px;
  padding: 10px;
  border-radius: 6px;
}

.message.user {
  background-color: #e3f2fd;
  align-self: flex-end;
}

.message.assistant {
  background-color: #fff;
  border: 1px solid #eee;
}

.role-label {
  font-weight: bold;
  font-size: 0.8rem;
  margin-bottom: 4px;
  color: #666;
}

.content {
  white-space: pre-wrap; /* 保留换行格式 */
  line-height: 1.6;
}

.input-area {
  display: flex;
  gap: 10px;
}

.input-box {
  flex: 1;
  padding: 12px;
  border: 1px solid #ccc;
  border-radius: 4px;
  font-size: 16px;
}

button {
  padding: 0 20px;
  background-color: #1976d2;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 16px;
}

button:disabled {
  background-color: #ccc;
  cursor: not-allowed;
}

.loading {
  font-size: 0.8rem;
  color: #999;
  margin-left: 10px;
}
</style>

第六步:运行测试

一切就绪!运行项目:

bash 复制代码
npm run dev

打开浏览器访问 http://localhost:3000

  1. 在输入框输入"你好,请介绍一下你自己"。
  2. 点击发送。
  3. 你应该能看到 DeepSeek 几乎瞬间开始逐字输出回复,体验非常流畅。

进阶:如何集成组件库(如 Element Plus)

如果要在实际项目中使用,我们通常会配合国内常用的组件库 Element Plus

  1. 安装 Element Plus: npm install element-plus (并按 Nuxt 官方文档配置)。
  2. 替换 app.vue 中的原生标签:
vue 复制代码
<script setup lang="ts">
import { useChat } from '@ai-sdk/vue';

// 使用 useChat 钩子,并指定 API 端点
const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
  api: '/api/chat',
});
</script>

<template>
  <div class="chat-container">
    <el-scrollbar height="600px">
      <div v-for="m in messages" :key="m.id">
        <el-card shadow="hover" :style="{ marginBottom: '10px', marginLeft: m.role === 'user' ? '50px' : '0', marginRight: m.role === 'user' ? '0' : '50px' }">
          <template #header>
             <el-tag :type="m.role === 'user' ? 'primary' : 'success'">{{ m.role === 'user' ? 'User' : 'AI' }}</el-tag>
          </template>
          <div style="white-space: pre-wrap;">{{ m.content }}</div>
        </el-card>
      </div>
    </el-scrollbar>
    
    <div style="margin-top: 20px; display: flex;">
      <!-- 注意:input 是 Ref,需要用 :modelValue 而不是 v-model -->
      <el-input 
        :modelValue="input" 
        @update:modelValue="input = $event"
        placeholder="请输入内容" 
        @keydown.enter="handleSubmit" 
      />
      <el-button type="primary" @click="handleSubmit" :loading="isLoading" style="margin-left: 10px;">发送</el-button>
    </div>
  </div>
</template>

注意:useChat 返回的 input 是一个 Ref 响应式对象,在 Element Plus 中需要使用 :modelValue + @update:modelValue 的方式来实现双向绑定,或者直接绑定事件。


总结

通过这篇教程,我们成功实现了:

  1. Nuxt 3 全栈框架搭建。
  2. Vercel AI SDK 接入标准 OpenAI 协议。
  3. DeepSeek 国内 API 的无缝集成。

快去试试吧!如果觉得有用,记得点赞收藏哦。

相关推荐
mCell7 小时前
为什么 Memo Code 先做 CLI:以及终端输入框到底有多难搞
前端·设计模式·agent
deephub8 小时前
Agent Lightning:微软开源的框架无关 Agent 训练方案,LangChain/AutoGen 都能用
人工智能·microsoft·langchain·大语言模型·agent·强化学习
萧曵 丶8 小时前
Vue 中父子组件之间最常用的业务交互场景
javascript·vue.js·交互
冬奇Lab9 小时前
一天一个开源项目(第20篇):NanoBot - 轻量级AI Agent框架,极简高效的智能体构建工具
人工智能·开源·agent
Amumu121389 小时前
Vue3扩展(二)
前端·javascript·vue.js
泓博10 小时前
Android中仿照View selector自定义Compose Button
android·vue.js·elementui
+VX:Fegn089511 小时前
计算机毕业设计|基于springboot + vue鲜花商城系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
pas13612 小时前
45-mini-vue 实现代码生成三种联合类型
前端·javascript·vue.js
阿里云云原生13 小时前
函数计算 AgentRun 重磅上线知识库功能,赋能智能体更“懂”你
agent
hbstream13 小时前
国内四大AI编程IDE对比(二):从零构建桌面应用实测
agent