Smart Turn — Phát hiện kết thúc lượt nói

Smart Turn v3.2 trả lời một câu hỏi duy nhất sau khi VAD báo có khoảng dừng: người nói đã nói xong hay đang ở giữa câu? Mô hình lắng nghe chính âm thanh — ngữ điệu, tốc độ, cao độ — thay vì bản chép lời, hỗ trợ 23 ngôn ngữ và chạy một lần cho mỗi khoảng dừng trên 8 giây cuối của lượt nói. Trọng số là bản phát hành Smart Turn v3.2 chính thức của Pipecat (BSD-2-Clause), được biên dịch sang CoreML cho nền tảng Apple và xuất sang ONNX cho Speech Core.

Vì sao chỉ VAD là chưa đủ

Bộ phát hiện hoạt động giọng nói chỉ nghe được khoảng lặng. Người ta hay dừng giữa câu để suy nghĩ, nên tác nhân chỉ dựa vào VAD sẽ hoặc ngắt lời họ, hoặc phải chờ một khoảng thời gian cố định khá dài sau mỗi câu. Smart Turn xác nhận từng khoảng dừng: câu đã nói xong được trả lời ngay, khoảng dừng giữa câu giữ cho tác nhân tiếp tục lắng nghe.

Cách hoạt động

Smart Turn không phải là VAD và không tự phát hiện giọng nói. Nó được ghép với Silero VAD dạng streaming, bộ này quyết định khi nào cần hỏi. Khi có bộ phân loại được gắn vào, một khoảng dừng đã xác nhận trở thành câu hỏi thay vì phán quyết:

  1. Hỏi một lần mỗi khoảng dừng. Khi Silero xác nhận khoảng dừng (minSilenceDuration sau ngưỡng offset), bộ xử lý đưa cho bộ phân loại âm thanh của lượt nói tính đến lúc đó — từ 0,5 giây trước thời điểm VAD bắt đầu cho đến chunk hiện tại, tối đa 8 giây cuối.
  2. Hoàn tất. Xác suất bằng hoặc vượt threshold (0,5): .speechEnded phát ra như bình thường.
  3. Giữ. Dưới ngưỡng thì đoạn vẫn mở. Nếu người nói tiếp tục, cùng đoạn đó tiếp diễn — không có .speechStarted thứ hai — và khoảng dừng xác nhận tiếp theo sẽ hỏi lại với lượt nói dài hơn.
  4. Giới hạn im lặng. Nếu im lặng đạt maxSilenceDuration (2,0 giây, tính từ lúc bắt đầu dừng), đoạn vẫn kết thúc, nên người nói bỏ lửng câu vẫn nhận được phản hồi. endTime của đoạn là lúc giọng nói dừng, không phải lúc giới hạn hết hạn.

flush() chốt đoạn đang giữ khi luồng kết thúc và reset() xóa nó. Bộ phân loại ném lỗi được tính là "hoàn tất", nên mô hình gặp sự cố không bao giờ làm cuộc hội thoại bị kẹt. isHoldingTurn báo trạng thái giữ và lastTurnCompletionProbability cho biết câu trả lời gần nhất.

Mô hình

Thuộc tínhGiá trị
Kiến trúcBộ mã hóa Whisper-Tiny + attention pooling + đầu MLP với đầu ra sigmoid
Tham số8,0M
Đầu vào8 giây cuối của âm thanh mono 16 kHz (128.000 mẫu); lượt nói ngắn hơn được đệm số 0 ở đầu, các tần số lấy mẫu khác được lấy mẫu lại
Đầu raXác suất trong [0, 1] rằng lượt nói đã hoàn tất; ngưỡng mặc định 0,5
Ngôn ngữ23 — Arabic, Bengali, Chinese, Danish, Dutch, English, Finnish, French, German, Hindi, Indonesian, Italian, Japanese, Korean, Marathi, Norwegian, Polish, Portuguese, Russian, Spanish, Turkish, Ukrainian, Vietnamese
Giấy phépBSD-2-Clause

Các bản xuất nhúng sẵn front-end log-mel của Whisper, bao gồm cả bước chuẩn hóa dạng sóng mà mô hình gốc được huấn luyện, nên bên gọi chỉ cần truyền âm thanh thô — không có bước trích xuất đặc trưng nào trong Swift hay C++.

Hiệu năng

Bản xuấtKích thướcĐộ chính xácĐộ trễ mỗi cửa sổ 8 giây
CoreML (CPU + Neural Engine)16,8 MB92,9%3,5 ms
ONNX fp32 (ONNX Runtime, CPU)33 MB92,9%~36 ms với 2 luồng, 20 ms với 4
ONNX int8 (ONNX Runtime, CPU)11,1 MB93,0%~36 ms với 2 luồng, 20 ms với 4

