Skip to content

Transcrição de Chamadas Estéreo com Diarização

Quando você grava chamadas em formato estéreo—com cada falante em um canal de áudio separado—você consegue 100% de precisão na identificação de falantes. Este guia mostra como transcrever gravações estéreo, identificar falantes automaticamente e extrair insights estruturados das suas chamadas.

Por que Diarização Estéreo?

AspectoDiarização PadrãoDiarização Estéreo
Identificação de falanteDetecção por IABaseada em canal (L/R)
PrecisãoBoaPerfeita (100%)
PerformanceNormalMais rápida
Labels de falanteSPEAKER 1, SPEAKER 2...SPEAKER_L, SPEAKER_R
Ideal paraÁudio mono, reuniõesGravações de call center

Ideal para Call Centers

A maioria dos sistemas PBX (FreeSWITCH, Asterisk) pode gravar chamadas em estéreo com cada parte em um canal separado. Isso elimina qualquer incerteza na identificação de falantes.

Pré-requisitos

  • Arquivo de áudio estéreo: MP3, WAV ou outro formato suportado com falantes em canais separados
  • Chave de API SipPulse AI: Obtenha a sua em sippulse.ai
  • Acesso ao modelo Pro: pulse-precision-pro

Modelo para Diarização Estéreo

O modelo pulse-precision-pro suporta diarização estéreo e mono:

ModeloVelocidadePrecisãoIdeal Para
pulse-precision-proOtimizadaMáximaTranscrições estéreo críticas de qualidade

Recursos do Modelo Pro

O modelo pulse-precision-pro inclui recursos avançados:

  • Diarização estéreo: 100% de precisão na identificação de falantes baseada em canal
  • VAD preset: Use vad_preset=telephony para áudio otimizado de banda estreita 8kHz
  • Máxima precisão: Melhor Word Error Rate (WER) para analytics de call center

Passo 1: Prepare seu Áudio

Para que a diarização estéreo funcione corretamente, seu áudio deve ter:

  • Canal esquerdo (L): Um falante (ex: o cliente)
  • Canal direito (R): Outro falante (ex: o atendente)

Configuração de Gravação

A maioria dos sistemas PBX suporta gravação estéreo:

  • FreeSWITCH: Use RECORD_STEREO=true no seu dialplan
  • Asterisk: Configure MixMonitor com a opção D para estéreo

Consistência de Canal

Garanta uma atribuição consistente de canais em todas as gravações. Documente se os clientes estão sempre no canal esquerdo ou direito para análise precisa.

Passo 2: Transcreva com Diarização Estéreo

Use o endpoint /v1/asr/transcribe com response_format=stereo_diarization e um dos modelos Pro.

bash
curl -X POST 'https://api.sippulse.ai/v1/asr/transcribe' \
  -H 'api-key: $SIPPULSE_API_KEY' \
  -F 'file=@gravacao-chamada.mp3' \
  -F 'model=pulse-precision-pro' \
  -F 'response_format=stereo_diarization' \
  -F 'language=pt' \
  -F 'vad_preset=telephony'
typescript
import FormData from "form-data";
import fs from "fs";

async function transcreverChamadaEstereo(
  caminhoArquivo: string
): Promise<TranscricaoEstereo> {
  const form = new FormData();
  form.append("file", fs.createReadStream(caminhoArquivo));
  form.append("model", "pulse-precision-pro");
  form.append("response_format", "stereo_diarization");
  form.append("language", "pt");
  form.append("vad_preset", "telephony"); // Otimizado para chamadas telefônicas

  const response = await fetch("https://api.sippulse.ai/v1/asr/transcribe", {
    method: "POST",
    headers: {
      "api-key": process.env.SIPPULSE_API_KEY!,
    },
    body: form,
  });

  if (!response.ok) {
    throw new Error(`Erro na API: ${response.status}`);
  }

  return response.json();
}

