LangChain/LlamaIndex 없이 '나만의 챗봇' 만들기

멘토 (시니어) · 2025-07-24 · 12

TL;DR — 프레임워크 없이 순수 코드로 AI 챗봇을 만드는 완벽 가이드. 백엔드(Python/FastAPI)와 프론트엔드(React) 구현부터 고급 기능까지 상세히 다룹니다.

LangChain/LlamaIndex 없이 '나만의 챗봇' 만들기

💡 "프레임워크 없이도 가능해요?"

안녕하세요! 시니어 개발자입니다. 최근 많은 분들이 이런 질문을 하시더군요.

"LangChain이나 LlamaIndex 없이는 챗봇을 못 만드나요?" "프레임워크가 너무 무거워서 부담스러워요..." "내가 원하는 기능만 딱 구현하고 싶은데..."

좋은 소식이 있습니다! 프레임워크 없이도 충분히 강력한 챗봇을 만들 수 있습니다. 오늘은 순수 코드로 나만의 챗봇을 구축하는 방법을 A부터 Z까지 공유하겠습니다.

Custom chatbot development 프레임워크 없이 만드는 진짜 내 챗봇

🎯 왜 프레임워크 없이 만들까?

📊 프레임워크 vs 커스텀 구현 비교

항목 프레임워크 사용 커스텀 구현
초기 개발 속도 빠름 ⚡ 보통 🚶
유연성 제한적 🔒 무한대 🚀
번들 크기 크다 (MB 단위) 📦 작다 (KB 단위) 🪶
학습 곡선 프레임워크 학습 필요 📚 기본 개념만 이해 🎯
유지보수 의존성 관리 필요 🔧 완전한 제어 가능 ⚙️
성능 최적화 제한적 ⚠️ 자유로움 ✨

💪 커스텀 구현의 장점

  1. 가벼움: 필요한 기능만 구현
  2. 투명함: 모든 코드를 이해하고 제어
  3. 최적화: 특정 용도에 맞춰 성능 튜닝
  4. 학습 효과: AI 챗봇의 핵심 원리 습득

Architecture comparison 심플하지만 강력한 아키텍처

🏗️ 기본 아키텍처 설계

📐 핵심 컴포넌트

┌─────────────────────────────────────────┐
│           사용자 인터페이스              │
│         (React/Vue/Vanilla JS)          │
└────────────────┬────────────────────────┘
                 │
┌────────────────▼────────────────────────┐
│            API 게이트웨이               │
│         (Express/FastAPI)               │
└────────────────┬────────────────────────┘
                 │
┌────────────────▼────────────────────────┐
│           대화 관리자                   │
│    (Context/Memory/Session)             │
└────────────────┬────────────────────────┘
                 │
┌────────────────▼────────────────────────┐
│            AI 엔진                      │
│    (OpenAI/Claude/Custom)               │
└─────────────────────────────────────────┘

🔑 각 컴포넌트의 역할

  1. UI Layer: 사용자 입력/출력 처리
  2. API Gateway: 요청 라우팅, 인증, 속도 제한
  3. Conversation Manager: 대화 상태 관리
  4. AI Engine: 실제 응답 생성

💻 Step 1: 백엔드 구현 (Python)

🐍 기본 구조 설정

# chatbot.py
import os
import json
from datetime import datetime
from typing import List, Dict, Optional
import openai
from dataclasses import dataclass, asdict

# OpenAI API 설정
openai.api_key = os.getenv("OPENAI_API_KEY")

@dataclass
class Message:
    """메시지 데이터 클래스"""
    role: str  # 'user' or 'assistant'
    content: str
    timestamp: datetime = None
    
    def __post_init__(self):
        if self.timestamp is None:
            self.timestamp = datetime.now()
    
    def to_dict(self):
        return {
            "role": self.role,
            "content": self.content,
            "timestamp": self.timestamp.isoformat()
        }

