Bài viết này sẽ hướng dẫn bạn cách tích hợp một mô hình AI vào ứng dụng toàn bộ (full-stack) sử dụng Node.js và tạo giao diện người dùng đơn giản.
1. Chuẩn bị Dự Án và Cấu Hình Môi Trường
1.1 Kiểm Tra và Cấu Hình Môi Trường Node.js
Bắt đầu bằng việc kiểm tra phiên bản Node.js:
node --version
Nếu chưa cài đặt, tải từ trang chủ của Node.js.
Tạo thư mục dự án và khởi tạo:
mkdir ai_chat_app && cd ai_chat_app
npm init -y
Cài đặt các gói cần thiết:
npm install express http-client cors dotenv
Giải thích nhanh về các gói:
express: Framework web phổ biến cho Node.js.http-client: HTTP client để gọi API.cors: Middleware quản lý chia sẻ tài nguyên liên miền.dotenv: Quản lý biến môi trường.
1.2 Lấy Thông Tin Truy Cập API
Sau khi triển khai mô hình trên nền tảng GPU, lưu thông tin như URL cơ sở API và khóa API trong file .env.
touch .env
Thêm nội dung vào file .env:
AI_API_BASE_URL=https://your-api-endpoint/v1
AI_API_KEY=your-api-key-here
PORT=3000
2. Xây Dựng Dịch Vụ Backend với Node.js
2.1 Tạo Server Express và Định Nghĩa Route Cơ Bản
Tạo file server.js:
// server.js
require('dotenv').config();
const express = require('express');
const httpClient = require('http-client');
const cors = require('cors');
const app = express();
const port = process.env.PORT || 3000;
app.use(cors());
app.use(express.json());
const AI_API_BASE = process.env.AI_API_BASE_URL;
const AI_API_KEY = process.env.AI_API_KEY;
const aiApiClient = httpClient.create({
baseURL: AI_API_BASE,
headers: {
'Authorization': `Bearer ${AI_API_KEY}`,
'Content-Type': 'application/json',
},
});
app.get('/health', (req, res) => {
res.json({ status: 'OK', message: 'Backend is running.' });
});
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});
2.2 Thực Hiện Giao Tiếp Core API
Thêm route chat:
app.post('/api/chat', async (req, res) => {
try {
const userMessage = req.body.message;
if (!userMessage || !userMessage.trim()) {
return res.status(400).json({ error: 'Message cannot be empty.' });
}
const requestBody = {
model: 'light-ai-model',
messages: [{ role: 'user', content: userMessage }],
stream: false
};
const response = await aiApiClient.post('/chat/completions', requestBody);
const aiReply = response.data.choices[0]?.message?.content || 'No reply.';
res.json({
reply: aiReply,
usage: response.data.usage
});
} catch (error) {
console.error('Error:', error.response?.data || error.message);
res.status(500).json({ error: 'Failed to get response.', details: error.message });
}
});
2.3 Thực Hiện Xuất Ra Stream
Thực hiện xuất ra theo dạng stream:
app.post('/api/chat/stream', async (req, res) => {
const userMessage = req.body.message;
if (!userMessage || !userMessage.trim()) {
return res.status(400).json({ error: 'Message cannot be empty.' });
}
const requestBody = {
model: 'light-ai-model',
messages: [{ role: 'user', content: userMessage }],
stream: true
};
try {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
const apiResponse = await aiApiClient.post('/chat/completions', requestBody, { responseType: 'stream' });
apiResponse.data.pipe(res);
apiResponse.data.on('error', (err) => {
console.error('Stream error:', err);
if (!res.headersSent) {
res.status(500).end();
}
});
} catch (error) {
console.error('Error setting up stream:', error);
if (!res.headersSent) {
res.status(500).json({ error: 'Failed to establish stream.' });
}
}
});
3. Phát Triển Giao Diện Chat Frontend
3.1 Tạo Giao Diện HTML Cho Chat
Tạo file public/index.html:
<!DOCTYPE html>
<html lang="vi">
3.2 Thực Hiện Logic Tương Tác Frontend
Tạo file public/app.js:
document.addEventListener('DOMContentLoaded', function() {
const chatHistory = document.getElementById('chat-history');
const messageInput = document.getElementById('message-input');
const sendButton = document.getElementById('send-button');
function sendMessage() {
const message = messageInput.value.trim();
if (!message) return;
messageInput.disabled = true;
sendButton.disabled = true;
appendMessage(message, 'user');
messageInput.value = '';
fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: message })
}).then(response => response.json()).then(data => {
messageInput.disabled = false;
sendButton.disabled = false;
messageInput.focus();
appendMessage(data.reply, 'ai');
}).catch(error => {
console.error('Request failed:', error);
});
}
function appendMessage(text, sender) {
const messageElem = document.createElement('div');
messageElem.className = `message ${sender}-message`;
messageElem.textContent = text;
chatHistory.appendChild(messageElem);
chatHistory.scrollTop = chatHistory.scrollHeight;
}
sendButton.addEventListener('click', sendMessage);
messageInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
sendMessage();
}
});
messageInput.focus();
});
3.3 Cung Cấp Tệp Tĩnh Với Express
Trong file server.js, thêm dòng sau:
app.use(express.static('public'));
4. Đề Xuất Tối Ưu Hóa và Triển Khai
Xem xét các bước tối ưu hóa và triển khai để nâng cao hiệu suất và bảo mật.