hermes-agent/website/docs/user-guide/features/wake-word.md
Teknium a832139ba3
feat(wake): eager-install voice deps with the desktop; wake probes never run pip
Two fixes from live testing (Teknium):

1. Desktop installs now ship the wake/voice stacks up front.
   install.sh + install.ps1 desktop stages run 'uv pip install
   -e .[wake,voice]' (best-effort, lazy-install remains the fallback)
   before building the app, so the first ear-click arms instantly
   instead of sitting through a multi-minute onnxruntime download.
   CLI-only installs keep the lazy path — [all] curation unchanged.

2. The vanished ear: the STT/TTS gate made wake.status call
   check_tts_requirements(), whose edge path runs _import_edge_tts →
   lazy_deps.ensure — a synchronous PIP INSTALL inside a status poll.
   On a venv without edge-tts that blew the desktop's 30s RPC timeout,
   armWakeWord caught the error, the atom never learned enabled=true,
   and the ear unmounted. _tts_ready is now a pure probe: deps missing
   + lazy installs allowed counts as ready (installs at first speak)
   WITHOUT touching pip; check_tts_requirements only runs once deps
   are present. Regression test asserts the probe never calls it while
   deps are missing.
2026-07-28 07:59:34 -07:00

9.3 KiB
Raw Blame History

sidebar_position title description
11 Wake Word Hands-free 'Hey Hermes' wake word — start a voice session by speaking, the 'Hey Siri' way

Wake Word ("Hey Hermes")

The wake word turns Hermes into a hands-free assistant across the CLI, TUI, and desktop app: with one setting on, Hermes listens in the background for a spoken trigger phrase. Say it, and Hermes starts a fresh session, opens the microphone, captures your command via the normal voice pipeline, and answers — exactly like "Hey Siri" or "Alexa". Use surface to pick which one listens.

Detection runs entirely on-device. The always-on listener only watches for the wake phrase; no audio leaves your machine until you actually speak a command to the agent.

How it works

  1. With wake_word.enabled: true (or after /wake on), a lightweight hotword detector listens on your default microphone.
  2. When it hears the wake phrase it pauses itself (freeing the mic), starts a new session, and records one utterance with voice mode's silence detection.
  3. Your speech is transcribed and sent to the agent. After it replies, the listener resumes automatically and waits for the next wake word.

It is off by default — nothing listens until you turn it on.

Engines

Engine Cost API key Notes
openWakeWord (default) Free None Local ONNX models. Ships a bundled "hey hermes" model (default); also supports hey_jarvis, alexa, hey_mycroft, … and custom models
sherpa Free None Open vocabulary — detects ANY typed phrase with zero training. Small English model auto-downloads on first use (~13 MB)
Porcupine Free tier / paid PORCUPINE_ACCESS_KEY Picovoice engine; built-in keywords + custom .ppn files

By default the phrase is "hey hermes" — a model for it ships with Hermes, so it works out of the box with no training. (On first use, openWakeWord downloads its shared feature-extraction models — a small one-time fetch.)

Both are lazy-installed the first time you enable the wake word (desktop installs made with --include-desktop pre-install them, so the ear works instantly). To install ahead of time:

cd ~/.hermes/hermes-agent && uv pip install -e ".[wake]"

Quick start

# In an interactive `hermes` session:
/wake on        # start listening (installs the engine on first use)
/wake status    # show phrase, provider, and state
/wake off       # stop listening

In the desktop app, click the ear icon in the composer.

The toggle IS the setting: turning the wake word on or off — via /wake or the desktop ear button — also writes wake_word.enabled to ~/.hermes/config.yaml, so your choice persists across sessions. You can also flip it by hand:

wake_word:
  enabled: true

Configuration

wake_word:
  enabled: false
  surface: auto               # eligible surface: "auto" | "cli" | "tui" | "gui"
  provider: openwakeword      # "openwakeword" (free, local) | "porcupine"
  phrase: "hey hermes"        # cosmetic label only — detection is keyed by the model/keyword below
  sensitivity: 0.5            # 0.0-1.0 — raise to reduce false triggers
  start_new_session: true     # start a fresh session on wake vs. continue the current one
  openwakeword:
    model: hey_hermes         # bundled default; OR a built-in name OR a path to a custom .onnx/.tflite
    inference_framework: onnx # "onnx" | "tflite"
  porcupine:
    keyword: jarvis           # built-in keyword OR path to a custom .ppn

sensitivity, phrase, and start_new_session apply to both engines. The openwakeword and porcupine blocks select the actual detection model.

Surfaces (CLI, TUI, GUI)

The wake word works in all three Hermes surfaces, and surface picks which one owns the listener and opens the new session when it fires:

surface Behavior
auto (default) All local surfaces are eligible; the first one to arm owns the listener.
cli Only the classic hermes CLI.
tui Only hermes --tui.
gui Only the desktop app.

The detector is on-device and single-mic, so only one surface listens at a time, including when Hermes surfaces run in separate processes. Ownership is sticky: the first eligible claimant keeps the listener until it stops, disconnects, or its process exits. Hermes does not silently fail over to another open surface. Set surface when you want to pin ownership instead of using first-claim wins. The TUI and desktop GUI share the same Python backend (tui_gateway), which runs the detector server-side and yields the mic to voice capture while a command records.

Using a different phrase

"Hey Hermes" works out of the box — the bundled openWakeWord model (model: hey_hermes) is the default. To wake on something else, the easiest path is the open-vocabulary engine:

Option A — sherpa (any phrase, zero training)

Type the phrase you want; it's tokenized at runtime — "hey coder", "computer", "wake up neo", anything:

wake_word:
  enabled: true
  provider: sherpa
  phrase: "hey coder"        # detection key — just type your phrase

The small English KWS model (~13 MB) downloads once on first use. Each profile can set its own phrase — "hey <profile>" for every profile you run.

Waking a specific profile (desktop)

With the sherpa engine, ONE listener can wake ANY profile. Every profile whose config has wake_word.enabled: true is enrolled automatically; its phrase defaults to hey <profile name> when unset. Say a profile's phrase and the desktop app live-switches to that profile, opens a fresh session there, and starts hands-free voice:

  • "hey hermes" → default profile
  • "hey coder" → the coder profile
  • "hey trader" → the trader profile

Set wake_word.profile_routing: false on the listener's profile to opt out and listen only for its own phrase. The CLI and TUI are single-profile processes: a wake phrase belonging to another profile prints the switch command (hermes -p <profile>) instead of routing.

Names are matched acoustically by their English subword sounds: two-word phrases with distinct, 2+ syllable names work best. Very short names, heavy non-English phonology, or two profiles with similar-sounding names will degrade accuracy — tune per-profile sensitivity if needed.

Option B — openWakeWord (free, trained model)

Name a built-in model (hey_jarvis, alexa, hey_mycroft, …), or train a custom model (≈7590 min on a free/Colab GPU) for maximum robustness, drop the .onnx file somewhere, and reference it:

wake_word:
  enabled: true
  provider: openwakeword
  phrase: "computer"
  openwakeword:
    model: ~/.hermes/wakewords/computer.onnx   # or a built-in name like hey_jarvis

Training references:

:::tip Pick a distinctive phrase Wake phrases that don't collide with everyday speech generalize best. Two syllables with an uncommon word ("hermes" qualifies) beat common words like "hello" or "stop". :::

Option C — Porcupine (custom keyword in seconds)

Create a "Hey Hermes" keyword in the Picovoice Console, download the .ppn, and:

wake_word:
  enabled: true
  provider: porcupine
  phrase: "hey hermes"
  porcupine:
    keyword: ~/.hermes/wakewords/hey_hermes.ppn

Set your access key in ~/.hermes/.env:

PORCUPINE_ACCESS_KEY=your-key-here

Requirements

  • A working microphone and the sounddevice + numpy audio stack (shared with voice mode).
  • An STT provider for transcribing the spoken command — local faster-whisper works out of the box; see Voice Mode for the full provider list.
  • A TTS provider for speaking the reply (the default edge-tts works with no key). The wake flow is fully hands-free, so the toggle refuses to arm until both STT and TTS are ready — hermes tools (Voice section) sets them up.
  • The wake engine deps (auto-installed, or hermes-agent[wake]).

/wake status reports exactly what's missing if the listener won't start.

"Listening" but never wakes (macOS)

macOS grants microphone access per process. STT working in the desktop app proves the renderer has mic access — the wake listener runs in the Python backend, which needs its own grant. Without it, CoreAudio hands the backend a "working" stream that only ever delivers silence, so the ear shows listening but the phrase never fires. Hermes detects this (/wake status shows "mic delivers only silence"; the desktop ear tooltip carries the same hint). Fix: System Settings → Privacy & Security → Microphone → enable the Hermes backend (it may appear as your terminal, python, or Hermes), then toggle the wake word off and on.

Notes & limits

  • Local surfaces only. The wake word runs in the CLI, TUI, and desktop GUI — wherever a local microphone is available. It does not run in the messaging gateway (Telegram, Discord, …), which has no mic.
  • One mic at a time. The detector releases the microphone while a command is recording and reclaims it once the turn ends, so it won't fight voice capture.
  • Privacy. Hotword detection is local. Set sensitivity higher if you get false triggers, lower if it misses you.