class ConversationMemory:
    """대화 메모리 관리 클래스"""
    def __init__(self, max_messages: int = 10):
        self.messages: List[Message] = []
        self.max_messages = max_messages
    
    def add_message(self, message: Message):
        """메시지 추가 (메모리 크기 제한)"""
        self.messages.append(message)
        if len(self.messages) > self.max_messages:
            self.messages.pop(0)
    
    def get_context(self) -> List[Dict]:
        """OpenAI API 형식으로 컨텍스트 반환"""
        return [{"role": msg.role, "content": msg.content} 
                for msg in self.messages]
    
    def clear(self):
        """대화 초기화"""
        self.messages = []

🧠 챗봇 코어 엔진

class Chatbot:
    """커스텀 챗봇 클래스"""
    def __init__(self, 
                 model: str = "gpt-3.5-turbo",
                 temperature: float = 0.7,
                 max_tokens: int = 500,
                 system_prompt: str = None):
        self.model = model
        self.temperature = temperature
        self.max_tokens = max_tokens
        self.system_prompt = system_prompt or self._default_system_prompt()
        self.memory = ConversationMemory()
        
    def _default_system_prompt(self) -> str:
        """기본 시스템 프롬프트"""
        return """당신은 친절하고 도움이 되는 AI 어시스턴트입니다. 
        사용자의 질문에 정확하고 유용한 답변을 제공하세요.
        답변은 간결하면서도 충분한 정보를 포함해야 합니다."""
    
    async def get_response(self, user_input: str) -> str:
        """사용자 입력에 대한 응답 생성"""
        # 사용자 메시지 추가
        user_message = Message(role="user", content=user_input)
        self.memory.add_message(user_message)
        
        # 전체 대화 컨텍스트 구성
        messages = [
            {"role": "system", "content": self.system_prompt}
        ] + self.memory.get_context()
        
        try:
            # OpenAI API 호출
            response = await openai.ChatCompletion.acreate(
                model=self.model,
                messages=messages,
                temperature=self.temperature,
                max_tokens=self.max_tokens
            )
            
            # 응답 추출
            assistant_response = response.choices[0].message.content
            
            # 어시스턴트 메시지 메모리에 추가
            assistant_message = Message(
                role="assistant", 
                content=assistant_response
            )
            self.memory.add_message(assistant_message)
            
            return assistant_response
            
        except Exception as e:
            return f"죄송합니다. 오류가 발생했습니다: {str(e)}"
    
    def set_system_prompt(self, prompt: str):
        """시스템 프롬프트 변경"""
        self.system_prompt = prompt
        
    def clear_memory(self):
        """대화 기록 초기화"""
        self.memory.clear()

🌐 FastAPI 서버 구현

# server.py
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import Optional
import uvicorn

app = FastAPI()

# CORS 설정
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 요청/응답 모델
class ChatRequest(BaseModel):
    message: str
    session_id: Optional[str] = None

class ChatResponse(BaseModel):
    response: str
    session_id: str

# 세션별 챗봇 인스턴스 관리
sessions = {}

@app.post("/api/chat", response_model=ChatResponse)
async def chat(request: ChatRequest):
    """채팅 엔드포인트"""
    # 세션 ID가 없으면 새로 생성
    session_id = request.session_id or str(uuid.uuid4())
    
    # 세션별 챗봇 인스턴스 가져오기 또는 생성
    if session_id not in sessions:
        sessions[session_id] = Chatbot()
    
    chatbot = sessions[session_id]
    
    try:
        # 응답 생성
        response = await chatbot.get_response(request.message)
        
        return ChatResponse(
            response=response,
            session_id=session_id
        )
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

@app.delete("/api/chat/{session_id}")
async def clear_chat(session_id: str):
    """대화 기록 초기화"""
    if session_id in sessions:
        sessions[session_id].clear_memory()
        return {"message": "대화가 초기화되었습니다."}
    else:
        raise HTTPException(status_code=404, detail="세션을 찾을 수 없습니다.")

@app.get("/api/health")
async def health_check():
    """헬스 체크"""
    return {"status": "healthy"}

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)

Backend architecture 깔끔한 백엔드 구조

🎨 Step 2: 프론트엔드 구현

⚛️ React 챗봇 UI

// Chatbot.jsx
import React, { useState, useEffect, useRef } from 'react';
import axios from 'axios';
import './Chatbot.css';

