Five Ways to Wire Three Agents, and the One Mistake That Sinks Most of Them
Microsoft Agent Framework ships five named multi-agent orchestration patterns as ready-made builders, and picking the wrong one is the single most common mistake a team makes once it moves past one agent: not a crash, a workflow that quietly does the wrong kind of coordination for the job.

Microsoft Agent Framework ships five built-in multi-agent orchestration patterns: sequential, concurrent, handoff, group chat, and magentic. A researcher, a fact-checker, and a writer run through each one to show which pattern actually fits which job.
Your multi-agent workflow ran without a single error and still gave you a worse answer than it should have. The bug was never in the code.
In this article: You will learn the five built-in multi-agent orchestration patterns Microsoft Agent Framework ships as ready-made builders, the decision rule for reaching for each one, and the single most common mistake teams make once they move past a single agent. We cover sequential, concurrent, handoff, group chat, and magentic orchestration, with a working example for each, plus how human-in-the-loop approval carries through all five the same way.
Picture a market-research workflow built from three specialist agents: a researcher that gathers findings, a fact-checker that verifies them, and a writer that turns verified findings into a report. Wiring three agents together by hand, one edge at a time, teaches you the primitives: what an executor is, what an edge does, and why a deterministic step boundary is what makes a workflow trustworthy. It also takes real code to express something that already has a name: a sequential pipeline.
Microsoft Agent Framework ships five of these named patterns as ready-made builders, and picking the wrong one is the single most common mistake a team makes once it moves past one agent. Not a syntax error, not a crash: a working workflow that quietly does the wrong kind of coordination for the job it was given.
The cost of picking the wrong pattern
Each pattern encodes an assumption about how your agents relate to each other, and that assumption is baked into the graph the builder produces for you. Run three independent research agents through a sequential pipeline and you have serialized work that had no dependency between its steps, tripling your latency for nothing. Run a customer-support triage flow through concurrent orchestration and every specialist answers the same ticket at once instead of the one agent whose job it actually is. Wire an open-ended research brief as a fixed sequential graph and you have pre-decided an order the task itself does not have yet. None of these fail loudly. They just cost you time, money, or a worse answer than the pattern that actually fit would have produced.
The five patterns sit on a rising scale of how much control you hand over versus how much the framework figures out for you:
| Pattern | What it does | Reach for it when |
|---|---|---|
| Sequential | Agents run one after another, each output feeding the next | The task has a fixed pipeline shape: research, then verify, then write |
| Concurrent | Agents run in parallel on the same or related input, results aggregated | The work splits into independent pieces with no dependency between them |
| Handoff | Agents transfer full ownership of the conversation to each other | The right agent for the next step depends on context you cannot predict up front |
| Group chat | Agents collaborate in a shared conversation under a selection strategy | The task benefits from iterative back-and-forth between a fixed set of participants |
| Magentic | A manager agent dynamically plans and coordinates specialist agents | The task is open-ended and you do not know the solution path before you start |
In case the table above does not render where you are reading this, here is the same decision rule as a flat list:
- Pattern: Sequential. What it does: agents run one after another, each output feeding the next. Reach for it when: the task has a fixed pipeline shape, research, then verify, then write.
- Pattern: Concurrent. What it does: agents run in parallel on the same or related input, results aggregated. Reach for it when: the work splits into independent pieces with no dependency between them.
- Pattern: Handoff. What it does: agents transfer full ownership of the conversation to each other. Reach for it when: the right agent for the next step depends on context you cannot predict up front.
- Pattern: Group chat. What it does: agents collaborate in a shared conversation under a selection strategy. Reach for it when: the task benefits from iterative back-and-forth between a fixed set of participants.
- Pattern: Magentic. What it does: a manager agent dynamically plans and coordinates specialist agents. Reach for it when: the task is open-ended and you do not know the solution path before you start.

