Nhận diện giới tính VAD
- Trạng thái: Đã phê duyệt (Accepted)
- Ngày: 18-06-2026 (Cập nhật: 23-06-2026)
- Chủ sở hữu: Đội ngũ Voice Engine
1. Bối cảnh (Context)
Pipecat chia luồng kiểm soát lượt thoại thành ba phân hệ độc lập. Voice Engine cần phải duy trì tính nhất quán giữa các phân hệ này:
- Chiến lược tắt tiếng người dùng (User-mute strategies): Ngăn chặn hoặc cho phép luồng âm thanh/bản dịch văn bản (transcription) truyền đi. Khi chiến lược tắt tiếng trả về
True, các khung dữ liệu âm thanh thô, dữ liệu STT (bao gồm cả kết quả tạm thời và kết quả cuối cùng) và các tín hiệu trạng thái nói của người dùng sẽ bị loại bỏ hoàn toàn trước khi tới các bộ xử lý phía sau. - Chiến lược lượt nói của người dùng (User-turn strategies): Phát hiện thời điểm người dùng bắt đầu hoặc kết thúc lượt nói. Khi người dùng bắt đầu nói, hệ thống có thể phát đi một tín hiệu ngắt (
InterruptionFrame). Cờ cho phép ngắt (enable_interruptions) được cấu hình cố định tại chiến lược bắt đầu nói (start strategy) chứ không nằm ở chiến lược kết thúc. - Bộ quản lý luồng (Flow manager): Là bộ quản lý trạng thái kịch bản (scenario-state machine). Trong chế độ
VAD_INTERRUPT, khi người dùng nói chen ngang lúc bot đang nói, Pipecat sẽ xóa toàn bộ các khung dữ liệu có thể bị ngắt khỏi hàng đợi xử lý. Hành động này sẽ vô tình làm mất khung lệnh thực thi sau khi nói (FunctionActionFrame). Nếu không có cơ chế tự động chuyển trạng thái, hệ thống sẽ bị kẹt mãi mãi tại node nói hiện tại.
Nguyên lý phối hợp quan trọng: Nếu chiến lược tắt tiếng (Mute) loại bỏ âm thanh của người dùng, chiến lược bắt đầu nói (Start Strategy) sẽ không bao giờ nhận được văn bản dịch (transcription) và do đó không bao giờ kích hoạt lệnh ngắt (InterruptionFrame). Việc xem xét chúng độc lập trước đây đã gây ra lỗi lãng phí tài nguyên hoặc lỗi im lặng hệ thống.
Voice Engine cung cấp 3 chế độ luồng thông qua cấu hình ConversationTurnFlow tương ứng với các trải nghiệm người dùng (UX) khác nhau:
| Chế độ Luồng (Flow) | Cam kết Trải nghiệm Người dùng (UX Contract) |
|---|---|
BASIC |
Luân phiên lượt nói nghiêm ngặt. Bot nói xong hoàn toàn thì người dùng mới nói. Không cho phép nói chen ngang khi bot đang nói. |
VAD_LISTEN |
Lượt nói lịch sự gồm nhiều câu. Người dùng có thể nói nhiều câu liên tục; việc ngắt lời chỉ được phép diễn ra GIỮA các lượt nói của bot, chứ không được ngắt khi bot đang phát âm thanh. |
VAD_INTERRUPT |
Phản hồi nhanh, nhạy bén. Người dùng có thể cắt ngang lời bot bất cứ lúc nào bằng cách nói đè lên âm thanh của bot. |
2. Các Quyết định Kỹ thuật (Decisions)
D1 — Hai chiến lược, một nguồn kiểm soát ý định cho mỗi luồng
Mỗi luồng hội thoại sẽ thiết lập đồng thời cả Chiến lược tắt tiếng (Mute Strategy) và Chiến lược bắt đầu lượt nói (Start Strategy). Chiến lược tắt tiếng đóng vai trò là người gác cổng; chiến lược bắt đầu lượt nói chỉ được kích hoạt khi âm thanh được phép đi qua. Hành vi cụ thể của từng luồng được đóng gói trực tiếp bên trong chính các chiến lược đó, giúp bộ dựng mã nguồn (builder) luôn tinh giản và không chứa logic phức tạp.
D2 — Chặn âm thanh trước câu nói đầu tiên của Bot là quy định bắt buộc cho tất cả các luồng
Cả 3 chế độ luồng đều tắt tiếng người dùng cho đến khi bot phát ra khung âm thanh đầu tiên (BotStartedSpeakingFrame). Nếu không có cổng chặn này, gói âm thanh đầu tiên của người dùng sẽ tranh chấp với câu thoại mở đầu của bot. Cờ hiệu nói đầu tiên này sẽ không bị xóa khi reset hệ thống — một khi bot đã mở lời, người dùng sẽ không bao giờ bị tắt tiếng vô thời hạn trong suốt thời gian phiên thoại còn lại.
Ngoài ra, chế độ BASIC và VAD_LISTEN sẽ tắt tiếng người dùng trong suốt khoảng thời gian thiết lập node (node-setup window — khoảng thời gian FlowManager đang đồng bộ thông điệp của nhà phát triển và schema của công cụ lên ngữ cảnh LLM). Chế độ VAD_INTERRUPT KHÔNG tắt tiếng trong cửa sổ thiết lập này để đảm bảo người dùng có thể nói vào bất kỳ khoảnh khắc nào.
D3 — Ma trận quyết định tắt tiếng (Mute Decision Matrix)
Chiến lược tắt tiếng sẽ trả về giá trị True (Tắt tiếng) khi bất kỳ điều kiện nào trong cột của luồng đó được thỏa mãn:
| Chế độ Luồng | bot_speaking |
function_call_in_progress |
llm_response_in_progress |
node_setup_count > 0 |
not first_speech_started |
|---|---|---|---|---|---|
VAD_LISTEN |
Tắt tiếng | Tắt tiếng | Tắt tiếng | Tắt tiếng | Tắt tiếng |
VAD_INTERRUPT |
Không | Tắt tiếng | Tắt tiếng | Không | Tắt tiếng |
Lưu ý quan trọng: Điểm mấu chốt của VAD_INTERRUPT nằm ở dòng bot_speaking và node_setup_count: Âm thanh được phép truyền đi khi bot đang nói và khi đang chuyển giữa các node để bộ STT có thể dịch văn bản, từ đó kích hoạt lệnh ngắt (InterruptionFrame). Chế độ BASIC và VAD_LISTEN tắt tiếng ở các trạng thái này nên chiến lược của chúng không thể kích hoạt lệnh ngắt giữa chừng.
D4 — Chiến lược bắt đầu: Quy tắc kích hoạt và Quỹ hạn mức ngắt lời
Quy tắc kích hoạt (Trigger rules):
| Chế độ Luồng | Khi Bot đang nói (Bot speaking) | Trường hợp khác (Otherwise) |
|---|---|---|
BASIC |
Không bao giờ kích hoạt | Số lượng từ $\ge 1$ |
VAD_LISTEN |
Không bao giờ kích hoạt | Số lượng từ $\ge 1$ |
VAD_INTERRUPT |
Số lượng từ $\ge \text{min_words}$ (mặc định 3) | Số lượng từ $\ge 1$ |
Quy tắc cho phép ngắt lời (enable_interruptions rules):
BASIC: Không bao giờ cho phép ngắt.VAD_LISTEN: Cho phép khi Bot KHÔNG nói VÀ còn trong hạn mức cho phép.VAD_INTERRUPT: Luôn cho phép khi còn trong hạn mức (không phụ thuộc vào trạng thái của bot).
D5 — Quỹ hạn mức ngắt lời hợp nhất, làm mới theo mỗi lượt của bot
Cả hai chế độ VAD_LISTEN và VAD_INTERRUPT đều chia sẻ chung một cấu hình hạn mức: INTERRUPTION_UTTERANCE_LIMIT (Mặc định: 3 lượt) và INTERRUPTION_WORD_LIMIT (Mặc định: 50 từ). Hạn mức này được đặt lại (reset) mỗi khi nhận được tín hiệu BotStartedSpeakingFrame, cấp cho người dùng một hạn mức ngắt lời mới cho mỗi câu nói của bot.
D6 — Chỉ có văn bản dịch cuối cùng (Final Transcription) mới khấu trừ hạn mức
Các bộ đếm số phát ngôn và số từ chỉ tăng lên khi nhận được kết quả dịch cuối cùng (TranscriptionFrame), chứ không tăng theo kết quả tạm thời (InterimTranscriptionFrame) nhằm tránh việc hết hạn mức chỉ vì một câu nói dài. Việc kích hoạt ngắt vẫn sử dụng kết quả tạm thời để đảm bảo phản hồi tức thì.
D7 — Đóng gói cửa sổ thiết lập node bằng các khung điều khiển
Mọi nhánh thiết lập node đều đi qua hàm tập trung FlowManager._set_node. Hàm này phát ra tín hiệu NodeSetupStartedFrame trước khi thiết lập ngữ cảnh mới và phát ra NodeSetupCompletedFrame trong khối lệnh finally sau khi hoàn tất. Việc này giúp đảm bảo người dùng bị tắt tiếng trong suốt quá trình chuẩn bị dữ liệu, tránh việc người dùng phản hồi dựa trên một ngữ cảnh cũ/lỗi thời.
D8 — Cơ chế dự phòng của bộ quản lý luồng cho VAD_INTERRUPT tại node nói (Speak Node)
Trong chế độ VAD_INTERRUPT, khi xảy ra ngắt lời, Pipecat sẽ xóa sạch các khung dữ liệu không thuộc loại UninterruptibleFrame trong hàng đợi, dẫn đến lệnh chuyển tiếp sau khi nói (FunctionActionFrame) bị mất, khiến bot bị kẹt tại node nói hiện tại. Để xử lý, hệ thống đăng ký theo dõi tín hiệu InterruptionFrame (vốn là tín hiệu hệ thống chạy trực tiếp không bị xóa) để chủ động chuyển đổi trạng thái: Nếu node hiện tại là Speak Node, hệ thống sẽ tự động ép luồng nhảy sang node tiếp theo theo nhánh mặc định.
D9 — Cơ chế bảo vệ chống lệnh chuyển tiếp lỗi thời (Stale Post-Action Guard)
Để tránh xung đột chạy đua (race condition) giữa hai lệnh chuyển tiếp (một từ cơ chế dự phòng ngắt lời ở D8 và một từ lệnh thực thi cũ chạy muộn của bot), hàm set_next_node được tích hợp bộ lọc thông minh: Hệ thống sẽ tự động hủy bỏ các lệnh chuyển tiếp xuất phát từ hành động cũ chạy muộn nếu node yêu cầu không còn trùng khớp với node hiện tại của hệ thống.
D10 — Cấu hình respond_immediately=False cho node lắng nghe sau khi bị ngắt lời
Sau khi bị ngắt lời bởi người dùng, hệ thống cố tình giữ thuộc tính "respond_immediately": False cho node lắng nghe (Listen Node) tiếp theo. Điều này giúp LLM chỉ chạy đúng một lần duy nhất sau khi toàn bộ văn bản dịch ngắt lời của người dùng được đẩy vào ngữ cảnh một cách trọn vẹn, tránh tạo ra câu phản hồi rác từ trợ lý ảo.
3. Hệ quả (Consequences)
Tích cực (Positive)
- Một nút tinh chỉnh cho mỗi loại trải nghiệm (UX): Hệ thống vận hành tối giản, vận hành đúng theo cấu hình luồng được chọn.
- Đồng bộ nhất quán: Chiến lược tắt tiếng và bắt đầu nói luôn đồng bộ tuyệt đối, loại bỏ hoàn toàn lỗi ngốn hạn mức ngầm trước đây.
- Làm mới hạn mức thông minh: Các cuộc gọi kéo dài không bị ảnh hưởng bởi thói quen ngắt lời của người dùng ở các lượt thoại trước đó.
- Tự phục hồi luồng: Hệ thống tự phục hồi khi bị ngắt lời ở chế độ
VAD_INTERRUPT, không còn hiện tượng bot im lặng hoặc kẹt luồng.
Hạn chế (Negative)
- Phụ thuộc mã nguồn thủ công: Khi thêm một chế độ luồng mới, nhà phát triển bắt buộc phải cập nhật đồng thời ở cả hai tệp độc lập (
user_mute.pyvàuser_start.py). - Logic ma trận ẩn: Sự khác biệt giữa hai chế độ này chỉ nằm ở 2 dòng trong ma trận tắt tiếng, rất dễ bị bỏ sót khi đọc mã nguồn cô lập.
- Phức tạp khi gỡ lỗi: Logic xử lý luồng nằm tách biệt tại bộ quản lý luồng (Flow Manager), khiến việc gỡ lỗi yêu cầu phân tích sâu phức tạp.