Salta ai contenuti

Riunioni e trascrizione

Control Center può registrare una chiamata e restituirti un resoconto ordinato — riepilogo, attività e decisioni — con il riconoscimento vocale e la separazione dei parlanti in esecuzione sul tuo hardware. Pensa a Granola, ma self-hosted e integrato nello stesso spazio di lavoro in cui vivono i tuoi agenti.

Una riunione è una sessione registrata e trascritta. Non è la stessa cosa di un evento del calendario: un evento è un impegno programmato sincronizzato da Google, mentre una riunione è qualcosa che hai effettivamente registrato. Le due cose possono essere collegate, dato che puoi avviare una registrazione da un evento, ma restano record distinti.

Ogni riunione è legata a uno spazio di lavoro, come tutto il resto in Control Center. Le riunioni registrate in uno spazio di lavoro non compaiono mai in un altro.

“Sul dispositivo” è l’istinto giusto ma il sostantivo sbagliato. Il client cattura l’audio e trasmette frame PCM16 a 16 kHz alla macchina che esegue cc_server. Quell’host fa tutto il resto: scrive i WAV per canale in <dataDir>/meetings/<meetingId>/, trascrive in un isolate worker, filtra l’eco e separa i parlanti a posteriori. Su un desktop autonomo quell’host è la stessa macchina. Se il server è remoto, non lo è — quindi “niente esce dal mio portatile” è vero solo quando il server è il tuo portatile.

C’è un’ulteriore eccezione, ed è quella importante.

Tutto ciò che precede il riepilogo — cattura, trascrizione, cancellazione dell’eco, diarizzazione, confronto delle impronte vocali — viene eseguito sul server senza alcuna chiamata di rete.

I modelli di embedding, diarizzazione e voce (ASR) vengono installati forzatamente al primo avvio (saltati in una demo), così la separazione dei parlanti e la registrazione esistono senza dover passare dalle impostazioni. L’ASR è comunque selezionabile — pesa diverse centinaia di megabyte e nel selettore sono incluse quattro build. Il modello vocale richiede comunque un riavvio dopo quel primo download, perché il trascrittore risolve il suo modello prima che il recupero possa terminare.

Le quattro voci del selettore, che differiscono per dimensioni, copertura linguistica e accuratezza:

  • Parakeet TDT 0.6B v3 — multilingue, l’impostazione predefinita consigliata (~600 MB)
  • Parakeet TDT 0.6B v2 — inglese (~480 MB)
  • Whisper large-v3-turbo — multilingue (~626 MB)
  • Whisper base.en — solo inglese, il piccolo default storico (~198 MB)

I modelli sono gli unici artefatti che il server recupera a runtime; ogni libreria nativa è inclusa nel bundle.

Una riunione registra due canali audio contemporaneamente:

  • Tu (“me”): il tuo microfono.
  • Loro (“them”): l’uscita audio della riunione, catturata da un loopback senza driver — tap di processo/dispositivo Core Audio su macOS, loopback WASAPI su Windows, un monitor PipeWire / PulseAudio su Linux.

Poiché i due canali vengono catturati separatamente, la trascrizione è attribuita ai parlanti fin dall’inizio: le tue parole sono etichettate me, tutti gli altri nella chiamata sono etichettati them.

La tua stessa voce rientra dall’uscita di sistema nel microfono e altrimenti verrebbe trascritta due volte. Due filtri lo impediscono. L’AEC3 a livello di segnale viene eseguito su ogni registrazione in modalità remota ed è obbligatorio — se la libreria AEC nativa non può essere caricata, la registrazione rifiuta di partire invece di trasmettere in silenzio un microfono inquinato dall’eco. (La modalità in presenza non ha un riferimento remoto da cancellare, quindi lì l’AEC è un passthrough identità.) In aggiunta, su ogni piattaforma viene eseguito un filtro di eco testuale lato host.

Tutta l’elaborazione del microfono a livello di sistema operativo è deliberatamente disattivata. Abilitare la cancellazione dell’eco o il guadagno automatico della piattaforma su macOS commuta il microfono su Voice Processing I/O, che in questa build ha ucciso sia il microfono sia il tap dell’audio di sistema.

Mentre registri, l’audio viene decodificato in finestre scorrevoli — tagliate su un breve silenzio finale o a una lunghezza massima della finestra — fuori dal thread dell’interfaccia. Ogni finestra diventa un segmento di trascrizione etichettato per parlante, con offset in millisecondi.

Le finestre che non hanno mai superato una soglia RMS vengono saltate senza essere decodificate affatto, perché un modello vocale trasforma il silenzio in token non vocali allucinati e la decodifica è CPU sprecata. La conseguenza pratica: un parlante costantemente molto silenzioso non produce alcuna trascrizione. Se qualcuno risulta vuoto, controlla il suo livello di ingresso prima di dare la colpa al modello.

