Mensagens de voz

Um áudio chega como texto para o agente. A transcrição acontece na entrada, antes de qualquer execução começar.

Mensagens de voz

Manda um áudio no Telegram ou no WhatsApp, ou no Discord (como anexo de um comando de barra, ou falado direto num canal que o bot escuta), e o que o agente recebe é texto. A transcrição acontece na chegada, antes mesmo de existir uma sessão e antes de qualquer decisão de roteamento, então o agente sempre vê uma mensagem comum, como qualquer outra.

Nem sempre foi assim. Antes, o gateway apenas salvava o arquivo no workspace do agente e passava o caminho adiante, deixando por conta dele descobrir como “escutar” aquilo: achar um transcritor, instalar, rodar, ler a saída. Cada áudio recebido virava um pequeno projeto de pesquisa, lento, inconsistente de uma vez para outra, e que ainda gastava uma barreira de permissão só para ler uma mensagem que tinha acabado de chegar.

Não há nada para configurar

Se você já tem uma conexão de modelo com a OpenAI ou com a Groq, a transcrição simplesmente já funciona: o Pepe reaproveita essa mesma credencial e pede ao provedor o modelo de transcrição dele (whisper-1 na OpenAI, whisper-large-v3-turbo na Groq), em vez do modelo de chat com o qual a conexão foi configurada. Basta mandar o áudio que ele já vem respondido, sem nenhum ajuste prévio.

Em quais canais isso funciona

Telegram, WhatsApp e Discord tratam um anexo da mesma forma: áudio vira transcrição, documento vira texto, e imagem vai para um modelo com visão como imagem mesmo. Os ajustes desta página valem para os três, então basta configurar a transcrição uma vez.

Dois detalhes que vale saber:

Slack, Microsoft Teams e Google Chat continuam recebendo apenas texto.

Como a rota é escolhida

O Pepe segue esta ordem de tentativas, e é normal faltar algum dos passos:

  1. media.audio.model: uma conexão de modelo, referenciada pelo nome. A cadeia de fallbacks daquela conexão também vale aqui, então o failover sai de graça.
  2. media.audio.command: um comando local, por exemplo whisper-cli -f {file}, onde {file} é trocado pelo caminho do áudio. Esse comando é tentado antes da detecção automática, de propósito: quem configura um transcritor local o faz justamente para manter o áudio fora da rede, e pular essa etapa para chamar um provedor externo anularia a ideia toda.
  3. Detecção automática: a rota sem nenhuma configuração, descrita acima.
  4. Nada disponível: o arquivo cai no colo do agente, que se vira com as próprias ferramentas. Esse caminho continua existindo como rede de segurança, mas não é a porta de entrada normal.

Configurando

Dá para apontar a transcrição para uma conexão de modelo específica, ou para um comando local, pela CLI:

pepe media audio --model groq --language pt --echo true
pepe media audio --command "whisper-cli -f {file}"   # mantém o áudio na máquina
pepe media audio off                                 # volta para detecção automática

--echo true devolve a transcrição para o próprio chat, assim quem falou consegue conferir se foi bem entendido. As mesmas opções aparecem na página Config do painel, e no pepe setup dentro de Mídia.

Por que transcrever primeiro faz diferença

Como o texto já existe antes de o roteamento entrar em ação, o roteamento consegue ler esse texto normalmente. Disso saem duas consequências, nenhuma das duas possível enquanto a transcrição só surgia dentro do turno do próprio agente:

Áudio vira texto; foto vira visão. A fala é transcrita já na porta de entrada. Já uma foto é enviada ao modelo como uma imagem que ele realmente enxerga (num modelo com suporte a visão, veja mais abaixo). Um documento, por sua vez, é extraído direto para texto.

Respondendo de volta com voz

Dá para responder a um áudio com outro áudio. Isso vem desligado por padrão; para ligar, basta apontar media.tts para uma conexão de modelo que sirva um /audio/speech compatível com OpenAI:

pepe media tts --model openai --voice nova
pepe media tts off

O registro que fica salvo continua sendo a resposta em texto; o áudio é só um extra, com um teto de duração para que uma resposta longa nunca vire um clipe de cinco minutos. Se o TTS falhar, a falha é silenciosa: a resposta em texto já saiu de qualquer forma, então nada se perde, ela só fica sem voz naquele turno específico. As mesmas configurações estão na página Config do painel, e no pepe setup em Mídia.

Fotos

Envie uma foto no Telegram, no WhatsApp ou no Discord e, com um modelo que suporte visão, o agente enxerga a imagem de verdade, não apenas um nome de arquivo. Antes disso, ele só recebia uma linha de texto do tipo “o usuário enviou uma foto, salva em …”, enquanto a imagem em si nunca chegava ao modelo, deixando o agente adivinhando, ou pior, inventando o que havia nela. Agora a imagem viaja junto com a própria mensagem.

Isso fica desligado até você avisar explicitamente que o modelo enxerga imagens. Nem todo endpoint compatível com OpenAI aceita uma imagem, e mandar uma para um modelo só de texto vira erro na hora, então a visão é algo que se liga conexão por conexão:

{
  "models": {
    "gpt4o": {
      "base_url": "https://api.openai.com/v1",
      "api_key": "${OPENAI_API_KEY}",
      "model": "gpt-4o",
      "vision": true
    }
  }
}

Com vision ativado, uma foto (com ou sem legenda) chega ao modelo como imagem já no turno em que foi enviada. Isso funciona do mesmo jeito em conexões compatíveis com OpenAI, Anthropic e Responses/Codex. A imagem acompanha só aquele turno específico: assim como acontece com uma transcrição, o que fica registrado de fato é a resposta do agente sobre a imagem, não os bytes dela, então a sessão nunca incha nem reenvia a mesma imagem a cada novo turno. Um modelo sem vision simplesmente cai de volta para o comportamento antigo: o caminho do arquivo entra no prompt, e cabe ao agente abri-lo com as próprias ferramentas.

O Telegram já manda cada foto em vários tamanhos pré-redimensionados, então o Pepe escolhe o maior que ainda cabe dentro do teto de bytes, sem precisar de nenhuma biblioteca de processamento de imagem instalada. Um álbum inteiro de fotos chega como várias imagens juntas. Os dois limites que controlam isso ficam em media.image, ambos com valor padrão:

{
  "media": {
    "image": { "max_mb": 5, "max_parts": 4 }
  }
}

Configuração

Todas as chaves abaixo são opcionais e ficam sob media.audio, no ~/.pepe/config.json:

{
  "media": {
    "audio": {
      "model": "groq",
      "language": "pt",
      "max_mb": 20,
      "timeout": 60,
      "echo": true
    }
  }
}

Para manter o áudio dentro da própria máquina, use um comando em vez de uma conexão de modelo:

{
  "media": {
    "audio": {
      "command": "whisper-cli -f {file}",
      "timeout": 120
    }
  }
}

Proteções