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.
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:
- Einmal pro Pause fragen. Sobald Silero eine Pause bestätigt (
minSilenceDurationnach 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. - Abgeschlossen. Wahrscheinlichkeit größer oder gleich
threshold(0,5):.speechEndedwird wie gewohnt ausgelöst. - 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. - 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. DieendTimedes 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
| Eigenschaft | Wert |
|---|---|
| Architektur | Whisper-Tiny-Encoder + Attention-Pooling + MLP-Kopf mit Sigmoid-Ausgabe |
| Parameter | 8,0M |
| Eingabe | Letzte 8 s Mono-Audio bei 16 kHz (128.000 Samples); kürzere Turns werden vorne mit Nullen aufgefüllt, andere Abtastraten werden resampelt |
| Ausgabe | Wahrscheinlichkeit in [0, 1], dass der Turn abgeschlossen ist; Standard-Schwelle 0,5 |
| Sprachen | 23 — Arabic, Bengali, Chinese, Danish, Dutch, English, Finnish, French, German, Hindi, Indonesian, Italian, Japanese, Korean, Marathi, Norwegian, Polish, Portuguese, Russian, Spanish, Turkish, Ukrainian, Vietnamese |
| Lizenz | BSD-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
| Export | Größe | Genauigkeit | Latenz pro 8-s-Fenster |
|---|---|---|---|
| CoreML (CPU + Neural Engine) | 16,8 MB | 92,9 % | 3,5 ms |
| ONNX fp32 (ONNX Runtime, CPU) | 33 MB | 92,9 % | ~36 ms mit 2 Threads, 20 ms mit 4 |
| ONNX int8 (ONNX Runtime, CPU) | 11,1 MB | 93,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()
| API | Standard | Wirkung |
|---|---|---|
turnCompletionThreshold | 0,5 | Abschlusswahrscheinlichkeit, ab der eine VAD-Pause den Turn des Nutzers beendet. Wird erst verwendet, sobald ein Klassifikator angebunden ist. |
turnCompletionMaxSilence | 2,0 s | Sekunden 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
| Einstellung | Standard | Wirkung |
|---|---|---|
threshold | 0,5 | Niedrigere 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. |
maxSilenceDuration | 2,0 s | Harte 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(). |
preRollDuration | 0,5 s | Audio 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
| Option | Befehl | Beschreibung |
|---|---|---|
--threshold | turn | Wahrscheinlichkeit, ab der der Turn als abgeschlossen gilt (Standard 0,5) |
--json | turn | Gibt probability, threshold, complete und latency_ms als JSON aus |
--model, -m | turn | HuggingFace-Repository mit demselben Aufbau (Standard aufklarer/Smart-Turn-v3.2-CoreML) |
--model-dir | turn | Lokales Verzeichnis mit smart_turn.mlmodelc und config.json; überspringt den Download |
--smart-turn | vad-stream | Bestätigt jede Pause mit Smart Turn, bevor ein Segment endet |
--turn-threshold | vad-stream | Smart-Turn-Abschlussschwelle (Standard 0,5) |
--turn-max-silence | vad-stream | Sekunden 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
| Modell | Backend | Größe | HuggingFace |
|---|---|---|---|
| Smart-Turn-v3.2 | CoreML | 16,8 MB | aufklarer/Smart-Turn-v3.2-CoreML |
| Smart-Turn-v3.2 | ONNX (fp32 + int8) | 33 MB / 11,1 MB | soniqo/Smart-Turn-v3.2-ONNX |
Quellen
- Sources/SpeechVAD —
SmartTurnModelundStreamingVADProcessor - docs/models/smart-turn-v3.md — Modellnotizen
- docs/inference/smart-turn.md — Inferenz und Feinabstimmung
- speech-core docs/models.md —
OnnxSmartTurn - Upstream: pipecat-ai/smart-turn / pipecat-ai/smart-turn-v3 (Daily / Pipecat, BSD-2-Clause)