Every builder produces a Workflow, the same object type a hand-built graph produces, so nothing else about running it, streaming its events, or checkpointing it changes based on which pattern you pick. What changes is how much of the graph you write yourself versus how much the builder writes for you.
Sequential: the graph you would otherwise build by hand
SequentialBuilder is the convenience version of a straight pipeline you could wire edge by edge yourself, minus any conditional branch. Give it a list of participants and it wires a straight pipeline where each agent consumes the previous agent's full conversation by default:
from agent_framework.orchestrations import SequentialBuilder
pipeline = SequentialBuilder(participants=[researcher, fact_checker, writer]).build()
result = await pipeline.run("Research quantum computing funding trends and write the report.")
That one line replaces a handful of add_edge calls, but it only replaces them because the plain three-step version does not route around a failed fact-check. SequentialBuilder connects participants in a straight line; it has no condition= parameter the way a hand-built graph's edge does. The moment you need the writer to run only when a FactCheckResult.approved flag is true, and a revision handler to run when it is not, you are back to hand-wiring conditional edges. Keep SequentialBuilder for the case where the pipeline really is unconditional; keep a manual graph for the case where a step's output decides what happens next.
Concurrent: independent subtopics, parallel research
A single researcher investigates one subtopic at a time. A research brief that actually needs coverage across funding rounds, government grants, and corporate R&D spend does not have to serialize those three lookups, because nothing about researching corporate R&D spend depends on what the funding-rounds researcher found. That is exactly the independence concurrent orchestration is built for. A small factory function gives each subtopic its own scoped researcher, reusing the same tools and a per-subtopic memory provider:
def make_subtopic_researcher(subtopic: str) -> Agent:
return Agent(
client=OpenAIChatClient(),
name=f"Researcher_{subtopic.replace(' ', '_')}", # ①
instructions=f"Research {subtopic} within quantum computing funding trends.",
tools=[look_up_topic, premium_market_data_lookup],
context_providers=[ResearchMemoryProvider(subtopic, findings_store)], # ②
)
funding_rounds = make_subtopic_researcher("private funding rounds") # ③
government_grants = make_subtopic_researcher("government grants")
corporate_rd = make_subtopic_researcher("corporate R&D spend")
① Each subtopic gets its own uniquely named agent, so the responses ConcurrentBuilder aggregates are easy to tell apart per participant.
② Each subtopic also gets its own ResearchMemoryProvider scoped to that subtopic, so the three parallel runs never share or overwrite each other's remembered findings.
③ Three factory calls create three independent researchers with no wiring between them, exactly the shape ConcurrentBuilder expects on the way in.
Note: The full extracted listing at code/microsoft_agent_framework/part-12-multi-agent-orchestration-patterns/listings/01-subtopic-researcher-factory.py shows the imports and the tool and memory-provider stand-ins elided here.
ConcurrentBuilder runs all three at once and, by default, aggregates their responses into a single AgentResponse with one message per participant, no custom aggregation logic required:
from agent_framework.orchestrations import ConcurrentBuilder
concurrent_research = ConcurrentBuilder(
participants=[funding_rounds, government_grants, corporate_rd]
).build()

That aggregated response is exactly the shape a fact-checker already expects as input: three sets of findings instead of one. Chaining a concurrent stage into a larger sequential pipeline, so concurrent research feeds the same fact-checker and writer, is a composition question worth its own treatment. For now, the concurrent stage on its own turns three serialized research calls into one round-trip.
Handoff: transferring ownership, not delegating a subtask
Handoff looks similar to the ordinary function-tool pattern, where an agent calls a tool and gets the result back to keep working with. It is not the same thing. When an agent calls a tool, control returns to that agent the moment the tool finishes; the calling agent still owns the task. Handoff transfers the whole conversation, and the receiving agent takes full ownership of it, including everything that happened before the handoff. Nothing routes back automatically unless you wire a handoff rule for it.
That distinction matters the moment a general researcher hits a question outside its depth. A generalist agent can gather surface-level findings on almost any topic, but a question about SEC filing requirements or export-control restrictions on quantum hardware needs a specialist, and it needs that specialist to see the whole conversation so far, not a summarized subtask:
from agent_framework.orchestrations import HandoffBuilder
workflow = (
HandoffBuilder(participants=[generalist, finance_specialist, policy_specialist])
.with_start_agent(generalist) # ①
.add_handoff(generalist, [finance_specialist, policy_specialist]) # ②
.add_handoff(finance_specialist, [generalist]) # ③
.add_handoff(policy_specialist, [generalist]) # ④
.build()
)
① Every conversation in this workflow starts with the generalist, regardless of what the question turns out to need. ② The generalist can hand off to either specialist once a question needs depth it does not have. ③ The finance specialist can only hand the conversation back to the generalist, not sideways to the policy specialist. ④ The policy specialist follows the same rule, which keeps routing a hub through the generalist instead of a mesh between specialists.
Note: The full extracted listing at code/microsoft_agent_framework/part-12-multi-agent-orchestration-patterns/listings/02-handoff-routing.py shows the three participant agents elided here.

