Como construí um podcast com a minha voz clonada, do roteiro ao blog
O pipeline completo do fghisi podcast: notas, roteiro com o Claude, revisão em pull request, voz clonada na ElevenLabs, pós-produção no ffmpeg, releases do GitHub, Eleventy e um feed RSS. Com as ferramentas, os comandos, os custos e os erros que me custaram créditos.
Por que um podcast
Eu escrevo no blog sobre engenharia de software e liderança. Mas muita gente que eu queria alcançar não lê artigo longo. Ouve, no carro, na academia, enquanto lava a louça.
Gravar toda semana não cabia na minha rotina. Então eu quis testar uma ideia: e se o podcast nascesse do mesmo jeito que o código deste site? Notas minhas, um roteiro revisado num pull request e uma automação fazendo o resto.
O resultado é o fghisi podcast. Cada episódio parte de um livro, de um artigo meu ou de um aprendizado, e é narrado pela minha voz, clonada com inteligência artificial. As ideias e as opiniões são minhas. A IA me ajuda a transformar isso em áudio.
Antes de escrever uma linha de código, eu desenhei o pipeline inteiro num documento. Este artigo segue esse desenho, etapa por etapa, e conta o que mudou quando ele encontrou o mundo real.
O desenho do início
Este foi o primeiro desenho do pipeline, do jeito que ficou no documento antes de qualquer código. A ideia central já estava ali: eu atuo em dois pontos, as notas e a revisão, e o resto roda sozinho. Dois blocos, o clone da voz e o guia de voz, seriam configurados uma vez e reaproveitados em todos os episódios.
Quase tudo isso foi construído. O que mudou no caminho está mais adiante, em "O plano e a realidade".
O fluxo de um episódio
São seis etapas. Duas são minhas e as outras rodam sozinhas:
- Clonagem da voz, feita uma vez e reaproveitada em todos os episódios.
- Notas, um arquivo
notas.mdque eu escrevo numa branchep/<data>-<tema>. É a única etapa totalmente manual, e é ela que garante que o episódio tem a minha opinião. - Roteiro, escrito com o Claude a partir das notas e de um guia de voz.
- Revisão, no pull request. Eu leio o roteiro em voz alta e ajusto o que não soa como eu.
- Áudio: a voz é gerada na ElevenLabs e montada no ffmpeg, com volume padronizado e trilha.
- Publicação: o MP3 vai para um release do GitHub e o site ganha a página do episódio e o item no feed RSS.
O tempo humano por episódio fica em escrever as notas, cerca de uma hora depois de ler, e revisar o roteiro, uns vinte minutos.
A stack
A regra que eu me dei foi usar o que o site já tinha, sempre que desse. Esta é a stack que ficou, com as alternativas que considerei:
| Etapa | Ferramenta | Alternativa considerada | Por quê |
|---|---|---|---|
| Voz clonada | ElevenLabs | Resemble AI, modelos abertos (F5-TTS, Chatterbox) | A voz mais natural em português que encontrei, com API simples |
| Roteiro | Claude, da Anthropic | Escrever à mão | Segue o guia de voz e a estrutura de cada tipo de episódio com consistência |
| Notas | Markdown no repositório | Readwise, para puxar destaques do Kindle | Versionado, sem custo, editável em qualquer lugar |
| Pós-produção | ffmpeg | Auphonic | Gratuito e roda no GitHub Actions |
| Arquivos de áudio | Releases de um repositório público no GitHub | Cloudflare R2, pasta na Hostinger | Sem conta nova, sem senha e sem custo |
| Site e feed | Eleventy, o gerador deste blog | Astro, um serviço de hospedagem de podcast | O episódio vive no meu domínio e sai no mesmo build do blog |
| Orquestração | GitHub Actions | n8n, Make | Já é o caminho do código do site, sem servidor extra |
Os scripts são em Python, só com a biblioteca padrão. As chamadas às APIs são urllib puro, sem SDK. Isso deixou o pipeline fácil de testar sem rede e de rodar no meu computador.
O repositório ficou assim:
podcast/
config/
guia-de-voz.md como eu falo: abertura, fechamento, ritmo, regras
prompt.md instruções do roteiro para cada tipo de episódio
pronuncia.json nomes e termos escritos como se leem
trilha.mp3 a trilha de todo episódio
episodios/
_modelo/ modelos das notas: livro, artigo e aprendizado
2026-10-de-senior-a-principal/
notas.md eu escrevo
roteiro.md gerado e revisado no PR
notas-do-episodio.md
linkedin.md
scripts/ gerar_roteiro, sintetizar, pos_producao, publicar, remixar
Etapa 1: clonar a voz
A ElevenLabs oferece dois tipos de clone:
| Opção | Áudio necessário | Qualidade | Plano mínimo |
|---|---|---|---|
| Clone rápido (Instant Voice Clone) | 1 a 3 minutos | Boa para começar; perde naturalidade em áudios longos | Starter |
| Clone profissional (Professional Voice Clone) | 30 minutos, ideal 3 horas | Quase indistinguível da voz original | Creator |
O plano era começar pelo clone rápido para validar o pipeline inteiro, enquanto eu juntava horas de gravação para o profissional. Quando ele ficasse pronto, bastaria trocar o voice_id. É o que estou fazendo.
Para a gravação, o roteiro que segui foi este:
- Ambiente: um cômodo silencioso, com cortinas, tapete ou roupas por perto para abafar o eco, e nada de ventilador ligado.
- Equipamento: microfone sempre na mesma distância, cerca de um palmo da boca.
- Formato: WAV, 44,1 ou 48 kHz, mono, sem música nem redução de ruído agressiva.
- Conteúdo: falar no tom do podcast, conversado, e não ler um texto. O clone copia o jeito da gravação: amostra lida dá clone com cara de leitura.
Etapa 2: as notas
Cada episódio nasce de um único arquivo. Tem um modelo para cada tipo de episódio. Este é o de artigo:
---
tipo: artigo
artigo: "De sênior a principal: o trabalho que acontece entre os times"
url: "https://fghisi.com.br/2026/10/16/de-senior-a-principal/"
minutos: 12
publicar_em: 2026-10-18
---
## Por que escrevi esse artigo
## O que quero que fique de quem ouvir (3 pontos)
## Uma história ou exemplo para contar em voz
## O que mudou no que eu penso desde que escrevi
## O que aplicar segunda-feira
O de livro tem as ideias principais com o meu comentário, onde eu discordo e para quem recomendo. O de aprendizado tem o contexto, o que eu achava antes e o que faria diferente.
Etapa 3: o roteiro
O roteiro sai de três arquivos juntos: as notas, o guia de voz e o prompt.
O guia de voz é escrito uma vez. Ele diz como eu abro e fecho os episódios, que expressões uso e quais nunca usaria, se explico siglas, e traz trechos dos meus artigos como referência de estilo. Também tem regras fixas: primeira pessoa do singular, nada de exemplos reais do meu time ou da empresa, nada de dados inventados.
A estrutura muda com o tipo de episódio:
| Tipo | O meio do episódio |
|---|---|
| Livro | Contexto dos autores, de três a cinco ideias-chave com o meu comentário, crítica e aplicação prática |
| Artigo | Por que escrevi, os pontos principais contados como conversa, o que mudou desde então e aplicação prática |
| Aprendizado | O contexto, o que eu achava antes, os aprendizados, o que faria diferente e aplicação prática |
Abertura e fechamento são fixos em todos: "Oi, eu sou o Fernando Ghisi, e este é o fghisi podcast" e o convite para as notas no blog.
As regras do prompt existem porque texto para ler e texto para ouvir são coisas diferentes:
- Frases curtas, sem listas, tabelas ou parênteses.
- Números por extenso e siglas explicadas na primeira menção.
- Nunca citar trechos de livros com mais de uma frase: paráfrase e comentário.
- Nunca inventar o que não está nas notas. Quando faltar um fato, marcar
[VERIFICAR]. O pipeline se recusa a gerar o áudio enquanto existir um. - Entregar junto o título, a descrição, as notas do episódio com as referências e um rascunho de post para o LinkedIn.
O roteiro pode ser gerado pela API do Claude, num workflow do Actions, com a saída forçada numa ferramenta (tool_choice), para vir sempre estruturado em blocos. Ou pode ser escrito numa conversa e enviado junto com as notas: quando o workflow encontra um roteiro.md pronto, ele não chama a API. Comecei pelo segundo jeito, sem chave da Anthropic.
A revisão é o portão humano. O roteiro chega num pull request, e é ali que se pega erro de fato ou frase que não soa como eu. Na primeira versão, o texto ficou certinho demais e parecia lido. Reescrevi o guia de voz para o tom de um engenheiro conversando com outro, com "pra", "tá", perguntas para quem ouve e exemplos como o PR parado em revisão ou o deploy de sexta-feira.
Etapa 4: a voz
A ElevenLabs gera um pedido por bloco do roteiro, no endpoint POST /v1/text-to-speech/{voice_id}. O corpo de cada pedido fica assim:
{
"text": "Oi! Eu sou o Fernando Guízi, e este é o f guízi podcast.",
"model_id": "eleven_multilingual_v2",
"seed": 42,
"previous_request_ids": ["…", "…", "…"],
"voice_settings": {
"stability": 0.4,
"similarity_boost": 0.75,
"style": 0.4,
"use_speaker_boost": true
}
}
Cada parâmetro tem um motivo:
previous_request_idsencadeia os pedidos, até três anteriores, para a entonação seguir contínua de um bloco para o outro.seedfixo faz uma regeneração sair parecida com a anterior.voice_settings: o padrão da API favorece estabilidade e soa monótono. Estabilidade mais baixa e estilo mais alto deixam a fala mais animada. Os valores ficam em variáveis do repositório, para ajustar sem mexer no código.- Pedidos de até 1.500 caracteres: pedidos longos tendem a perder energia no fim.
A pronúncia ganhou um arquivo próprio, porque a voz errava nomes, inclusive o meu. Cada nome é escrito como se lê em português, e a troca vale só para o texto que vai para a voz. A transcrição no site fica com a grafia certa:
{
"Ghisi": "Guízi",
"fghisi": "f guízi",
"Caleb Mellas": "Quêileb Mélas",
"deploy": "deplói",
"PR": "pê érre"
}
Antes de começar, o script consulta quantos créditos sobram no mês, quando a chave tem permissão para isso. Se o episódio não couber, ele para sem gastar nada, em vez de ficar pela metade.
Etapa 4, parte 2: a pós-produção
O primeiro episódio tinha um defeito: o volume caía em alguns trechos. A causa era a normalização numa passada só, que vai mexendo no ganho ao longo do áudio. A pós-produção ficou assim:
- Cada parte no mesmo volume. O script mede cada parte que veio da ElevenLabs e aplica o ganho que falta para chegar ao alvo.
- Junção, com pausas curtas entre os blocos e mais curtas dentro de um mesmo bloco.
- Trilha. Ela abre o episódio sozinha, abaixa quando eu falo e encerra o episódio.
- Normalização em duas passadas, para −16 LUFS, o padrão de podcast. A primeira mede, a segunda aplica um ganho único.
- Tags ID3: título, autor, a capa e o aviso de que a narração é gerada por IA.
- Duração e tamanho em bytes, que o feed RSS exige.
A normalização em duas passadas, no ffmpeg:
# 1ª passada: mede
ffmpeg -i bruto.wav -af loudnorm=I=-16:TP=-1.5:LRA=11:print_format=json -f null -
# 2ª passada: aplica um ganho único com os valores medidos
ffmpeg -i bruto.wav -af loudnorm=I=-16:TP=-1.5:LRA=11:linear=true:measured_I=…:measured_TP=…:measured_LRA=…:measured_thresh=…:offset=… episodio.mp3
A trilha abaixa sob a voz com um filtro de sidechain: a própria voz controla o volume da música.
[música][voz]sidechaincompress=threshold=0.015:ratio=10:attack=60:release=700
Medi o resultado. Com a música por baixo, o volume da fala ficou igual ao da voz sozinha: −16,6 LUFS nos dois casos.
A trilha também é feita por código. É uma faixa original no estilo do pop rock dos anos oitenta: shuffle em meio-tempo, guitarra com chorus no contratempo, uma guitarra solo com vibrato e bends, baixo, bateria e teclados, tudo sintetizado nota a nota em Python. A guitarra usa o algoritmo de Karplus-Strong, que simula uma corda dedilhada com um buffer de ruído que vai sendo suavizado. Como a faixa é minha, não precisei me preocupar com direitos autorais para publicar.
Etapa 5: a publicação
Os MP3 não entram no git. Eles ficam nos releases de um repositório público, separado do site. O repositório do site é privado, e um release privado não abre para os players. O publicar sobe dois arquivos por episódio: o episódio pronto e a voz sozinha, sem trilha.
O site é o próprio blog, gerado pelo Eleventy. Cada episódio é um arquivo Markdown com o endereço do áudio, a duração e o tamanho. Do mesmo build saem:
/podcast/, com a capa e a lista de episódios;/podcast/<episódio>/, com o player, as notas e a transcrição completa, que ajuda na busca e na acessibilidade;/podcast/feed.xml, o feed RSS, com as tags de podcast que os aplicativos esperam.
Cada item do feed tem o essencial para os aplicativos:
<item>
<title>De sênior a principal: o trabalho entre os times</title>
<enclosure url="https://fghisi.com.br/podcast/audio/2026-10-de-senior-a-principal.mp3"
length="7532377" type="audio/mpeg"/>
<itunes:duration>474</itunes:duration>
<itunes:explicit>false</itunes:explicit>
</item>
Episódio com data futura não gera página nem item no feed até o dia. O deploy roda todo dia de manhã, então dá para agendar.
Por enquanto, o podcast vive só no blog. O feed já está pronto para os aplicativos de podcast, com capa de 3000 × 3000 px e as tags que eles pedem. Levar o podcast até eles é o próximo passo.
A capa, aliás, também sai de um script: as letras da logo do site são convertidas em curvas a partir da fonte, e o resultado vira um PNG de 3000 px.
A orquestração
O plano tinha três workflows. Ficaram dois, mais o deploy que o site já tinha:
| Workflow | Gatilho | O que faz |
|---|---|---|
| Roteiro | push de notas.md numa branch ep/* |
Gera o roteiro com o Claude, se ainda não existir, e abre o PR |
| Áudio | label gerar-audio no PR |
Confere o token, gera a voz, monta o episódio, guarda o MP3 na execução, publica o release, grava a página e comenta o link no PR |
| Áudio | label remixar no PR |
O mesmo, mas reaproveita a voz já publicada: refaz só a mixagem, sem gastar créditos |
| Deploy | merge na main e todo dia às 6h |
Gera o site, baixa os MP3 dos releases para dentro dele e publica na Hostinger |
Os secrets do repositório são três: a chave da ElevenLabs, o voice_id e um token do GitHub com acesso só ao repositório de áudios. A chave da Anthropic é opcional. As configurações da voz e o modelo ficam em variáveis, que não são secretas.
Os testes cobrem os scripts sem rede, com as APIs simuladas: o front matter, a divisão dos blocos, a pronúncia, a recusa de [VERIFICAR], o encadeamento dos pedidos, os comandos do ffmpeg e a página do episódio. Para o áudio em si, instalei o ffmpeg e testei com vozes do próprio macOS gravadas em volumes diferentes.
O plano e a realidade
Boa parte do desenho original sobreviveu. Mas algumas decisões mudaram no caminho:
| No plano | Na prática | Por quê |
|---|---|---|
| Astro | Eleventy | É o gerador que o site já usa |
| Cloudflare R2 | Releases do GitHub | Sem conta nova; o repositório público resolve |
| Plano Creator e clone profissional | Plano Starter e clone rápido | Validar tudo antes de pagar mais |
| Roteiro sempre pela API | Roteiro na conversa ou pela API | Começar sem mais uma chave |
| Três workflows | Dois, mais o deploy | A publicação aproveita o deploy do site |
| MP3 tocado direto do GitHub | MP3 servido pelo site | O Safari do iPhone não tocava |
| Só voz | Voz com trilha | Deixou o episódio com cara de podcast |
O que deu errado
Vale contar, porque foi onde eu mais aprendi.
O token errado custou três vozes. O token que publica no repositório de áudios apontava para o repositório errado. A voz era gerada, a publicação falhava, e o áudio se perdia junto com a máquina do Actions. Três vezes, e cada tentativa gastou créditos. A correção é a de qualquer sistema: falhar cedo. Agora o workflow confere a permissão antes de gerar a voz, criando o release como rascunho, e guarda o MP3 na execução assim que ele fica pronto.
A correção não valeu na hora. O evento de pull request roda o workflow da junção da branch com a base, e essa junção não foi atualizada quando a correção entrou na develop. Uma tentativa ainda rodou com a versão antiga. Lição: atualizar a branch do episódio antes de tentar de novo.
O Actions não podia abrir pull request. A configuração do repositório não deixa, e o workflow do roteiro falhava no último passo. Agora ele só avisa, e o PR é aberto à mão.
O Safari do iPhone não tocava. O GitHub entrega o arquivo do release como download, com Content-Type: application/octet-stream, e o Safari do iOS se recusa a tocar assim dentro da página. A solução foi servir o MP3 pelo próprio site: no deploy, o pipeline baixa cada áudio para dentro dele.
A voz soou robotizada. Boa parte veio do roteiro certinho e das configurações padrão, e foi resolvida. O resto é o limite do clone rápido, que copia o timbre mas não o meu jeito de falar.
Quanto custa
| Item | Custo |
|---|---|
| ElevenLabs, plano Starter | cerca de US$ 5 por mês, com créditos para uns dois episódios |
| ElevenLabs, plano Creator (se crescer) | cerca de US$ 22 por mês, com o clone profissional |
| Roteiro com o Claude | centavos por episódio pela API, ou nada, se escrito na conversa |
| GitHub, Actions e releases | US$ 0 |
| Hospedagem e domínio | já pagos pelo site |
Os cuidados
- Direito autoral. A Lei 9.610/98, no artigo 46, inciso III, permite citar trechos para crítica ou estudo, na medida justificada e indicando autor e obra. Por isso o prompt proíbe citações longas e pede paráfrase e comentário. Não sou advogado; em caso de dúvida, vale consultar um.
- Transparência. A página do podcast, cada episódio e as tags do MP3 dizem que a narração é gerada por IA a partir da minha voz. O primeiro episódio abre explicando isso.
- Segurança da voz. A chave da ElevenLabs fica só nos secrets, o token do GitHub só enxerga o repositório de áudios, e o
voice_idnão aparece em lugar público. A verificação em duas etapas na conta da ElevenLabs é o próximo passo. - Empresa. A mesma regra dos artigos: nada de exemplos do meu time ou da empresa em que eu trabalho.
O que eu levo disso
O podcast confirmou uma coisa que eu já via no amardoar: a IA acelera muito, mas o que dá segurança é o processo. Branch, pull request, revisão, testes e deploy automático. O mesmo ciclo que a gente roda nos times.
E confirmou outra: o erro mais caro é o que aparece no fim. Mover as verificações para o começo do pipeline, antes da etapa que gasta dinheiro, é a primeira melhoria que eu faria em qualquer automação parecida.
O primeiro episódio está no ar. Se você ouvir, me conta o que achou.
Fontes
Gostou do artigo?
Comentários
Ainda não há comentários. Que tal ser a primeira pessoa a comentar?