Để xây một chatbot AI trước đây bạn phải ghép nối hàng chục thư viện và viết rất nhiều code lặp đi lặp lại. Vercel AI SDK giải quyết vấn đề đó: nó cung cấp một API thống nhất cho streaming text, tool calls, và hội thoại nhiều lượt — hỗ trợ OpenAI, Anthropic, Google và nhiều provider khác — tích hợp trực tiếp vào project Next.js.

Hướng dẫn này đưa bạn qua toàn bộ quá trình từ đầu. Kết thúc bài bạn sẽ có một giao diện chat streaming chạy được ở local và deploy lên Vercel chỉ với một lệnh.

Kết Quả Cuối Cùng

  • Một route /api/chat stream phản hồi từ LLM
  • Giao diện chat React render từng token khi chúng đến
  • Lịch sử hội thoại được truyền theo mỗi request
  • Cấu trúc project gọn gàng, dễ mở rộng

Yêu cầu: Node.js 18+, API key OpenAI miễn phí (hoặc bất kỳ provider nào AI SDK hỗ trợ), và hiểu biết cơ bản về Next.js.


Bước 1 — Tạo Project Next.js

Nếu bắt đầu từ đầu, tạo app mới với App Router:

npx create-next-app@latest ai-chatbot --typescript --tailwind --app
cd ai-chatbot

Chấp nhận các lựa chọn mặc định. Flag --app bật App Router, cần thiết cho cách tiếp cận streaming trong bài này.


Bước 2 — Cài Vercel AI SDK

npm install ai @ai-sdk/openai
  • ai là core SDK — cung cấp hàm streamText và hook useChat.
  • @ai-sdk/openai là provider cho OpenAI. Thay bằng @ai-sdk/anthropic hoặc @ai-sdk/google nếu bạn muốn dùng model khác.

Bước 3 — Thêm API Key

Tạo file .env.local ở thư mục gốc:

OPENAI_API_KEY=sk-...

Đừng commit file này. Nó đã có sẵn trong .gitignore mặc định của create-next-app.


Bước 4 — Tạo API Route

AI SDK được thiết kế tối ưu cho Next.js Route Handlers. Tạo file app/api/chat/route.ts:

import { openai } from '@ai-sdk/openai';
import { streamText } from 'ai';
 
// Cho phép streaming tối đa 30 giây
export const maxDuration = 30;
 
export async function POST(req: Request) {
  const { messages } = await req.json();
 
  const result = streamText({
    model: openai('gpt-4o-mini'),
    system: 'Bạn là trợ lý hữu ích. Hãy trả lời ngắn gọn và rõ ràng.',
    messages,
  });
 
  return result.toDataStreamResponse();
}

Một vài điểm cần chú ý:

  • messages theo định dạng { role, content } chuẩn. Client gửi toàn bộ lịch sử hội thoại mỗi request để model có ngữ cảnh.
  • streamText trả về một stream. toDataStreamResponse() chuyển nó thành Response mà browser có thể nhận theo từng phần.
  • maxDuration ngăn timeout mặc định 10 giây của Vercel cắt ngang phản hồi dài.

Bước 5 — Xây Giao Diện Chat

Thay toàn bộ nội dung app/page.tsx bằng giao diện chat tối giản nhưng hoàn chỉnh:

'use client';
 
import { useChat } from 'ai/react';
 
