Smart Turn — Turn-Ende-Erkennung

Smart Turn v3.2 beantwortet nach jeder vom VAD gemeldeten Pause eine einzige Frage: Hat die Person zu Ende gesprochen, oder steckt sie mitten im Satz? Das Modell hört auf das Audio selbst — Prosodie, Tempo, Intonation — statt auf ein Transkript, deckt 23 Sprachen ab und läuft einmal pro Pause auf den letzten 8 Sekunden des Turns. Die Gewichte sind die offizielle Smart-Turn-v3.2-Veröffentlichung von Pipecat (BSD-2-Clause), für Apple-Plattformen nach CoreML kompiliert und für Speech Core nach ONNX exportiert.

Warum ein VAD allein nicht reicht

Ein Voice-Activity-Detector hört nur Stille. Menschen machen mitten im Satz Denkpausen, also unterbricht ein reiner VAD-Agent entweder, oder er wartet nach jedem Satz einen langen, festen Timeout ab. Smart Turn bestätigt jede Pause: Ein beendeter Satz bekommt sofort eine Antwort, eine Pause mitten im Satz lässt den Agenten weiter zuhören.

Funktionsweise

Smart Turn ist kein VAD und erkennt selbst keine Sprache. Es wird mit dem Streaming-Silero VAD kombiniert, der entscheidet, wann gefragt wird. Mit angeschlossenem Klassifikator wird eine bestätigte Pause zur Frage statt zum Urteil:

  1. Einmal pro Pause fragen. Sobald Silero eine Pause bestätigt (minSilenceDuration nach der Offset-Schwelle), übergibt der Prozessor dem Klassifikator das bisherige Audio des Turns — von 0,5 s vor dem VAD-Onset bis zum aktuellen Chunk, begrenzt auf die letzten 8 Sekunden.
  2. Abgeschlossen. Wahrscheinlichkeit größer oder gleich threshold (0,5): .speechEnded wird wie gewohnt ausgelöst.
  3. Halten. Unterhalb der Schwelle bleibt das Segment offen. Spricht die Person weiter, läuft dasselbe Segment weiter — ohne zweites .speechStarted — und die nächste bestätigte Pause fragt erneut mit dem längeren Turn.
  4. Stille-Obergrenze. Erreicht die Stille maxSilenceDuration (2,0 s, gemessen ab Beginn der Pause), endet das Segment trotzdem, sodass auch jemand, der den Satz verklingen lässt, eine Antwort bekommt. Die endTime des Segments liegt dort, wo die Sprache aufhörte, nicht beim Ablauf der Obergrenze.

flush() schließt ein gehaltenes Segment am Ende des Streams ab, reset() verwirft es. Ein Klassifikator, der einen Fehler wirft, zählt als „abgeschlossen“, sodass ein ausfallendes Modell das Gespräch nie blockiert. isHoldingTurn meldet den Haltezustand, lastTurnCompletionProbability die letzte Antwort.

Modell

EigenschaftWert
ArchitekturWhisper-Tiny-Encoder + Attention-Pooling + MLP-Kopf mit Sigmoid-Ausgabe
Parameter8,0M
EingabeLetzte 8 s Mono-Audio bei 16 kHz (128.000 Samples); kürzere Turns werden vorne mit Nullen aufgefüllt, andere Abtastraten werden resampelt
AusgabeWahrscheinlichkeit in [0, 1], dass der Turn abgeschlossen ist; Standard-Schwelle 0,5
Sprachen23 — Arabic, Bengali, Chinese, Danish, Dutch, English, Finnish, French, German, Hindi, Indonesian, Italian, Japanese, Korean, Marathi, Norwegian, Polish, Portuguese, Russian, Spanish, Turkish, Ukrainian, Vietnamese
LizenzBSD-2-Clause

Die Exporte enthalten das Whisper-Log-Mel-Frontend samt der Wellenform-Normalisierung, mit der das Ursprungsmodell trainiert wurde, sodass rohes Audio übergeben wird — es gibt keine Merkmalsextraktion in Swift oder C++.

Leistung

ExportGrößeGenauigkeitLatenz pro 8-s-Fenster
CoreML (CPU + Neural Engine)16,8 MB92,9 %3,5 ms
ONNX fp32 (ONNX Runtime, CPU)33 MB92,9 %~36 ms mit 2 Threads, 20 ms mit 4
ONNX int8 (ONNX Runtime, CPU)11,1 MB93,0 %~36 ms mit 2 Threads, 20 ms mit 4

Die Genauigkeit wurde auf 1.000 Clips des Upstream-Testsets gemessen; die CoreML- und ONNX-fp32-Exporte stimmen auf diesen Clips exakt mit dem Upstream-fp32-Modell überein. Die Latenz gilt für ein 8-Sekunden-Fenster auf einem Apple M5 Pro. Ein Aufruf pro Pause verlängert die Pause, die der VAD ohnehin abgewartet hat, nicht spürbar.

Swift-API

Einmalige Prüfung einer aufgezeichneten Äußerung:

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
}