interface TranscricaoEstereo {
  text: string;
  segments: Array<{
    speaker: "SPEAKER_L" | "SPEAKER_R";
    text: string;
    initial_time: number;
    end_time: number;
  }>;
  words: Array<{
    word: string;
    speaker: "SPEAKER_L" | "SPEAKER_R";
    start: number;
    end: number;
  }>;
}
python
import os
import requests

def transcrever_chamada_estereo(caminho_arquivo: str) -> dict:
    """
    Transcreve uma gravação de chamada estéreo com diarização de falantes.

    Args:
        caminho_arquivo: Caminho para o arquivo de áudio estéreo

    Returns:
        Transcrição com segmentos e palavras rotulados por falante
    """
    with open(caminho_arquivo, "rb") as arquivo_audio:
        response = requests.post(
            "https://api.sippulse.ai/v1/asr/transcribe",
            headers={"api-key": os.getenv("SIPPULSE_API_KEY")},
            files={"file": arquivo_audio},
            data={
                "model": "pulse-precision-pro",
                "response_format": "stereo_diarization",
                "language": "pt",
                "vad_preset": "telephony",  # Otimizado para chamadas telefônicas
            },
        )

    response.raise_for_status()
    return response.json()

Passo 3: Entenda a Resposta

A resposta da diarização estéreo inclui três componentes principais:

Estrutura da Resposta

json
{
  "text": "00:02-00:05 | SPEAKER L:\nOlá, como posso ajudá-lo hoje?\n\n00:05-00:08 | SPEAKER R:\nOi, estou ligando sobre minha conta...",
  "segments": [
    {
      "speaker": "SPEAKER_L",
      "text": "Olá, como posso ajudá-lo hoje?",
      "initial_time": 2.1,
      "end_time": 5.3
    },
    {
      "speaker": "SPEAKER_R",
      "text": "Oi, estou ligando sobre minha conta...",
      "initial_time": 5.5,
      "end_time": 8.2
    }
  ],
  "words": [
    {
      "word": "Olá,",
      "speaker": "SPEAKER_L",
      "start": 2.1,
      "end": 2.5
    },
    {
      "word": "como",
      "speaker": "SPEAKER_L",
      "start": 2.5,
      "end": 2.7
    }
  ]
}

Campos Principais

CampoDescrição
textTranscrição formatada com timestamps e labels de falante
segmentsArray de segmentos de fala com falante, texto e timing
wordsTimestamps em nível de palavra com atribuição de falante
speakerSPEAKER_L (canal esquerdo) ou SPEAKER_R (canal direito)

Passo 4: Analise com Análise Estruturada

Após a transcrição, use Análise Estruturada para extrair insights da conversação.

Configurando sua Análise

  1. Navegue até Análise Estruturada no dashboard do SipPulse AI
  2. Crie uma nova análise ou selecione um preset existente como "Análise de Conversação"
  3. Configure seu schema com os campos que deseja extrair
  4. Copie o ID da Análise usando o botão de copiar ao lado do nome da análise

O template Análise de Conversação extrai:

  • Total de perguntas e taxa de resposta
  • Se a chamada atingiu seu objetivo (venda, resolução, etc.)
  • Nível de interesse do cliente (0-1)
  • Principais objeções e se foram resolvidas
  • Tom do atendimento e nível de empatia
  • Score geral de sucesso (0-1)
  • Recomendações de melhoria

Execute a Análise via API

Use o ID da Análise copiado para executar a análise programaticamente:

typescript
interface AnaliseConversacao {
  total_perguntas: number;
  perguntas_respondidas: string[];
  taxa_resposta: number;
  venda_realizada: boolean;
  interesse_cliente: number;
  objecoes_principais: string[];
  objecoes_resolvidas: boolean;
  tom_atendimento: string;
  nivel_empatia: number;
  pontuacao_geral: number;
  recomendacoes: string[];
  proximos_passos: string;
}

