Pular para o conteúdo

Reuniões e transcrição

O Control Center consegue gravar uma chamada e devolver a você um texto final bem organizado — um resumo, itens de ação e decisões — com reconhecimento de fala e separação de falantes rodando no seu próprio hardware. Pense no Granola, mas auto-hospedado e conectado ao mesmo espaço de trabalho em que os seus agentes vivem.

Uma reunião é uma sessão gravada e transcrita. Ela não é a mesma coisa que um evento de calendário: um evento é um compromisso agendado, sincronizado do Google, enquanto uma reunião é algo que você de fato gravou. Os dois podem ser vinculados, já que você pode iniciar uma gravação a partir de um evento, mas continuam sendo registros distintos.

Toda reunião pertence a um espaço de trabalho, como tudo o mais no Control Center. Reuniões gravadas em um espaço de trabalho nunca aparecem em outro.

“No dispositivo” é o instinto certo, mas o substantivo errado. O cliente captura o áudio e transmite quadros PCM16 de 16 kHz para a máquina que executa o cc_server. Esse host faz todo o resto: grava os WAVs por canal em <dataDir>/meetings/<meetingId>/, transcreve em um isolate de worker, filtra o eco e faz a diarização a posteriori. Em um desktop autônomo, esse host é a mesma máquina. Apontado para um servidor remoto, não é — então “nada sai do meu notebook” só é verdade quando o servidor é o seu notebook.

Há mais uma exceção, e ela é a importante.

Tudo antes do resumo — captura, transcrição, cancelamento de eco, diarização, correspondência de impressão vocal — roda no servidor sem nenhuma chamada de rede.

Os modelos de embedding, diarização e fala (ASR) são instalados à força na primeira inicialização (ignorados em uma demo), então a separação de falantes e a gravação existem sem nenhuma visita às configurações. O ASR ainda é selecionável — são várias centenas de megabytes, e quatro compilações vêm no seletor. A fala ainda precisa de uma reinicialização depois desse primeiro download, porque o transcritor resolve o modelo antes que o download consiga terminar.

As quatro entradas do seletor, que diferem em tamanho, cobertura de idiomas e precisão:

  • Parakeet TDT 0.6B v3 — multilíngue, o padrão recomendado (~600 MB)
  • Parakeet TDT 0.6B v2 — inglês (~480 MB)
  • Whisper large-v3-turbo — multilíngue (~626 MB)
  • Whisper base.en — somente inglês, o pequeno padrão histórico (~198 MB)

Os modelos são os únicos artefatos que o servidor baixa em tempo de execução; todas as bibliotecas nativas vêm no pacote.

Uma reunião grava dois canais de áudio ao mesmo tempo:

  • Você (“me”): seu microfone.
  • Eles (“them”): a saída de áudio da reunião, capturada por um loopback sem driver — taps de processo/dispositivo do Core Audio no macOS, loopback WASAPI no Windows, um monitor PipeWire / PulseAudio no Linux.

Como os dois canais são capturados separadamente, a transcrição já nasce atribuída por falante: as suas palavras são marcadas como me e todos na chamada são marcados como them.

A sua própria voz vaza da saída do sistema de volta para o microfone e, caso contrário, seria transcrita duas vezes. Dois filtros impedem isso. O AEC3 em nível de sinal roda em toda gravação em modo remoto e é obrigatório — se a biblioteca nativa de AEC não conseguir carregar, a gravação se recusa a iniciar em vez de transmitir silenciosamente um microfone poluído por eco. (O modo presencial não tem referência remota para cancelar, então ali o AEC é uma passagem direta.) Além dele, um filtro de eco em nível de texto roda no lado do host, em todas as plataformas.

Todo o processamento de microfone no nível do sistema operacional é desativado de propósito. Ativar o cancelamento de eco ou o ganho automático do próprio macOS muda o microfone para Voice Processing I/O, o que, nesta compilação, matou tanto o microfone quanto o tap de áudio do sistema.

Enquanto você grava, o áudio é decodificado em janelas deslizantes — cortadas em um breve silêncio à direita, ou em um comprimento máximo de janela — fora da thread de UI. Cada janela vira um segmento de transcrição marcado por falante, com deslocamentos em milissegundos.

Janelas que nunca passaram de um limite de RMS são ignoradas sem sequer serem decodificadas, porque um modelo de fala transforma silêncio em tokens sem fala alucinados, e a decodificação é CPU desperdiçada. A consequência prática: um falante que fala sempre muito baixo não produz transcrição alguma. Se alguém voltar vazio, confira o nível de entrada da pessoa antes de culpar o modelo.

