Implementation:Microsoft Semantic kernel Orchestration Patterns
Overview
Semantic Kernel provides three built-in orchestration classes for coordinating multiple agents: ConcurrentOrchestration, GroupChatOrchestration, and HandoffOrchestration. Each implements a distinct collaboration pattern -- parallel execution, managed group conversation, or dynamic agent-to-agent handoff -- and all share a common invocation model through the InProcessRuntime.
Source files:
ConcurrentOrchestration.cs:L18-82GroupChatOrchestration.cs:L15-97HandoffOrchestration.cs:L17-114
Principle page: Multi-Agent Orchestration
Principle:Microsoft_Semantic_kernel_Multi_Agent_Orchestration
Code Reference
ConcurrentOrchestration
public class ConcurrentOrchestration : AgentOrchestration<string[]>
{
public ConcurrentOrchestration(params ChatCompletionAgent[] agents);
public Task<OrchestrationResult<string[]>> InvokeAsync(
string input,
InProcessRuntime runtime,
CancellationToken cancellationToken = default);
}
All agents receive the same input in parallel. Results are collected as a string[] with one entry per agent.
GroupChatOrchestration
public class GroupChatOrchestration : AgentOrchestration<string>
{
public GroupChatOrchestration(
GroupChatManager manager,
params ChatCompletionAgent[] agents);
public Task<OrchestrationResult<string>> InvokeAsync(
string input,
InProcessRuntime runtime,
CancellationToken cancellationToken = default);
}
Agents take turns in a managed conversation. The GroupChatManager controls turn order and termination.
RoundRobinGroupChatManager
public class RoundRobinGroupChatManager : GroupChatManager
{
public int MaximumInvocationCount { get; set; }
}
Cycles through agents in the order they were provided, stopping after MaximumInvocationCount total turns.
HandoffOrchestration
public class HandoffOrchestration : AgentOrchestration<string>
{
public HandoffOrchestration(
OrchestrationHandoffs handoffs,
params ChatCompletionAgent[] agents);
public Task<OrchestrationResult<string>> InvokeAsync(
string input,
InProcessRuntime runtime,
CancellationToken cancellationToken = default);
}
Agents dynamically hand off control based on conversation context and defined handoff rules.
OrchestrationHandoffs Builder
public class OrchestrationHandoffs
{
public static OrchestrationHandoffs StartWith(ChatCompletionAgent startAgent);
public OrchestrationHandoffs Add(
ChatCompletionAgent fromAgent,
ChatCompletionAgent[] toAgents);
}
A fluent builder that defines the starting agent and the directed graph of permitted handoff transitions.
I/O Contract
ConcurrentOrchestration
Input:
├── agents: ChatCompletionAgent[] → All agents to run in parallel
├── input: string → The message sent to all agents
└── runtime: InProcessRuntime → Must be started before invocation
Output: OrchestrationResult<string[]>
└── GetValueAsync() → string[]
├── results[0]: Response from first agent
├── results[1]: Response from second agent
└── results[N]: Response from Nth agent
GroupChatOrchestration
Input:
├── manager: GroupChatManager → Controls turn order and termination
├── agents: ChatCompletionAgent[] → Participants in the group chat
├── input: string → Initial message to start the conversation
└── runtime: InProcessRuntime → Must be started before invocation
Output: OrchestrationResult<string>
└── GetValueAsync() → string
└── The final message from the last agent to speak
HandoffOrchestration
Input:
├── handoffs: OrchestrationHandoffs → Defines start agent and handoff graph
├── agents: ChatCompletionAgent[] → All participating agents
├── input: string → Initial message routed to start agent
└── runtime: InProcessRuntime → Must be started before invocation
Output: OrchestrationResult<string>
└── GetValueAsync() → string
└── The final response from whichever agent last handled the conversation
Usage Examples
Concurrent Orchestration
// Create specialized agents
ChatCompletionAgent writer = new()
{
Name = "Writer",
Instructions = "Write a short story on the given topic.",
Kernel = kernel
};
ChatCompletionAgent reviewer = new()
{
Name = "Reviewer",
Instructions = "Write a critical review of the given topic.",
Kernel = kernel
};
// Set up runtime and orchestration
InProcessRuntime runtime = new();
await runtime.StartAsync();
ConcurrentOrchestration orchestration = new(writer, reviewer);
OrchestrationResult<string[]> result = await orchestration.InvokeAsync(
"Write a story about a brave knight", runtime);
// Collect results
string[] results = await result.GetValueAsync(TimeSpan.FromSeconds(300));
await runtime.RunUntilIdleAsync();
Console.WriteLine($"Writer: {results[0]}");
Console.WriteLine($"Reviewer: {results[1]}");
Group Chat Orchestration
ChatCompletionAgent writer = new()
{
Name = "Writer",
Instructions = "Write or revise content based on feedback.",
Kernel = kernel
};
ChatCompletionAgent reviewer = new()
{
Name = "Reviewer",
Instructions = "Review the content and provide constructive feedback.",
Kernel = kernel
};
InProcessRuntime runtime = new();
await runtime.StartAsync();
GroupChatOrchestration groupChat = new(
new RoundRobinGroupChatManager { MaximumInvocationCount = 5 },
writer, reviewer);
OrchestrationResult<string> result = await groupChat.InvokeAsync(
"Write a haiku about autumn", runtime);
string finalResponse = await result.GetValueAsync(TimeSpan.FromSeconds(300));
await runtime.RunUntilIdleAsync();
Console.WriteLine($"Final: {finalResponse}");
Handoff Orchestration
ChatCompletionAgent triageAgent = new()
{
Name = "Triage",
Instructions = "Assess the customer request. Hand off to Billing for payment issues or Refund for return requests.",
Kernel = kernel
};
ChatCompletionAgent billingAgent = new()
{
Name = "Billing",
Instructions = "Handle billing and payment inquiries. Hand back to Triage if the issue is not billing-related.",
Kernel = kernel
};
ChatCompletionAgent refundAgent = new()
{
Name = "Refund",
Instructions = "Process refund and return requests.",
Kernel = kernel
};
InProcessRuntime runtime = new();
await runtime.StartAsync();
HandoffOrchestration handoff = new(
OrchestrationHandoffs.StartWith(triageAgent)
.Add(triageAgent, [billingAgent, refundAgent])
.Add(billingAgent, [triageAgent]),
triageAgent, billingAgent, refundAgent);
OrchestrationResult<string> result = await handoff.InvokeAsync(
"I was charged twice for my order #12345", runtime);
string finalResponse = await result.GetValueAsync(TimeSpan.FromSeconds(300));
await runtime.RunUntilIdleAsync();
Console.WriteLine($"Resolution: {finalResponse}");
Related Pages
- Multi-Agent Orchestration (Principle) -- The conceptual foundation for orchestration patterns.
- InProcessRuntime (Implementation) -- The runtime required by all orchestrations.
- Orchestration Runtime (Principle) -- The runtime concept powering orchestrations.
- OrchestrationResult GetValueAsync (Implementation) -- Collecting results from orchestrations.
- ChatCompletionAgent (Implementation) -- The agents used within orchestrations.
- Chat Completion Agent Creation (Principle) -- Creating agents for orchestration participation.