Pular para o conteúdo principal
Mande uma mensagem de voz no Telegram e a resposta volta como voice note — transcrição na entrada (Whisper embarcado por padrão, zero config), síntese na saída, com qualquer backend TTS configurado. Tudo aqui é agnóstico a provedor: do say do macOS ao motor embarcado Kokoro.

Modos (CHATCLI_GATEWAY_VOICE_REPLY)

ValorComportamento
auto (padrão)Responde na mesma moeda: mensagem de voz → resposta com áudio; texto → só texto.
alwaysToda resposta final sai com áudio anexado.
neverRespostas só em texto.
Valores booleanos legados seguem válidos: truealways, falsenever. Um valor desconhecido cai em auto — um typo nunca silencia o gateway.
# padrão já é auto; só é preciso exportar para always/never
export CHATCLI_GATEWAY_VOICE_REPLY=always
O daemon herda o ambiente da shell que executou /gateway start — e o .env nunca sobrescreve variável já exportada. Se o comportamento não bater com o .env, confira o modo efetivo no boot do daemon: gateway.log registra voice replies enabled (mode=...).

Controle por conversa — tool @voice

Cada conversa manda na própria preferência pedindo em linguagem natural: o modelo chama a tool @voice, e a escolha fica gravada por sessão (sobrevive a restart do daemon) com precedência sobre o modo global.
Pedido na conversaEfeito
”responde em áudio” / “quero ouvir sua voz”@voice on — toda resposta desta conversa sai com voz
”para de mandar áudio”@voice off — só texto nesta conversa
”volta ao normal”@voice auto — de volta ao padrão (voz responde voz)
A preferência fica em ~/.chatcli/gateway_voice_prefs.json (escrita atômica), chaveada por plataforma:chat. Hierarquia de decisão: preferência da conversamodo globalin-kind.

Escrita para o ouvido (speech-aware)

Quando a resposta vai virar áudio, duas camadas garantem fala natural:
  1. O modelo sabe antes de escrever: uma diretiva avisa que a resposta será falada — prosa conversacional, frases curtas, sem emojis, sem listas/tabelas/markdown.
  2. Garantia dura no sanitizador: antes da síntese, StripForSpeech achata markdown (código removido, links viram rótulo, tabelas viram prosa) e remove emojis e pictogramas — motores de TTS leem o nome Unicode deles em voz alta, enterrando a mensagem. Acentos do português ficam intactos.
O texto visual no chat continua com a formatação completa; só o áudio é higienizado.

Voice note que toca (transcode)

Backends que ignoram o formato pedido (o say do macOS emite aiff, espeak emite wav) produziam um arquivo que o Telegram mostra com tamanho mas não reproduz. Com ffmpeg no PATH, o gateway transcoda wav/aiff → OGG/Opus (perfil de voice note: 48 kHz mono) para qualquer provedor; formatos já comprimidos passam direto, e sem ffmpeg o clipe original segue como arquivo de áudio — degradação visível, nunca resposta perdida.

Pipeline completo

voice note (Telegram) ─→ transcrição (whisper local-first) ─→ agent loop
                                                                 │ diretiva speech-aware

texto da resposta ─→ StripForSpeech ─→ TTS (qualquer backend) ─→ transcode ogg/opus ─→ sendVoice
A decisão “esta resposta fala?” é uma regra única (preferência da conversa → modo global → in-kind) compartilhada entre o runner e o agent loop — os dois nunca divergem.

Solução de problemas

SintomaCausa provávelVerificação
Tudo volta com áudio, até textoModo always ativo (env exportada na shell do daemon)gateway.log: voice replies enabled (mode=...)
Áudio chega mas não tocaFormato cru sem ffmpeg para transcodarwhich ffmpeg; instale para voice notes nativas
Nunca responde em vozModo never, ou nenhum backend TTS/configIntegrações · Gateway · Resposta em voz
Voz errada para o idiomaVozes do motor embarcadoCHATCLI_TTS_VOICE (en) / CHATCLI_TTS_VOICE_PT (pt)

Relacionado