Measuring conversational latency correctly
A voice agent's response time has to be timed on the audio wire: from the moment the caller's speech state ends to the moment the agent's speech state begins. Timing transcript rows instead reports milliseconds for replies the caller waited seconds for, because a transcript row is written when a sentence is complete rather than when it started. Moving the clock changed the reported figures by whole seconds.
The clock that reported eight milliseconds#
The first realtime call reviewed in the Voice Lab carried 8 ms beside replies the caller had audibly waited seconds for. The arithmetic was correct; the events being subtracted were transcript rows. A transcript row for a spoken sentence arrives when the sentence is complete, so subtracting one row from the next measures the gap between two finished sentences and never touches the silence the caller actually sat in.
The failure mode is quiet, which is why it earns a page. A latency figure derived from transcripts is stable, plausible and internally consistent. It simply is not the quantity the caller experiences, and every tuning decision taken against it optimises the wrong thing.
What TurnClock times#
The worker's TurnClock subtracts two audio-state transitions and nothing else:
- the caller's speech state leaving
speaking— their last word as the session heard it; - the agent's speech state entering
speaking— the first audio going back down the line.
Every reply posts a turn_timing event with that interval. Every interruption posts a barge_in event carrying how long the voice kept talking after the caller spoke over it. Both are events on the call, not derived rows, so a review months later reads what the wire did rather than what a later summariser inferred.
The thresholds a timing is judged against#
| Constant | Value | What it decides |
|---|---|---|
TARGET_RESPONSE_MS | 2.5 s | The reply time the review treats as good |
SLOW_RESPONSE_MS | 4 s | Past this a reply is reported slow, with the setting that addresses it |
MODEL_BOUND_SHARE | 60% | When the model's own first token is this much of the wait, no control is offered |
| Barge-in tolerance | 2 s | Talking over the caller for longer is an ignored interruption |
| Best measured median | 3.3 s | Median reply on the best call measured on the realtime path |
The thresholds exist so a finding can name a control. A reply over four seconds is reported with the specific setting that would shorten it — reply length, the model's end-of-speech silence, the instruction budget — rather than as a bare complaint about speed.
Attributing the wait before offering a control#
A slow reply is not automatically a tuning problem. When the model's own first token accounts for 60% or more of the wait, voice_quality.review says so and offers no control to turn, because turning one would change nothing. Those MODEL_LIMITED findings are listed and counted but do not lower score; score_all keeps the old number so the two can be read side by side.
The same discipline produced a measurable result elsewhere. A reply-length change reviewed across 24 production calls and two controlled ones on 2026-09-06 moved the median words per reply from 23–40 to 19, re-introductions from 1–4 to 0 and stacked questions from 2–4 to 0, at a reply median of 2.9 s and no latency regression. The claim is only defensible because the before and after were timed the same way.
What this measurement cannot tell you#
- It covers the realtime path only. A turn-based carrier call has no worker and no
TurnClock, and a browser call has no worker at all. - It starts at the caller's last word as the session heard it, so time spent in end-of-turn detection sits inside the number rather than before it.
- The floor is the model's first token plus that detection: 3.3 s median on the best measured call, not the 1–2 s a demo video implies.
- Carrier-side delay — the PSTN leg, codec and network — is outside the measurement entirely and is UNKNOWN here.
- A timing is only interpretable next to the settings that produced it, which is why the resolved configuration is snapshotted on
call.qualityat the end of the call.
Questions#
Why not measure from the transcript, which is already stored?
Because the transcript answers a different question. Its rows mark when text was finalised, and finalisation happens after the audio it describes. The gap between two rows is the gap between two completed sentences, which can be near zero while the caller waits. Store the transcript for what was said and time the audio states for how long it took.
Is 3.3 seconds a target or a floor?
It is the best median measured on the realtime path, not a target — the target is 2.5 s. The distance between them is mostly the model's first token and its end-of-turn detection, which is why part of it is reported as model-bound rather than offered as a slider.
What use is a barge-in event after the interruption has already happened?
It converts an impression into a count. Two seconds of talking over a caller is audible and forgettable; a run of barge_in events past the two-second tolerance is a specific finding with a specific interruption-threshold setting attached to it, and it can be shown to have gone away.