Voice Agent
The Core Agent's brain, on a phone call. Sixty-seven fields, and the four that decide whether the caller stays on the line.
Everything the last lesson taught still applies: a prompt, attached tools, a loop that decides. What changes is that somebody is on the line, in real time, and a two-second pause is now a person wondering whether the call dropped.
So this node is the agent plus everything conversation needs — who speaks first, whether you can interrupt it, what it does when you go quiet, and when it gives up. Sixty-seven fields. You will set about eight.
No workflow on this page. A Voice Agent is never wired into the flow — it is attached to a call step, and that needs a telephony provider the tutorial organisation does not have. Written from the node.
First, the mode
| Field | What it holds |
|---|---|
voice_mode | Custom: separate STT+LLM+TTS providers. Realtime: single low-latency provider (OpenAI, Azure, Gemini). One of: custom · realtime Defaults to realtime. |
name | Display name for the voice agent Defaults to Voice Agent. |
system_prompt | System instructions for the agent (compiled from prompt_sections when prompt_mode='builder') Defaults to You are a helpful voice assistant. Keep responses brief and conversational.. |
model | LLM model to use Defaults to gpt-3.5-turbo. |
temperature | Creativity level (0=focused, 1=creative) Defaults to 0.7. |
max_tokens | Maximum response length (keep low for voice) Defaults to 250. |
voice_mode
- What it holds
- Custom: separate STT+LLM+TTS providers. Realtime: single low-latency provider (OpenAI, Azure, Gemini). One of:
custom · realtimeDefaults torealtime.
name
- What it holds
- Display name for the voice agent Defaults to
Voice Agent.
system_prompt
- What it holds
- System instructions for the agent (compiled from prompt_sections when prompt_mode='builder') Defaults to
You are a helpful voice assistant. Keep responses brief and conversational..
model
- What it holds
- LLM model to use Defaults to
gpt-3.5-turbo.
temperature
- What it holds
- Creativity level (0=focused, 1=creative) Defaults to
0.7.
max_tokens
- What it holds
- Maximum response length (keep low for voice) Defaults to
250.
voice_mode is the architectural decision and everything else follows from it.
- realtime — one provider does the whole loop: speech in, speech out, no separate transcription step. Much lower latency, which on a phone call is most of the experience. Fewer knobs, and you take the provider's voice.
- custom — three providers chained: something transcribes, a model thinks, something speaks. Slower, and every part is yours to choose. Use it when you need a specific voice or a specific transcription language that the realtime providers do not do well.
Start with realtime. Latency is the thing callers notice and the thing hardest to fix later. Move to custom when you have a concrete reason, not in anticipation of one.
Keep max_tokens small. It defaults to 250 and that is deliberate — spoken replies must be short. A model allowed to write four paragraphs will, and the caller will hear ninety seconds of monologue with no way in.
The first eight seconds
| Field | What it holds |
|---|---|
first_message_mode | Who speaks first in the conversation One of: assistant_speaks_first · user_speaks_first · assistant_waits Defaults to assistant_speaks_first. |
first_message | Opening message from assistant |
first_message_delay | Seconds to wait for user before speaking (default: 8s) Defaults to 8. |
enable_interruptions | Allow user to interrupt assistant Defaults to false. |
interruption_threshold | Confidence threshold for interruption (lower = easier to interrupt) Defaults to 0.1. |
first_message_mode
- What it holds
- Who speaks first in the conversation One of:
assistant_speaks_first · user_speaks_first · assistant_waitsDefaults toassistant_speaks_first.
first_message
- What it holds
- Opening message from assistant
first_message_delay
- What it holds
- Seconds to wait for user before speaking (default: 8s) Defaults to
8.
enable_interruptions
- What it holds
- Allow user to interrupt assistant Defaults to
false.
interruption_threshold
- What it holds
- Confidence threshold for interruption (lower = easier to interrupt) Defaults to
0.1.
first_message_mode decides who opens. Having the assistant speak first is almost always right on an outbound call and usually right on an inbound one — silence after a connection reads as a fault.
Interruptions are off by default, and that is the single worst default for a voice product. With enable_interruptions off, the agent finishes its sentence no matter what the caller does. Everyone tries to interrupt an automated voice — it is how people talk — and being unable to is the moment they decide they are talking to a machine that is not listening. Turn it on. interruption_threshold then tunes how easily: lower is easier to interrupt, and 0.1 is already quite easy.
Silence, and endings
| Field | What it holds |
|---|---|
end_conversation_on_silence | End call after silence timeout Defaults to false. |
silence_timeout_seconds | Seconds of silence before ending Defaults to 30. |
enable_inactivity_nudge | Proactively re-engage the user after a period of silence instead of staying quiet Defaults to false. |
inactivity_nudge_seconds | Seconds of silence before the assistant nudges the user Defaults to 10. |
inactivity_nudge_message | What the assistant says to re-engage. Leave empty to let the AI generate a natural check-in based on the conversation |
max_nudges_per_call | Maximum consecutive nudges without a user response (counter resets when the user speaks) Defaults to 2. |
end_call_on_farewell | Automatically end call when AI says goodbye/farewell Defaults to false. |
max_conversation_duration | Maximum call duration in seconds Defaults to 600. |
end_conversation_on_silence
- What it holds
- End call after silence timeout Defaults to
false.
silence_timeout_seconds
- What it holds
- Seconds of silence before ending Defaults to
30.
enable_inactivity_nudge
- What it holds
- Proactively re-engage the user after a period of silence instead of staying quiet Defaults to
false.
inactivity_nudge_seconds
- What it holds
- Seconds of silence before the assistant nudges the user Defaults to
10.
inactivity_nudge_message
- What it holds
- What the assistant says to re-engage. Leave empty to let the AI generate a natural check-in based on the conversation
max_nudges_per_call
- What it holds
- Maximum consecutive nudges without a user response (counter resets when the user speaks) Defaults to
2.
end_call_on_farewell
- What it holds
- Automatically end call when AI says goodbye/farewell Defaults to
false.
max_conversation_duration
- What it holds
- Maximum call duration in seconds Defaults to
600.
Calls do not end cleanly. People put the phone down without saying goodbye, get distracted, or wait for the agent to speak while the agent waits for them. Four settings cover that: a nudge when someone goes quiet, a hang-up after longer silence, an ending when the agent says goodbye, and a hard ceiling on the whole call.
max_conversation_duration is the real limit on call length — the one the earlier voice lessons pointed at. The inbound and outbound nodes have timeouts that bound how long the workflow waits; neither ends a call. This does, and it defaults to ten minutes. If you have ever wondered what stops a stuck agent talking to a voicemail for an hour, it is this field and nothing else.
The nudge is worth configuring properly. Ten seconds of silence followed by “are you still there?” is natural. The same words after two seconds is rude, and after forty is too late. Two nudges is the right maximum — a third is a machine that will not take a hint.
What comes back
| Reference | What you get |
|---|---|
{{alias.output}} | The agent's conclusion — the verdict, the answer, whatever the prompt asked it to arrive at. Read this downstream; do not wire the agent into the flow. |
{{alias.transcript}} | What was said, both sides. Where you look when a call went wrong. |
{{alias.call_status}} · {{alias.duration_seconds}} | How it ended and how long it took. |
{{alias.tool_calls}} | What it did during the call. The proof that a booking or a CRM update actually happened. |
{{alias.output}}
- What you get
- The agent's conclusion — the verdict, the answer, whatever the prompt asked it to arrive at. Read this downstream; do not wire the agent into the flow.
{{alias.transcript}}
- What you get
- What was said, both sides. Where you look when a call went wrong.
{{alias.call_status}} · {{alias.duration_seconds}}
- What you get
- How it ended and how long it took.
{{alias.tool_calls}}
- What you get
- What it did during the call. The proof that a booking or a CRM update actually happened.
That first row is the wiring rule and it catches people. The agent is attached, not connected — the flow continues from the call step, which fires when the call ends, and the agent's result is data you read. Wiring the agent itself into the canvas produces a branch that fires before anyone has spoken.
What breaks
Watch out: Up to 4 integration ids (llm/realtime/synthesizer/transcriber) + voice_mode + prompt_mode + nested voice/transcriber settings.
Attach as a call target via `voice_agent:` on the call/trigger step (not a normal flow edge).
This node has three sets of defaults and they disagree. The documented schema, an internal defaults table and the code's own fallbacks give different values for the same fields — the opening delay, whether silence ends the call, whether responses are cached, and even which mode you are in. Whichever wins depends on how the node was created. The practical rule: for any field whose behaviour you care about, set it explicitly rather than relying on the default being what the panel says it is.
The last-resort model is an old one. If neither the node nor the integration names a model, the fallback is a model several generations behind. It will work, and it will sound worse and reason worse than anything you would have chosen. Name the model.
Written prompts do not read aloud. Everything from the prompting lesson holds, plus one rule: the output is going to be spoken. No bullet lists, no “as I mentioned above”, no URLs, no reference numbers read as digits. Add a constraint saying so, and then listen to a real call before you believe it.
Tools take time the caller can hear. A CRM lookup that takes two seconds is invisible in a chat workflow and awkward on a call. Either keep tools fast, or have the prompt cover the gap — telling the agent to say “let me check that for you” before it looks something up is a real technique, not a trick.
Try it
- Build the smallest agent — a name, a prompt,
voice_modeonrealtime— attach it to a call step, and call yourself. Say nothing and see what it does. - Now try to interrupt it. Then turn
enable_interruptionson and try again. That comparison is the whole argument. - Set
max_conversation_durationto 60 and have a long conversation. The call ends mid-sentence, which is what your ten-minute default will do to somebody eventually. - Turn on the inactivity nudge with a 10-second delay and then go quiet. Listen to whether the wording sounds like a person.
- Attach a tool and ask for something that needs it. Time the silence while it runs, then add a line to the prompt telling it to say it is checking.
Next: Creating a Knowledge Base — because every agent so far has been answering from a model's general knowledge, and the interesting answers are the ones only your organisation knows.
Related lessons
Base rates — what a piece of evidence is actually worth
A face-recognition system that is 99.9% accurate and almost entirely wrong, and a number that sent an innocent woman to prison. Both are the same arithmetic, and it is the arithmetic that decides what any piece of evidence is worth.
ReadConfirmation and survivorship — what you never looked for
Two questions about evidence you did not go looking for. One is a rule you have to discover, and one is a pattern in five famous people — and in both, the thing that would have told you the truth is the thing nobody checks.
ReadLoss aversion, sunk cost and regression — what it costs you
Four questions you answer about yourself rather than about a scenario, and your own answers are the finding. Then the pattern that makes praise look useless and criticism look like it works, whatever you actually do.
Read