Depois que você para, a diarização roda offline sobre a gravação e divide o canal remoto em falantes individuais (Person 1, Person 2, …) que você pode renomear. O número de falantes é inferido de um limite de agrupamento, não de uma contagem que você define — então ele é um artefato do limite, e um falante baixo ou sobreposto pode ser fundido com um vizinho. A transcrição é renderizada como linhas [mm:ss] SPEAKER: text.

Renomear um falante faz mais do que rotular uma transcrição. O Control Center pode salvar a impressão vocal do falante como um perfil de voz, para reconhecê-lo automaticamente em reuniões futuras. Quando você dá nome a um falante diarizado, o app pergunta se você quer salvar a impressão vocal; se você salvar, ela é combinada a um centroide contínuo daquele nome e comparada com novos falantes por similaridade de cosseno, de modo que a mesma pessoa aparece com o nome dela na próxima vez, sem você precisar rotulá-la de novo.

Os perfis de voz pertencem ao espaço de trabalho e nunca cruzam essa fronteira. Gerencie-os em Configurações → Espaço de trabalho → Reuniões. Renomear um falante que já havia sido salvo remove o registro do nome antigo e oferece registrar o novo; excluir um perfil remove a impressão vocal armazenada, mas deixa intactos quaisquer nomes já aplicados a reuniões anteriores.

Quando uma gravação para, o servidor publica um evento de domínio MeetingRecordingStopped. O modelo de pipeline meeting_summary embutido é disparado por esse evento. O gravador não espera por ele; a reunião simplesmente passa pelo seu ciclo de vida de status:

recording → processing → done

O agente de resumo recebe o título, as suas anotações brutas ao vivo e a transcrição, e devolve JSON estruturado. As seções que ele escreve são moldadas pelo modelo de notas de reunião ativo do espaço de trabalho (Configurações → Espaço de trabalho → Reuniões); o modelo ativo se aplica a resumos novos e reexecutados. As etapas de persistência do pipeline então gravam esse JSON em linhas distintas:

  • enhancedNotes e summary → as notas da reunião
  • cada item de ação → uma linha MeetingActionItem (conteúdo, responsável, link opcional de ticket)
  • cada decisão → uma linha MeetingDecision

Itens de ação e decisões nunca são extraídos de markdown de forma livre, apenas dos arrays estruturados do agente. Se uma execução não produz saída estruturada, as etapas de persistência são ignoradas e a transcrição bruta é mantida como reserva, para que você nunca perca o registro. Nada no pipeline marca a reunião como done — um único reconciliador faz isso a partir do evento terminal da execução, então a falha de uma etapa irmã não consegue deixar encalhada uma reunião escrita pela metade que já parece pronta.

Superfície O que mostra
/meetings A lista de reuniões, com contagens de itens de ação e decisões
/meetings/record O HUD de gravação ao vivo: as suas notas de um lado, a transcrição em streaming do outro
/meetings/:meetingId Os detalhes de uma reunião: guias Notas, Transcrição, Tarefas e Decisões
Configurações → Espaço de trabalho → Reuniões Modelos de notas e perfis de voz salvos deste espaço de trabalho

A captura não precisa de driver nas três plataformas de desktop (taps do Core Audio no macOS, WASAPI no Windows, PipeWire no Linux).

O cliente web também grava, o que o enquadramento de desktop do resto desta página pode esconder: o navegador captura o seu microfone via getUserMedia e o áudio da reunião via getDisplayMedia (compartilhamento de tela), reduz a amostragem para PCM16 de 16 kHz e transmite para o mesmo host. Três ressalvas se aplicam ali:

  • A superfície compartilhada precisa carregar uma faixa de áudio. Safari, Firefox e o compartilhamento de tela inteira do macOS não fornecem nenhuma, e a gravação se recusa a iniciar.
  • Não existe AEC3 nativo. O cancelamento de eco do próprio navegador é solicitado no microfone; o filtro de eco de texto do lado do host continua rodando. O HUD mostra um medidor de nível de entrada alimentado pelo PCM do microfone.
  • Refazer resumo e cancelar o processamento existem apenas no desktop.

A ação Gravar e vincular do calendário existe apenas no desktop e informa isso claramente na web, ainda que o próprio fluxo de gravação da tela de reuniões funcione lá.