Speaker Diarization
NeMo Labs Voice Agent uses streaming Sortformer to identify which speaker is talking during each user
turn. In a multi-person conversation, the large language model (LLM) receives a speaker identity with
every utterance.
Diarization is on by default in server_configs/default.yaml.
How Speaker Diarization Works
The diarization service (nemo_voice_agent/pipecat/services/nemo/diar.py) sits immediately after STT in
the pipeline and before turn-taking. It is a pass-through stage: it never produces transcripts, only
speaker labels.
- While VAD reports the user is speaking, the service buffers incoming audio frames. Raw frames arrive
at 16 ms. The service groups them into
frame_len_in_secschunks (80 ms by default, or five raw frames) before invoking the model. - Inference runs in a background thread (
asyncio.to_thread), so the audio pipeline is not blocked.NeMoStreamingDiarServiceinstreaming_diar.pykeeps a persistent Sortformer streaming state plus a speaker cache across chunks, and returns a per-frame speaker-probability array. - Probabilities are thresholded at
diar.threshold, and the speaker holding the most frames in the chunk becomes the dominant speaker. - When the dominant speaker changes, a
DiarResultFrameis pushed downstream. The turn-taking service consumes it and prepends a tag such as<speaker_0>to the user’s transcript text, so the LLM sees the speaker identity inline in the conversation context. - On
VADUserStoppedSpeakingFrame, the current speaker and the audio buffer are cleared, so each user turn starts a fresh attribution.
The stock system prompt in default.yaml tells the LLM to use speaker tags for speaker identification
without echoing them. If you replace the system prompt, retain that instruction. For prompt configuration,
refer to Prompts.
Supported Models
The following models are available for local and hosted speaker diarization.
Both are four-speaker streaming Sortformer checkpoints pulled from Hugging Face on first start. A local
.nemo file path also works — build_diarizer calls SortformerEncLabelModel.restore_from when the
configured model ends in .nemo, and from_pretrained otherwise.
Configuration
Diarization is configured under the top-level diar: block of
examples/generic_voice_agent/server/server_configs/default.yaml.
The following keys control the local diarization service:
Two things to know about device:
- The builder passes
config_manager.STT_DEVICEto the diarization service, so the diarizer followsstt.device, notdiar.device. Changestt.deviceto move both services to another device. - The underlying
DiarizationConfigdevice is set from that same value, so ASR and diarization always share a device. The shipped value iscuda.
Remember the configuration precedence rule described in
Server configuration: a model sub-YAML
referenced by model_config: takes precedence over matching keys in default.yaml.
Limits
The following limits come from the service implementation and the shipped diarization configuration.
Different turns can come from different speakers. This is the supported multi-speaker mode. Splitting a single turn between two people is not supported.
Accuracy Considerations
Account for the following behavior when you decide whether diarization fits your conversation environment.
- The diarization model is not robust to noise. In a noisy room, it can drop or confuse speakers. Use a noise-cancelling microphone or a quiet environment.
- It works best when the voices are clearly distinct. The model is more likely to merge or swap similar-sounding speakers and accents with limited representation in the training data.
- Speaker identity is not stable across a reset. The client’s Reset button sends the RTVI
resetclient message and callsreset()on the diarization service. This action clears the Sortformer streaming state and speaker cache, so speaker numbering restarts.
Disable Diarization
Disable diarization when you have a single known user, limited latency or VRAM capacity, or mislabeled speakers:
With enabled: false, the runtime changes as follows:
build_diarreturnsNone, andserver.pyomits the stage from the pipeline — no model is loaded and no GPU memory is used.config_manager.USE_DIARbecomesFalse, which is forwarded to the turn-taking service asuse_diar=False, so no speaker tags are added to transcripts.
Several shipped configurations disable diarization:
Related Topics
Use these pages to understand the adjacent recognition and turn-taking stages or to change diarization settings.
- Speech Recognition — the STT stage that feeds diarization.
- Turn Taking — consumes
DiarResultFrameand writes the speaker tag into the transcript. - Server configuration — full configuration layout and merge rules.
- Troubleshooting — what to check when speakers are mislabeled.