Bài 35.5: Bài tập thực hành chatbot AI

Bài 35.5: Bài tập thực hành chatbot AI
Chào mừng trở lại với chuỗi bài học Lập trình Web Front-end! Trong bài này, chúng ta sẽ bước vào một thế giới đầy hứa hẹn và thú vị: thế giới của Trí tuệ Nhân tạo (AI). Cụ thể, chúng ta sẽ tập trung vào việc thực hành xây dựng một giao diện đơn giản để tương tác với một chatbot AI.
Tại sao một Front-end Developer lại cần biết về điều này? Bởi vì tích hợp AI vào các ứng dụng web đang trở thành một xu hướng ngày càng phổ biến và quan trọng. Từ những chatbot hỗ trợ khách hàng, công cụ sáng tạo nội dung, cho đến các hệ thống phân tích dữ liệu phức tạp, AI đang thay đổi cách chúng ta xây dựng và sử dụng web.
Bài tập thực hành này sẽ giúp bạn hiểu được luồng hoạt động cơ bản khi kết nối giao diện người dùng (front-end) với một hệ thống AI (thường thông qua API backend). Chúng ta sẽ không đi sâu vào cách huấn luyện AI, mà sẽ tập trung vào việc làm thế nào để gửi yêu cầu của người dùng tới AI và hiển thị phản hồi nhận được.
Hãy cùng bắt tay vào xây dựng!
Chuẩn bị Giao diện Cơ bản
Để bắt đầu, chúng ta cần một giao diện tối thiểu để người dùng có thể nhập tin nhắn và xem phản hồi từ chatbot. Một cấu trúc HTML đơn giản sẽ bao gồm:
- Một khu vực để hiển thị các tin nhắn (lịch sử trò chuyện).
- Một ô nhập liệu để người dùng gõ tin nhắn.
- Một nút để gửi tin nhắn.
Đây là mã HTML cơ bản cho cấu trúc này:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Simple AI Chatbot</title>
<style>
/* CSS tối thiểu để dễ nhìn */
#chat-container {
width: 400px;
margin: 50px auto;
border: 1px solid #ccc;
padding: 10px;
box-shadow: 2px 2px 5px rgba(0,0,0,0.1);
font-family: sans-serif;
}
#message-area {
height: 300px;
border: 1px solid #eee;
overflow-y: auto;
padding: 10px;
margin-bottom: 10px;
}
.message {
margin-bottom: 8px;
padding: 5px 10px;
border-radius: 5px;
}
.user-message {
text-align: right;
background-color: #dcf8c6;
margin-left: 50px; /* Example styling */
}
.ai-message {
text-align: left;
background-color: #e9e9eb;
margin-right: 50px; /* Example styling */
}
#input-area {
display: flex;
}
#user-input {
flex-grow: 1;
padding: 8px;
border: 1px solid #ccc;
margin-right: 5px;
}
#send-button {
padding: 8px 15px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
#send-button:hover {
background-color: #0056b3;
}
</style>
</head>
<body>
<div id="chat-container">
<div id="message-area">
<!-- Messages will appear here -->
<div class="ai-message message">Xin chào! Tôi có thể giúp gì cho bạn?</div>
</div>
<div id="input-area">
<input type="text" id="user-input" placeholder="Nhập tin nhắn của bạn...">
<button id="send-button">Gửi</button>
</div>
</div>
<script src="script.js"></script> <!-- Our JavaScript file -->
</body>
</html>
Giải thích Code HTML:
- Chúng ta tạo một
div
chính vớiid="chat-container"
để chứa toàn bộ khung chat. - Bên trong,
div id="message-area"
sẽ là nơi hiển thị các tin nhắn. Thuộc tínhoverflow-y: auto;
trong CSS giúp khu vực này có thanh cuộn khi nội dung quá dài. div id="input-area"
chứa ô nhập liệu (input type="text"
) và nút gửi (button
).- Chúng ta thêm một chút CSS cơ bản để các phần tử này hiển thị dễ nhìn hơn, phân biệt tin nhắn người dùng và AI bằng class
user-message
vàai-message
. - Cuối cùng, chúng ta liên kết tới một file JavaScript (
script.js
) nơi chứa logic xử lý.
Xử lý Logic với JavaScript
Phần chính của bài thực hành nằm ở đây: sử dụng JavaScript để kết nối giao diện với khả năng xử lý của AI. Giả định rằng chúng ta có một điểm cuối API (endpoint) ở backend (hoặc một dịch vụ AI bên ngoài) mà chúng ta có thể gửi tin nhắn của người dùng tới đó và nhận lại phản hồi.
Chúng ta sẽ cần các bước sau:
- Lấy nội dung tin nhắn từ ô nhập liệu khi nút "Gửi" được nhấn.
- Hiển thị tin nhắn của người dùng lên khu vực hiển thị tin nhắn.
- Gửi tin nhắn này đến API của AI.
- Nhận phản hồi từ AI.
- Hiển thị phản hồi của AI lên khu vực hiển thị tin nhắn.
- Xóa nội dung trong ô nhập liệu sau khi gửi.
Hãy tạo file script.js
và viết mã:
// Lấy các phần tử DOM cần thiết
const messageArea = document.getElementById('message-area');
const userInput = document.getElementById('user-input');
const sendButton = document.getElementById('send-button');
// Hàm để tạo và hiển thị một tin nhắn mới trong khung chat
function displayMessage(message, sender) {
const messageElement = document.createElement('div');
messageElement.classList.add('message'); // Thêm class chung
messageElement.classList.add(sender + '-message'); // Thêm class riêng cho người gửi (user/ai)
messageElement.textContent = message; // Nội dung tin nhắn
messageArea.appendChild(messageElement); // Thêm tin nhắn vào khu vực hiển thị
// Cuộn xuống cuối để xem tin nhắn mới nhất
messageArea.scrollTop = messageArea.scrollHeight;
}
// Hàm chính để xử lý việc gửi tin nhắn
async function sendMessage() {
const message = userInput.value.trim(); // Lấy nội dung và loại bỏ khoảng trắng thừa
if (!message) {
return; // Không làm gì nếu ô nhập liệu trống
}
// 1. Hiển thị tin nhắn của người dùng ngay lập tức
displayMessage(message, 'user');
// Xóa nội dung trong ô nhập liệu
userInput.value = '';
// Vô hiệu hóa nút gửi và ô nhập liệu trong khi chờ AI trả lời
sendButton.disabled = true;
userInput.disabled = true;
userInput.placeholder = 'Đang chờ phản hồi từ AI...';
try {
// 2. Gửi tin nhắn đến API của AI
// CHÚ Ý: Thay '/api/chat' bằng URL endpoint thực tế của bạn.
// Endpoint này sẽ nhận tin nhắn, gửi đến AI (ví dụ: OpenAI, Gemini...), và trả về phản hồi.
const response = await fetch('/api/chat', {
method: 'POST', // Gửi dữ liệu
headers: {
'Content-Type': 'application/json' // Cho biết nội dung gửi đi là JSON
},
body: JSON.stringify({ prompt: message }) // Chuyển tin nhắn thành chuỗi JSON và gửi đi
});
if (!response.ok) {
// Xử lý lỗi nếu response không thành công (ví dụ: status 400, 500)
throw new Error(`Lỗi HTTP: ${response.status}`);
}
const data = await response.json(); // Phân tích phản hồi từ API (mong đợi là JSON)
// 3. Hiển thị phản hồi từ AI
// Giả định API trả về JSON có cấu trúc như { text: "Phản hồi của AI..." }
const aiResponseText = data.text || "Xin lỗi, tôi không hiểu."; // Lấy phản hồi hoặc thông báo lỗi mặc định
displayMessage(aiResponseText, 'ai');
} catch (error) {
// Xử lý lỗi trong quá trình gọi API
console.error('Lỗi khi gọi API AI:', error);
displayMessage('Đã xảy ra lỗi khi kết nối với AI.', 'ai'); // Thông báo lỗi cho người dùng
} finally {
// Luôn kích hoạt lại nút gửi và ô nhập liệu sau khi xử lý xong (dù thành công hay thất bại)
sendButton.disabled = false;
userInput.disabled = false;
userInput.placeholder = 'Nhập tin nhắn của bạn...';
userInput.focus(); // Tự động focus lại vào ô nhập liệu
}
}
// Lắng nghe sự kiện click vào nút Gửi
sendButton.addEventListener('click', sendMessage);
// Lắng nghe sự kiện nhấn phím Enter trong ô nhập liệu
userInput.addEventListener('keypress', function(event) {
// Kiểm tra nếu phím được nhấn là 'Enter' (mã 13 hoặc 'Enter')
if (event.key === 'Enter' || event.keyCode === 13) {
event.preventDefault(); // Ngăn chặn hành động mặc định (xuống dòng)
sendMessage(); // Gọi hàm gửi tin nhắn
}
});
Giải thích Code JavaScript:
- DOM References: Lấy tham chiếu đến các phần tử HTML chúng ta đã tạo bằng
document.getElementById
. Điều này giúp chúng ta dễ dàng tương tác với chúng trong mã JS. displayMessage(message, sender)
Function:- Hàm này có nhiệm vụ tạo một
div
mới cho mỗi tin nhắn. - Nó thêm các class CSS (
message
vàuser-message
hoặcai-message
) để sau này dễ dàng tạo kiểu. - Đặt nội dung tin nhắn bằng
textContent
. - Thêm
div
tin nhắn vàomessageArea
. - Quan trọng:
messageArea.scrollTop = messageArea.scrollHeight;
giúp tự động cuộn xuống cuối cùng của khu vực hiển thị tin nhắn, đảm bảo người dùng luôn nhìn thấy tin nhắn mới nhất.
- Hàm này có nhiệm vụ tạo một
sendMessage()
Function:- Đây là hàm bất đồng bộ (
async
) vì chúng ta sẽ thực hiện việc gọi API (fetch
) mất một khoảng thời gian. - Nó lấy giá trị từ
userInput
, sử dụng.trim()
để loại bỏ khoảng trắng ở đầu và cuối. - Nếu tin nhắn rỗng, hàm dừng lại (
return
). - Bước 1: Hiển thị tin nhắn người dùng: Gọi
displayMessage
để thêm tin nhắn của người dùng vào giao diện ngay lập tức. Điều này tạo cảm giác phản hồi nhanh. - Xóa và Vô hiệu hóa: Xóa nội dung ô nhập liệu và tạm thời vô hiệu hóa ô nhập liệu/nút gửi để ngăn người dùng gửi nhiều tin nhắn cùng lúc khi đang chờ phản hồi.
- Khối
try...catch...finally
: Được sử dụng để xử lý các thao tác có thể gây lỗi (như gọi API) và đảm bảo rằng các hành động dọn dẹp (kích hoạt lại nút/ô nhập liệu) luôn được thực hiện. - Bước 2: Gọi API với
fetch
:await fetch('/api/chat', {...})
: Sử dụngfetch
để gửi yêu cầu HTTP POST đến URL/api/chat
. Đây là điểm mà front-end của bạn giao tiếp với backend (hoặc một dịch vụ AI trực tiếp nếu API cho phép).method: 'POST'
: Chỉ định đây là yêu cầu gửi dữ liệu.headers: { 'Content-Type': 'application/json' }
: Báo cho server biết rằng dữ liệu trong body là JSON.body: JSON.stringify({ prompt: message })
: Chuyển đổi đối tượng JavaScript{ prompt: message }
thành một chuỗi JSON để gửi đi.prompt
là tên trường dữ liệu mà API của AI thường mong đợi.
- Xử lý Phản hồi:
if (!response.ok)
: Kiểm tra xem yêu cầu có thành công không (mã trạng thái HTTP 2xx). Nếu không, ném lỗi.const data = await response.json()
: Phân tích phản hồi nhận được từ server, giả định nó là JSON.- Bước 3: Hiển thị phản hồi AI: Lấy phần
text
từ dữ liệu phản hồi (data.text
) và hiển thị nó bằng cách gọidisplayMessage
với người gửi là'ai'
.
- Xử lý Lỗi (
catch
): Nếu có bất kỳ lỗi nào xảy ra trong quá trìnhfetch
hoặc xử lý phản hồi, lỗi sẽ được bắt ở đây, ghi log ra console, và hiển thị một tin nhắn lỗi đơn giản cho người dùng. - Kết thúc (
finally
): Dù có lỗi hay không, phần này luôn chạy để kích hoạt lại nút gửi và ô nhập liệu, sẵn sàng cho tin nhắn tiếp theo.
- Đây là hàm bất đồng bộ (
- Event Listeners:
sendButton.addEventListener('click', sendMessage);
: Khi nút "Gửi" được click, hàmsendMessage
sẽ được gọi.userInput.addEventListener('keypress', ...)
: Cho phép người dùng nhấn phím Enter trong ô nhập liệu để gửi tin nhắn, giống như nhấn nút Gửi.event.preventDefault()
rất quan trọng để ngăn hành vi mặc định của phím Enter (thường là xuống dòng hoặc gửi form theo cách truyền thống).
Chạy Thử Bài Thực hành
Để chạy bài thực hành này, bạn cần:
- Lưu mã HTML trên vào một file tên là
index.html
. - Lưu mã JavaScript trên vào một file tên là
script.js
trong cùng thư mục vớiindex.html
. - Mở file
index.html
trong trình duyệt của bạn.
Bạn sẽ thấy giao diện khung chat đơn giản. Khi bạn gõ tin nhắn và nhấn "Gửi" (hoặc Enter), tin nhắn của bạn sẽ xuất hiện. Tuy nhiên, vì chúng ta chưa có một backend thực sự tại /api/chat
để kết nối với AI, bạn sẽ thấy lỗi trong console của trình duyệt (thường là lỗi 404 Not Found hoặc lỗi mạng) và tin nhắn lỗi "Đã xảy ra lỗi khi kết nối với AI." sẽ hiển thị.
Đây là điều hoàn toàn bình thường cho bài thực hành này! Mục tiêu chính là giúp bạn làm quen với:
- Cấu trúc HTML cho giao diện chat.
- Cách sử dụng JavaScript để lấy dữ liệu nhập từ người dùng.
- Cách sử dụng
fetch
để mô phỏng việc gửi dữ liệu đến một API backend/AI. - Cách xử lý phản hồi (dù trong ví dụ này là xử lý lỗi).
- Cách cập nhật giao diện (thêm tin nhắn).
Để thực sự làm việc với một AI, bạn sẽ cần một backend server hoặc sử dụng các SDK/thư viện AI nếu nhà cung cấp AI cho phép gọi trực tiếp từ front-end (ít phổ biến do vấn đề bảo mật API key). Backend server sẽ là nơi an toàn để lưu trữ API key của AI và xử lý các yêu cầu phức tạp hơn.
Nâng Cao (Ý tưởng)
Sau khi nắm vững các khái niệm cơ bản này, bạn có thể thử sức với các bài tập nâng cao hơn:
- Lưu trữ lịch sử chat: Thay vì chỉ hiển thị tin nhắn hiện tại, lưu toàn bộ cuộc hội thoại (mảng các đối tượng
{ role: 'user'/'ai', content: '...' }
) và gửi toàn bộ lịch sử này lên AI trong mỗi yêu cầu mới (để AI có ngữ cảnh). - Hiển thị trạng thái gõ: Thêm một indicator "AI is typing..." khi đang chờ phản hồi.
- Format tin nhắn: Cho phép tin nhắn có định dạng cơ bản (bold, italic, code block) nếu API AI hỗ trợ trả về markdown hoặc HTML.
- Xử lý streaming: Một số API AI hỗ trợ trả về phản hồi theo từng phần (streaming). Tìm hiểu cách xử lý luồng dữ liệu nhận được để hiển thị phản hồi của AI từng chữ một thay vì chờ toàn bộ phản hồi.
- Kết nối với backend: Nếu có kiến thức về backend (Node.js, Python, v.v.), hãy xây dựng một endpoint
/api/chat
thực tế, sử dụng thư viện client của các dịch vụ AI (nhưopenai
,google-generative-ai
) để gọi API AI và trả kết quả về front-end.
Bài tập thực hành này là bước đệm quan trọng để bạn có thể bắt đầu tích hợp sức mạnh của AI vào các ứng dụng web của mình. Việc hiểu luồng dữ liệu và cách tương tác giữa front-end và backend/AI là kỹ năng không thể thiếu trong kỷ nguyên số hiện nay.
Hãy thử nghiệm, tùy chỉnh mã và tìm hiểu sâu hơn về các API AI khác nhau. Chúc bạn thực hành hiệu quả!
Comments