Synchronous agents are the wrong shape for an event-driven enterprise

Your agent calls a tool and waits for an answer. Your enterprise emits an event and moves on. When the action completes later, somewhere else, out of band, the agent is either polling the future into existence or missing it entirely.

B

Balagei G Nagarajan

5 MIN READ


Even the latest frontier model runs strictly turn-based and synchronous: it has no sense of elapsed time and can't act while it waits.Researchers building real-time agents found this is architectural, not a function of model size, so against an event-driven backend a state-of-the-art agent either polls until rate limits die or misses the completion event entirely (Asynchronous Tool Usage for Real-Time Agents, arXiv:2410.21620, 2024).

A synchronous agent holding a phone line waiting for an answer, while around it an enterprise hums with events flying between services on their own paths, none of them returning to the waiting agent
Trace across the async boundary with standardized spans so a lost event is diagnosable.
— from “Synchronous agents are the wrong shape for an event-driven enterprise”

Key facts.

  • Current agents operate strictly turn-based and synchronous, oblivious to the passage of time, which forces query and tool-use to happen sequentially and blocks multitasking, the mismatch with event-driven systems (Asynchronous Tool Usage for Real-Time Agents, arXiv:2410.21620, 2024).
  • In an eventually-consistent architecture the real outcome is a downstream state that settles after the call returns, so the acknowledgment the agent blocks for is a receipt, not the result (AWS Builders' Library, Challenges with distributed systems).
  • Correlating an async action with its later completion event needs a stable correlation ID carried end to end, the standard pattern for tracking work across service boundaries (W3C Trace Context).

Why does the synchronous assumption break in an event-driven system?

Because the agent's loop expects a direct answer that the architecture never sends. A tool-calling agent emits a call, blocks, and reasons over the immediate response (async-tools). An event-driven enterprise is fire-and-forget and eventually consistent: the agent's action publishes an event, downstream services react on their own schedule through choreography, and the result, if there's one, comes back out of band as another event, not as a return value. There may be no synchronous channel that says done, and the state may not be queryable yet because the ledger or index hasn't caught up. So the agent is holding a line that will never ring, while the work happens elsewhere.

What goes wrong when an agent forces it to be synchronous?

It polls, or it misses the event. To fake a synchronous answer, the agent asks again and again, is the order ready, is it ready now, which wastes its turns and hammers the service with calls that mostly return not yet, exhausting rate limits for almost no information. Or it gives up after one check and proceeds as if the action completed, missing the event that actually carried the result and stalling or acting on stale state. Both are the same root error: treating an asynchronous, eventually-consistent action as if it returned an immediate, authoritative answer. And because the real outcome lands later, the agent's report of success is about the acknowledgment, not the settled state.

# Don't poll the future. Wait on the event, keyed for safe correlation.
key = action.idempotency_key
emit_event("order.submitted", payload, key=key)
result = await wait_for_event("order.completed", key=key, timeout=TIMEOUT)
if result is None: escalate_or_compensate(key) # event never arrived
# vs the anti-pattern: while True: poll("is it ready?") # burns quota, learns little

How do you fit an agent to event-driven reality?

Make the agent event-native instead of poll-native. Replace polling with webhook or event-listener subscriptions so the agent is notified when the result lands, rather than asking repeatedly. Give every action a correlation ID, and wait for the matching completion event with a timeout and an explicit compensate-or-escalate path if it never comes. Run long-lived, multi-step flows on a durable execution engine so an agent step can suspend on an event and resume later with exactly-once semantics, rather than blocking a synchronous loop. Trace across the async boundary with standardized spans so a lost event is diagnosable. The agent then participates in the event flow, reacting when state actually changes, instead of pretending the enterprise answers on its clock.

Two patterns: top, an agent in a tight loop repeatedly polling a service that mostly answers not-yet, burning calls; bottom, an agent that emits an event and subscribes to a completion event, idle until the result arrives, with a correlation ID linking them

Synchronous loop versus event-native agent

RealitySynchronous agentEvent-native agent
Action completes laterPolls until quota diesSubscribes to completion event
No answer channelAssumes done, stallsWaits on event with timeout
Eventually consistent stateReads stale dataActs on the event that confirms it
Long-running flowBlocks the loopSuspends and resumes durably
Lost eventSilent stallTimeout + compensate/escalate

The pattern is that a synchronous tool-calling loop is the wrong abstraction for an asynchronous, eventually-consistent enterprise, so the agent polls the future into expensive existence or misses it entirely. Make it event-native, subscribe to completion, correlate by key, suspend and resume durably, and the agent moves at the architecture's pace instead of fighting it. Designing the agent to react to real state changes rather than to demand synchronous answers is reliability at the pattern level, which is what VibeModel builds as the Pattern Intelligence Layer.

Frequently asked questions

Can a more capable model handle our event-driven systems on its own?
A sync agent in an async stack is a call into a mailbox; a newer model still polls or stalls on the event. (arXiv:2410.21620)

Why is polling so costly for an agent?
Each poll spends a turn and an API call to mostly learn not yet, so it burns the agent's context budget and the service's rate limit while gaining little. A subscription delivers the result once, when it actually exists.

How does the agent match an action to its later event?
With a correlation ID, conveniently the same idempotency key that makes the action safe to retry. The agent waits for the completion event carrying that key, with a timeout and a compensation path if it never arrives.

Do I need a workflow engine?
For long-running async flows it helps a lot: the agent step can suspend on an event and resume durably with exactly-once semantics, instead of blocking a synchronous loop or losing state on a restart.


Share this post

Join the discussion

Have a take, a war story, or a question? Sign in with GitHub to comment and react. Comments are powered by GitHub Discussions, ad-free and yours to moderate.

Continue Reading

Find where your agent breaks, before you build it

Faultmap maps where your agent will fail from the goal and your data, then hands you the first test suite it has to pass.