In Teil 6 habe ich ASR, LLM und TTS mit NVIDIA Pipecat zu einem vollständigen, lokalen Voice-Loop verdrahtet. In Teil 7 habe ich diesem Loop ein Gehirn mit zwei Tools gegeben: Der NAT-Agent hängt jetzt als „LLM-Stufe“ im Loop und kann über Tools echte Aktionen ausführen, etwa die Zeit ansagen oder die GPU-Auslastung melden. Es fehlt aber noch ein letztes, in der Praxis entscheidendes Stück: Der Agent hört bisher immer zu und reagiert auf jedes Geräusch und jedes Gespräch im Raum.
Genau das ändern wir in diesem abschließenden Teil mit einem Wake-Word als Türsteher. Erst auf ein Schlüsselwort hin öffnet sich der Loop und der Agent hört wirklich hin. Und natürlich bleiben wir auch hier dem Serien-Prinzip treu: Das Wake-Word läuft vollständig lokal auf eigener Hardware, ohne Cloud und ohne fremden Dienst.
In der nachfolgenden Architekturübersicht stelle ich den genauen Aufbau einmal da.
Warum überhaupt ein Wake-Word?
Ein Sprachassistent, der ununterbrochen lauscht und auf jedes Wort anspringt, ist im Alltag schnell anstrengend und auch ein wenig unheimlich. Drei Gründe sprechen klar für einen Türsteher:
- Kein Fehlauslösen: Hintergrundgespräche, der Fernseher oder ein Telefonat sollen den Agenten nicht versehentlich zu einer Antwort verleiten.
- Klare Kontrolle: Du entscheidest bewusst, wann der Agent zuhört, statt dass er es immer tut.
- Souveränität: Genau wie ASR, LLM und TTS soll auch die Worterkennung lokal bleiben. Ein Wake-Word, das seine Audiodaten in die Cloud schickt, würde das ganze Konzept dieser Serie unterlaufen.
Das letzte Wort ist mir besonders wichtig: Viele bekannte Wake-Word-Engines setzen einen Online-Account oder einen Lizenz-Key voraus. Wir verzichten bewusst darauf und gehen unseren eigenen lokalen Weg.
Der lokale Weg: Wake-Phrase aus dem Transkript
Der eleganteste Trick: Wir brauchen für das Wake-Word gar kein zusätzliches Modell. Unsere Parakeet-ASR aus Teil 6 transkribiert ohnehin schon alles, was ins Mikrofon kommt. Wir müssen also nur eine Stufe einziehen, die den Transkript-Strom mitliest und den Loop erst nach einer bestimmten Phrase „aufmacht“.
Genau dafür hat Pipecat den WakeCheckFilter: Er überwacht die TranscriptionFrames auf eine oder mehrere Wake-Phrasen und lässt Frames erst nach einem Treffer weiter nach unten in die Pipeline. Über einen Keepalive-Timeout bleibt der Agent danach für eine kurze Zeit „wach“, damit du nicht vor jeder Folgefrage erneut das Schlüsselwort sagen musst.
Der große Vorteil: kein neues Modell, keine zusätzliche Abhängigkeit, kein API-Key. Die Worterkennung läuft auf derselben lokalen ASR, die wir schon haben.
Hinweis zur Pipecat-Version: In der hier verwendeten Pipecat-Version 0.0.98 ist der WakeCheckFilter vorhanden. Ein kurzer Import-Test in der virtuellen Umgebung pipecat bestätigt das:
Befehl: source ~/venvs/pipecat/bin/activate
Befehl: python -c "from pipecat.processors.filters.wake_check_filter import WakeCheckFilter; print('ok')"
In neueren Pipecat-Releases wird dieser Filter zugunsten von WakePhraseUserTurnStartStrategy abgelöst (deprecated). Wer eine neuere Version einsetzt, nimmt entsprechend den neuen Baustein. Dabei bleibt das Prinzip (Wake-Phrase aus dem Transkript) identisch.
Voraussetzungen
- Der laufende Voice-Agent aus Teil 7: der Pipecat-Bot im Projektordner
voice_agent_websocket, die beiden NIMs (Parakeet + Magpie) und der NAT-Server (nat serve) als Gehirn. - Zugriff auf die Bot-Datei, in der die Pipeline zusammengesteckt wird (bei mir
bot.pybzw. die Enhanced-Variante), denn dort hängen wir den Filter ein. - Eine Wake-Phrase, die deine ASR zuverlässig versteht (dazu gleich mehr).
Schritt 1: Eine gute Wake-Phrase wählen
Weil wir auf das ASR-Transkript hören, ist die Wake-Phrase letztlich einfach Text, den Parakeet zuverlässig erkennen muss. Ein paar Faustregeln aus der Praxis:
- Zwei, drei Silben, klar und unverwechselbar: kurze Allerweltswörter werden im Gespräch zu oft zufällig getroffen.
- Gut transkribierbar im Deutschen: Phrasen, die Parakeet sauber und stabil verschriftlicht, funktionieren am besten. Exotische Kunstwörter landen schnell als irgendetwas anderes im Transkript.
- Nicht im normalen Gesprächsfluss: idealerweise etwas, das du nur sagst, wenn du den Agenten wirklich ansprichst.
Passend zu meinem Assistenten habe ich mich für „Hey Marvin“ entschieden. In der Praxis zeigte sich aber schnell: Parakeet schreibt diese Phrase nicht immer gleich – mal sauber als „hey marvin“, mal leicht verschluckt. Deshalb hinterlege ich mehrere Varianten und zusätzlich das einzelne „Marvin“, das am zuverlässigsten trifft. Der große Vorteil meines Aufbaus: Die Phrasen stehen in einer config.yml und lassen sich jederzeit ändern, ohne den Code anzufassen. Und weil mein Türsteher jede Entscheidung mitloggt (dazu gleich), sehe ich im Log schwarz auf weiß, was die ASR tatsächlich verstanden hat – und kann die Phrasenliste gezielt nachschärfen.
Schritt 2: Den Wake-Gate einbauen (config.yml + Pipeline)
Pipecat bringt für genau diesen Zweck den oben erwähnten WakeCheckFilter mit, und damit bin ich auch gestartet. In meinem Setup (der nvidia_pipecat-Fork mit aktivierter Speculative Speech) bin ich aber auf zwei Grenzen gestoßen:
- Der Standard-Filter gated nur finale Transkripte. Die Interim-Transkripte der Speculative-Speech-Pipeline laufen am Tor vorbei und der Agent reagiert dann trotz Türsteher.
- Nach dem ersten Einschlafen wachte er bei mir nicht zuverlässig wieder auf soll heißen der Assistent funktionierte praktisch nur ein einziges Mal.
Deshalb habe ich daraus einen kleinen eigenen WakeGate gemacht. Im großen und ganzen ist es ein schlanker Frame-Processor, der erbt von Pipecats FrameProcessor und der genau das tut, was ich brauche: Er gated Interim und Final gleichermaßen, weckt nach dem Einschlafen zuverlässig erneut auf, setzt seinen Zustand pro Äußerung sauber zurück und loggt jede Entscheidung. Seine Wake-Phrase(n) und das Keepalive liest er aus einer config.yml. So ändere ich das Wake-Wort, ohne den Code anzufassen.
Im Alltag fasst du also nur diese eine Datei an. Sie liegt im selben Ordner wie die Bot-Datei und ist denkbar einfach:
# Konfiguration des Pipecat-Voice-Bots (Teil 8)
wake_word:
enabled: true # Tuersteher an/aus
phrases: # eine oder mehrere Wake-Phrasen (Gross-/Kleinschreibung egal)
- "Hey Marvin"
- "Marvin"
keepalive_timeout: 15 # Sekunden, die der Loop nach dem Wecken offen bleibt
Im Code passiert dann nur zweierlei: Der WakeGate wird aus dieser Config aufgebaut, und er wird an die richtige Stelle in der Pipeline eingefügt. Er wird direkt hinter die ASR und vor den User-Context-Aggregator eingefügt. Genau dort entsteht das fertige Transkript: Alles, was vor dem Wake-Wort gesprochen wird, transkribiert Parakeet zwar, aber der Türsteher hält es zurück. Erst nach dem Treffer fließen die Transkripte weiter zum NAT-Agenten. Ist in der Config enabled: false gesetzt, fällt der Gate komplett aus der Pipeline und der Agent reagiert wie in Teil 7 auf jede Sprachnachricht die er hört.
Die komplette Bot-Datei also die WakeGate-Klasse, die nötigen Importe und die fertig verdrahtete Pipeline lege ich wie immer ins GitHub-Repo, damit du nichts abtippen musst:
URL: github.com/custom-build-robots/nemo-agent-toolkit-examples
Stolperfalle Modulname: Wenn du die Bot-Datei so wie ich unter einem neuen Namen speicherst, denk an die letzte Zeile mit uvicorn.run("<modulname>:app", ...). Der String muss zum Dateinamen deiner Python-Datei passen, sonst startet uvicorn das alte Modul (ohne den Gate), und es sieht so aus, als würde der Türsteher gar nicht greifen.
Hinweis zu Speculative Speech: Für einen sauberen ersten Test habe ich in der .env ENABLE_SPECULATIVE_SPEECH=false gesetzt. Da mein WakeGate auch die Interim-Transkripte gated, kannst du Speculative Speech anschließend wieder einschalten, wenn du den Latenzvorteil aus Teil 6/7 zurückhaben willst. Das kannst Du einfach gegentesten.
Hinweis zur Begrüßung: Die Selbstvorstellung beim Verbindungsaufbau wird direkt in den LLM gequeued und läuft damit am Türsteher vorbei. Der Agent stellt sich also weiterhin vor, sobald du die Seite öffnest bzw. über das Drop-Down ein neues Modell lädst. Möchtest du echte Stille bis zum ersten Wecken, machst du den Begrüßungs-Block in on_client_connected von wake_cfg["enabled"] abhängig.
Schritt 3: Keepalive – wie lange der Agent wach bleibt
Niemand möchte vor jeder einzelnen Frage erneut das Wake-Wort sagen. Der Keepalive-Timeout hält den Loop nach dem Wecken eine Weile offen, sodass Folgefragen direkt durchgehen. Läuft die Zeit ohne neue Sprache ab, „schläft“ der Agent wieder ein und wartet erneut auf das Schlüsselwort.
Hier lohnt sich das Augenmaß:
- Zu kurz: du wirst ständig zum Wiederholen des Wake-Worts gezwungen.
- Zu lang: der Türsteher verliert seinen Sinn, weil der Agent quasi dauernd wach ist.
Ich bin bei 15 Sekunden gelandet. Das fühlt sich im Gespräch gut an: Eine Folgefrage geht problemlos ohne erneutes „Hey Marvin“ durch, aber nach einer normalen Sprechpause kippt der Agent zuverlässig wieder in den Schlafzustand. Der Wert steht in der config.yml (keepalive_timeout) und lässt sich jederzeit anpassen. Wichtig: Jede neue Nutzer-Äußerung im wachen Zustand setzt das Fenster zurück, der Timer zählt also ab der letzten Äußerung.
Schritt 4: End-to-End testen
Jetzt der ehrliche Test. Bot, NIMs und NAT-Server laufen wie in Teil 7. Öffne die Test-UI über den SSH-Tunnel:
Befehl: ssh -L 8100:localhost:8100 ingmar@192.168.2.119
URL: http://localhost:8100/static/index.html
Probiere bewusst drei Fälle durch:
- Ohne Wake-Wort: Sprich einfach drauflos („Wie spät ist es?“). Erwartung: Parakeet transkribiert, aber der Agent bleibt still das heißt der Türsteher hält dicht.
- Mit Wake-Wort: „Hey Marvin, wie spät ist es?“ der Loop öffnet, der NAT-Agent zieht das Tool
current_datetimeund Magpie sagt die Uhrzeit. - Wieder einschlafen und erneut wecken: Nach gut 15 Sekunden Pause ignoriert der Agent einen Satz ohne Wake-Wort; sagst du dann erneut „Hey Marvin, …“, wacht er wieder sauber auf.
Das Schöne am eigenen WakeGate: Im Bot-Log kannst du jede Entscheidung mitlesen. So sieht das in der Praxis aus:
Wake-Word aktiv: ['Hey Marvin', 'Marvin'] (Keepalive 15.0s)
WakeGate: schläft, ignoriere Transkript: 'wie spät ist es'
WakeGate: GEWECKT durch 'hey marvin' -> Rest: 'wie spät ist es'
WakeGate: Keepalive abgelaufen -> schläft wieder ein.
Genau diese ignoriere Transkript-Zeile ist Gold wert: Sie zeigt dir, was Parakeet wirklich verstanden hat. Wacht der Agent trotz „Hey Marvin“ nicht auf, steht hier meist eine leicht verfälschte Variante. Wenn dem so ist, dann ergänzt du sie einfach in den phrases der config.yml, ganz ohne Code-Änderung.
Schritt 5: Akustisches Wake-Word als Alternative (Ausblick)
Der transkript-basierte Weg hat einen Preis: Die ASR läuft dauerhaft, weil sie ja ständig mithören muss, um die Wake-Phrase zu finden. Effizienter ist ein akustisches Wake-Word, das direkt auf dem Audiosignal arbeitet und die schwere ASR/LLM-Kette erst dann anwirft, wenn das Schlüsselwort fällt.
- openWakeWord: quelloffen und vollständig lokal die zur Serie passende Variante, wenn man auf ein dediziertes akustisches Modell gehen will.
- Picovoice Porcupine: technisch sehr gut und sparsam, benötigt aber einen Picovoice-AccessKey (Account). Damit verlässt man die „komplett lokal, kein fremder Dienst“-Linie dieser Serie. Deshalb hier nur als Erwähnung für Dich.
Für den Abschluss dieser Serie reicht der lokale Transkript-Weg aus Schritt 2 aber vollkommen. Es ging mir ja darum eine lauffähige lokale version zu zeiten. Das akustische Wake-Word hebe ich mir für einen möglichen Folgebeitrag auf.
Latenz & Praxis
Der Türsteher selbst kostet kaum Zeit. Er liest ja nur den ohnehin vorhandenen Transkript-Text mit. Die spürbare Latenz bleibt die aus Teil 7: ASR → NAT-Agent → TTS. Das gilt weiter: klein und schnell schlägt groß und schlau. Der einzige echte Nachteil der Transkript-Variante ist die permanent laufende ASR. Auf der A6000 Ada fällt das nicht ins Gewicht, auf schwächerer Hardware wäre das akustische Wake-Word aus Schritt 5 der sparsamere Weg.
Ein praktischer Tipp aus dem Test: Nutze beim Ausprobieren Kopfhörer. Sonst transkribiert Parakeet Magpies eigene Sprachausgabe mit, was den Keepalive-Timer künstlich wachhält und das Einschlafen verzögert.
Fazit: Die Serie ist komplett
Mit dem Wake-Word steht der letzte Baustein, und damit ist die Reise rund: vom ersten NIM bis zum handelnden, weckbaren Assistenten. Ich habe Dir gezeigt wie Du alles lokal, auf Deutsch, auf eigener Hardware einrichtest.
Der Weg im Schnelldurchlauf:
- Teil 1–4: die einzelnen Speech-NIMs – ASR (Parakeet/Canary) und TTS (Magpie) lokal aufgesetzt.
- Teil 5: der NAT-Agent als Orchestrator mit Tools.
- Teil 6: ASR, LLM und TTS mit Pipecat zum unterbrechbaren Voice-Loop verdrahtet.
- Teil 7: den NAT-Agenten als Gehirn in den Loop gehängt – der Assistent kann jetzt handeln.
- Teil 8: das Wake-Word als Türsteher davor – der Agent hört erst auf Zuruf.
Herausgekommen ist ein souveräner Voice-Agent, der ohne Cloud auskommt und den du Stück für Stück um eigene Tools erweitern kannst. Für mich geht es jetzt genau dort weiter: weg von der reinen Zeitansage, hin zu Tools, die mir im Alltag echten Mehrwert bringen und an der Optimierung der Latenz z. B. weg von Ollama hin zu vLLM, damit sich das Gespräch noch natürlicher anfühlt.
Danke fürs Mitbauen über diese Serie hinweg aus einzelnen Bausteinen ist ein vollständiger, eigener Sprachassistent geworden, der komplett unter meiner Kontrolle läuft. Was als Nächstes auf dem Blog kommt, verrate ich noch nicht aber Marvin bekommt mit Sicherheit noch ein paar nützlichere Fähigkeiten als die schlecht gelaunte Zeitansage.
Wenn du das nachgebaut hast: Schreib mir gern in die Kommentare, welche Wake-Phrase bei dir am zuverlässigsten funktioniert und welche Tools du als Erstes an deinen Agenten hängst.
Artikelübersicht – Souveräner lokaler Voice-Agent:
Teil 1: NVIDIA Nemotron ASR Streaming lokal mit NeMoTeil 2: NVIDIA NIM lokal – deutsche Spracherkennung (Parakeet)
Teil 3: NVIDIA Canary lokal – mehrsprachige Spracherkennung & Übersetzung
Teil 4: NVIDIA Magpie TTS lokal – deutsche Sprachausgabe
Teil 5: NeMo Agent Toolkit (NAT) – Orchestrator lokal aufsetzen
Teil 6: Lokaler Voice-Agent – ASR, LLM & TTS mit Pipecat zum Loop
Teil 7: Voice-Agent mit Tool-Calling – NAT als Gehirn
Teil 8: Souveräner Voice-Agent – lokales Wake-Word als Türsteher







Ein toller Guide der leicht zugänglich und verständlich ist. Perfekt für ein kleines Side-Project geeignet. Aktuell half mir noch mein…
Thank you for this great tutorial, could you share n8n workflow and comfyui workflow please?
Hallo Anton, die Meldung besagt das in meinem Beisiel Methoden verwendet werden die veraltet (deprecated) sind. Also müsstest Du die…
Danke für das Tool! Ich habe erst kürzlich angefangen mich mit der Thematik zu beschäftigen und bin für meine Erwartungen…
Hallo, ich habe ihre Anleitung befolgt und bekomme im letzten Schritt leider immer folgende Meldung im Terminal: bash <(wget -qO-…