
Cache de Resultados de Transcrição: Deduplicação e Idempotência
Summarize this article with:
A transcrição é determinística: o mesmo arquivo de áudio pelo mesmo motor e modelo sempre retorna a mesma transcrição. Fazer cache do resultado com uma chave de hash de conteúdo e um hash de parâmetros elimina cobranças repetidas da API pelo mesmo conteúdo. O custo de armazenamento de uma transcrição em cache é tão pequeno que uma única re-transcrição evitada paga por armazenar esse resultado por anos. Este guia aborda o modelo de idempotência, a construção da chave de cache, os padrões de armazenamento em Postgres e Redis, a invalidação em atualizações de modelo e a aritmética que mostra se vale a pena construir o cache.
A transcrição é determinística. O mesmo arquivo de áudio enviado pelo mesmo motor e modelo produz a mesma transcrição todas as vezes. Ainda assim, a maioria dos sistemas em produção re-transcreve os mesmos arquivos repetidamente, pagando à API de transcrição a cada chamada. O cache trata esse determinismo como um ativo.
Este guia apresenta o padrão de cache usado em produção: chaves de hash de conteúdo para deduplicação idempotente, armazenamento em Postgres e Redis, invalidação em atualizações de modelo e a aritmética que mostra quão rápido o cache se paga.
Por Que a Idempotência É a Base
Antes de pensar em Redis ou Postgres, pense no que torna o cache correto aqui: a transcrição é uma função pura de (conteúdo do áudio, motor, modelo, parâmetros). Mesmas entradas, mesma saída, sempre. Essa propriedade é chamada de idempotência em um sentido mais amplo: repetir a operação não produz nenhuma informação nova.
O cache é apenas o mecanismo para explorá-la. Construa o mecanismo corretamente e cada reenvio, cada execução de backfill, cada botão de "regenerar" que não precisa regenerar se torna um acerto gratuito.
Quatro padrões que desperdiçam dinheiro em pipelines sem cache:
- Um usuário envia a mesma entrevista duas vezes. Transcrita duas vezes.
- Um botão de "regenerar" chama a API em vez de retornar o resultado armazenado.
- Um script de backfill reprocessa arquivos que já têm transcrições.
- Uma funcionalidade entra em produção e re-transcreve um corpus quando apenas o processamento posterior mudou.
Cada um desperdiça uma chamada de API. Somados entre milhares de usuários, a conta cresce. Observamos taxas de re-transcrição de 25% a 45% em pipelines sem cache, embora sua taxa real dependa do seu fluxo de trabalho.
Deduplicação por Hash de Conteúdo: A Chave de Cache
A chave de cache correta é um hash SHA-256 do conteúdo do arquivo de áudio, combinado com um hash dos parâmetros de motor e modelo:
function cacheKey(audioBuffer, opts) {
const audioHash = crypto.createHash('sha256').update(audioBuffer).digest('hex');
const paramHash = crypto.createHash('sha256')
.update(JSON.stringify({ engine: opts.engine, language: opts.language, model: opts.model }))
.digest('hex')
.slice(0, 16);
return `transcript:${audioHash}:${paramHash}`;
}
O hash do áudio é a identidade do conteúdo. O hash de parâmetros distingue "transcrito com Deepgram Nova-3 English" de "transcrito com gpt-4o-transcribe". Ambas as partes precisam coincidir para haver um acerto de cache.
Por que não usar o nome do arquivo? Nomes de arquivo são metadados, não identidade. interview.mp3 e interview_final.mp3 podem ser o mesmo arquivo. Dois arquivos diferentes podem compartilhar um nome. O hash de conteúdo resolve ambos os casos corretamente.
Fazendo Hash de Arquivos Grandes Sem Carregá-los na Memória
Para arquivos de áudio de centenas de MB, carregar o buffer completo só para fazer o hash é desperdício. Faça o hash em streaming:
import { createReadStream } from 'fs';
import { createHash } from 'crypto';
function hashFile(path) {
return new Promise((resolve, reject) => {
const hash = createHash('sha256');
const stream = createReadStream(path);
stream.on('data', chunk => hash.update(chunk));
stream.on('end', () => resolve(hash.digest('hex')));
stream.on('error', reject);
});
}
Isso lê em blocos e atualiza o hash incrementalmente, mantendo o uso de memória estável independentemente do tamanho do arquivo.
Armazenamento: Postgres para Persistência, Redis para o Caminho Quente
Postgres como Armazenamento Principal
O Postgres é a escolha padrão certa. As transcrições são acessadas com pouca frequência e o armazenamento é barato. O schema:
CREATE TABLE transcript_cache (
cache_key TEXT PRIMARY KEY,
audio_hash TEXT NOT NULL,
engine TEXT NOT NULL,
language TEXT NOT NULL,
transcript JSONB NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
last_accessed TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_audio_hash ON transcript_cache (audio_hash);
Consulta com rastreamento de acesso em uma única query:
async function getCached(key) {
const row = await db.query(
`UPDATE transcript_cache SET last_accessed = NOW() WHERE cache_key = $1 RETURNING transcript`,
[key]
);
return row.rows[0]?.transcript;
}
O padrão UPDATE ... RETURNING atualiza o timestamp de acesso e retorna a linha em uma única viagem de ida e volta.
Redis como Camada de Caminho Quente
Se um pequeno conjunto de transcrições é acessado com muita frequência (gravações recentes, conteúdo compartilhado popular), o Redis reduz a carga do Postgres e traz as consultas para abaixo de milissegundos:
async function getCached(key) {
const redisResult = await redis.get(key);
if (redisResult) return JSON.parse(redisResult);
const pgResult = await db.query(
'SELECT transcript FROM transcript_cache WHERE cache_key = $1',
[key]
);
if (pgResult.rows[0]) {
await redis.setex(key, 3600, JSON.stringify(pgResult.rows[0].transcript));
return pgResult.rows[0].transcript;
}
return null;
}
O TTL do Redis é de 1 hora neste exemplo. O Postgres não tem TTL; as entradas permanecem até serem removidas explicitamente. Para a maioria dos pipelines, o Postgres sozinho é suficiente. Adicione o Redis somente depois de ter medido que a latência do Postgres é o gargalo real.
O Padrão Completo: Insert Seguro Contra Condições de Corrida
async function getOrTranscribe(audioBuffer, opts) {
const key = cacheKey(audioBuffer, opts);
const cached = await getCached(key);
if (cached) return cached;
const result = await transcribeAPI(audioBuffer, opts);
await db.query(
`INSERT INTO transcript_cache (cache_key, audio_hash, engine, language, transcript)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT (cache_key) DO NOTHING`,
[key, hashAudio(audioBuffer), opts.engine, opts.language, result]
);
return result;
}
ON CONFLICT DO NOTHING é essencial. Duas requisições simultâneas do mesmo arquivo de áudio podem ambas perder o cache e ambas chamar a API. O primeiro insert vence; o segundo é descartado silenciosamente. Ambos os chamadores recebem seu resultado. Sem necessidade de lock, sem entradas duplicadas.
A Matemática do Custo de Armazenamento
É aqui que a economia do cache se torna concreta.
O custo para transcrever. O Deepgram Nova-3 pré-gravado em inglês custa US$ 0,0043 por minuto (a partir de julho de 2026, conforme deepgram.com/pricing). Uma hora de áudio custa cerca de US$ 0,26. O gpt-4o-transcribe da OpenAI custa US$ 0,006 por minuto, ou seja, cerca de US$ 0,36 por hora (conforme preços em developers.openai.com).
O custo para armazenar. O texto simples da transcrição de uma hora de inglês falado tem aproximadamente 48 KB (cerca de 800 caracteres por minuto em UTF-8). A resposta JSONB completa do Deepgram com timestamps no nível de palavra, utterances, tópicos e sentimentos fica entre 500 KB e 1 MB por hora, dependendo do número de falantes e do nível de detalhe. No AWS RDS PostgreSQL, o armazenamento custa cerca de US$ 0,115 por GB-mês (conforme páginas de preços da AWS, julho de 2026).
Armazenar 1 MB de JSON do Deepgram por um mês custa cerca de US$ 0,000115. A US$ 0,26 por re-transcrição evitada, um único acerto de cache paga por armazenar aquela transcrição por cerca de 2.200 meses.
A questão do ponto de equilíbrio não é o custo de armazenamento. É o tempo de engenharia. Para um sistema que já roda Postgres, o padrão tem cerca de 30 linhas de código. Para um pipeline que gasta US$ 200 por mês em transcrição com uma taxa de re-transcrição de 10%, isso significa US$ 20 por mês recuperados, ou aproximadamente US$ 240 por ano a partir de uma tarde de trabalho.
Para mais táticas de economia de custos no lado da API, veja como otimizar custos das chamadas da API de transcrição e a comparação mais ampla de preços de transcrição.
Invalidação em Atualizações de Modelo
O design da chave de cache lida com atualizações de modelo automaticamente. Quando você muda do Whisper Large-v3 para um modelo mais novo, o nome do modelo faz parte do hash de parâmetros, então as entradas existentes deixam de corresponder. Novas requisições geram novas chaves e disparam transcrições novas. As entradas antigas se tornam órfãs que expiram via sua query de remoção por último acesso.
Dois outros cenários de invalidação:
Erros reportados por clientes. Um usuário sinaliza uma transcrição ruim e quer refazer. Exponha um endpoint administrativo:
async function invalidateCacheForJob(jobId) {
const job = await db.jobs.findOne({ id: jobId });
await db.query(
'DELETE FROM transcript_cache WHERE cache_key = $1',
[job.cache_key]
);
}
Remoção por tamanho. Tabelas de transcrições crescem indefinidamente sem remoção. Se isso virar uma preocupação, faça purge pela data de último acesso:
DELETE FROM transcript_cache
WHERE last_accessed < NOW() - INTERVAL '90 days';
Para a maioria das equipes, as transcrições são pequenas em relação a outros dados e a tabela continua gerenciável por anos. Meça antes de remover.
Cache no Nível de Segmento para Arquivos Longos
Para gravações muito longas (podcasts de várias horas, áudio de conferências de dia inteiro), um cache plano ou erra completamente ou armazena um blob muito grande. Cache por segmento é mais granular:
Divida o áudio em blocos de 5 minutos. Faça o hash de cada bloco independentemente e verifique o cache por bloco. Transcreva apenas os segmentos que não estão em cache. Isso também ajuda fluxos de edição: se um usuário reenvia um arquivo com uma pequena alteração, apenas os segmentos modificados precisam ser re-transcritos.
Cache no nível de palavra é exagero. A sobrecarga de armazenamento por consulta não compensa. Segmentos de 5 a 10 minutos são a granularidade certa.
Taxas de Acerto de Cache por Caso de Uso
Faixas realistas de pipelines em produção:
| Caso de uso | Taxa de acerto típica |
|---|---|
| Usuário reenvia o mesmo arquivo | 8% a 15% |
| Reexecuções de ferramenta interna | 60% a 80% |
| Backfill / reprocessamento em lote | 80% a 95% |
| SaaS multi-tenant (usuários independentes) | 0,5% a 3% |
A primeira linha é o caso típico de produção. Uma taxa de acerto de 10% sobre uma conta de transcrição de US$ 500/mês economiza US$ 50 por mês. O caso de backfill é o cenário de maior retorno: se você algum dia precisar reprocessar um corpus, o cache transforma um gasto potencialmente grande em quase zero.
Monitorando a Eficácia do Cache
Adicione contadores desde o início para saber que o cache está funcionando:
async function getOrTranscribe(audioBuffer, opts) {
const key = cacheKey(audioBuffer, opts);
const cached = await getCached(key);
if (cached) {
metrics.increment('transcription.cache.hit');
return cached;
}
metrics.increment('transcription.cache.miss');
const result = await transcribeAPI(audioBuffer, opts);
await saveToCache(key, result);
return result;
}
Monitore a proporção de acertos/erros ao longo do tempo. Se a taxa de acerto cair inesperadamente, algo mudou: uma onda de invalidação de cache, um novo formato de arquivo que contorna o hash ou uma atualização de motor que gerou novas chaves para conteúdo existente.
Integração Com Webhooks
O cache combina bem com pipelines assíncronos de webhook, abordado em webhook vs polling para transcrições. O handler do webhook calcula a chave de cache a partir do hash original do áudio e dos parâmetros da API e grava na tabela de cache ao concluir. Requisições futuras do mesmo áudio dispensam completamente a API.
Para equipes construindo ferramentas internas, o cache costuma ser a otimização de maior ROI assim que o volume passa de cerca de 50 horas por mês, como abordado em construindo uma ferramenta interna de transcrição.

Se o seu caso de uso é transcrição de áudio direta, sem gerenciar uma camada de cache, o ConvertAudioToText cuida da deduplicação e do armazenamento de resultados no servidor, então você recebe resultados instantaneamente em arquivos reenviados sem nenhuma infraestrutura para manter.
Perguntas Frequentes
Devo fazer hash do nome do arquivo ou do conteúdo do arquivo?
Faça hash do conteúdo. Nomes de arquivo mentem: a mesma entrevista enviada como interview.mp3 e interview_final.mp3 deve atingir a mesma entrada de cache. Dois arquivos diferentes ambos chamados interview.mp3 não devem colidir. O SHA-256 dos bytes do áudio é o único sinal de identidade confiável.
O que acontece quando atualizo para um modelo mais novo do Whisper ou Deepgram?
As entradas antigas do cache continuam válidas, mas deixarão de corresponder às novas solicitações. Como o nome do modelo faz parte do hash de parâmetros na chave de cache, qualquer mudança na versão do modelo gera uma chave diferente e dispara uma nova transcrição. As entradas antigas expiram naturalmente pela sua política de remoção por último acesso; você não precisa excluí-las em massa.
O Redis é necessário ou o Postgres sozinho dá conta do cache?
O Postgres sozinho é suficiente para a maioria dos pipelines. Consultas de cache em um índice de chave primária são rápidas. Adicione o Redis como camada de caminho quente apenas se você mediu alto tráfego em um pequeno conjunto de transcrições acessadas recentemente e a latência do Postgres é o gargalo real.
Qual taxa de acerto de cache devo esperar?
Depende muito do seu caso de uso. O reenvio do mesmo arquivo pelo usuário (o caso mais comum) normalmente gera taxas de acerto de 8% a 15%. Trabalhos de backfill ou reprocessamento em lote podem chegar a 80% a 95%. SaaS multi-tenant em que usuários enviam independentemente arquivos não relacionados é o caso mais difícil: 0,5% a 3%, o que pode não justificar a complexidade até que seu volume de transcrição seja alto.
Fontes
- Preços do Deepgram Nova-3: https://deepgram.com/pricing (verificado em julho de 2026)
- Preços dos modelos de transcrição da OpenAI: https://developers.openai.com/api/docs/pricing (verificado em julho de 2026)
- Preços de armazenamento do AWS RDS PostgreSQL: https://aws.amazon.com/rds/postgresql/pricing/ (referenciado em julho de 2026)
- Preços do Upstash Redis: https://upstash.com/pricing/redis (verificado em julho de 2026)
Try transcription free
Convert any audio or video to clean, unwatermarked text — speaker labels, timestamps, and AI summaries included. First 10 minutes free, no account.
Related Articles

Speechmatics Alternative for Non-Developers: Web Transcription Without Code
Speechmatics is genuinely excellent for developers: 50 hours free per month, 56 languages, on-prem deployment. If you need a drag-and-drop web app with flat $9.99/mo pricing instead of an API, here is an honest comparison of the two.

Best Transcription Tools with API Access (2026)
Which transcription SaaS tools actually give you API keys, and on which plan? Verified pricing and plan gates for Descript, Sonix, Fireflies, Happy Scribe, AssemblyAI, and more.