const Chatbot = () => {
  const [messages, setMessages] = useState([]);
  const [input, setInput] = useState('');
  const [loading, setLoading] = useState(false);
  const [sessionId, setSessionId] = useState(null);
  const messagesEndRef = useRef(null);
  
  const API_URL = process.env.REACT_APP_API_URL || 'http://localhost:8000';
  
  // 자동 스크롤
  const scrollToBottom = () => {
    messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
  };
  
  useEffect(() => {
    scrollToBottom();
  }, [messages]);
  
  // 메시지 전송
  const sendMessage = async () => {
    if (!input.trim()) return;
    
    const userMessage = {
      role: 'user',
      content: input,
      timestamp: new Date().toISOString()
    };
    
    setMessages(prev => [...prev, userMessage]);
    setInput('');
    setLoading(true);
    
    try {
      const response = await axios.post(`${API_URL}/api/chat`, {
        message: input,
        session_id: sessionId
      });
      
      const assistantMessage = {
        role: 'assistant',
        content: response.data.response,
        timestamp: new Date().toISOString()
      };
      
      setMessages(prev => [...prev, assistantMessage]);
      setSessionId(response.data.session_id);
      
    } catch (error) {
      console.error('Error:', error);
      const errorMessage = {
        role: 'assistant',
        content: '죄송합니다. 오류가 발생했습니다. 다시 시도해주세요.',
        timestamp: new Date().toISOString()
      };
      setMessages(prev => [...prev, errorMessage]);
    } finally {
      setLoading(false);
    }
  };
  
  // Enter 키 처리
  const handleKeyPress = (e) => {
    if (e.key === 'Enter' && !e.shiftKey) {
      e.preventDefault();
      sendMessage();
    }
  };
  
  // 대화 초기화
  const clearChat = async () => {
    if (sessionId) {
      try {
        await axios.delete(`${API_URL}/api/chat/${sessionId}`);
        setMessages([]);
        setSessionId(null);
      } catch (error) {
        console.error('Error clearing chat:', error);
      }
    }
  };
  
  return (
    <div className="chatbot-container">
      <div className="chatbot-header">
        <h2>🤖 나만의 AI 챗봇</h2>
        <button onClick={clearChat} className="clear-btn">
          대화 초기화
        </button>
      </div>
      
      <div className="messages-container">
        {messages.map((message, index) => (
          <div
            key={index}
            className={`message ${message.role}`}
          >
            <div className="message-content">
              {message.content}
            </div>
            <div className="message-time">
              {new Date(message.timestamp).toLocaleTimeString()}
            </div>
          </div>
        ))}
        {loading && (
          <div className="message assistant loading">
            <div className="typing-indicator">
              <span></span>
              <span></span>
              <span></span>
            </div>
          </div>
        )}
        <div ref={messagesEndRef} />
      </div>
      
      <div className="input-container">
        <textarea
          value={input}
          onChange={(e) => setInput(e.target.value)}
          onKeyPress={handleKeyPress}
          placeholder="메시지를 입력하세요..."
          rows="3"
          disabled={loading}
        />
        <button 
          onClick={sendMessage} 
          disabled={loading || !input.trim()}
        >
          전송
        </button>
      </div>
    </div>
  );
};

export default Chatbot;

🎨 스타일링 (CSS)

/* Chatbot.css */
.chatbot-container {
  max-width: 800px;
  margin: 0 auto;
  height: 90vh;
  display: flex;
  flex-direction: column;
  background: #f5f5f5;
  border-radius: 10px;
  overflow: hidden;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}

