Skip to content
Expedify
AI & the Knowledge Base

Module · The AI nodes

Voice Agent

Lesson 3 of 6 · 11 min

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

voice_mode

What it holds
Custom: separate STT+LLM+TTS providers. Realtime: single low-latency provider (OpenAI, Azure, Gemini). One of: custom · realtime Defaults to realtime.

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

first_message_mode

What it holds
Who speaks first in the conversation One of: assistant_speaks_first · user_speaks_first · assistant_waits Defaults to assistant_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

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

{{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

  1. Build the smallest agent — a name, a prompt, voice_mode on realtime — attach it to a call step, and call yourself. Say nothing and see what it does.
  2. Now try to interrupt it. Then turn enable_interruptions on and try again. That comparison is the whole argument.
  3. Set max_conversation_duration to 60 and have a long conversation. The call ends mid-sentence, which is what your ten-minute default will do to somebody eventually.
  4. Turn on the inactivity nudge with a 10-second delay and then go quiet. Listen to whether the wording sounds like a person.
  5. 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.