Principle:Microsoft Semantic kernel Orchestration Result Collection
Overview
Orchestration Result Collection is the principle of collecting and processing results from multi-agent orchestrations with timeout management. In Microsoft Semantic Kernel, orchestration invocations are asynchronous operations that may involve multiple agents working over extended periods. The OrchestrationResult<T> abstraction provides a structured way to await these results with configurable timeout boundaries, ensuring that the calling application can reliably retrieve output without hanging indefinitely.
This principle belongs to Workflow 3: Agent Conversation and Orchestration and represents the final step in the orchestration lifecycle -- gathering the fruits of multi-agent collaboration.
Description
The Asynchronous Result Problem
Multi-agent orchestrations present a unique result collection challenge:
- Variable execution time -- Different orchestration patterns take unpredictable amounts of time. A concurrent orchestration with two agents might complete in seconds, while a group chat with iterative refinement might take minutes.
- Multiple result sources -- In concurrent orchestrations, results come from multiple agents simultaneously. In group chat and handoff orchestrations, the final result is the last message produced.
- Indeterminate completion -- The orchestration framework cannot always predict when all agents will finish, especially in handoff patterns where the number of agent transitions depends on conversation content.
The OrchestrationResult Abstraction
OrchestrationResult<T> addresses these challenges by serving as a future (also known as a promise) for orchestration output:
- It is returned immediately by
orchestration.InvokeAsync(), before any agents have completed. - The caller uses
GetValueAsync(timeout)to await the actual result with a specified maximum wait time. - The type parameter
Tvaries by orchestration pattern:OrchestrationResult<string[]>for concurrent orchestrations (one result per agent).OrchestrationResult<string>for group chat and handoff orchestrations (single final result).
Timeout Management
The timeout parameter in GetValueAsync is critical for production reliability:
- Too short -- The orchestration may not have completed, resulting in a timeout exception. This is problematic for complex orchestrations involving multiple AI model calls.
- Too long -- The application may appear to hang if an agent encounters an issue. Users expect timely responses.
- Just right -- The timeout should account for the expected number of agent invocations, each of which involves at least one AI model call (typically 2-30 seconds per call).
A general guideline is to estimate the maximum number of sequential agent invocations and multiply by the expected per-invocation latency, then add a generous buffer.
The RunUntilIdle Pattern
After collecting results, the caller should invoke runtime.RunUntilIdleAsync() to ensure all runtime resources are properly cleaned up. While GetValueAsync returns the result, the runtime may still have pending internal housekeeping tasks. The RunUntilIdleAsync call ensures:
- All in-flight messages are processed.
- All agent actors have finished their work.
- The runtime is in a clean state for potential reuse.
The Complete Result Collection Sequence
The canonical result collection pattern follows three steps:
- Invoke --
var result = await orchestration.InvokeAsync(input, runtime)-- returns the result future. - Await --
var value = await result.GetValueAsync(TimeSpan.FromSeconds(N))-- blocks until the result is available or the timeout expires. - Cleanup --
await runtime.RunUntilIdleAsync()-- ensures the runtime completes all pending work.
This three-step pattern ensures reliable, predictable result collection with proper resource cleanup.
Usage
Orchestration Result Collection is used in every multi-agent orchestration scenario:
- Retrieving parallel results from concurrent orchestrations.
- Getting the final conversational output from group chat orchestrations.
- Obtaining the resolution from handoff orchestration workflows.
- Implementing timeout boundaries for production reliability.
Concurrent Orchestration Results
InProcessRuntime runtime = new();
await runtime.StartAsync();
ConcurrentOrchestration orchestration = new(writer, reviewer);
OrchestrationResult<string[]> result = await orchestration.InvokeAsync(
"Write a story about courage", runtime);
// Await results with 5-minute timeout
string[] results = await result.GetValueAsync(TimeSpan.FromSeconds(300));
await runtime.RunUntilIdleAsync();
// results[0] = writer's response
// results[1] = reviewer's response
Group Chat Result
GroupChatOrchestration groupChat = new(
new RoundRobinGroupChatManager { MaximumInvocationCount = 5 },
writer, reviewer);
OrchestrationResult<string> result = await groupChat.InvokeAsync("Write a haiku", runtime);
// Single string result — the last message in the group chat
string finalOutput = await result.GetValueAsync(TimeSpan.FromSeconds(300));
await runtime.RunUntilIdleAsync();
Handoff Result
HandoffOrchestration handoff = new(
OrchestrationHandoffs.StartWith(triageAgent)
.Add(triageAgent, [billingAgent, refundAgent])
.Add(billingAgent, [triageAgent]),
triageAgent, billingAgent, refundAgent);
OrchestrationResult<string> result = await handoff.InvokeAsync(
"I need help with my bill", runtime);
string resolution = await result.GetValueAsync(TimeSpan.FromSeconds(300));
await runtime.RunUntilIdleAsync();
Theoretical Basis
Orchestration Result Collection draws from the Future/Promise pattern in concurrent programming, where an asynchronous computation returns a placeholder object that will eventually hold the result. This pattern, implemented across languages (C# Task<T>, Java Future<V>, JavaScript Promise<T>), provides a clean abstraction for working with values that are not yet available.
The timeout mechanism reflects bounded wait principles from real-time systems design, where operations must complete within defined time constraints to maintain system responsiveness. Unbounded waits are considered an anti-pattern in production systems because they can lead to resource exhaustion, user frustration, and cascading failures.
The RunUntilIdleAsync pattern follows the graceful shutdown paradigm from service-oriented architecture, ensuring that all in-flight operations complete cleanly before resources are released.
Related Pages
- OrchestrationResult GetValueAsync (Implementation) -- The concrete API for collecting orchestration results.
- Orchestration Runtime (Principle) -- The runtime that produces results and manages the idle state.
- InProcessRuntime (Implementation) -- The runtime that provides
RunUntilIdleAsync. - Multi-Agent Orchestration (Principle) -- The orchestration patterns that produce results.
- Orchestration Patterns (Implementation) -- The orchestration APIs that return
OrchestrationResult<T>.