.chatbot-header {
  background: #2c3e50;
  color: white;
  padding: 1rem;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.chatbot-header h2 {
  margin: 0;
  font-size: 1.5rem;
}

.clear-btn {
  background: #e74c3c;
  color: white;
  border: none;
  padding: 0.5rem 1rem;
  border-radius: 5px;
  cursor: pointer;
  transition: background 0.3s;
}

.clear-btn:hover {
  background: #c0392b;
}

.messages-container {
  flex: 1;
  overflow-y: auto;
  padding: 1rem;
  background: white;
}

.message {
  margin-bottom: 1rem;
  display: flex;
  flex-direction: column;
}

.message.user {
  align-items: flex-end;
}

.message.assistant {
  align-items: flex-start;
}

.message-content {
  max-width: 70%;
  padding: 0.75rem 1rem;
  border-radius: 10px;
  word-wrap: break-word;
}

.message.user .message-content {
  background: #3498db;
  color: white;
}

.message.assistant .message-content {
  background: #ecf0f1;
  color: #2c3e50;
}

.message-time {
  font-size: 0.75rem;
  color: #7f8c8d;
  margin-top: 0.25rem;
}

.typing-indicator {
  display: flex;
  align-items: center;
  gap: 4px;
}

.typing-indicator span {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #7f8c8d;
  animation: typing 1.4s infinite;
}

.typing-indicator span:nth-child(2) {
  animation-delay: 0.2s;
}

.typing-indicator span:nth-child(3) {
  animation-delay: 0.4s;
}

@keyframes typing {
  0%, 60%, 100% {
    transform: translateY(0);
  }
  30% {
    transform: translateY(-10px);
  }
}

.input-container {
  display: flex;
  padding: 1rem;
  background: #ecf0f1;
  gap: 1rem;
}

.input-container textarea {
  flex: 1;
  padding: 0.75rem;
  border: 1px solid #bdc3c7;
  border-radius: 5px;
  resize: none;
  font-family: inherit;
}

.input-container button {
  padding: 0.75rem 2rem;
  background: #3498db;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  transition: background 0.3s;
}

.input-container button:hover:not(:disabled) {
  background: #2980b9;
}

.input-container button:disabled {
  background: #95a5a6;
  cursor: not-allowed;
}

Frontend UI 깔끔한 챗봇 UI

🚀 Step 3: 고급 기능 구현

💾 대화 내역 저장 (PostgreSQL)

# database.py
import asyncpg
from datetime import datetime
import json

class ChatDatabase:
    """대화 내역 데이터베이스 관리"""
    def __init__(self, database_url: str):
        self.database_url = database_url
        self.pool = None
    
    async def init(self):
        """데이터베이스 연결 풀 초기화"""
        self.pool = await asyncpg.create_pool(self.database_url)
        
        # 테이블 생성
        async with self.pool.acquire() as conn:
            await conn.execute('''
                CREATE TABLE IF NOT EXISTS conversations (
                    id SERIAL PRIMARY KEY,
                    session_id VARCHAR(255) NOT NULL,
                    user_id VARCHAR(255),
                    message_role VARCHAR(50) NOT NULL,
                    message_content TEXT NOT NULL,
                    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
                );
                
                CREATE INDEX IF NOT EXISTS idx_session_id 
                ON conversations(session_id);
            ''')
    
    async def save_message(self, session_id: str, message: Message, 
                          user_id: Optional[str] = None):
        """메시지 저장"""
        async with self.pool.acquire() as conn:
            await conn.execute('''
                INSERT INTO conversations 
                (session_id, user_id, message_role, message_content)
                VALUES ($1, $2, $3, $4)
            ''', session_id, user_id, message.role, message.content)
    
    async def get_conversation_history(self, session_id: str) -> List[Message]:
        """대화 내역 조회"""
        async with self.pool.acquire() as conn:
            rows = await conn.fetch('''
                SELECT message_role, message_content, created_at
                FROM conversations
                WHERE session_id = $1
                ORDER BY created_at ASC
            ''', session_id)
            
            return [
                Message(
                    role=row['message_role'],
                    content=row['message_content'],
                    timestamp=row['created_at']
                )
                for row in rows
            ]

🔍 의미 검색 기능 (Embeddings)

# embeddings.py
import numpy as np
from typing import List, Tuple

class SemanticSearch:
    """임베딩 기반 의미 검색"""
    def __init__(self):
        self.embeddings = []
        self.documents = []
    
    async def get_embedding(self, text: str) -> List[float]:
        """텍스트를 임베딩 벡터로 변환"""
        response = await openai.Embedding.acreate(
            model="text-embedding-ada-002",
            input=text
        )
        return response['data'][0]['embedding']
    
    async def add_document(self, document: str):
        """문서 추가 및 임베딩 생성"""
        embedding = await self.get_embedding(document)
        self.embeddings.append(embedding)
        self.documents.append(document)
    
    def cosine_similarity(self, a: List[float], b: List[float]) -> float:
        """코사인 유사도 계산"""
        a = np.array(a)
        b = np.array(b)
        return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
    
    async def search(self, query: str, top_k: int = 5) -> List[Tuple[str, float]]:
        """쿼리와 가장 유사한 문서 검색"""
        query_embedding = await self.get_embedding(query)
        
        similarities = [
            (doc, self.cosine_similarity(query_embedding, emb))
            for doc, emb in zip(self.documents, self.embeddings)
        ]
        
        # 유사도 기준으로 정렬
        similarities.sort(key=lambda x: x[1], reverse=True)
        
        return similarities[:top_k]

# 챗봇에 검색 기능 통합
class EnhancedChatbot(Chatbot):
    """검색 기능이 추가된 챗봇"""
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.search_engine = SemanticSearch()
        self.knowledge_base_loaded = False
    
    async def load_knowledge_base(self, documents: List[str]):
        """지식 베이스 로드"""
        for doc in documents:
            await self.search_engine.add_document(doc)
        self.knowledge_base_loaded = True
    
    async def get_response(self, user_input: str) -> str:
        """검색 결과를 활용한 응답 생성"""
        context = ""
        
        # 지식 베이스에서 관련 정보 검색
        if self.knowledge_base_loaded:
            search_results = await self.search_engine.search(user_input, top_k=3)
            if search_results:
                context = "\n\n관련 정보:\n"
                for doc, score in search_results:
                    if score > 0.7:  # 유사도 임계값
                        context += f"- {doc}\n"
        
        # 컨텍스트를 포함한 프롬프트 생성
        enhanced_input = user_input
        if context:
            enhanced_input = f"{user_input}\n{context}"
        
        return await super().get_response(enhanced_input)

🔐 인증 및 권한 관리

# auth.py
from datetime import datetime, timedelta
import jwt
from passlib.context import CryptContext

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

class AuthManager:
    """인증 관리자"""
    def __init__(self, secret_key: str):
        self.secret_key = secret_key
    
    def create_access_token(self, user_id: str, expires_delta: timedelta = None):
        """JWT 액세스 토큰 생성"""
        to_encode = {"sub": user_id}
        if expires_delta:
            expire = datetime.utcnow() + expires_delta
        else:
            expire = datetime.utcnow() + timedelta(hours=24)
        
        to_encode.update({"exp": expire})
        encoded_jwt = jwt.encode(to_encode, self.secret_key, algorithm="HS256")
        return encoded_jwt
    
    def verify_token(self, token: str):
        """토큰 검증"""
        try:
            payload = jwt.decode(token, self.secret_key, algorithms=["HS256"])
            user_id: str = payload.get("sub")
            if user_id is None:
                return None
            return user_id
        except jwt.PyJWTError:
            return None

# FastAPI에 인증 미들웨어 추가
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials

security = HTTPBearer()
auth_manager = AuthManager(secret_key="your-secret-key")

async def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security)):
    """현재 사용자 가져오기"""
    token = credentials.credentials
    user_id = auth_manager.verify_token(token)
    if user_id is None:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid authentication credentials",
            headers={"WWW-Authenticate": "Bearer"},
        )
    return user_id