export default function ChatPage() {
  const { messages, input, handleInputChange, handleSubmit, isLoading } =
    useChat();
 
  return (
    <main className="flex flex-col h-screen max-w-2xl mx-auto p-4">
      <h1 className="text-2xl font-bold mb-4">Chatbot AI</h1>
 
      {/* Danh sách tin nhắn */}
      <div className="flex-1 overflow-y-auto space-y-4 mb-4">
        {messages.map((m) => (
          <div
            key={m.id}
            className={`p-3 rounded-lg ${
              m.role === 'user'
                ? 'bg-blue-100 ml-auto max-w-md'
                : 'bg-gray-100 mr-auto max-w-md'
            }`}
          >
            <p className="text-sm font-semibold capitalize mb-1">
              {m.role === 'user' ? 'Bạn' : 'Trợ lý'}
            </p>
            <p className="text-sm whitespace-pre-wrap">{m.content}</p>
          </div>
        ))}
        {isLoading && (
          <div className="bg-gray-100 p-3 rounded-lg mr-auto max-w-md">
            <p className="text-sm text-gray-500">Đang xử lý...</p>
          </div>
        )}
      </div>
 
      {/* Form nhập liệu */}
      <form onSubmit={handleSubmit} className="flex gap-2">
        <input
          value={input}
          onChange={handleInputChange}
          placeholder="Hỏi gì đó..."
          disabled={isLoading}
          className="flex-1 border rounded-lg px-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-400"
        />
        <button
          type="submit"
          disabled={isLoading || !input.trim()}
          className="bg-blue-500 text-white px-4 py-2 rounded-lg text-sm disabled:opacity-50"
        >
          Gửi
        </button>
      </form>
    </main>
  );
}

useChat xử lý toàn bộ state: theo dõi messages, quản lý input, gọi POST /api/chat, và nối từng token vào tin nhắn cuối khi stream đến. Bạn có streaming hoàn toàn miễn phí, không cần viết fetch hay ReadableStream thủ công.


Bước 6 — Chạy Ở Local

npm run dev

Mở http://localhost:3000. Gõ một tin nhắn và xem phản hồi stream vào từng token một. Toàn bộ lịch sử hội thoại được gửi theo mỗi request, nên model nhớ những gì bạn đã nói trước đó trong session.


Bước 7 — Deploy Lên Vercel

npx vercel

Khi được hỏi, thêm OPENAI_API_KEY làm biến môi trường trong Vercel dashboard (Settings > Environment Variables). Streaming hoạt động trên Vercel ngay lập tức, không cần cấu hình thêm.


Mẹo Thực Tế

Đổi model mà không cần viết lại code. Thay openai('gpt-4o-mini') bằng anthropic('claude-3-5-haiku-20241022') sau khi cài @ai-sdk/anthropic. Định dạng messages và cách streaming hoạt động giống hệt nhau giữa các provider.

Thêm system prompt theo từng người dùng. Truyền chuỗi system động dựa trên dữ liệu session (ví dụ tên người dùng hoặc gói đăng ký) để cá nhân hoá phản hồi mà không cần thay đổi phía client.

Giới hạn độ dài lịch sử hội thoại. Lịch sử dài làm tăng độ trễ và chi phí. Cắt messages còn N lượt gần nhất ở phía server trước khi truyền vào streamText:

const recentMessages = messages.slice(-10);

Xử lý lỗi rõ ràng. Hook useChat có trường error. Hiển thị nó trong UI để người dùng biết khi có sự cố, thay vì thấy màn hình im lặng không phản hồi.

Rate-limit route của bạn. Nếu không có bảo vệ, bất kỳ ai cũng có thể gọi API và đốt hết quota của bạn. Thư viện như @upstash/ratelimit kết hợp Vercel KV cho phép giới hạn theo IP chỉ trong khoảng 20 dòng code.


Mở Rộng Thêm

Sau khi vòng lặp cơ bản hoạt động, AI SDK còn hỗ trợ:

  • Tool calls — cho phép model gọi các hàm bạn định nghĩa (tìm kiếm, tính toán, tra cứu dữ liệu)
  • Structured output — dùng generateObject để nhận JSON có kiểu dữ liệu thay vì text tự do
  • Multi-modal input — truyền hình ảnh kèm text với các model tương thích
  • Resumable streams — khôi phục stream khi mạng bị ngắt giữa chừng

Tài liệu chính thức của AI SDK được cập nhật thường xuyên và có ví dụ hoạt động cho tất cả các pattern trên.