Pango AIPango AI Team
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_enabled và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ị:
    1. 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).
    2. Idempotency Key: Luôn sinh một mã duy nhất idempotencyKey cho 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ý.
    3. Singleton Network Promise: Sử dụng cờ boolean hoặc khóa Promise ở tầng Service để ngăn chặn hai request sendAiMessage chạ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 = scrollHeight mà 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.
autoScrollHelper.ts
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 được COMPLETED hay FAILED, hãy chủ động gọi unsubscribe() 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 return hoặc onDestroy).
  • 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.