Semantic Kernel: Multi-agent Orchestration

A single agent handling everything sounds simple until you try it on a task with more than one moving part. Ask one agent to research a topic, write code, review the code, and produce a final report, and you usually end up with an agent that does all four jobs poorly. Semantic Kernel’s new multi-agent orchestration API addresses this directly by giving you five ready-made patterns for coordinating multiple agents, each doing one job well, instead of forcing one agent to wear every hat.

Why bother splitting work across agents

A single-agent setup works fine for narrow, well-defined tasks. The trouble starts when a task has multiple distinct skills bundled together, for example research, analysis, and writing. Push all of that into one system prompt and you often get an agent that half-follows every instruction instead of fully following any of them.

Splitting the work into specialized agents, each with a tight scope and a clear job, tends to produce more consistent output. A researcher agent stays a researcher agent. A reviewer agent stays a reviewer agent. The orchestration layer is what decides how these agents talk to each other, and Semantic Kernel now ships five patterns for that: sequential, concurrent, group chat, handoff, and magentic.

Sequential orchestration

In this pattern, agents form a pipeline. Agent A finishes its work, hands the output to Agent B, which hands its output to Agent C, and so on. A typical example is a document passing through a summarization agent, then a translation agent, then a quality-assurance agent, with each stage building directly on the previous one’s output.

Sequential orchestration: each agent processes the task in turn and passes its output to the next.
Sequential orchestration: each agent processes the task in turn and passes its output to the next.

Sequential is the easiest pattern to reason about because the data flow is linear and every stage is auditable. The trade-off is latency: since each stage waits for the previous one to finish, total run time is the sum of every agent’s response time. Use this pattern only when a stage genuinely depends on the output of the one before it. If two stages could run independently, forcing them into a sequential pipeline just adds wait time for no benefit.

Concurrent orchestration

Concurrent orchestration runs multiple agents on the same input at the same time, then collects their independent results for you to compare or aggregate. This suits scenarios where you want several perspectives on the same problem, such as brainstorming, ensemble reasoning, or a voting setup where you pick the best of several candidate answers.

Concurrent orchestration: agents work on the same task in parallel and their outputs are collected.
Concurrent orchestration: agents work on the same task in parallel and their outputs are collected.

Concurrent is faster wall-clock-wise than sequential because agents run in parallel, but it costs more in tokens since you are paying for N full agent runs instead of one. It also does not decide anything for you. Someone or something still has to pick, merge, or rank the parallel outputs, so plan for that aggregation step separately; it is not automatic.

Group chat orchestration

Group chat orchestration simulates a conversation among multiple agents, with an optional human participant in the loop. A group chat manager decides which agent speaks next and when to pause for human input, which makes this pattern useful for simulating meetings, debates, or any collaborative discussion where the next speaker is not fixed in advance.

Group chat orchestration: a manager coordinates turn-taking among agents and can involve a human.
Group chat orchestration: a manager coordinates turn-taking among agents and can involve a human.

The manager is doing real work here, deciding turn order and when the conversation is actually done. That is also the biggest risk with this pattern: without a clear termination condition, a group chat can keep passing the conversation around longer than intended, and every extra turn is another paid agent call. Set an explicit limit on rounds or a clear completion signal before you run this in production.

Handoff orchestration

Handoff orchestration lets an agent transfer the conversation to a different agent based on context, similar to how a support call gets escalated to a specialist. A general support agent might hand a billing question to a billing agent, or a technical issue to a troubleshooting agent, so that the agent best suited to the request actually handles it.

Handoff orchestration: an agent transfers control to another agent based on context or user request.
Handoff orchestration: an agent transfers control to another agent based on context or user request.

This pattern maps closely to real support workflows, which is exactly why teams like it, but its quality depends entirely on how well each agent recognizes when it is out of its depth. If the handoff logic is vague, you can end up with agents bouncing the same request back and forth instead of resolving it. Write explicit handoff criteria for each agent rather than leaving the decision to a loosely worded instruction.

Magentic orchestration

Magentic orchestration is based on the MagenticOne pattern from AutoGen, and it is the most open-ended of the five. A Magentic manager coordinates a team of specialized agents, deciding which agent acts next based on the evolving context and how much progress has been made so far, rather than following a fixed sequence.

The manager keeps a shared context, tracks progress, and adjusts the plan as it goes, which makes this pattern suitable for tasks where you do not know the solution path in advance and expect multiple rounds of research, computation, or revision. A good example is asking for a report comparing the energy efficiency of different machine learning models: the manager assigns a research agent to gather data, hands analysis to a coder agent, and repeats that cycle until it has enough to produce the final report.