Dopo che interrompi, la diarizzazione viene eseguita offline sulla registrazione e suddivide il canale remoto in singoli parlanti (Persona 1, Persona 2, …) che puoi rinominare. Il numero di parlanti viene dedotto da una soglia di clustering, non da un conteggio impostato da te — quindi è un artefatto della soglia e un parlante silenzioso o sovrapposto può essere unito a un vicino. La trascrizione viene resa come righe [mm:ss] SPEAKER: text.

Rinominare un parlante fa più che etichettare una singola trascrizione. Control Center può salvare l’impronta vocale del parlante come profilo vocale, così da riconoscerlo automaticamente nelle riunioni future. Quando assegni un nome a un parlante diarizzato, ti viene chiesto se salvare l’impronta vocale; se lo fai, questa confluisce in un centroide in aggiornamento per quel nome e viene confrontata con i nuovi parlanti tramite similarità coseno, così la stessa persona compare con il suo nome la prossima volta senza che tu debba rietichettarla.

I profili vocali sono legati allo spazio di lavoro e non ne oltrepassano mai il confine. Li gestisci in Impostazioni → Spazio di lavoro → Riunioni. Rinominare un parlante già salvato in precedenza rimuove il vecchio nome e propone di registrare quello nuovo; eliminare un profilo rimuove l’impronta vocale memorizzata ma lascia intatti i nomi già applicati alle riunioni passate.

Quando una registrazione si interrompe, il server pubblica un evento di dominio MeetingRecordingStopped. Il modello di pipeline integrato meeting_summary viene attivato da quell’evento. Il registratore non lo attende; la riunione si limita a passare attraverso il suo ciclo di vita di stato:

recording → processing → done

L’agente di riepilogo riceve il titolo, le tue note veloci prese dal vivo e la trascrizione, e restituisce JSON strutturato. Le sezioni che scrive sono determinate dal modello attivo di note riunione dello spazio di lavoro (Impostazioni → Spazio di lavoro → Riunioni); il modello attivo si applica ai riepiloghi nuovi e rieseguiti. I passaggi di persistenza della pipeline scrivono poi quel JSON in righe distinte:

  • enhancedNotes e summary → le note della riunione
  • ogni attività → una riga MeetingActionItem (contenuto, responsabile, link al ticket facoltativo)
  • ogni decisione → una riga MeetingDecision

Le attività e le decisioni non vengono mai estratte da markdown libero, ma solo dagli array strutturati dell’agente. Se un’esecuzione non produce output strutturato, i passaggi di persistenza vengono saltati e la trascrizione grezza viene conservata come fallback, così non perdi mai il record. Nulla nella pipeline contrassegna la riunione come done — lo fa un singolo riconciliatore a partire dall’evento terminale dell’esecuzione, così il fallimento di un passaggio vicino non può lasciare in sospeso una riunione scritta a metà che sembra già finita.

Superficie Cosa mostra
/meetings L’elenco delle riunioni, con i conteggi di attività e decisioni
/meetings/record L’HUD della registrazione dal vivo: da un lato le tue note, dall’altro la trascrizione in streaming
/meetings/:meetingId Il dettaglio di una riunione: schede Note, Trascrizione, Attività e Decisioni
Impostazioni → Spazio di lavoro → Riunioni Modelli di note e profili vocali salvati per questo spazio di lavoro

La cattura è senza driver su tutte e tre le piattaforme desktop (tap Core Audio su macOS, WASAPI su Windows, PipeWire su Linux).

Anche il client web registra, cosa che il resto dell’impostazione desktop di questa pagina può nascondere: il browser prende il microfono tramite getUserMedia e l’audio della riunione tramite getDisplayMedia (condivisione dello schermo), esegue il downsampling a PCM16 a 16 kHz e trasmette allo stesso host. In quel caso valgono tre avvertenze:

  • La superficie condivisa deve contenere una traccia audio. La condivisione a schermo intero di Safari, Firefox e macOS non ne fornisce alcuna e la registrazione rifiuta di partire.
  • Non c’è AEC3 nativo. Viene richiesta la cancellazione dell’eco del browser sul microfono; il filtro di eco testuale lato host viene comunque eseguito. L’HUD mostra un misuratore di livello di ingresso pilotato dal PCM del microfono.
  • Rieseguire il riepilogo e annullare l’elaborazione sono disponibili solo su desktop.

L’azione Registra e collega del calendario è disponibile solo su desktop e sul web lo segnala chiaramente, anche se il flusso di registrazione della schermata Riunioni funziona lì.