Vercel AI Chatbot 模板教學|2025 快速建立聊天機器人

Vercel AI Chatbot 模板教學|2025 快速建立聊天機器人

想建立一個像 ChatGPT 一樣的聊天介面?

Vercel 官方提供了 AI Chatbot 模板。

開箱即用,功能完整,只要設定 API Key 就能部署。

這篇文章教你如何使用和客製化這個模板。


什麼是 AI Chatbot 模板?

Vercel AI Chatbot 是一個開源的聊天機器人模板。

內建功能

功能 說明
多輪對話 記住上下文,連續對話
串流回應 即時顯示 AI 回覆
Markdown 渲染 支援程式碼、表格等格式
程式碼高亮 自動語法高亮
對話歷史 儲存和載入歷史對話
使用者認證 內建登入系統
深色模式 支援淺色/深色主題
響應式設計 手機和桌面都適用

技術架構

  • 框架: Next.js 14(App Router)
  • 樣式: Tailwind CSS
  • 元件: shadcn/ui
  • AI: Vercel AI SDK
  • 認證: NextAuth.js
  • 資料庫: 可選(Vercel KV、PostgreSQL)

GitHub 倉庫

github.com/vercel/ai-chatbot


快速開始

方法一:一鍵部署

最快的方式是使用 Vercel 的部署按鈕:

  1. 前往 github.com/vercel/ai-chatbot
  2. 點擊「Deploy」按鈕
  3. 設定環境變數
  4. 完成部署

方法二:Clone 專案

# Clone 專案
git clone https://github.com/vercel/ai-chatbot.git
cd ai-chatbot

# 安裝依賴
pnpm install

# 複製環境變數範例
cp .env.example .env.local

設定環境變數

編輯 .env.local

# OpenAI API Key(必要)
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxx

# NextAuth 設定(必要)
AUTH_SECRET=your-random-secret-key

# 可選:GitHub OAuth
AUTH_GITHUB_ID=your-github-oauth-id
AUTH_GITHUB_SECRET=your-github-oauth-secret

# 可選:Vercel KV(用於儲存對話歷史)
KV_URL=your-kv-url
KV_REST_API_URL=your-kv-rest-url
KV_REST_API_TOKEN=your-kv-token
KV_REST_API_READ_ONLY_TOKEN=your-kv-read-only-token

執行開發伺服器

pnpm dev

打開 http://localhost:3000,就能看到聊天介面了。


模板結構解析

專案結構

ai-chatbot/
├── app/                    # Next.js App Router
│   ├── (chat)/            # 聊天相關頁面
│   │   ├── page.tsx       # 主聊天頁面
│   │   └── chat/[id]/     # 特定對話頁面
│   ├── api/               # API Routes
│   │   ├── chat/          # 聊天 API
│   │   └── auth/          # 認證 API
│   └── layout.tsx         # 根 Layout
├── components/            # UI 元件
│   ├── chat.tsx          # 聊天元件
│   ├── message.tsx       # 訊息元件
│   └── sidebar.tsx       # 側邊欄
├── lib/                   # 工具函數
│   ├── ai/               # AI 相關
│   └── auth/             # 認證相關
└── public/               # 靜態資源

核心元件

Chat 元件(主要聊天介面):

// components/chat.tsx
export function Chat({ id, initialMessages }) {
  const { messages, input, handleSubmit, handleInputChange } = useChat({
    id,
    initialMessages,
  });

  return (
    <div className="flex flex-col h-full">
      <ChatMessages messages={messages} />
      <ChatInput
        input={input}
        onInputChange={handleInputChange}
        onSubmit={handleSubmit}
      />
    </div>
  );
}

API Route(處理聊天請求):

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

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

  const result = await streamText({
    model: openai('gpt-4-turbo'),
    messages,
  });

  return result.toDataStreamResponse();
}

客製化指南

修改 AI 模型

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

const result = await streamText({
  // 改用 Claude
  model: anthropic('claude-3-opus-20240229'),
  messages,
});

自訂 System Prompt

// app/api/chat/route.ts
const result = await streamText({
  model: openai('gpt-4-turbo'),
  system: `你是一個專業的技術支援助理。

規則:
1. 使用繁體中文回答
2. 回答要簡潔明瞭
3. 如果是程式問題,提供程式碼範例
4. 不確定的事情不要亂說`,
  messages,
});

修改主題顏色

/* app/globals.css */
:root {
  --primary: 220 90% 50%;
  --primary-foreground: 0 0% 100%;
}

.dark {
  --primary: 220 90% 60%;
  --primary-foreground: 0 0% 100%;
}
// components/header.tsx
import Image from 'next/image';

export function Header() {
  return (
    <header>
      <Image
        src="/your-logo.png"
        alt="Logo"
        width={120}
        height={40}
      />
    </header>
  );
}

移除認證(公開使用)

如果不需要登入功能:

// app/(chat)/page.tsx
// 移除 auth 檢查
export default async function Page() {
  // const session = await auth();
  // if (!session) redirect('/login');

  return <Chat />;
}

加入自訂功能

加入歡迎訊息

// components/chat.tsx
const { messages } = useChat({
  initialMessages: [
    {
      id: 'welcome',
      role: 'assistant',
      content: '您好!我是您的 AI 助理,有什麼我可以幫忙的嗎?',
    },
  ],
});

加入快速問題按鈕

// components/suggested-questions.tsx
const suggestions = [
  '如何開始使用?',
  '價格方案有哪些?',
  '如何聯繫客服?',
];

export function SuggestedQuestions({ onSelect }) {
  return (
    <div className="flex gap-2 flex-wrap">
      {suggestions.map((q) => (
        <button
          key={q}
          onClick={() => onSelect(q)}
          className="px-3 py-1 bg-slate-100 rounded-full text-sm"
        >
          {q}
        </button>
      ))}
    </div>
  );
}

