“Do any of your locations have a table for six at eight?” One question, three systems. Asking them one after the other means the caller waits for all three in sequence. Scatter and Gather is a voice agent pattern that asks them all at once. It waits for the answers and merges them into one reply: “Downtown and Airport both have eight o’clock. Uptown can do quarter past.”
Use it when
The answer depends on several sources or specialists:
- Availability across several locations or calendars.
- Prices from several suppliers.
- A second opinion: the same question to a stricter model, or a policy checker, before the agent commits.
Multi-location clients are where this pattern earns its keep for agencies. A dental group, a franchise, a trades business with three vans. The caller should not need to know which branch to ask.
Avoid it when
One source is enough. Every extra worker adds cost, and the slowest one sets the wait.
Latency
The wait is the slowest worker, not the sum. Set a shared timeout and answer with what came back: “Downtown and Airport have tables. I couldn’t reach Uptown just now, want me to text you when I do?”
Pair it with an acknowledgement, or with Background Worker when even the slowest is too slow for the turn.
Build it on LiveKit
Inside one tool, start the workers in parallel and gather with a timeout. The model sees one tool result, already merged.
import asyncio
from livekit.agents import RunContext, function_tool
LOCATIONS = ["downtown", "uptown", "airport"]
@function_tool()
async def find_table(ctx: RunContext, party_size: int, time: str) -> str:
"""Check every location for a table at the requested time."""
jobs = [asyncio.wait_for(booking.check(loc, party_size, time), timeout=3.0) for loc in LOCATIONS]
results = await asyncio.gather(*jobs, return_exceptions=True)
lines = []
for loc, r in zip(LOCATIONS, results):
lines.append(f"{loc}: unavailable right now" if isinstance(r, Exception) else f"{loc}: {r}")
return "\n".join(lines)
Pipecat ships this as job_group(), which sends one request to many agents on its worker bus and collects the responses with a shared timeout. On LiveKit today, parallel async tools are the straightforward way.
How it combines
Scatter and Gather usually lives inside one task of a Supervisor or one step of a Guided Path. Use it with model routing for the second-opinion variant: a fast model drafts, a stricter model checks, and the merge keeps the stricter answer when they disagree.
Common questions
Is Scatter and Gather the same as MapReduce?
It has the same shape: scatter is the map and gather is the reduce. MapReduce splits one large dataset and runs the same function on each chunk for throughput. Scatter and Gather sends one question to different sources inside a single conversational turn, under a deadline, and the merge is usually done by the language model.
How long does the caller wait in Scatter and Gather?
As long as the slowest worker, capped by a shared timeout. Answer with the results that arrived and offer to follow up on the rest.
Where does the name come from?
It comes from the Scatter-Gather pattern in Enterprise Integration Patterns, which broadcasts a request to several recipients and aggregates the replies. It is also called fan-out and fan-in.
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].