An den Streaming-VAD angeschlossen, sodass jede bestätigte Pause geprüft wird, bevor ein Segment endet:

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 implementiert das Protokoll TurnCompletionProvider aus AudioCommon, sodass stattdessen jeder andere Klassifikator mit derselben Form angeschlossen werden kann. fromPretrained(cacheDir:offlineMode:) folgt denselben Cache- und Offline-Regeln wie die übrigen Modelle.

VoicePipeline

VoicePipeline nimmt denselben Klassifikator entgegen, sodass ein Sprachagent Nutzer nicht mehr unterbricht, ohne dass sich an STT, TTS oder VAD etwas ändert. Zwei Felder von PipelineConfig und eine Methode binden ihn ein; sie erfordern speech-swift v0.0.27 oder neuer (SpeechCore.xcframework aus 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()
APIStandardWirkung
turnCompletionThreshold0,5Abschlusswahrscheinlichkeit, ab der eine VAD-Pause den Turn des Nutzers beendet. Wird erst verwendet, sobald ein Klassifikator angebunden ist.
turnCompletionMaxSilence2,0 sSekunden Stille, gemessen ab Beginn der Pause, nach denen ein vom Klassifikator abgelehnter Turn trotzdem endet. 0 = nie.
setTurnCompletion(_:)Bindet einen beliebigen TurnCompletionProvider an. Vor start() aufrufen; nil löst ihn wieder. Ein Klassifikator, der einen Fehler wirft, zählt als abgeschlossen, sodass ein ausfallendes Modell das Gespräch nie blockiert.

Mit angebundenem Klassifikator beendet eine VAD-Pause den Turn des Nutzers nur, wenn die Wahrscheinlichkeit den Schwellenwert erreicht; darunter hört die Pipeline weiter zu, wieder einsetzende Sprache setzt denselben Turn fort (kein zweites speechStarted), und turnCompletionMaxSilence beendet den gehaltenen Turn trotzdem. Eager STT respektiert das Veto, und der Klassifikator läuft einmal pro Pause auf dem Audio-Thread – rufen Sie daher nach dem Laden prewarm() auf.

Feinabstimmung

EinstellungStandardWirkung
threshold0,5Niedrigere Werte beenden Turns früher und unterbrechen öfter; höhere Werte warten länger, bevor geantwortet wird. Beobachte lastTurnCompletionProbability in echten Gesprächen, bevor du den Wert änderst.
maxSilenceDuration2,0 sHarte Obergrenze, gemessen ab Beginn der Pause. Eine abgelehnte Pause kann den Turn nie länger halten. 0 deaktiviert die Obergrenze: Ein gehaltenes Segment wartet dann auf Sprache oder flush().
preRollDuration0,5 sAudio vor dem VAD-Onset, das in die Eingabe des Klassifikators aufgenommen wird, damit auch eine abgeschnittene erste Silbe das Modell erreicht.

Sileros minSilenceDuration entscheidet weiterhin, wann eine Pause bestätigt wird — und damit, wann der Klassifikator gefragt wird. Rufe nach dem Laden prewarm() auf, damit die erste echte Pause nicht die Graph-Kompilierung bezahlt.

CLI-Nutzung

# 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
OptionBefehlBeschreibung
--thresholdturnWahrscheinlichkeit, ab der der Turn als abgeschlossen gilt (Standard 0,5)
--jsonturnGibt probability, threshold, complete und latency_ms als JSON aus
--model, -mturnHuggingFace-Repository mit demselben Aufbau (Standard aufklarer/Smart-Turn-v3.2-CoreML)
--model-dirturnLokales Verzeichnis mit smart_turn.mlmodelc und config.json; überspringt den Download
--smart-turnvad-streamBestätigt jede Pause mit Smart Turn, bevor ein Segment endet
--turn-thresholdvad-streamSmart-Turn-Abschlussschwelle (Standard 0,5)
--turn-max-silencevad-streamSekunden Stille nach einer abgelehnten Pause, nach denen das Segment trotzdem endet (Standard 2,0)

speech turn lädt die Datei (beliebige Abtastrate), bewertet die letzten 8 Sekunden und gibt die Wahrscheinlichkeit, das Über- oder Unterschreiten der Schwelle und die Inferenzzeit aus. Damit lässt sich die Schwelle auf eigenen Aufnahmen abstimmen. Die vollständige Optionsliste steht in der CLI-Referenz.

C++ / Android

Speech Core liefert dasselbe Modell als OnnxSmartTurn für Linux, Windows und Android (nur ONNX Runtime; es gibt keine LiteRT-Variante). VoicePipeline::set_turn_completion() bindet es vor start() ein; danach beendet eine VAD-Pause den Turn nur noch, wenn die Wahrscheinlichkeit turn_completion_threshold (0,5) erreicht, und turn_completion_max_silence (2,0 s) beendet einen gehaltenen Turn trotzdem. Die C-API stellt denselben Hook als sc_turn_completion_vtable_t + sc_pipeline_set_turn_completion() bereit, sodass Kotlin- oder Swift-Hosts ihren eigenen Klassifikator genauso anbinden können wie den 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);

Die speech-CLI von Speech Core bewertet eine Aufnahme genauso wie unter macOS:

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

Modelldateien, der C-API-Vertrag und die CLI sind auf der Seite Speech Core und in docs/models.md des Repositorys dokumentiert.

Modell-Downloads

ModellBackendGrößeHuggingFace
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

Quellen