Độ chính xác đo trên 1.000 clip từ tập kiểm thử gốc; bản xuất CoreML và ONNX fp32 khớp hoàn toàn với mô hình fp32 gốc trên các clip đó. Độ trễ là một cửa sổ 8 giây trên Apple M5 Pro. Một lần gọi mỗi khoảng dừng không thêm gì đáng kể vào khoảng dừng mà VAD đã chờ sẵn.

API Swift

Kiểm tra một lần trên một phát ngôn đã ghi:

import SpeechVAD

let turn = try await SmartTurnModel.fromPretrained()
try turn.prewarm()   // compile the graph before the first real pause

let probability = try turn.turnCompleteProbability(
    audio: utterance, sampleRate: 16_000)
if probability >= SmartTurnModel.defaultThreshold {
    // finished turn — reply now
}

Gắn vào VAD streaming, để mọi khoảng dừng đã xác nhận đều được kiểm tra trước khi đoạn kết thúc:

let vad = try await SileroVADModel.fromPretrained(engine: .coreml)
let turn = try await SmartTurnModel.fromPretrained()

let processor = StreamingVADProcessor(
    model: vad,
    config: .sileroDefault,
    turnCompletion: turn,
    turnCompletionConfig: TurnCompletionConfig(threshold: 0.5, maxSilenceDuration: 2.0))

for chunk in microphoneChunks {            // Float32 @ 16 kHz
    for event in processor.process(samples: chunk) {
        switch event {
        case .speechStarted(let time): print("speech at \(time)s")
        case .speechEnded(let segment): print("turn \(segment.startTime)–\(segment.endTime)s")
        }
    }
}
let remaining = processor.flush()

SmartTurnModel tuân theo giao thức TurnCompletionProvider trong AudioCommon, nên có thể gắn bất kỳ bộ phân loại nào khác có cùng hình dạng thay thế. fromPretrained(cacheDir:offlineMode:) tuân theo cùng quy tắc bộ nhớ đệm và ngoại tuyến như các mô hình khác.

VoicePipeline

VoicePipeline nhận cùng bộ phân loại này, nên tác nhân giọng nói không còn ngắt lời người dùng mà không cần đổi gì trong thiết lập STT, TTS hay VAD. Chỉ cần hai trường của PipelineConfig và một phương thức; chúng yêu cầu speech-swift v0.0.27 trở lên (SpeechCore.xcframework từ speech-core v0.0.14).

import SpeechCore
import SpeechVAD

let smartTurn = try await SmartTurnModel.fromPretrained()
try smartTurn.prewarm()

var config = PipelineConfig()
config.turnCompletionThreshold = 0.5   // pause ends the turn when p >= this
config.turnCompletionMaxSilence = 2.0  // seconds of silence that end a held turn anyway; 0 = never

let pipeline = VoicePipeline(stt: stt, tts: tts, vad: vad, config: config, onEvent: { print($0) })
pipeline.setTurnCompletion(smartTurn)   // before start(); nil detaches
pipeline.start()
APIMặc địnhTác dụng
turnCompletionThreshold0,5Xác suất hoàn tất mà tại đó hoặc cao hơn, khoảng dừng của VAD sẽ kết thúc lượt nói của người dùng. Chỉ dùng khi đã gắn bộ phân loại.
turnCompletionMaxSilence2,0 giâySố giây im lặng, tính từ lúc bắt đầu khoảng dừng, sau đó lượt nói bị bộ phân loại từ chối vẫn kết thúc. 0 = không bao giờ.
setTurnCompletion(_:)Gắn bất kỳ TurnCompletionProvider nào. Gọi trước start(); truyền nil để tháo ra. Bộ phân loại ném lỗi được coi là "hoàn tất", nên mô hình hỏng không bao giờ làm cuộc trò chuyện bị kẹt.

Khi đã gắn bộ phân loại, khoảng dừng của VAD chỉ kết thúc lượt nói của người dùng khi xác suất đạt ngưỡng; dưới ngưỡng, pipeline tiếp tục lắng nghe, giọng nói tiếp tục sẽ nối vào cùng lượt (không có speechStarted thứ hai), và turnCompletionMaxSilence vẫn kết thúc lượt đang giữ. Eager STT tôn trọng quyết định từ chối này, và bộ phân loại chạy một lần mỗi khoảng dừng trên luồng âm thanh, vì vậy hãy gọi prewarm() sau khi tải.

Tinh chỉnh

