Mensagens de voz
Uma mensagem de voz chega como texto. A transcrição acontece à entrada, antes de o agente correr.
Mensagens de voz
Envia uma mensagem de voz ao teu bot do Telegram e o agente recebe texto. O áudio é transcrito à chegada, antes de existir uma sessão e antes de qualquer decisão de encaminhamento, por isso o que chega ao agente é uma mensagem vulgar.
Nem sempre foi assim. O gateway gravava o ficheiro na área de trabalho do agente e entregava-lhe o caminho, deixando-o descobrir sozinho como escutar: encontrar um transcritor, instalá-lo, executá-lo, ler o resultado. Cada mensagem de voz tornava-se um pequeno projeto de investigação. Era lento, saía diferente de cada vez, e gastava uma barreira de permissão só para ler a mensagem que acabara de chegar.
Nada para configurar
Se já tens uma ligação de modelo à OpenAI ou à Groq, a transcrição já funciona. O Pepe
reaproveita essa credencial e pede ao provider o modelo de transcrição dele (whisper-1 na
OpenAI, whisper-large-v3-turbo na Groq) em vez do modelo de chat com que a ligação foi
configurada. Envia uma mensagem de voz e ela é respondida. Não há nada a definir.
Como a rota é escolhida
O Pepe tenta estas rotas por esta ordem, e qualquer uma delas pode não existir:
media.audio.model: uma ligação de modelo, referida pelo nome. A cadeia defallbacksdessa ligação também se aplica aqui, por isso o failover não custa nada de extra.media.audio.command: um comando local, por exemplowhisper-cli -f {file}. O{file}é substituído pelo caminho do áudio. Isto é tentado antes da deteção automática, de propósito: quem configurou um transcritor local fê-lo para o áudio não sair da máquina, e passar-lhe à frente para chamar um provider derrotaria o objetivo.- Deteção automática: a rota sem configuração descrita acima.
- Nada disponível: o ficheiro vai para o agente, que se desenrasca com as ferramentas que tem. Esse caminho fica como rede de segurança; não é a porta de entrada.
Configurar isto
Aponta a transcrição para uma ligação de modelo específica, ou 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 à deteção automática
--echo true devolve a transcrição ao chat, para quem falou ver o que foi entendido. As
mesmas opções estão na página Config do painel, e no pepe setup em Media.
Porque é que transcrever primeiro faz diferença
Como as palavras existem antes de o encaminhamento correr, o encaminhamento consegue lê-las. Daqui saem duas consequências, nenhuma delas possível enquanto a transcrição só aparecia dentro do turno do agente:
- Um comando barra falado funciona. Diz
/helpou/stopnuma mensagem de voz e o comando é executado, tal como se o tivesses escrito, em vez de se tornar um turno do agente sobre um ficheiro largado numa pasta. - Um bot num grupo pode ser interpelado por voz. Num grupo que exige menção, a barreira passa a ler as palavras em vez da legenda. Uma mensagem de voz não tem legenda, por isso antes disto não havia nada para a barreira ler, e era impossível dirigir-se ao bot a falar.
Responder de volta
Responde a uma mensagem de voz com outra mensagem de voz. Desligado por predefinição;
aponta media.tts para uma ligação de modelo que sirva um /audio/speech compatível com
OpenAI e liga-se:
pepe media tts --model openai --voice nova
pepe media tts off
A resposta em texto continua a ser o registo que fica guardado. O áudio é um extra, e tem
um limite de tamanho para uma resposta longa nunca se tornar um clip de cinco minutos. Uma
falha no TTS é silenciosa: a resposta em texto já foi enviada, por isso nada se perde, só
não ganha voz nesse turno. As mesmas opções estão na página Config do painel, e no
pepe setup em Media.
Fotografias
Envie uma fotografia ao seu bot do Telegram e, num modelo com visão, o agente vê a imagem
de verdade, não um nome de ficheiro. Antes recebia apenas uma linha de texto (“o utilizador
enviou uma fotografia, guardada em …”) enquanto a própria imagem nunca chegava ao modelo,
pelo que o agente ficava a adivinhar, ou a inventar, o que lá estava. Agora a imagem segue
juntamente com a mensagem.
Fica desligado a menos que diga que o modelo enxerga. Nem todos os endpoints compatíveis com a OpenAI aceitam uma imagem, e enviar uma para um modelo só de texto é um erro, por isso a visão é opcional por ligação:
{
"models": {
"gpt4o": {
"base_url": "https://api.openai.com/v1",
"api_key": "${OPENAI_API_KEY}",
"model": "gpt-4o",
"vision": true
}
}
}
Com vision ativo, uma fotografia (com ou sem legenda) chega ao modelo como imagem no turno em
que é recebida. Funciona da mesma forma em ligações compatíveis com a OpenAI, Anthropic e
Responses/Codex. A imagem acompanha apenas esse turno: como uma transcrição, o registo que fica
é a resposta do agente sobre ela, não os bytes, pelo que nunca incha a sessão nem é reenviada a
cada turno. Um modelo sem vision volta ao comportamento antigo (o caminho do ficheiro no
prompt, para o agente abrir com as suas próprias ferramentas).
O Telegram já envia cada fotografia em vários tamanhos pré-redimensionados, por isso o Pepe
escolhe o maior que cabe no teto de bytes, sem qualquer biblioteca de processamento de imagem
para instalar. Um álbum de fotografias é enviado como várias imagens em conjunto. Os limites
ficam em media.image e ambos têm predefinições:
max_mb: a maior imagem aceite, em megabytes. Predefinição5. Uma fotografia demasiado grande (ou de um tipo não suportado) volta ao prompt com o caminho do ficheiro.max_parts: quantas imagens um turno pode transportar, para álbuns. Predefinição4. O que passar disso é entregue como caminho de ficheiro.
{
"media": {
"image": { "max_mb": 5, "max_parts": 4 }
}
}
Configuração
Todas as chaves são opcionais e vivem em media.audio, no ~/.pepe/config.json:
model: o nome de uma ligação de modelo com a qual transcrever.command: um transcritor local. O{file}é substituído pelo caminho do áudio.language: uma sugestão de idioma passada ao provider.max_mb: limite de tamanho para o ficheiro recebido. Por omissão,20.timeout: quanto tempo uma transcrição pode demorar, em segundos. Por omissão,60.echo: devolve a transcrição ao chat como📝 ..., para quem falou conferir o que foi entendido.
{
"media": {
"audio": {
"model": "groq",
"language": "pt",
"max_mb": 20,
"timeout": 60,
"echo": true
}
}
}
Para manter o áudio na máquina, usa um comando em vez de uma ligação:
{
"media": {
"audio": {
"command": "whisper-cli -f {file}",
"timeout": 120
}
}
}
Salvaguardas
- Um ficheiro abaixo de 1 KB é recusado antes de qualquer pedido. Com esse tamanho está vazio ou truncado, e não apenas silencioso, e nenhum transcritor diria nada de útil sobre ele. Recusar não custa nada; enviá-lo custa um pedido.
- Um ficheiro acima do
max_mbé recusado da mesma forma, antes de custar um pedido. - Um comando encravado é abandonado ao fim do
timeout, em vez de encravar a conversa que está atrás dele. - Um áudio sem fala nenhuma recebe uma resposta curta, não um turno do agente. O ficheiro foi lido, apenas não tinha nada lá dentro, e responder a uma mensagem vazia só produziria uma resposta confusa.