# 보호된 엔드포인트
@app.post("/api/chat/protected")
async def protected_chat(
    request: ChatRequest,
    current_user: str = Depends(get_current_user)
):
    """인증이 필요한 채팅 엔드포인트"""
    # 사용자별 챗봇 인스턴스 관리
    user_session_id = f"{current_user}:{request.session_id}"
    # ... 나머지 로직

Advanced features 고급 기능으로 더 똑똑한 챗봇

📊 성능 최적화

응답 속도 개선

# 1. 응답 스트리밍
async def stream_response(self, user_input: str):
    """스트리밍 방식으로 응답 생성"""
    messages = [
        {"role": "system", "content": self.system_prompt}
    ] + self.memory.get_context()
    
    response = await openai.ChatCompletion.acreate(
        model=self.model,
        messages=messages,
        temperature=self.temperature,
        max_tokens=self.max_tokens,
        stream=True  # 스트리밍 활성화
    )
    
    full_response = ""
    async for chunk in response:
        if chunk['choices'][0]['delta'].get('content'):
            content = chunk['choices'][0]['delta']['content']
            full_response += content
            yield content
    
    # 완성된 응답을 메모리에 저장
    assistant_message = Message(role="assistant", content=full_response)
    self.memory.add_message(assistant_message)

# 2. 캐싱 구현
from functools import lru_cache
import hashlib