async function analisarConversacao(
  analiseId: string,
  textoTranscricao: string
): Promise<AnaliseConversacao> {
  const response = await fetch(
    `https://api.sippulse.ai/v1/structured-analyses/${analiseId}/execute`,
    {
      method: "POST",
      headers: {
        "api-key": process.env.SIPPULSE_API_KEY!,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        content: textoTranscricao,
      }),
    }
  );

  if (!response.ok) {
    throw new Error(`Erro na API: ${response.status}`);
  }

  const resultado = await response.json();
  return resultado.content;
}
python
import os
import requests
from typing import TypedDict

class AnaliseConversacao(TypedDict):
    total_perguntas: int
    perguntas_respondidas: list[str]
    taxa_resposta: float
    venda_realizada: bool
    interesse_cliente: float
    objecoes_principais: list[str]
    objecoes_resolvidas: bool
    tom_atendimento: str
    nivel_empatia: float
    pontuacao_geral: float
    recomendacoes: list[str]
    proximos_passos: str

def analisar_conversacao(
    analise_id: str,
    texto_transcricao: str
) -> AnaliseConversacao:
    """
    Analisa uma transcrição de chamada usando Análise Estruturada.

    Args:
        analise_id: ID do preset de Análise de Conversação
        texto_transcricao: O texto da transcrição formatada

    Returns:
        Resultados da análise estruturada
    """
    response = requests.post(
        f"https://api.sippulse.ai/v1/structured-analyses/{analise_id}/execute",
        headers={
            "api-key": os.getenv("SIPPULSE_API_KEY"),
            "Content-Type": "application/json",
        },
        json={"content": texto_transcricao},
    )

    response.raise_for_status()
    return response.json()["content"]

Exemplo Completo: Pipeline End-to-End

Aqui está um exemplo completo que transcreve uma chamada estéreo e a analisa:

typescript
import FormData from "form-data";
import fs from "fs";

async function processarGravacaoChamada(
  caminhoAudio: string,
  analiseId: string
) {
  // Passo 1: Transcrever com diarização estéreo
  console.log("Transcrevendo áudio...");
  const form = new FormData();
  form.append("file", fs.createReadStream(caminhoAudio));
  form.append("model", "pulse-precision-pro");
  form.append("response_format", "stereo_diarization");
  form.append("language", "pt");
  form.append("vad_preset", "telephony");

  const respostaTranscricao = await fetch(
    "https://api.sippulse.ai/v1/asr/transcribe",
    {
      method: "POST",
      headers: { "api-key": process.env.SIPPULSE_API_KEY! },
      body: form,
    }
  );

  const transcricao = await respostaTranscricao.json();
  console.log(`Transcreveu ${transcricao.segments.length} segmentos`);

  // Passo 2: Analisar a conversação
  console.log("Analisando conversação...");
  const respostaAnalise = await fetch(
    `https://api.sippulse.ai/v1/structured-analyses/${analiseId}/execute`,
    {
      method: "POST",
      headers: {
        "api-key": process.env.SIPPULSE_API_KEY!,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ content: transcricao.text }),
    }
  );

  const analise = await respostaAnalise.json();

  // Passo 3: Retornar resultados combinados
  return {
    transcricao: {
      text: transcricao.text,
      segments: transcricao.segments,
      quantidadeFalantes: 2,
    },
    analise: analise.content,
  };
}

// Uso - obtenha o analiseId do dashboard clicando no botão de copiar
const resultado = await processarGravacaoChamada(
  "./gravacoes/chamada-suporte.mp3",
  "sa_abc123def456" // Seu ID de Análise do dashboard
);

console.log("Score Geral:", resultado.analise.pontuacao_geral);
console.log("Interesse do Cliente:", resultado.analise.interesse_cliente);
console.log("Recomendações:", resultado.analise.recomendacoes);
python
import os
import requests

