Bikin chatbot AI itu dulu requires ML expertise + GPU + bulan-bulan training. Sekarang? 2 jam, satu file Next.js, dan pemahaman Claude API basic. Tutorial ini gue test sendiri — dari blank Vercel project sampai chatbot live, total 1 jam 47 menit.

Customer service chatbot adalah salah satu use case AI paling impactful untuk bisnis. Tapi banyak yang mikir butuh expertise tinggi. Realita-nya, dengan Claude API, lo bisa build production-grade chatbot dalam beberapa jam.

Tutorial ini end-to-end: setup, code, deploy, monitoring. Stack yang gue pake: Claude API + Next.js 15 + Vercel.

Prerequisites

Cost estimate: ~$5 untuk testing + bulan pertama deployment.

Step 1: Setup Project (10 menit)

Bikin Next.js project baru:

npx create-next-app@latest my-chatbot
cd my-chatbot
npm install @anthropic-ai/sdk

Follow prompt, pilih:

Setup environment variable di .env.local:

ANTHROPIC_API_KEY=sk-ant-xxxxx

Step 2: API Route untuk Claude (20 menit)

Bikin API route di app/api/chat/route.ts:

import Anthropic from '@anthropic-ai/sdk';
import { NextRequest, NextResponse } from 'next/server';

const anthropic = new Anthropic({
  apiKey: process.env.ANTHROPIC_API_KEY,
});

const SYSTEM_PROMPT = `Lo customer service untuk RUSH.IT, blog tech 
Indonesia. Jawab dalam bahasa Indonesia santai tapi profesional. 
Kalau nggak tau jawaban, jujur bilang dan suggest contact human team.`;

export async function POST(req: NextRequest) {
  try {
    const { messages } = await req.json();
    
    const response = await anthropic.messages.create({
      model: 'claude-sonnet-4-6',
      max_tokens: 1024,
      system: SYSTEM_PROMPT,
      messages: messages,
    });
    
    return NextResponse.json({
      message: response.content[0].text,
    });
  } catch (error) {
    console.error('API error:', error);
    return NextResponse.json(
      { error: 'Something went wrong' },
      { status: 500 }
    );
  }
}

Yang penting di sini:

Step 3: Frontend Chat UI (30 menit)

Bikin component components/Chat.tsx:

'use client';

import { useState, useRef, useEffect } from 'react';

interface Message {
  role: 'user' | 'assistant';
  content: string;
}

export default function Chat() {
  const [messages, setMessages] = useState<Message[]>([]);
  const [input, setInput] = useState('');
  const [loading, setLoading] = useState(false);
  const messagesEndRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
  }, [messages]);

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    if (!input.trim() || loading) return;

    const userMessage: Message = { role: 'user', content: input };
    const newMessages = [...messages, userMessage];
    setMessages(newMessages);
    setInput('');
    setLoading(true);

    try {
      const res = await fetch('/api/chat', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ messages: newMessages }),
      });
      
      const data = await res.json();
      setMessages([...newMessages, {
        role: 'assistant',
        content: data.message,
      }]);
    } catch (error) {
      setMessages([...newMessages, {
        role: 'assistant',
        content: 'Maaf, ada error. Coba lagi nanti ya.',
      }]);
    } finally {
      setLoading(false);
    }
  }

  return (
    <div className="flex flex-col h-screen max-w-2xl mx-auto">
      <div className="flex-1 overflow-y-auto p-4 space-y-4">
        {messages.map((msg, i) => (
          <div
            key={i}
            className={`p-4 rounded ${
              msg.role === 'user'
                ? 'bg-blue-100 ml-auto max-w-md'
                : 'bg-gray-100 mr-auto max-w-md'
            }`}
          >
            {msg.content}
          </div>
        ))}
        {loading && (
          <div className="bg-gray-100 p-4 rounded max-w-md">
            Typing...
          </div>
        )}
        <div ref={messagesEndRef} />
      </div>
      
      <form onSubmit={handleSubmit} className="p-4 border-t">
        <div className="flex gap-2">
          <input
            value={input}
            onChange={(e) => setInput(e.target.value)}
            placeholder="Ketik pesan..."
            className="flex-1 p-3 border rounded"
            disabled={loading}
          />
          <button
            type="submit"
            disabled={loading}
            className="px-6 bg-blue-500 text-white rounded"
          >
            Kirim
          </button>
        </div>
      </form>
    </div>
  );
}

Update app/page.tsx:

import Chat from '@/components/Chat';

export default function Home() {
  return <Chat />;
}

Test local: npm run dev, buka localhost:3000.

