Some tools are slow. A quote from a supplier’s API, availability across three booking systems, generating a PDF estimate, a payment that takes its time to clear. If the agent waits for them inside the turn, the caller sits in silence.
A Background Worker is a voice agent pattern that moves a slow job out of the conversational turn. The agent starts it, says something useful (“I’m pulling that quote now, while I do, is this for the kitchen or the bathroom?”), and keeps the conversation going. When the job finishes, its result rejoins the call. If the caller has already hung up, it reaches them by text instead.
Use it when
A tool takes more than about two seconds. Quotes, multi-system availability, document generation, slow payments, anything that calls a third party you do not control.
Avoid it when
The very next sentence depends on the result and there is nothing useful to say meanwhile. A short acknowledgement and a wait is better than filler. For waits under two seconds, a thinking sound or a quick “one moment” (capabilities, not patterns) is enough.
Latency
Nothing added to the turn. You do need a plan for how the result is announced: interrupting the caller mid-sentence with a quote is worse than waiting for a natural pause.
Build it on LiveKit
An async function tool that calls ctx.update() early is non-blocking. The agent voices the acknowledgement, the conversation carries on, and the return value is delivered when it arrives.
from livekit.agents import Agent, RunContext, function_tool
class Estimator(Agent):
@function_tool()
async def get_quote(self, ctx: RunContext, job: str, postcode: str) -> str:
"""Get a priced quote for a job. Takes 5 to 20 seconds."""
await ctx.update("Started pulling the quote from the supplier.")
quote = await suppliers.quote(job=job, postcode=postcode)
if call.hung_up: # your own flag, set when the caller disconnects
await sms.send(call.caller_phone, f"Your quote: {quote.summary}")
return f"Quote ready: {quote.total} including parts and labour."
When the job must finish even if the worker crashes or the caller hangs up, run it as a durable workflow, for example on Temporal. That is a capability you attach to the worker, not a different pattern.
Worker or Scatter and Gather?
A Background Worker moves one slow job out of the turn. Scatter and Gather splits one question across many workers and merges what comes back. They pair well: when even the slowest of the scattered workers is too slow for the turn, run the whole gather in the background.
How it combines
Workers sit beside any holding pattern. In a Guided Path, a worker checks slot availability while the path collects the caller’s details. In Bookends, the “text them if they hung up” branch is just the wrap-up delivering a worker’s late result.
Common questions
How do I stop a slow tool from causing silence on a voice call?
Make the tool non-blocking. In LiveKit Agents, an async function tool that calls ctx.update() early lets the agent voice an acknowledgement and keep talking while the job runs.
When is a thinking sound enough?
For waits under about two seconds, a thinking sound or a quick "one moment" is enough. Longer jobs belong in a Background Worker.
What if the job must finish even if the call drops?
Run it as a durable workflow, for example on Temporal. Durability is a capability you attach to the worker, not a separate pattern.
Sources and further reading
Mahimai Raja
Mahimai builds production voice agents on LiveKit for small businesses and maintains the open-source livekit-starter. He is building ShipVoice, the voice AI platform for agencies. Find him on X or at [email protected].