Smart Turn — определение конца реплики
Smart Turn v3.2 отвечает на один вопрос после того, как VAD сообщил о паузе: говорящий закончил или остановился посреди фразы? Модель слушает сам звук — просодию, темп, интонацию — а не транскрипт, поддерживает 23 языка и запускается один раз на паузу на последних 8 секундах реплики. Веса — официальный релиз Pipecat Smart Turn v3.2 (BSD-2-Clause), скомпилированный в CoreML для платформ Apple и экспортированный в ONNX для Speech Core.
Детектор голосовой активности слышит только тишину. Люди делают паузы посреди фразы, чтобы подумать, поэтому агент, полагающийся только на VAD, либо перебивает, либо ждёт долгий фиксированный тайм-аут после каждой фразы. Smart Turn подтверждает каждую паузу: законченная фраза получает мгновенный ответ, пауза посреди фразы оставляет агент слушать.
Как это работает
Smart Turn — не VAD и сам речь не обнаруживает. Он работает в паре с потоковым Silero VAD, который решает, когда задать вопрос. С подключённым классификатором подтверждённая пауза становится вопросом, а не вердиктом:
- Один запрос на паузу. Когда Silero подтверждает паузу (
minSilenceDurationпосле порога offset), процессор передаёт классификатору звук реплики на этот момент — от 0,5 с до начала речи по VAD и до текущего чанка, не более последних 8 секунд. - Завершено. Вероятность не ниже
threshold(0,5):.speechEndedсрабатывает как обычно. - Удержание. Ниже порога сегмент остаётся открытым. Если говорящий продолжает, тот же сегмент продолжается — без второго
.speechStarted, — а следующая подтверждённая пауза снова задаёт вопрос уже с более длинной репликой. - Предел тишины. Если тишина достигает
maxSilenceDuration(2,0 с, считая от начала паузы), сегмент всё равно завершается, так что говорящий, который замолкает на полуслове, тоже получает ответ.endTimeсегмента — момент, когда прекратилась речь, а не когда истёк предел.
flush() завершает удерживаемый сегмент в конце потока, reset() сбрасывает его. Классификатор, выбросивший ошибку, считается ответившим «завершено», поэтому сбой модели никогда не останавливает разговор. isHoldingTurn сообщает о состоянии удержания, lastTurnCompletionProbability — последний ответ.
Модель
| Свойство | Значение |
|---|---|
| Архитектура | Энкодер Whisper-Tiny + attention pooling + MLP-голова с сигмоидным выходом |
| Параметры | 8,0M |
| Вход | Последние 8 с моно-аудио 16 кГц (128 000 отсчётов); более короткие реплики дополняются нулями в начале, другие частоты дискретизации передискретизируются |
| Выход | Вероятность в [0, 1], что реплика завершена; порог по умолчанию 0,5 |
| Языки | 23 — Arabic, Bengali, Chinese, Danish, Dutch, English, Finnish, French, German, Hindi, Indonesian, Italian, Japanese, Korean, Marathi, Norwegian, Polish, Portuguese, Russian, Spanish, Turkish, Ukrainian, Vietnamese |
| Лицензия | BSD-2-Clause |
В экспорты встроен log-mel фронтенд Whisper, включая нормализацию сигнала, на которой обучалась исходная модель, поэтому на вход подаётся сырой звук — извлечения признаков в Swift или C++ нет.
Производительность
| Экспорт | Размер | Точность | Задержка на окно 8 с |
|---|---|---|---|
| CoreML (CPU + Neural Engine) | 16,8 МБ | 92,9 % | 3,5 мс |
| ONNX fp32 (ONNX Runtime, CPU) | 33 МБ | 92,9 % | ~36 мс при 2 потоках, 20 мс при 4 |
| ONNX int8 (ONNX Runtime, CPU) | 11,1 МБ | 93,0 % | ~36 мс при 2 потоках, 20 мс при 4 |
Точность измерена на 1 000 клипах из исходного тестового набора; экспорты CoreML и ONNX fp32 на этих клипах в точности совпадают с исходной fp32-моделью. Задержка — одно окно 8 секунд на Apple M5 Pro. Один вызов на паузу не добавляет ничего заметного к паузе, которую VAD уже выждал.
Swift API
Разовая проверка записанного высказывания:
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
}
Подключение к потоковому VAD — каждая подтверждённая пауза проверяется до завершения сегмента:
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 реализует протокол TurnCompletionProvider из AudioCommon, поэтому вместо него можно подключить любой другой классификатор той же формы. fromPretrained(cacheDir:offlineMode:) следует тем же правилам кэша и офлайн-режима, что и остальные модели.
VoicePipeline
VoicePipeline принимает тот же классификатор, поэтому голосовой агент перестаёт перебивать людей без изменений в настройке STT, TTS или VAD. Подключение — два поля PipelineConfig и один метод; требуется speech-swift v0.0.27 или новее (SpeechCore.xcframework из 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()
| API | По умолчанию | Эффект |
|---|---|---|
turnCompletionThreshold | 0,5 | Вероятность завершения, начиная с которой пауза VAD завершает реплику пользователя. Используется только после подключения классификатора. |
turnCompletionMaxSilence | 2,0 с | Секунды тишины, отсчитываемые от начала паузы, после которых реплика, отклонённая классификатором, всё равно завершается. 0 = никогда. |
setTurnCompletion(_:) | — | Подключает любой TurnCompletionProvider. Вызывайте до start(); передайте nil, чтобы отключить. Классификатор, выбросивший ошибку, считается ответившим «завершено», поэтому сбой модели никогда не останавливает разговор. |
С подключённым классификатором пауза VAD завершает реплику пользователя, только если вероятность достигает порога; ниже него конвейер продолжает слушать, возобновившаяся речь продолжает ту же реплику (без второго speechStarted), а turnCompletionMaxSilence всё равно завершает удерживаемую реплику. Eager STT подчиняется этому вето, а классификатор выполняется один раз на паузу в аудиопотоке, поэтому после загрузки вызовите prewarm().
Настройка
| Параметр | По умолчанию | Эффект |
|---|---|---|
threshold | 0,5 | Меньшие значения завершают реплики раньше и чаще перебивают; большие — дольше ждут перед ответом. Прежде чем менять, понаблюдайте за lastTurnCompletionProbability в реальных разговорах. |
maxSilenceDuration | 2,0 с | Жёсткий предел, считая от начала паузы. Отклонённая пауза никогда не удержит реплику дольше. 0 отключает предел: удерживаемый сегмент тогда ждёт речи или flush(). |
preRollDuration | 0,5 с | Звук до начала речи по VAD, включаемый во вход классификатора, чтобы обрезанный первый слог всё же дошёл до модели. |
minSilenceDuration у Silero по-прежнему решает, когда пауза считается подтверждённой, а значит, когда опрашивать классификатор. После загрузки вызовите prewarm(), чтобы первая реальная пауза не тратила время на компиляцию графа.
Использование 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
| Опция | Команда | Описание |
|---|---|---|
--threshold | turn | Вероятность, начиная с которой реплика считается завершённой (по умолчанию 0,5) |
--json | turn | Выводит probability, threshold, complete и latency_ms в формате JSON |
--model, -m | turn | Репозиторий HuggingFace с той же структурой (по умолчанию aufklarer/Smart-Turn-v3.2-CoreML) |
--model-dir | turn | Локальный каталог с smart_turn.mlmodelc и config.json; пропускает загрузку |
--smart-turn | vad-stream | Подтверждать каждую паузу через Smart Turn перед завершением сегмента |
--turn-threshold | vad-stream | Порог завершения Smart Turn (по умолчанию 0,5) |
--turn-max-silence | vad-stream | Секунды тишины после отклонённой паузы, по истечении которых сегмент завершается в любом случае (по умолчанию 2,0) |
speech turn загружает файл (любая частота дискретизации), оценивает последние 8 секунд и выводит вероятность, пройден ли порог, и время инференса. Используйте его, чтобы подобрать порог на собственных записях. Полный список флагов — в справочнике CLI.
C++ / Android
Speech Core поставляет ту же модель как OnnxSmartTurn для Linux, Windows и Android (только ONNX Runtime; варианта LiteRT нет). VoicePipeline::set_turn_completion() подключает её до start(); после этого пауза VAD завершает реплику пользователя, только если вероятность достигает turn_completion_threshold (0,5), а turn_completion_max_silence (2,0 с) всё равно завершает удерживаемую реплику. C API предоставляет тот же хук через sc_turn_completion_vtable_t + sc_pipeline_set_turn_completion(), так что хосты на Kotlin или Swift могут подключить собственный классификатор так же, как 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 из Speech Core оценивает запись так же, как на macOS:
speech download-models # fetches the int8 graph with the other ONNX models
speech turn recording.wav --threshold 0.5 --json
Файлы модели, контракт C API и CLI описаны на странице Speech Core и в docs/models.md репозитория.
Загрузки модели
| Модель | Бэкенд | Размер | HuggingFace |
|---|---|---|---|
| Smart-Turn-v3.2 | CoreML | 16,8 МБ | aufklarer/Smart-Turn-v3.2-CoreML |
| Smart-Turn-v3.2 | ONNX (fp32 + int8) | 33 МБ / 11,1 МБ | soniqo/Smart-Turn-v3.2-ONNX |
Исходники
- Sources/SpeechVAD —
SmartTurnModelиStreamingVADProcessor - docs/models/smart-turn-v3.md — заметки о модели
- docs/inference/smart-turn.md — инференс и настройка
- speech-core docs/models.md —
OnnxSmartTurn - Исходная модель: pipecat-ai/smart-turn / pipecat-ai/smart-turn-v3 (Daily / Pipecat, BSD-2-Clause)