加入打字指示器

// components/typing-indicator.tsx
export function TypingIndicator() {
  return (
    <div className="flex items-center gap-1 p-2">
      <div className="w-2 h-2 bg-slate-400 rounded-full animate-bounce" />
      <div className="w-2 h-2 bg-slate-400 rounded-full animate-bounce delay-100" />
      <div className="w-2 h-2 bg-slate-400 rounded-full animate-bounce delay-200" />
    </div>
  );
}

加入訊息評分

// components/message-feedback.tsx
export function MessageFeedback({ messageId, onFeedback }) {
  return (
    <div className="flex gap-2 mt-2">
      <button
        onClick={() => onFeedback(messageId, 'good')}
        className="text-slate-400 hover:text-green-500"
      >
        👍
      </button>
      <button
        onClick={() => onFeedback(messageId, 'bad')}
        className="text-slate-400 hover:text-red-500"
      >
        👎
      </button>
    </div>
  );
}

儲存對話歷史

使用 Vercel KV

模板內建支援 Vercel KV:

  1. 在 Vercel Dashboard 建立 KV 資料庫
  2. 連接到專案
  3. 環境變數會自動設定
// lib/chat.ts
import { kv } from '@vercel/kv';

export async function saveChat(id: string, messages: Message[]) {
  await kv.set(`chat:${id}`, messages);
}

export async function getChat(id: string) {
  return await kv.get(`chat:${id}`);
}

使用其他資料庫

// lib/chat.ts
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

export async function saveChat(userId: string, messages: Message[]) {
  await prisma.chat.create({
    data: {
      userId,
      messages: JSON.stringify(messages),
    },
  });
}

部署到 Vercel

設定環境變數

  1. 進入 Vercel Dashboard
  2. 選擇專案 → Settings → Environment Variables
  3. 加入必要的環境變數:
    - OPENAI_API_KEY
    - AUTH_SECRET
    - 其他選用的變數

部署

# 使用 Git
git push origin main
# Vercel 自動部署

# 或使用 CLI
vercel

驗證部署

  1. 打開部署的網址
  2. 測試聊天功能
  3. 確認串流回應正常

效能優化

減少冷啟動

// 使用 Edge Runtime
export const runtime = 'edge';

限制回應長度

const result = await streamText({
  model: openai('gpt-4-turbo'),
  messages,
  maxTokens: 1000, // 限制回應長度
});

使用較快的模型

// GPT-3.5 比 GPT-4 快
model: openai('gpt-3.5-turbo'),

快取常見問題

import { unstable_cache } from 'next/cache';

const getCachedResponse = unstable_cache(
  async (question: string) => {
    // 查詢預設回答
    return await db.faq.findFirst({
      where: { question: { contains: question } },
    });
  },
  ['faq-cache'],
  { revalidate: 3600 }
);

常見問題 FAQ

Q1:如何支援多語言?

可以在 System Prompt 中指定語言:

system: `請根據用戶使用的語言回覆。
如果用戶用中文提問,請用繁體中文回答。
如果用戶用英文提問,請用英文回答。`,

Q2:如何限制使用次數?

import { Ratelimit } from '@upstash/ratelimit';
import { kv } from '@vercel/kv';

const ratelimit = new Ratelimit({
  redis: kv,
  limiter: Ratelimit.slidingWindow(10, '1 h'), // 每小時 10 次
});

export async function POST(req: Request) {
  const ip = req.headers.get('x-forwarded-for');
  const { success } = await ratelimit.limit(ip);

  if (!success) {
    return Response.json({ error: '請求過於頻繁' }, { status: 429 });
  }

  // 繼續處理...
}

Q3:如何加入檔案上傳功能?

AI Chatbot 模板目前不內建檔案上傳。

可以整合 Vercel Blob 或其他儲存服務。

Q4:如何追蹤使用量和成本?

  1. 在 OpenAI Dashboard 查看 API 使用量
  2. 在程式中加入 logging
  3. 使用 Vercel Analytics

Q5:可以嵌入到現有網站嗎?

可以使用 iframe:

<iframe
  src="https://your-chatbot.vercel.app"
  width="400"
  height="600"
  style="border: none; border-radius: 12px;"
/>

或開發獨立的 Widget 元件。


進階整合

整合知識庫(RAG)

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

// 1. 搜尋相關文件
const queryEmbedding = await embed({
  model: openai.embedding('text-embedding-3-small'),
  value: userQuestion,
});

const relevantDocs = await searchVectorDB(queryEmbedding);

// 2. 加入上下文
const result = await streamText({
  model: openai('gpt-4-turbo'),
  system: `請根據以下資料回答問題:
${relevantDocs.map(d => d.content).join('\n\n')}`,
  messages,
});

整合外部 API

import { tool } from 'ai';
import { z } from 'zod';

const result = await streamText({
  model: openai('gpt-4-turbo'),
  messages,
  tools: {
    checkOrderStatus: tool({
      description: '查詢訂單狀態',
      parameters: z.object({
        orderId: z.string(),
      }),
      execute: async ({ orderId }) => {
        const order = await fetch(`/api/orders/${orderId}`);
        return order.json();
      },
    }),
  },
});

Vercel 部署失敗?

Build Error、環境變數、自訂網域,我們幫你快速排除問題。

解決 Vercel 問題


延伸閱讀

分享文章:
V

VibeFix

專門解決 AI Vibe Coding 後的疑難雜症,讓你的專案順利上線。

這篇文章有幫到你嗎?

如果還有問題,讓我們直接幫你解決!

聯繫我們