Setelah step ini lo udah punya chatbot working. Sisa 1 jam buat polish, security, dan deployment.

Step 4: Streaming Response (15 menit)

Default response baru muncul setelah complete generation. Untuk UX yang lebih baik, pakai streaming.

Update API route:

export async function POST(req: NextRequest) {
  const { messages } = await req.json();
  
  const stream = await anthropic.messages.stream({
    model: 'claude-sonnet-4-6',
    max_tokens: 1024,
    system: SYSTEM_PROMPT,
    messages: messages,
  });

  const encoder = new TextEncoder();
  const readable = new ReadableStream({
    async start(controller) {
      for await (const event of stream) {
        if (event.type === 'content_block_delta') {
          const text = event.delta.type === 'text_delta' 
            ? event.delta.text 
            : '';
          controller.enqueue(encoder.encode(text));
        }
      }
      controller.close();
    },
  });

  return new Response(readable, {
    headers: { 'Content-Type': 'text/plain; charset=utf-8' },
  });
}

Update frontend untuk handle streaming:

// Replace the fetch logic in handleSubmit
const res = await fetch('/api/chat', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ messages: newMessages }),
});

if (!res.body) return;

const reader = res.body.getReader();
const decoder = new TextDecoder();
let assistantMessage = '';

setMessages([...newMessages, { role: 'assistant', content: '' }]);

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  
  const text = decoder.decode(value);
  assistantMessage += text;
  
  setMessages(prev => {
    const updated = [...prev];
    updated[updated.length - 1] = {
      role: 'assistant',
      content: assistantMessage,
    };
    return updated;
  });
}

Step 5: Rate Limiting & Security (15 menit)

Production chatbot wajib ada protection. Install rate limiter:

npm install @upstash/ratelimit @upstash/redis

Setup di API route:

import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';

const ratelimit = new Ratelimit({
  redis: Redis.fromEnv(),
  limiter: Ratelimit.slidingWindow(10, '1 m'), // 10 req/min
});

export async function POST(req: NextRequest) {
  const ip = req.headers.get('x-forwarded-for') ?? 'anonymous';
  const { success } = await ratelimit.limit(ip);
  
  if (!success) {
    return NextResponse.json(
      { error: 'Rate limit exceeded' },
      { status: 429 }
    );
  }
  
  // ... rest of the code
}

Daftar Upstash gratis untuk Redis serverless. Setup env variables.

Step 6: Knowledge Base Integration (15 menit)

Untuk chatbot yang lebih useful, tambahin context spesifik bisnis lo. Update system prompt:

const SYSTEM_PROMPT = `Lo customer service untuk RUSH.IT.

KNOWLEDGE BASE:
- RUSH.IT adalah blog tech Indonesia
- Topik utama: SEO, Dev, AI, Cybersec
- Update artikel 2-3x seminggu
- Newsletter rilis tiap Senin
- Email kontak: [email protected]

GUIDELINES:
- Jawab dalam bahasa Indonesia
- Tone: santai tapi profesional
- Kalau pertanyaan complex, redirect ke email kontak
- Jangan reveal informasi internal yang sensitive
- Selalu helpful, never argumentative`;

Untuk knowledge base yang lebih besar, lo bisa pakai:

Tapi untuk MVP, hardcoded knowledge di system prompt cukup.

Step 7: Deploy ke Vercel (10 menit)

# Install Vercel CLI
npm install -g vercel

# Deploy
vercel

Add environment variables di Vercel dashboard:

Live dalam 2-3 menit.

Step 8: Monitoring & Analytics (10 menit)

Wajib track untuk production:

Simple logging di API route:

// Log to your analytics service
await logConversation({
  userId: ip,
  userMessage: messages[messages.length - 1].content,
  assistantMessage: response.content[0].text,
  timestamp: new Date(),
  tokenUsage: response.usage,
});

Cost Analysis

Untuk chatbot dengan ~1000 conversations/bulan (rata-rata 5 message exchange):

Total: $15-25/bulan untuk chatbot production-ready. Compare dengan customer service human Rp 5jt+/bulan — ROI clear.

Optimization Tips

1. Caching Common Queries

Pertanyaan umum bisa di-cache:

2. Prompt Engineering

Iterate system prompt based on real conversation log:

3. Fallback to Human

Set threshold untuk redirect ke human team:

Kesimpulan

Bikin chatbot AI di 2026 = building blocks yang udah tersedia. Tantangan-nya:

Tutorial ini foundation. Dari sini lo bisa expand:

Source code lengkap available di GitHub (link akan ditambahkan). Happy building!