Handoff orchestration is interactive by design: when an agent does not call the handoff tool, the workflow has no other path forward, so it pauses and asks for human input instead of guessing. You will see a request_info event carrying a HandoffAgentUserRequest any time an agent responds without handing off, and you respond to it the same way you would respond to a tool-approval request.
Gotcha: if your workflow needs to run unattended, that pause-on-every-non-handoff behavior will stall it the first time an agent answers directly instead of routing. HandoffBuilder.with_autonomous_mode() covers this by feeding the agent a default continuation message instead of waiting on a human, but it is an experimental feature you opt into deliberately, not the default.
Group chat: iterative refinement between a fixed set of participants
Where handoff transfers ownership once, group chat keeps the same participants in the loop across multiple rounds, refining a shared conversation instead of passing it along. A fact-checker and a writer are a natural fit for this: instead of one fact-check followed by one write, they can go back and forth, the writer revising based on the fact-checker's notes until the fact-checker actually approves.
The next_speaker function is the entire selection strategy, alternating turns between the writer and the fact-checker by round number. The termination condition then reads the fact-checker's own verdict off the last message, ending the loop the moment it contains the word "approved."
from agent_framework.orchestrations import GroupChatBuilder, GroupChatState
def next_speaker(state: GroupChatState) -> str:
names = list(state.participants.keys())
return names[state.current_round % len(names)] # ①
review_loop = GroupChatBuilder(
participants=[writer, fact_checker],
selection_func=next_speaker, # ②
termination_condition=lambda conversation: "approved" in conversation[-1].text.lower(), # ③
).build()
① The current round number, taken modulo the participant count, is the entire round-robin rule: writer, then fact-checker, then writer again.
② The builder takes the custom function in place of its default selection logic, so next_speaker decides every turn instead of the framework guessing.
③ The loop ends the instant the fact-checker's latest message contains the word "approved," not after a fixed number of rounds.
Note: The full extracted listing at code/microsoft_agent_framework/part-12-multi-agent-orchestration-patterns/listings/03-group-chat-review-loop.py shows the writer and fact-checker agents elided here.

Because the fact-checker returns a structured FactCheckResult rather than prose, the writer's next turn is parsing approved and notes out of a JSON response instead of reading free-form feedback, which keeps the loop's termination condition simple and machine-checkable rather than relying on the model to phrase approval consistently. Group chat also supports an agent-based orchestrator instead of a hand-written selector function: a full agent that decides who speaks next using its own judgment, useful once your selection logic gets more complicated than round-robin between two participants.
Magentic: a manager that plans instead of a graph that is already fixed
Every pattern so far assumes you already know the shape of the coordination: a straight line, a parallel fan-out, a handoff mesh, a two-party review loop. An open-ended research brief, "produce a fact-checked report on quantum computing funding trends, digging into whatever subtopics turn out to matter," does not have a shape yet. That is what magentic orchestration is for: a manager agent that plans, delegates, checks progress, and replans, coordinating a researcher, fact-checker, and writer without you specifying the order they run in.
The manager agent is defined separately from the three specialists it coordinates, and it never appears in participants, only in manager_agent. The stall and reset counts below are the guardrails that keep an open-ended plan from running forever once the team gets stuck.
from agent_framework.orchestrations import MagenticBuilder
manager = Agent(
client=OpenAIChatClient(),
name="ResearchManager",
instructions="Coordinate the team to produce a fact-checked market research report.", # ①
)
workflow = MagenticBuilder(
participants=[researcher, fact_checker, writer],
manager_agent=manager, # ②
max_round_count=10,
max_stall_count=3, # ③
max_reset_count=2, # ④
).build()
result = await workflow.run( # ⑤
"Produce a fact-checked report on quantum computing funding trends, "
"digging into whatever subtopics turn out to matter."
)
① The manager's instructions describe coordination, not research, fact-checking, or writing; those jobs stay with the three participants.
② The manager is passed as manager_agent, a distinct role from the participants list it plans and delegates across.
③ max_stall_count caps how many rounds can pass without progress before the manager is forced to replan.
④ max_reset_count caps how many times the manager can throw out its plan and start over before giving up entirely.
⑤ The brief handed to run names no subtopics and no order, exactly the open-ended shape magentic orchestration exists to handle.
Note: The full extracted listing at code/microsoft_agent_framework/part-12-multi-agent-orchestration-patterns/listings/04-magentic-manager-workflow.py shows the researcher, fact-checker, and writer agents elided here.

