Từ nối
- Trạng thái: Đã chấp thuận (Accepted)
- Ngày: 19-06-2026
- Chủ sở hữu: Đội ngũ Voice Engine (Voice Engine team)
1. Bối cảnh (Context)
Filler là cụm từ trì hoãn hoặc câu lấp liếm mà bot nói ở một nút nhận diện (nút listen) khi không có từ khóa (keyword) hoặc tuyến đường mặc định (default route) nào khớp, đồng thời lượt thoại hiện tại không phải là dạng chỉ nhận diện từ khóa (keyword-only). Nó giúp “kéo dài thời gian” trong lúc hệ thống chờ phản hồi thực tế từ mô hình ngôn ngữ lớn (LLM) được tạo ra, tránh để khách hàng gặp phải khoảng lặng im lặng gây khó chịu.
Hệ thống Voice Engine hiện hỗ trợ hai dạng kịch bản mẫu (archetypes) đòi hỏi các loại dữ liệu filler khác nhau:
| Dạng kịch bản | Các nút thoại hiện diện | Loại dữ liệu Filler |
|---|---|---|
| Thông thường (Normal) | Nút machine và/hoặc prompt (Dựa trên tính năng tổng hợp biểu cảm TTS) | Văn bản được tổng hợp linh hoạt qua TTS |
| Chỉ ghi âm (Record-only) | Chỉ có nút record (Sử dụng âm thanh ghi âm sẵn) | File âm thanh ghi âm sẵn (Audio file) |
Thiết kế cũ (Legacy): Trường cấu hình cũ sử dụng một danh sách phẳng dạng chuỗi ký tự (filler_words: list[str]). Mọi phần tử đều bị coi là văn bản TTS, và nếu danh sách này trống, hệ thống sẽ tự động dùng một danh sách mặc định toàn cục ẩn (DEFAULT_FILLER_WORDS) được nhúng sâu trong dịch vụ xử lý LLM. Thiết kế cũ này gặp phải 2 vấn đề lớn:
- Không hỗ trợ luồng phát file âm thanh: Kịch bản dạng chỉ ghi âm sẽ bỏ qua hoàn toàn filler và chuyển thẳng đến LLM, khiến người gọi phải nghe không gian im lặng khi mô hình đang xử lý. Một danh sách chuỗi văn bản thông thường không thể mang URL của file âm thanh.
- Sự phụ thuộc ngầm toàn cục: Việc tự động chuyển sang cấu hình mặc định khi danh sách trống làm cho hành vi filler của từng kịch bản trở nên khó đoán định và gắn chặt mã nguồn dịch vụ với các hằng số cấu hình hệ thống.
2. Quyết định Kỹ thuật (Decisions)
D1 — Sử dụng đối tượng kết nối (Connector Object) thay vì chuỗi văn bản phẳng
Filler hiện được mô hình hóa thành một cấu trúc dữ liệu đối tượng rõ ràng (FillerConnector) bao gồm các trường thông tin cụ thể:
| Trường dữ liệu | Kiểu dữ liệu | Ý nghĩa chức năng |
|---|---|---|
voice_type |
Chuỗi phân loại | Xác định cách filler được xử lý (nhận giá trị "voice_ai" hoặc "voice_record"). |
title |
Chuỗi văn bản | Nhãn hiển thị trực quan; đối với chế độ voice_ai, đây chính là nội dung văn bản để chạy TTS. |
file_url |
Đường dẫn / Null | URL trỏ tới file âm thanh ghi âm sẵn (Chỉ áp dụng cho chế độ voice_record). |
transcript |
Chuỗi văn bản / Null | Văn bản dịch lại của file âm thanh tương ứng (Chỉ áp dụng cho chế độ voice_record). |
D2 — Kịch bản mang danh sách connectors thay thế hoàn toàn cho trường cũ
Kịch bản hiện tại sẽ đính kèm một danh sách các đối tượng connector thô dưới dạng cấu trúc dict. Danh sách cấu hình này được truyền nguyên vẹn từ payload của tác vụ xuyên suốt qua quá trình xử lý cho đến khi bot chạy thực tế. Dữ liệu thô này không phân tích (parse) ngay mà sẽ xử lý muộn hơn ở tầng chạy kịch bản (Runner).
D3 — Chế độ thoại (Voice mode) được tự động suy diễn từ cấu trúc kịch bản
Hệ thống tự động phân tích cấu hình các cờ (flags) của nút thoại để đưa ra chế độ filler phù hợp:
- Chỉ có nút ghi âm (Cờ
record = True, các cờmachinevàprompt = False) $\rightarrow$ Hệ thống chọn chế độvoice_record. - Tất cả các trường hợp khác (Bao gồm cả khi thiếu cờ cấu hình) $\rightarrow$ Hệ thống mặc định chọn chế độ
voice_ai.
D4 — Việc lọc chế độ filler được xử lý tập trung tại tầng Runner
Tầng chạy kịch bản (Runner) là nơi duy nhất chuyển đổi danh sách dữ liệu thô thành danh sách đối tượng chuẩn hóa để dịch vụ LLM sử dụng. Hệ thống sẽ áp dụng bộ lọc nghiêm ngặt, chỉ giữ lại các connector đáp ứng đầy đủ điều kiện:
- Dữ liệu hợp lệ.
- Trường
voice_typekhớp chính xác với chế độ đã suy diễn ở bước D3. - Trường
titlekhông trống. Đặc biệt đối với chế độvoice_record, bắt buộc cảfile_urlvàtranscriptphải chứa giá trị thực tế (chuỗi rỗng sẽ lập tức bị loại bỏ). Nhờ việc lọc trước này, dịch vụ LLM nhận được danh sách hoàn toàn sạch và không cần kiểm tra lại loại chế độ nữa.
D5 — Loại bỏ cơ chế tự động dùng giá trị mặc định (No default fallback)
Hằng số mặc định cũ cùng các hàm làm sạch tự động đã bị xóa bỏ hoàn toàn. Khi danh sách connector trống, hệ thống hiểu là không sử dụng câu lấp liếm: lượt thoại nhận diện không khớp sẽ chuyển thẳng đến LLM. Thay đổi này giúp hành vi của từng kịch bản tường minh, không còn các cài đặt ẩn.
D6 — Hai luồng xử lý Streaming độc lập cho mỗi chế độ
Tại hàm xử lý kết nối hội thoại, khi không khớp từ khóa và cần chạy câu lấp liếm:
- Đối với chế độ
voice_ai: Nếu có connector hợp lệ, hệ thống sẽ lấy chuỗi văn bản từ trườngtitle, thực hiện cắt câu ngắn và đẩy lười (lazy) vào trước luồng dữ liệu phản hồi từ LLM. Cơ chế này giúp phản hồi câu lấp liếm phát ra cực nhanh để giữ chân người gọi. - Đối với chế độ
voice_record: Nếu connector được chọn có đầy đủ URL file âm thanh và văn bảntranscript, hệ thống sẽ chủ động đẩy ngay một khung dữ liệu âm thanh vào hàng đợi để trình phát âm thanh kích hoạt clip ngay lập tức, sau đó gọi xử lý LLM song song. Nếu thông tin thiếu hụt, hệ thống bỏ qua filler và gọi trực tiếp LLM.
D7 — Cơ chế lựa chọn ngẫu nhiên đơn giản
Hệ thống thực hiện chọn ngẫu nhiên một câu lấp liếm từ danh sách bằng hàm chọn ngẫu nhiên tiêu chuẩn. Bước này hoàn toàn không cần lọc lại chế độ vì danh sách đầu vào đã được chuẩn hóa thuần khiết từ trước đó.
3. Hệ quả (Consequences)
- Điểm tích cực: Quản lý tập trung thông qua một danh sách duy nhất; trải nghiệm kịch bản chỉ ghi âm mượt mượt hơn nhờ xóa bỏ khoảng im lặng tuyệt đối; hành vi kịch bản hoàn toàn minh bạch.
- Điểm hạn chế / Lưu ý: Ràng buộc cấu trúc dữ liệu cực kỳ nghiêm ngặt (sai lệch khóa sẽ gây lỗi
TypeError); cần sự đồng bộ định nghĩa cấu trúc cờ nút thoại giữa các tầng xử lý khác nhau.
Xác nhận hành vi quan trọng: Cần lưu ý rằng câu lấp liếm dạng âm thanh ghi sẵn (voice_record filler) đóng vai trò là clip phát chờ trước khi có phản hồi từ LLM chứ không phải thay thế cho phản hồi đó. Hệ thống luôn gọi LLM song song để lấy câu trả lời thực tế từ mô hình.