Thiết lậpMặc địnhTác dụng
threshold0,5Giá trị thấp hơn kết thúc lượt sớm hơn và ngắt lời thường xuyên hơn; giá trị cao hơn chờ lâu hơn trước khi trả lời. Hãy theo dõi lastTurnCompletionProbability trên các hội thoại thật trước khi thay đổi.
maxSilenceDuration2,0 giâyGiới hạn cứng, tính từ lúc bắt đầu dừng. Một khoảng dừng bị từ chối không bao giờ giữ lượt lâu hơn mức này. 0 tắt giới hạn: đoạn đang giữ sẽ chờ giọng nói hoặc flush().
preRollDuration0,5 giâyÂm thanh trước thời điểm VAD bắt đầu được đưa vào đầu vào của bộ phân loại, để âm tiết đầu bị cắt vẫn đến được mô hình.

minSilenceDuration của Silero vẫn quyết định khi nào khoảng dừng được xác nhận, và do đó khi nào bộ phân loại được hỏi. Gọi prewarm() sau khi tải để khoảng dừng thật đầu tiên không phải trả giá cho việc biên dịch đồ thị.

Sử dụng CLI

# Probability, complete/incomplete verdict and inference time for one utterance
speech turn utterance.wav

# Custom threshold, JSON output
speech turn utterance.wav --threshold 0.7 --json

# Local bundle, no download
speech turn utterance.wav --model-dir ~/smart-turn-coreml

# Streaming VAD with Smart Turn confirming each pause; mid-sentence pauses merge
speech vad-stream call.wav --smart-turn
speech vad-stream call.wav --smart-turn --turn-threshold 0.6 --turn-max-silence 1.5
Tùy chọnLệnhMô tả
--thresholdturnXác suất từ mức này trở lên thì lượt nói được coi là hoàn tất (mặc định 0,5)
--jsonturnXuất probability, threshold, completelatency_ms dưới dạng JSON
--model, -mturnKho HuggingFace có cùng cấu trúc (mặc định aufklarer/Smart-Turn-v3.2-CoreML)
--model-dirturnThư mục cục bộ chứa smart_turn.mlmodelcconfig.json; bỏ qua bước tải
--smart-turnvad-streamXác nhận từng khoảng dừng bằng Smart Turn trước khi kết thúc đoạn
--turn-thresholdvad-streamNgưỡng hoàn tất của Smart Turn (mặc định 0,5)
--turn-max-silencevad-streamSố giây im lặng sau một khoảng dừng bị từ chối mà vẫn kết thúc đoạn (mặc định 2,0)

speech turn tải tệp (mọi tần số lấy mẫu), chấm điểm 8 giây cuối và in ra xác suất, có vượt ngưỡng hay không, cùng thời gian suy luận. Dùng nó để tinh chỉnh ngưỡng trên bản ghi của chính bạn. Danh sách đầy đủ các cờ nằm trong tài liệu tham khảo CLI.

C++ / Android

Speech Core cung cấp cùng mô hình này dưới tên OnnxSmartTurn cho Linux, Windows và Android (chỉ ONNX Runtime; không có bản LiteRT). VoicePipeline::set_turn_completion() gắn nó trước start(); sau đó khoảng dừng của VAD chỉ kết thúc lượt nói của người dùng khi xác suất đạt turn_completion_threshold (0,5), và turn_completion_max_silence (2,0 giây) vẫn kết thúc lượt đang giữ. C API cung cấp cùng điểm móc nối qua sc_turn_completion_vtable_t + sc_pipeline_set_turn_completion(), nên host Kotlin hoặc Swift có thể nối bộ phân loại riêng của mình giống như với VAD.

#include <speech_core/models/onnx_smart_turn.h>

speech_core::OnnxSmartTurn model("/models/smart-turn-v3.2-int8.onnx");

// Audio of the turn so far at any rate (resampled to 16 kHz internally).
float p = model.turn_complete_probability(turn.data(), turn.size(), rate);

// Or let the pipeline ask it on every VAD pause (before start()).
pipeline.set_turn_completion(&model);

CLI speech của Speech Core chấm điểm bản ghi giống như trên macOS:

speech download-models        # fetches the int8 graph with the other ONNX models
speech turn recording.wav --threshold 0.5 --json

Tệp mô hình, hợp đồng C API và CLI được mô tả trên trang Speech Core và trong docs/models.md của kho mã.

Tải xuống mô hình

Mô hìnhBackendKích thướcHuggingFace
Smart-Turn-v3.2CoreML16,8 MBaufklarer/Smart-Turn-v3.2-CoreML
Smart-Turn-v3.2ONNX (fp32 + int8)33 MB / 11,1 MBsoniqo/Smart-Turn-v3.2-ONNX

Nguồn