The manager tracks a progress ledger every round: whether the request is satisfied, whether the team is stuck in a loop, whether progress is actually being made, and who should act next. max_stall_count caps how many non-progressing rounds the manager tolerates before it forces a replan, and max_reset_count caps how many times it is allowed to reset the whole plan before giving up, which is what keeps an open-ended task from spinning forever on a question none of your three agents can actually answer.
In production: MagenticBuilder supports enable_plan_review=True, which pauses the workflow before execution starts so a human can approve or revise the manager's proposed plan, and pauses again on any stall-triggered replan. Python defaults plan review to off; turn it on for anything you are not prepared to let run fully autonomously the first time, because a manager coordinating three agents against real tools and real spend is exactly the kind of run you want a chance to veto before it starts spending.
Human-in-the-loop works the same way everywhere
Every pattern in this article carries forward the same tool-approval discipline you would want for any agent that can write files or call paid APIs. Whether it is sequential, concurrent, handoff, group chat, or magentic, an agent that calls a tool wrapped with @tool(approval_mode="always_require") pauses the whole workflow and emits a request_info event, and the workflow only continues once you respond. A fact-checker's tool call still needs approval no matter which pattern is coordinating it. What differs between patterns is only the shape of the pause: handoff and magentic add their own request types on top of tool approval, HandoffAgentUserRequest for routing decisions and MagenticPlanReviewRequest for plan sign-off, but you handle all three the same way, by watching the event stream for request_info and resuming the run with a response once a human has weighed in.
Choosing a pattern for a market-research agent
A researcher, fact-checker, and writer trio is not locked into one pattern. It runs sequentially when the task really is research, then verify, then write, in that fixed order, which is most of the time. It runs concurrently when a brief names several independent subtopics worth researching in parallel before a single fact-check and write pass. It runs under a magentic manager when the brief is open-ended enough that you do not know which subtopics matter until the manager's first planning round figures it out. Handoff and group chat are not the default mode, but they are the right call the moment a research question needs a domain specialist mid-task, or the fact-checker and writer need more than one round to converge on an approved report.
Do this today
- Run the five-pattern decision table against your own workflow before you touch a builder. Does the work have a fixed order, split into independent pieces, need ownership to transfer, benefit from iteration, or have no known shape yet? That answer picks the pattern for you.
- Audit any sequential pipeline you already have for hidden independence. If two steps do not actually depend on each other's output, that pipeline is a concurrent workflow wearing a sequential costume, and it is paying serialized latency for nothing.
- Wire
enable_plan_review=Trueon any magentic workflow before its first unattended run. A manager coordinating several agents against real tools and real spend deserves one human veto before it starts spending. - Treat
HandoffBuilder.with_autonomous_mode()as an opt-in, not a default. Reach for it only once you have deliberately decided the workflow should run without a human catching every non-routing response. - Watch for
request_infoevents regardless of which pattern you picked. Tool approval, handoff routing requests, and magentic plan review all surface through the same event, so one handler covers all five patterns.
The takeaway
Five builders, one underlying Workflow object, and a decision that has almost nothing to do with code and everything to do with how your agents actually relate to each other: does the work have a fixed order, does it split into independent pieces, does ownership need to transfer, does it benefit from iterative back-and-forth, or is the solution path unknown until a manager plans it. Get that decision right and the builder does the wiring. Get it wrong and you will ship a workflow that runs without ever throwing an error while quietly doing the wrong kind of coordination for the job.
A researcher, fact-checker, and writer trio can now run five different ways. The harder problem, pausing any of those runs on human input and picking them back up exactly where they left off without losing state, is a good problem to have, and it is the natural next step once a team is coordinating well enough to be worth trusting with a long, unattended run.