class CachedChatbot(Chatbot):
    """캐싱이 적용된 챗봇"""
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.cache = {}
        self.cache_ttl = 3600  # 1시간
    
    def _get_cache_key(self, messages: List[Dict]) -> str:
        """캐시 키 생성"""
        content = json.dumps(messages, sort_keys=True)
        return hashlib.md5(content.encode()).hexdigest()
    
    async def get_response(self, user_input: str) -> str:
        """캐시를 활용한 응답 생성"""
        messages = [
            {"role": "system", "content": self.system_prompt}
        ] + self.memory.get_context()
        
        cache_key = self._get_cache_key(messages)
        
        # 캐시 확인
        if cache_key in self.cache:
            cached_response, timestamp = self.cache[cache_key]
            if datetime.now().timestamp() - timestamp < self.cache_ttl:
                return cached_response
        
        # 캐시 미스 시 일반 응답 생성
        response = await super().get_response(user_input)
        
        # 캐시 저장
        self.cache[cache_key] = (response, datetime.now().timestamp())
        
        return response

📈 모니터링 및 분석

# monitoring.py
from prometheus_client import Counter, Histogram, Gauge
import time

# 메트릭 정의
request_count = Counter('chatbot_requests_total', 'Total requests')
request_duration = Histogram('chatbot_request_duration_seconds', 'Request duration')
active_sessions = Gauge('chatbot_active_sessions', 'Active sessions')
error_count = Counter('chatbot_errors_total', 'Total errors')

class MonitoredChatbot(Chatbot):
    """모니터링이 적용된 챗봇"""
    
    @request_duration.time()
    async def get_response(self, user_input: str) -> str:
        """모니터링이 적용된 응답 생성"""
        request_count.inc()
        
        try:
            response = await super().get_response(user_input)
            return response
        except Exception as e:
            error_count.inc()
            raise e

# 로깅 설정
import logging

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

class LoggedChatbot(MonitoredChatbot):
    """로깅이 추가된 챗봇"""
    
    async def get_response(self, user_input: str) -> str:
        logger.info(f"User input: {user_input[:100]}...")
        
        start_time = time.time()
        response = await super().get_response(user_input)
        duration = time.time() - start_time
        
        logger.info(f"Response generated in {duration:.2f}s")
        
        return response

🎯 실전 프로젝트: 도메인 특화 챗봇

💼 고객 서비스 챗봇