Magentic orchestration: a manager dynamically assigns tasks to specialized agents based on evolving context. Diagram originally published by AutoGen.
Magentic orchestration: a manager dynamically assigns tasks to specialized agents based on evolving context. Diagram originally published by AutoGen.

Magentic is the closest thing here to a fully autonomous agent loop, and that flexibility is also its main cost risk. Because the manager decides on the fly how many rounds are needed, an open-ended task can run far longer, and far more expensively, than you expect if you do not cap the number of iterations. Reserve this pattern for genuinely exploratory tasks rather than anything with a known, fixed set of steps, where one of the simpler patterns will be cheaper and easier to debug.

One interface for all five patterns

The useful part of this API is that switching patterns does not mean rewriting your agent logic. Every orchestration type follows the same five steps regardless of which one you pick.

  • Define your agents and their capabilities.
  • Create an orchestration by passing in the agents, and a manager if the pattern needs one.
  • Optionally add callbacks or transforms to customize input and output handling.
  • Start a runtime and invoke the orchestration with your task.
  • Await the result asynchronously.

Here is the Python version using sequential orchestration as the example. Swapping the class name is all it takes to try a different pattern.

# Choose an orchestration pattern with your agents
orchestration = SequentialOrchestration(members=[agent_a, agent_b])
# or ConcurrentOrchestration, GroupChatOrchestration, HandoffOrchestration, MagenticOrchestration, ...
 
# Start the runtime
runtime = InProcessRuntime()
runtime.start()
 
# Invoke the orchestration
result = await orchestration.invoke(task="Your task here", runtime=runtime)
 
# Get the result
final_output = await result.get()
 
await runtime.stop_when_idle()

This creates a sequential orchestration over two agents, starts an in-process runtime, invokes the orchestration with a task string, and waits for the final output. For sequential and handoff patterns, final_output is the last agent’s response; for concurrent, expect a collection of responses rather than a single string. A common mistake is skipping runtime.start() before invoke, which throws immediately, or forgetting the final stop_when_idle() call, which leaves background tasks running after your script should have exited.

The .NET version follows the identical five steps, just with C# naming conventions.

// Choose an orchestration pattern with your agents
SequentialOrchestration orchestration = new(agentA, agentB)
{
    LoggerFactory = this.LoggerFactory
};  // or ConcurrentOrchestration, GroupChatOrchestration, HandoffOrchestration, MagenticOrchestration, ...
 
// Start the runtime
InProcessRuntime runtime = new();
await runtime.StartAsync();
 
// Invoke the orchestration and get the result
OrchestrationResult<string> result = await orchestration.InvokeAsync(task, runtime);
string text = await result.GetValueAsync();
 
await runtime.RunUntilIdleAsync();

Note the naming difference: Python uses snake_case async methods like invoke and get, while .NET uses PascalCase with an Async suffix, InvokeAsync and GetValueAsync. The equivalent of stop_when_idle() here is RunUntilIdleAsync(), and skipping it causes the same problem as on the Python side, pending agent messages that never get flushed before the process moves on. If you are maintaining both a Python and a .NET version of the same orchestration, keep a short mapping table of these method names handy; the logic is identical, only the casing and suffixes change.

Picking a pattern for your use case

Sequential is the right default when a task naturally breaks into ordered stages, each depending on the one before. Concurrent is worth the extra token cost when you genuinely need multiple independent takes on the same input, but budget for a separate step to merge or rank the results. Group chat suits deliberative work with a human reviewer in the loop, provided you set a hard limit on conversation rounds. Handoff fits triage and support-style workflows, as long as each agent has explicit, unambiguous rules for when to pass control along. Magentic is for open-ended, research-style tasks where you cannot predict the number of steps upfront, and it is the pattern most likely to surprise you on cost if left unbounded.

Whichever pattern you choose, treat token cost and latency as first-class design constraints, not an afterthought. More agents and more rounds mean more calls, and it is worth logging how many turns each orchestration actually takes in a test run before you push it to production. Add explicit iteration caps to group chat and magentic orchestrations specifically, since these are the two patterns without a naturally fixed number of steps.

Getting started

Semantic Kernel ships sample code for each pattern in its official GitHub repository, under python/samples/getting_started_with_agents/multi_agent_orchestration for Python and dotnet/samples/GettingStartedWithAgents/Orchestration for .NET. The Microsoft Learn documentation for Semantic Kernel multi-agent orchestration covers the API in more depth than a single blog post can, and is worth reading before you commit to a pattern for a production workload.

Start with the sample closest to your use case, run it as-is to see the expected output shape, then swap in your own agents one at a time. Changing the orchestration class name later, once your agents are defined, is a much smaller change than most people expect given how differently these patterns behave at runtime.

Leave a Reply

Discover more from Behind the Stack

Subscribe now to keep reading and get access to the full archive.

Continue reading