Module 01: Chatbot Service (API)
Module 01 • API Gateway
Best Practices Khi Tích Hợp Chatbot Service
Để đảm bảo trải nghiệm người dùng cuối đạt mức mượt mà cao nhất, không bị giật lag hay treo trạng thái loading, Pango AI Team khuyến nghị tuân thủ các quy tắc sau:
1. Tránh Hiện Tượng Chớp Nút Chat Khi Mở App
Vấn Đề Thường Gặp
Nhiều ứng dụng vẽ ngay nút Chat trên giao diện, sau đó mới gọi API kiểm tra. Nếu bot đang tắt (
isEnabled: false), nút chat sẽ đột ngột biến mất, gây cảm giác chớp nháy khó chịu cho người dùng.
Giải pháp:
- Lưu cache trạng thái
agent_is_enabledvào Local Storage từ phiên mở app trước đó. - Trong lúc gọi
fetchAgentConfigDetail, giữ nguyên trạng thái cache cũ (hoặc ẩn mờ nhẹ với hiệu ứng skeleton / opacity). - Chỉ cập nhật hiển thị sau khi API phản hồi.
2. Khóa Trùng Lặp & Chống Đúp Request (Concurrency Lock & Idempotency)
- Vấn đề: Người dùng bấm liên tục vào nút "Gửi" nhiều lần hoặc mạng chập chờn gửi 2 request cùng lúc.
- Khuyến nghị:
- Disable UI: Vô hiệu hóa nút gửi ngay khi người dùng chạm vào (Disable input & submit button).
- Idempotency Key: Luôn sinh một mã duy nhất
idempotencyKeycho mỗi câu hỏi (kèm timestamp mili-giây và random hash). Nếu người dùng bấm 2 lần cùng một key, Gateway sẽ bỏ qua request trùng và trả về phiên đang xử lý. - Singleton Network Promise: Sử dụng cờ boolean hoặc khóa Promise ở tầng Service để ngăn chặn hai request
sendAiMessagechạy song song.
3. Mặc Định Cuộn Xuống Tin Nhắn Cuối Cùng (Smooth Auto-Scroll)
Khi người dùng mở khung chat hoặc sau khi nạp xong lịch sử tin nhắn từ API MyConversation:
- Gán ngay lập tức
scrollTop = scrollHeightmà không dùng animation trễ, giúp người dùng thấy ngay câu trả lời mới nhất mà không phải vuốt màn hình. - Trong quá trình streaming câu trả lời realtime từ Firestore: Kiểm tra nếu người dùng đang ở đáy màn hình thì tự động cuộn theo từng dòng chữ mới sinh ra; nếu người dùng đang chủ động cuộn ngược lên trên để đọc lịch sử, hãy tạm ngưng auto-scroll để tránh giật vị trí đọc.
export function scrollToBottom(element: HTMLElement, smooth = false) {
if (!element) return;
element.scrollTo({
top: element.scrollHeight,
behavior: smooth ? 'smooth' : 'auto',
});
}4. Thiết Lập Timeout An Toàn 180s Cho Firebase Listener
- Mặc dù Firebase Firestore có cơ chế tự động kết nối lại khi rớt mạng, một số trường hợp thiết bị chuyển mạng (4G sang Wi-Fi hoặc đi vào thang máy) có thể khiến socket bị kẹt.
- Luôn bao bọc listener bằng một
setTimeout(..., 180000)(180 giây). Nếu sau thời gian này mà chưa nhận đượcCOMPLETEDhayFAILED, hãy chủ động gọiunsubscribe()và thông báo cho người dùng: "Kết nối gián đoạn, quý khách vui lòng bấm gửi lại."
5. Giải Phóng Bộ Nhớ Khi Đóng Màn Hình (Teardown & Cleanup)
Trong React / React Native / Vue / Flutter, khi người dùng thoát khỏi màn hình chat hoặc đóng widget:
- Luôn gọi hàm
unsubscribe()của Firestore listener trong hàm cleanup (useEffect returnhoặconDestroy). - Không để tồn tại các listener ngầm (Zombie listeners) gây rò rỉ bộ nhớ (Memory Leak) và tốn pin thiết bị của người dùng.