class CustomerServiceBot(EnhancedChatbot):
    """고객 서비스 특화 챗봇"""
    
    def __init__(self):
        system_prompt = """당신은 전문적인 고객 서비스 상담원입니다.
        다음 원칙을 따라주세요:
        1. 항상 공손하고 전문적인 태도 유지
        2. 고객의 문제를 정확히 파악
        3. 단계별로 명확한 해결책 제시
        4. 추가 도움이 필요한지 확인
        5. 긍정적인 마무리
        """
        
        super().__init__(
            system_prompt=system_prompt,
            temperature=0.3  # 더 일관된 응답을 위해 낮은 temperature
        )
        
        # FAQ 데이터베이스 로드
        self.load_faq_database()
    
    async def load_faq_database(self):
        """FAQ 데이터베이스 로드"""
        faqs = [
            "Q: 배송은 얼마나 걸리나요? A: 일반 배송은 2-3일, 특급 배송은 당일 도착합니다.",
            "Q: 반품은 어떻게 하나요? A: 구매 후 14일 이내 미개봉 상품에 한해 반품 가능합니다.",
            "Q: 결제 방법은 무엇이 있나요? A: 신용카드, 계좌이체, 간편결제를 지원합니다.",
            # ... 더 많은 FAQ
        ]
        
        await self.load_knowledge_base(faqs)
    
    async def get_response(self, user_input: str) -> str:
        """고객 서비스에 최적화된 응답"""
        # 감정 분석
        sentiment = await self.analyze_sentiment(user_input)
        
        # 부정적 감정일 경우 더 신중한 응답
        if sentiment == "negative":
            self.temperature = 0.2
        
        response = await super().get_response(user_input)
        
        # 응답에 이모지 추가 (친근감)
        if sentiment == "positive":
            response = "😊 " + response
        elif sentiment == "negative":
            response = "🤝 " + response
        
        return response
    
    async def analyze_sentiment(self, text: str) -> str:
        """간단한 감정 분석"""
        negative_words = ["화나", "짜증", "못", "안돼", "최악", "나쁜"]
        positive_words = ["좋", "감사", "훌륭", "최고", "만족"]
        
        text_lower = text.lower()
        
        negative_score = sum(1 for word in negative_words if word in text_lower)
        positive_score = sum(1 for word in positive_words if word in text_lower)
        
        if negative_score > positive_score:
            return "negative"
        elif positive_score > negative_score:
            return "positive"
        else:
            return "neutral"

Customer service bot 전문적인 고객 서비스 봇

🚀 배포 및 운영

🐳 Docker 컨테이너화

# Dockerfile
FROM python:3.9-slim

WORKDIR /app

# 의존성 설치
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 앱 복사
COPY . .

# 환경 변수
ENV PYTHONUNBUFFERED=1

# 헬스체크
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
  CMD curl -f http://localhost:8000/api/health || exit 1

# 실행
CMD ["uvicorn", "server:app", "--host", "0.0.0.0", "--port", "8000"]

☸️ Kubernetes 배포

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: chatbot-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: chatbot-api
  template:
    metadata:
      labels:
        app: chatbot-api
    spec:
      containers:
      - name: chatbot-api
        image: your-registry/chatbot-api:latest
        ports:
        - containerPort: 8000
        env:
        - name: OPENAI_API_KEY
          valueFrom:
            secretKeyRef:
              name: chatbot-secrets
              key: openai-api-key
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
---
apiVersion: v1
kind: Service
metadata:
  name: chatbot-api-service
spec:
  selector:
    app: chatbot-api
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8000
  type: LoadBalancer

📊 결론: 프레임워크 없이도 충분해요!

우리가 구현한 것들

  1. 핵심 챗봇 엔진

    • 대화 컨텍스트 관리
    • 메모리 최적화
    • 세션 관리
  2. 고급 기능 🚀

    • 의미 검색 (Embeddings)
    • 대화 내역 저장
    • 인증 및 권한 관리
  3. 성능 최적화

    • 응답 스트리밍
    • 캐싱
    • 모니터링
  4. 실전 응용 💼

    • 도메인 특화 봇
    • 감정 분석
    • FAQ 통합

💡 핵심 교훈

  1. 프레임워크는 도구일 뿐: 핵심 원리를 이해하면 직접 구현 가능
  2. 필요한 것만 구현: 불필요한 복잡성 제거
  3. 완전한 제어권: 모든 부분을 커스터마이징 가능
  4. 학습 효과: AI 챗봇의 작동 원리 완벽 이해

🎯 이런 분들께 추천

  • 가벼운 챗봇이 필요한 경우
  • 특정 기능만 필요한 경우
  • 완전한 커스터마이징이 필요한 경우
  • AI 챗봇의 원리를 깊이 이해하고 싶은 경우

다음 포스트 예고: "단순 반복 업무를 자동화하는 'AI 에이전트' 구축 실전 가이드"

여러분이 만든 커스텀 챗봇 경험을 댓글로 공유해주세요! 🤖


태그: #Chatbot #CustomChatbot #NoPramework #OpenAI #Python #FastAPI #React #AIEngineering #챗봇개발 #커스텀챗봇