ในฐานะที่ผมเคยพัฒนาระบบ AI หลายสิบโปรเจกต์ ทั้งระบบ Customer Service Automation สำหรับ E-commerce ที่มี Traffic สูงสุด 50,000 Requests ต่อวินาที และ RAG System สำหรับองค์กรขนาดใหญ่ ปัญหาที่พบบ่อยที่สุดคือการจัดการ Code ที่ยุ่งเหยิงเมื่อ Logic ของ AI ปะปนกับ Business Logic และ Database Logic ในที่สุดผมจึงหันมาใช้ MVC Pattern ที่ปรับแต่งสำหรับ AI API โดยเฉพาะ ซึ่งช่วยลด Bug ได้ถึง 60% และเพิ่มความเร็วในการพัฒนา 3 เท่า
บทความนี้จะอธิบายวิธีการ implement MVC สำหรับ AI API โดยใช้ HolySheep AI เป็นตัวอย่าง พร้อมโค้ดที่พร้อมใช้งานจริงใน Node.js และ Python
ทำไมต้อง MVC สำหรับ AI API
ในโปรเจกต์แรกของผมที่เป็น Chatbot สำหรับ E-commerce ผมเขียนทุกอย่างในไฟล์เดียว ทั้ง Prompt Engineering, API Calling, Database Query, และ Response Formatting ผลลัพธ์คือเมื่อลูกค้าต้องการเปลี่ยน AI Provider จาก GPT-4 เป็น Claude ต้องแก้ไขโค้ดทั้งหมด ใช้เวลาทั้งหมด 2 สัปดาห์
MVC Pattern สำหรับ AI API แก้ปัญหานี้โดยการแยก:
- Model (AI Layer): จัดการ Prompt, Token Calculation, Response Parsing
- View (Response Layer): จัดการ Output Formatting, Caching, Rate Limiting
- Controller (Business Logic): จัดการ Routing, Validation, Orchestration
Architecture Overview
จากประสบการณ์ที่ผมพัฒนา Production System มาหลายปี สถาปัตยกรรม MVC สำหรับ AI API ที่ดีควรประกอบด้วย:
┌─────────────────────────────────────────────────────────┐
│ CONTROLLER LAYER │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ ChatCtrl │ │ EmbedCtrl │ │ RAGCtrl │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────┐
│ MODEL LAYER │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ PromptMgr │ │ TokenCalc │ │ AIProvider │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────┐
│ VIEW LAYER │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ CacheView │ │ FormatView │ │ StreamView │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────┐
│ EXTERNAL: HolySheep AI │
│ base_url: https://api.holysheep.ai/v1 │
└─────────────────────────────────────────────────────────┘
Implementation ด้วย Node.js
โค้ดตัวอย่างนี้เป็นระบบ E-commerce AI Assistant ที่ผมพัฒนาจริงสำหรับร้านค้าออนไลน์แห่งหนึ่ง ซึ่งสามารถตอบคำถามเกี่ยวกับสินค้า, สถานะการสั่งซื้อ, และแนะนำสินค้าที่เกี่ยวข้อง ใช้งานได้ทันทีเพียงแค่ใส่ API Key ของคุณ
// ============================================
// AI API MVC Pattern - Node.js Implementation
// E-commerce AI Assistant with HolySheep AI
// ============================================
const https = require('https');
// ============================================
// MODEL LAYER: AI Core Components
// ============================================
class AIModel {
constructor() {
this.baseURL = 'https://api.holysheep.ai/v1';
this.apiKey = process.env.HOLYSHEEP_API_KEY || 'YOUR_HOLYSHEEP_API_KEY';
}
// Prompt Manager - แยก Prompt Logic ออกมาจาก Controller
buildProductQueryPrompt(userQuery, productContext) {
return [
{
role: 'system',
content: `คุณเป็นผู้ช่วยขายสินค้าออนไลน์ที่เชี่ยวชาญ
ตอบให้กระชับ มีประโยชน์ และเป็นมิตร
ถ้าไม่แน่ใจให้แนะนำให้ลูกค้าติดต่อเจ้าหน้าที่
ราคาที่แสดงเป็นบาท (THB)`
},
{
role: 'user',
content: ข้อมูลสินค้าที่เกี่ยวข้อง:\n${productContext}\n\nคำถามลูกค้า: ${userQuery}
}
];
}
// Token Calculator - คำนวณค่าใช้จ่ายล่วงหน้า
calculateTokens(messages, model = 'gpt-4.1') {
let totalChars = 0;
messages.forEach(msg => {
totalChars += msg.content.length;
});
// Rough estimate: 4 characters ≈ 1 token
const estimatedTokens = Math.ceil(totalChars / 4);
// Price per 1M tokens (2026)
const prices = {
'gpt-4.1': 8,
'claude-sonnet-4.5': 15,
'gemini-2.5-flash': 2.50,
'deepseek-v3.2': 0.42
};
const pricePerMillion = prices[model] || 8;
const estimatedCost = (estimatedTokens / 1000000) * pricePerMillion;
return {
estimatedTokens,
estimatedCost: parseFloat(estimatedCost.toFixed(4))
};
}
// AI Provider - Abstraction Layer สำหรับเปลี่ยน Provider ได้ง่าย
async callAI(messages, model = 'gpt-4.1') {
const { estimatedTokens, estimatedCost } = this.calculateTokens(messages, model);
const options = {
hostname: 'api.holysheep.ai',
port: 443,
path: '/v1/chat/completions',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': Bearer ${this.apiKey}
}
};
const payload = JSON.stringify({
model: model,
messages: messages,
max_tokens: 2048,
temperature: 0.7
});
return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
let data = '';
res.on('data', (chunk) => data += chunk);
res.on('end', () => {
try {
const parsed = JSON.parse(data);
if (parsed.error) {
reject(new Error(parsed.error.message));
} else {
resolve({
content: parsed.choices[0].message.content,
usage: parsed.usage,
cost: estimatedCost,
latency: Date.now() - startTime
});
}
} catch (e) {
reject(e);
}
});
});
req.on('error', reject);
const startTime = Date.now();
req.write(payload);
req.end();
});
}
}
// ============================================
// VIEW LAYER: Response Formatting
// ============================================
class AIView {
// Cache Response
static formatCachedResponse(cachedData, requestId) {
return {
success: true,
source: 'cache',
requestId: requestId,
data: cachedData,
cached: true
};
}
// Standard Response
static formatSuccessResponse(aiResponse, metadata = {}) {
return {
success: true,
source: 'ai',
data: {
answer: aiResponse.content,
model: metadata.model || 'gpt-4.1'
},
metadata: {
tokens: aiResponse.usage?.total_tokens || 0,
cost: aiResponse.cost || 0,
latency: aiResponse.latency || 0,
...metadata
}
};
}
// Error Response
static formatErrorResponse(error, code = 'AI_ERROR') {
return {
success: false,
error: {
code: code,
message: error.message || 'เกิดข้อผิดพลาดไม่ทราบสาเหตุ',
timestamp: new Date().toISOString()
}
};
}
// Rate Limit Response
static formatRateLimitResponse(retryAfter) {
return {
success: false,
error: {
code: 'RATE_LIMITED',
message: กรุณารอ ${retryAfter} วินาทีก่อนทำรายการต่อ,
retryAfter: retryAfter
}
};
}
}
// ============================================
// CONTROLLER LAYER: Business Logic
// ============================================
class AIController {
constructor() {
this.model = new AIModel();
this.cache = new Map();
this.rateLimits = new Map();
this.RATE_LIMIT = 60; // requests per minute
this.CACHE_TTL = 300000; // 5 minutes
}
// Rate Limiting Check
checkRateLimit(clientId) {
const now = Date.now();
const clientRequests = this.rateLimits.get(clientId) || [];
// Filter out old requests
const recentRequests = clientRequests.filter(
time => now - time < 60000
);
if (recentRequests.length >= this.RATE_LIMIT) {
const oldestRequest = recentRequests[0];
const retryAfter = Math.ceil((oldestRequest + 60000 - now) / 1000);
return { allowed: false, retryAfter };
}
recentRequests.push(now);
this.rateLimits.set(clientId, recentRequests);
return { allowed: true };
}
// Cache Key Generator
generateCacheKey(userId, query) {
return ${userId}:${query.trim().toLowerCase()};
}
// Check Cache
checkCache(key) {
const cached = this.cache.get(key);
if (cached && Date.now() - cached.timestamp < this.CACHE_TTL) {
return cached.data;
}
return null;
}
// Main Handler - E-commerce Product Query
async handleProductQuery(req) {
const { userId, query, productContext, model = 'gpt-4.1' } = req;
// 1. Rate Limit Check
const rateCheck = this.checkRateLimit(userId);
if (!rateCheck.allowed) {
return AIView.formatRateLimitResponse(rateCheck.retryAfter);
}
// 2. Cache Check
const cacheKey = this.generateCacheKey(userId, query);
const cached = this.checkCache(cacheKey);
if (cached) {
return AIView.formatCachedResponse(cached, cacheKey);
}
try {
// 3. Build Prompt (Model Layer)
const messages = this.model.buildProductQueryPrompt(query, productContext);
// 4. Cost Estimation (Model Layer)
const { estimatedCost } = this.model.calculateTokens(messages, model);
// 5. Call AI (Model Layer)
const aiResponse = await this.model.callAI(messages, model);
// 6. Format Response (View Layer)
const response = AIView.formatSuccessResponse(aiResponse, {
userId,
estimatedCost,
model
});
// 7. Cache Result
this.cache.set(cacheKey, {
data: response.data,
timestamp: Date.now()
});
return response;
} catch (error) {
console.error('AI Controller Error:', error);
return AIView.formatErrorResponse(error, 'AI_SERVICE_ERROR');
}
}
}
// ============================================
// USAGE EXAMPLE
// ============================================
async function main() {
const controller = new AIController();
const request = {
userId: 'user_12345',
query: 'iPhone 15 Pro ราคาเท่าไหร่ และมีสีอะไรบ้าง',
productContext: `
สินค้า: iPhone 15 Pro
ราคา: 45,900 บาท
สี: Natural Titanium, Blue Titanium, White Titanium, Black Titanium
ความจุ: 128GB, 256GB, 512GB, 1TB
หน้าจอ: 6.1 นิ้ว Super Retina XDR
ชิป: A17 Pro
`,
model: 'gpt-4.1'
};
try {
const result = await controller.handleProductQuery(request);
console.log('ผลลัพธ์:', JSON.stringify(result, null, 2));
/*
ตัวอย่างผลลัพธ์:
{
"success": true,
"source": "ai",
"data": {
"answer": "iPhone 15 Pro ราคา 45,900 บาท ค่ะ
มีให้เลือก 4 สี: Natural Titanium, Blue Titanium,
White Titanium และ Black Titanium ค่ะ",
"model": "gpt-4.1"
},
"metadata": {
"tokens": 245,
"cost": 0.00196,
"latency": 847
}
}
*/
} catch (error) {
console.error('Error:', error);
}
}
// Export for module usage
module.exports = { AIModel, AIView, AIController };
// Run if called directly
if (require.main === module) {
main();
}
Implementation ด้วย Python
สำหรับโปรเจกต์ที่ใช้ Python ผมมักจะใช้ FastAPI ซึ่งเข้ากันได้ดีกับ MVC Pattern และรองรับ Async ได้ดี ตัวอย่างนี้เป็น RAG (Retrieval-Augmented Generation) System ที่ผมพัฒนาให้องค์กรขนาดใหญ่แห่งหนึ่ง
# ============================================
AI API MVC Pattern - Python FastAPI Implementation
Enterprise RAG System with HolySheep AI
============================================
from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import JSONResponse
from pydantic import BaseModel, Field
from typing import List, Optional, Dict, Any
import httpx
import hashlib
import time
import os
from datetime import datetime, timedelta
from collections import defaultdict
============================================
MODEL LAYER: AI Core Components
============================================
class RAGModel:
"""Model Layer - จัดการ AI Logic ทั้งหมด"""
BASE_URL = "https://api.holysheep.ai/v1"
API_KEY = os.getenv("HOLYSHEEP_API_KEY", "YOUR_HOLYSHEEP_API_KEY")
# ราคา ณ ปี 2026 (USD per Million Tokens)
PRICES = {
"gpt-4.1": 8.00,
"claude-sonnet-4.5": 15.00,
"gemini-2.5-flash": 2.50,
"deepseek-v3.2": 0.42 # ประหยัดที่สุด!
}
def __init__(self):
self.client = httpx.AsyncClient(timeout=60.0)
self.vector_store = {} # Simulated vector database
async def embed_text(self, text: str, model: str = "text-embedding-3-small") -> List[float]:
"""สร้าง Embedding vector สำหรับ RAG"""
response = await self.client.post(
f"{self.BASE_URL}/embeddings",
headers={
"Authorization": f"Bearer {self.API_KEY}",
"Content-Type": "application/json"
},
json={
"model": model,
"input": text
}
)
if response.status_code != 200:
raise Exception(f"Embedding API Error: {response.text}")
data = response.json()
return data["data"][0]["embedding"]
def build_rag_prompt(self, retrieved_docs: List[Dict], query: str) -> List[Dict]:
"""สร้าง Prompt ที่มี Context จาก Retrieved Documents"""
system_prompt = """คุณเป็นผู้เชี่ยวชาญด้านเอกสารองค์กร
ใช้ข้อมูลจาก Context ที่ให้มาตอบคำถาม
ถ้าไม่มีข้อมูลใน Context ให้ตอบว่า "ไม่พบข้อมูลที่เกี่ยวข้อง"
อ้างอิงแหล่งที่มาทุกครั้ง"""
context = "\n\n".join([
f"[แหล่งที่มา {i+1}] {doc['content']}"
for i, doc in enumerate(retrieved_docs)
])
return [
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"Context:\n{context}\n\nคำถาม: {query}"}
]
async def generate_answer(
self,
messages: List[Dict],
model: str = "deepseek-v3.2" # ใช้ DeepSeek ประหยัดค่าใช้จ่าย
) -> Dict[str, Any]:
"""เรียก AI เพื่อสร้างคำตอบ"""
start_time = time.time()
response = await self.client.post(
f"{self.BASE_URL}/chat/completions",
headers={
"Authorization": f"Bearer {self.API_KEY}",
"Content-Type": "application/json"
},
json={
"model": model,
"messages": messages,
"max_tokens": 2048,
"temperature": 0.3
}
)
latency_ms = int((time.time() - start_time) * 1000)
if response.status_code != 200:
raise Exception(f"AI API Error: {response.text}")
data = response.json()
usage = data.get("usage", {})
# คำนวณค่าใช้จ่ายจริง
prompt_tokens = usage.get("prompt_tokens", 0)
completion_tokens = usage.get("completion_tokens", 0)
total_tokens = usage.get("total_tokens", 0)
price_per_token = self.PRICES.get(model, 8.00)
actual_cost = (total_tokens / 1_000_000) * price_per_token
return {
"answer": data["choices"][0]["message"]["content"],
"model": model,
"usage": {
"prompt_tokens": prompt_tokens,
"completion_tokens": completion_tokens,
"total_tokens": total_tokens
},
"cost": round(actual_cost, 6),
"latency_ms": latency_ms
}
def cosine_similarity(self, vec1: List[float], vec2: List[float]) -> float:
"""คำนวณ Cosine Similarity ระหว่าง 2 vectors"""
dot_product = sum(a * b for a, b in zip(vec1, vec2))
norm1 = sum(a * a for a in vec1) ** 0.5
norm2 = sum(b * b for b in vec2) ** 0.5
return dot_product / (norm1 * norm2 + 1e-8)
async def retrieve_documents(
self,
query: str,
top_k: int = 5,
threshold: float = 0.7
) -> List[Dict]:
"""ค้นหาเอกสารที่เกี่ยวข้องจาก Vector Store"""
query_vector = await self.embed_text(query)
results = []
for doc_id, doc_data in self.vector_store.items():
similarity = self.cosine_similarity(query_vector, doc_data["embedding"])
if similarity >= threshold:
results.append({
"id": doc_id,
"content": doc_data["content"],
"metadata": doc_data["metadata"],
"score": round(similarity, 4)
})
results.sort(key=lambda x: x["score"], reverse=True)
return results[:top_k]
async def add_document(
self,
content: str,
metadata: Dict = None
) -> str:
"""เพิ่มเอกสารเข้า Vector Store"""
doc_id = hashlib.md5(content.encode()).hexdigest()
embedding = await self.embed_text(content)
self.vector_store[doc_id] = {
"content": content,
"embedding": embedding,
"metadata": metadata or {},
"created_at": datetime.now().isoformat()
}
return doc_id
============================================
VIEW LAYER: Response Formatting
============================================
class RAGView:
"""View Layer - จัดการ Response Formatting"""
@staticmethod
def success_response(
answer: str,
sources: List[Dict],
metadata: Dict
) -> Dict[str, Any]:
return {
"success": True,
"data": {
"answer": answer,
"sources": [
{
"id": src["id"],
"content": src["content"][:200] + "..." if len(src["content"]) > 200 else src["content"],
"score": src["score"]
}
for src in sources
]
},
"metadata": {
"model": metadata.get("model"),
"tokens_used": metadata.get("usage", {}).get("total_tokens", 0),
"cost_usd": metadata.get("cost", 0),
"latency_ms": metadata.get("latency_ms", 0),
"sources_count": len(sources)
}
}
@staticmethod
def error_response(
code: str,
message: str,
details: Dict = None
) -> Dict[str, Any]:
response = {
"success": False,
"error": {
"code": code,
"message": message,
"timestamp": datetime.now().isoformat()
}
}
if details:
response["error"]["details"] = details
return response
@staticmethod
def rate_limit_response(retry_after: int) -> JSONResponse:
return JSONResponse(
status_code=429,
content={
"success": False,
"error": {
"code": "RATE_LIMITED",
"message": f"เกินขีดจำกัดการใช้งาน กรุณารอ {retry_after} วินาที",
"retry_after": retry_after
}
},
headers={"Retry-After": str(retry_after)}
)
============================================
CONTROLLER LAYER: Business Logic
============================================
class RAGController:
"""Controller Layer - จัดการ Business Logic และ Routing"""
def __init__(self):
self.model = RAGModel()
self.rate_limiter = RateLimiter(requests_per_minute=60)
self.query_cache = QueryCache(ttl_seconds=300)
async def handle_query(
self,
user_id: str,
query: str,
top_k: int = 5,
model: str = "deepseek-v3.2"
) -> Dict[str, Any]:
"""Main entry point สำหรับ Query"""
# 1. Rate Limiting Check
rate_check = self.rate_limiter.check(user_id)
if not rate_check["allowed"]:
return RAGView.rate_limit_response(rate_check["retry_after"])
# 2. Cache Check
cache_key = self.query_cache.generate_key(query)
cached = self.query_cache.get(cache_key)
if cached:
return {
**RAGView.success_response(
cached["answer"],
cached["sources"],
cached["metadata"]
),
"cached": True
}
try:
# 3. Retrieve Documents (Model Layer)
retrieved_docs = await self.model.retrieve_documents(
query,
top_k=top_k
)
if not retrieved_docs:
return RAGView.success_response(
"ไม่พบเอกสารที่เกี่ยวข้องกับคำถามของคุณ",
[],
{"model": model, "sources_count": 0}
)
# 4. Build RAG Prompt (Model Layer)
messages = self.model.build_rag_prompt(retrieved_docs, query)
# 5. Generate Answer (Model Layer)
result = await self.model.generate_answer(messages, model)
# 6. Format Response (View Layer)
response = RAGView.success_response(
result["answer"],
retrieved_docs,
result
)
# 7. Cache Result
self.query_cache.set(cache_key, {
"answer": result["answer"],
"sources": retrieved_docs,
"metadata": result
})
return response
except Exception as e:
print(f"RAG Controller Error: {str(e)}")
return RAGView.error_response(
"RAG_ERROR",
f"เกิดข้อผิดพลาดในระบบ: {str(e)}"
)
async def handle_document_ingestion(
self,
documents: List[Dict[str, Any]]
) -> Dict[str, Any]:
"""Ingest documents into RAG system"""
doc_ids = []
for doc in documents:
doc_id = await self.model.add_document(
content=doc["content"],
metadata=doc.get("metadata", {})
)
doc_ids.append(doc_id)
return {
"success": True,
"data": {
"ingested_count": len(doc_ids),
"document_ids": doc_ids
}
}
============================================
HELPER CLASSES
============================================
class RateLimiter:
"""Simple in-memory rate limiter"""
def __init__(self, requests_per_minute: int = 60):
self.requests_per_minute = requests_per_minute
self.requests = defaultdict(list)
def check(self, client_id: str) -> Dict[str, Any]:
now = time.time()
client_requests = self.requests[client_id]
# Remove old requests
self.requests[client_id] = [
ts for ts in client_requests
if now - ts < 60
]
if len(self.requests[client_id]) >= self.requests_per_minute:
oldest = self.requests[client_id][0]
retry_after = int(60 - (now - oldest)) + 1
return {"allowed": False, "retry_after": retry_after}
self.requests[client_id].append(now)
return {"allowed": True, "retry_after": 0}
class QueryCache:
"""Simple in-memory query cache"""
def __init__(self, ttl_seconds: int = 300):
self.cache = {}
self.ttl = ttl_seconds