arizuko

arizuko › concepts › voice

voice

The agent reads and writes only text. Whisper turns an incoming voice note into text. The send_voice tool turns text into an ogg file, which the chat platform delivers as a voice note.

Whisper turns a voice note into text before the agent reads it

When a voice note arrives at an adapter, such as Telegram, WhatsApp or Discord, the adapter downloads the audio and gives it to routd. If VOICE_TRANSCRIPTION_ENABLED=true, routd posts the audio to the Whisper server at WHISPER_BASE_URL before the agent sees the message. The transcript comes back as plain text, and routd adds it to the attachment tag in the prompt:

<attachment path="/home/node/media/20260515/note.ogg"
            mime="audio/ogg"
            transcript="hey can you check the inbox">

The agent reads transcript= like any other text and never transcribes again. The raw audio stays on disk in media/<date>/ in case a skill needs the bytes.

send_voice turns text into a voice note

To reply with voice, the agent calls the send_voice MCP tool with the JID of the chat, the text and an optional voice name. routd does the rest:

  1. routd picks the voice. An explicit argument wins. Otherwise, routd uses voice: from the PERSONA.md frontmatter, and otherwise the TTS_VOICE environment variable.
  2. routd hashes the text, voice and model with sha256. If <data_dir>/tts/<hash>.ogg already exists, routd uses it again and synthesizes nothing.
  3. If the file does not exist, routd posts to TTS_BASE_URL at the OpenAI-shaped path /v1/audio/speech. The bundled ttsd daemon forwards the request to a local Kokoro-FastAPI container.
  4. routd gives the ogg file to the channel adapter, which sends it with the native voice message of the platform.

The text limit is 5000 characters. The TTS endpoint is compatible with OpenAI, so to use Piper, Coqui or the OpenAI cloud, change TTS_BACKEND_URL on ttsd.

Telegram, WhatsApp and Discord deliver voice notes

PlatformHow send_voice is delivered
TelegramsendVoice, with the push-to-talk UI
WhatsAppaudio with ptt:true through Baileys
Discordan audio/ogg attachment
Every other adapter, such as Slack and emailnot supported: chanlib.ErrUnsupported, and the agent uses send instead

The agent calls send_voice when its instructions say so

The system never turns a text reply into voice by itself. The agent chooses send_voice or send for each reply.

Two failures have two different results

With TTS_ENABLED=false on the instance, send_voice returns an “unsupported” error with a hint, and the agent sends plain text with send. If ttsd itself is down, nothing turns voice off for you. routd does not watch the /health of ttsd, so the call fails with a synthesis error, and the agent must send text instead.

Related pages cover the persona and the address

The voice: frontmatter that sets a voice per agent lives in PERSONA.md. ant shows the folder layout. jid shows the shape of the JIDs in send_voice(chatJid, …).