def processar_gravacao_chamada(caminho_audio: str, analise_id: str) -> dict:
    """
    Pipeline completo: transcreve chamada estéreo e analisa.

    Args:
        caminho_audio: Caminho para o arquivo de áudio estéreo
        analise_id: ID do preset de Análise de Conversação (copie do dashboard)

    Returns:
        Resultados combinados de transcrição e análise
    """
    api_key = os.getenv("SIPPULSE_API_KEY")

    # Passo 1: Transcrever com diarização estéreo
    print("Transcrevendo áudio...")
    with open(caminho_audio, "rb") as arquivo_audio:
        resposta_transcricao = requests.post(
            "https://api.sippulse.ai/v1/asr/transcribe",
            headers={"api-key": api_key},
            files={"file": arquivo_audio},
            data={
                "model": "pulse-precision-pro",
                "response_format": "stereo_diarization",
                "language": "pt",
                "vad_preset": "telephony",
            },
        )

    resposta_transcricao.raise_for_status()
    transcricao = resposta_transcricao.json()
    print(f"Transcreveu {len(transcricao['segments'])} segmentos")

    # Passo 2: Analisar a conversação
    print("Analisando conversação...")
    resposta_analise = requests.post(
        f"https://api.sippulse.ai/v1/structured-analyses/{analise_id}/execute",
        headers={
            "api-key": api_key,
            "Content-Type": "application/json",
        },
        json={"content": transcricao["text"]},
    )

    resposta_analise.raise_for_status()
    analise = resposta_analise.json()

    # Passo 3: Retornar resultados combinados
    return {
        "transcricao": {
            "text": transcricao["text"],
            "segments": transcricao["segments"],
            "quantidade_falantes": 2,
        },
        "analise": analise["content"],
    }


if __name__ == "__main__":
    # Obtenha o analise_id do dashboard clicando no botão de copiar
    resultado = processar_gravacao_chamada(
        "./gravacoes/chamada-suporte.mp3",
        "sa_abc123def456"  # Seu ID de Análise do dashboard
    )

    print(f"Score Geral: {resultado['analise']['pontuacao_geral']}")
    print(f"Interesse do Cliente: {resultado['analise']['interesse_cliente']}")
    print(f"Recomendações: {resultado['analise']['recomendacoes']}")

Exemplo de Saída

Veja como a resposta completa fica para uma chamada de suporte:

json
{
  "transcricao": {
    "text": "00:00-00:03 | SPEAKER L:\nObrigado por ligar para o Suporte TechCorp, meu nome é Ana. Como posso ajudar?\n\n00:03-00:09 | SPEAKER R:\nOi Ana, estou tendo problemas para entrar na minha conta. Aparece que a senha está incorreta, mas tenho certeza que estou digitando certo.\n\n00:09-00:15 | SPEAKER L:\nSinto muito por esse inconveniente. Vou te ajudar com isso. Pode me informar o email associado à sua conta?\n\n00:15-00:18 | SPEAKER R:\nClaro, é joao.silva@email.com.\n\n00:18-00:25 | SPEAKER L:\nObrigada, João. Estou vendo sua conta aqui. Parece que houve várias tentativas de login, então a conta foi bloqueada temporariamente por segurança.\n\n00:25-00:28 | SPEAKER R:\nAh, isso explica. Como faço para desbloquear?\n\n00:28-00:38 | SPEAKER L:\nPosso desbloquear para você agora mesmo. Também vou enviar um link de redefinição de senha para seu email. Você deve receber nos próximos minutos. Posso ajudar em mais alguma coisa?\n\n00:38-00:42 | SPEAKER R:\nNão, era só isso mesmo. Muito obrigado pela ajuda, Ana!\n\n00:42-00:45 | SPEAKER L:\nDe nada, João! Tenha um ótimo dia!",
    "segments": [
      {
        "speaker": "SPEAKER_L",
        "text": "Obrigado por ligar para o Suporte TechCorp, meu nome é Ana. Como posso ajudar?",
        "initial_time": 0.0,
        "end_time": 3.2
      },
      {
        "speaker": "SPEAKER_R",
        "text": "Oi Ana, estou tendo problemas para entrar na minha conta. Aparece que a senha está incorreta, mas tenho certeza que estou digitando certo.",
        "initial_time": 3.5,
        "end_time": 9.1
      },
      {
        "speaker": "SPEAKER_L",
        "text": "Sinto muito por esse inconveniente. Vou te ajudar com isso. Pode me informar o email associado à sua conta?",
        "initial_time": 9.4,
        "end_time": 15.0
      },
      {
        "speaker": "SPEAKER_R",
        "text": "Claro, é joao.silva@email.com.",
        "initial_time": 15.2,
        "end_time": 18.0
      },
      {
        "speaker": "SPEAKER_L",
        "text": "Obrigada, João. Estou vendo sua conta aqui. Parece que houve várias tentativas de login, então a conta foi bloqueada temporariamente por segurança.",
        "initial_time": 18.3,
        "end_time": 25.5
      },
      {
        "speaker": "SPEAKER_R",
        "text": "Ah, isso explica. Como faço para desbloquear?",
        "initial_time": 25.8,
        "end_time": 28.2
      },
      {
        "speaker": "SPEAKER_L",
        "text": "Posso desbloquear para você agora mesmo. Também vou enviar um link de redefinição de senha para seu email. Você deve receber nos próximos minutos. Posso ajudar em mais alguma coisa?",
        "initial_time": 28.5,
        "end_time": 38.0
      },
      {
        "speaker": "SPEAKER_R",
        "text": "Não, era só isso mesmo. Muito obrigado pela ajuda, Ana!",
        "initial_time": 38.3,
        "end_time": 42.0
      },
      {
        "speaker": "SPEAKER_L",
        "text": "De nada, João! Tenha um ótimo dia!",
        "initial_time": 42.2,
        "end_time": 45.0
      }
    ],
    "quantidade_falantes": 2
  },
  "analise": {
    "total_perguntas": 3,
    "perguntas_respondidas": [
      "Como posso ajudar?",
      "Pode me informar o email?",
      "Posso ajudar em mais alguma coisa?"
    ],
    "taxa_resposta": 1.0,
    "venda_realizada": false,
    "interesse_cliente": 0.75,
    "objecoes_principais": [],
    "objecoes_resolvidas": true,
    "tom_atendimento": "profissional, amigável e empático",
    "nivel_empatia": 0.9,
    "conhecimento_tecnico": 0.85,
    "pontuacao_geral": 0.92,
    "recomendacoes": [
      "Considerar oferecer dicas proativas de segurança",
      "Mencionar tempo estimado para email de redefinição"
    ],
    "proximos_passos": "Cliente receberá email de redefinição e recuperará acesso à conta"
  }
}

Melhores Práticas

Qualidade do Áudio

  • Taxa de amostragem: 16kHz ou superior para melhores resultados (áudio de telefonia 8kHz também é suportado)
  • Profundidade de bits: Mínimo de 16 bits
  • Formato: MP3 ou WAV funcionam bem

Atribuição de Canal

  • Seja consistente: Sempre atribua a mesma parte ao mesmo canal
  • Documente: Registre se os atendentes estão no L ou R na sua configuração
  • Mapeie falantes: Na sua aplicação, mapeie SPEAKER_L/SPEAKER_R para labels significativos (Atendente/Cliente)

Escolhendo a Abordagem Certa

CenárioModelo RecomendadoResponse Format
Gravações de chamadas estéreopulse-precision-prostereo_diarization
Gravações mono com múltiplos falantespulse-precision-prodiarization

Dicas de Performance

  • Use vad_preset=telephony: Otimizado para características de áudio de chamadas telefônicas
  • Processamento em lote: Para grandes volumes, processe arquivos em paralelo
  • Combine com anonimização: Adicione anonymize=true para remover PII automaticamente

Próximos Passos