Skip to lesson

MicrosoftMicrosoft Certified: Multi-Agent AI Solutions Expert2026-102.4 Implement multi-agent orchestration

Aggregate parallel results under explicit policy

Completed by the knowledge check

Learning objective

Implement parallel fan-out/fan-in with aggregation, quorum, and partial-failure policy

Core idea

Fan-out sends available input to independent workers. Fan-in collects their results. Aggregation decides what those results mean together. A quorum defines how much acceptable participation or agreement is sufficient, while partial-failure policy decides what happens when a branch cannot contribute.

Microsoft Agent Framework provides concurrent orchestration and custom aggregators. The documented default collection behavior is not your business policy for voting, mandatory checks, or degraded responses. Implement those decisions explicitly in a custom aggregator or collecting executor.

Keep three questions separate: did each branch return, did its result pass validation, and does the set of accepted results justify the final decision? Combining them into a count of successful calls produces answers that look complete while omitting required evidence.

How it works

Fan out independent work

Python ConcurrentBuilder(participants=[researcher, marketer, legal]).build() gives the input to participants that process it independently. Participants can be agents or executors. The .NET entry point for the agent pattern is AgentWorkflowBuilder.BuildConcurrent().

Use concurrent orchestration when branches need the same available input or separately prepared inputs. A worker that consumes another worker’s analysis is dependent, so place it after a join rather than among those independent participants. See 2.4.1 for prerequisite gates.

Concurrency reduces the time spent waiting on independent work, but it also makes multiple requests compete for capacity. Choose the worker set from the required perspectives rather than multiplying agents to make an answer appear more credible. Agreement between similar models using the same evidence is not independent corroboration.

For a custom graph, fan out with AddFanOutEdge in .NET or add_multi_selection_edge_group(..., selection_func=...) in Python. Fan in with AddFanInBarrierEdge in .NET or add_fan_in_edges in Python. These mechanisms define message flow. Your collecting executor defines whether the received messages satisfy the decision contract.

Replace collection with meaningful aggregation

The Python default aggregator produces one AgentResponse containing one assistant message per participant. It preserves separate contributions rather than proving consensus or producing a domain-approved answer.

Use ConcurrentBuilder(...).with_aggregator(callback).build() for a custom aggregation function. The documented Python callback consumes list[AgentExecutorResponse]. Each response includes executor_id and agent_response, letting the aggregator associate a contribution with its producer.

An aggregator can synthesize text, rank alternatives, or apply deterministic acceptance rules. Choose deterministic code for mandatory checks and vote counting. A synthesis model can explain accepted evidence, but it should not silently decide that a required safety review is optional because two other workers agree.

Define an application result envelope with a task identifier, branch identifier, status, evidence, and validated value. These fields are your contract, not built-in Agent Framework response fields. Retain the producer identity during normalization so duplicate or stale responses cannot become additional votes.

Specify the quorum before counting

A quorum must name its denominator and eligibility rules. Two acceptable results from three assigned independent branches is different from two matching answers among the branches that happened to return. Define whether abstentions, timeouts, invalid schemas, and conflicting evidence count against completion.

Also distinguish participation quorum from agreement quorum. Two complete reports satisfy a participation threshold even when they disagree. A decision requiring two matching recommendations needs an explicit comparison rule over normalized values, not a count of messages or a model’s statement that the team agrees.

Mandatory roles can override a numeric threshold. An application may require two of three commercial estimates while always requiring legal acceptance. That is a role-aware policy, not a majority of all participants. Put the rule in the custom aggregator or collecting executor and return its reason in the final payload.

The concurrent documentation specifies collection and custom aggregation; it does not define a quorum argument for these business rules. This lesson therefore implements quorum as application logic. Do not invent a builder option such as a majority threshold when the documented API does not supply one.

Represent partial failures deliberately

For expected branch failures, use custom executors to turn recoverable execution outcomes into typed status messages that the aggregator can inspect. An absent result, an exception escaping a worker, and a returned failed status are different situations. A custom aggregator receives returned responses; its existence alone does not establish that every worker exception becomes a response.

Your application policy might require all branches, accept a qualified subset, or return an incomplete decision. Define the deadline and failure classification in the worker wrapper. Ensure a branch that times out or fails validation has an explicit terminal status when the application intends to continue with partial results.

Do not treat omitted evidence as favorable evidence. A failed security analysis cannot be counted as a clean finding. If a mandatory branch is unavailable, the collector yields an incomplete result with the missing role rather than a positive recommendation assembled from the remaining prose.

For best-effort analysis, disclose the participating and missing branches in the result. Keep recovery bounded and preserve the task identity when retrying. See 0.10 for Python concurrency and result validation; this lesson owns the aggregate decision, not the language-level exception mechanics.

Expose branch progress separately

In Python, only the concurrent aggregator’s output surfaces as a terminal "output" event by default. Configure intermediate_output_from=[researcher, legal] to surface selected branch outputs as "intermediate" events as well.

Do not let the first branch update satisfy the caller’s request for an aggregate. The collector is the component that yields the terminal result through ctx.yield_output(...) in a custom executor workflow. Its payload must identify whether the policy accepted, degraded, or rejected the evidence set. Selected branches surface intermediate events, while only the collector yields the terminal result.

Selected branches surface intermediate events, while only the collector yields the terminal resultThe input fans out to the researcher, marketer and legal participants, which process it independently. Every branch result goes to the collector, which yields the terminal output and states whether the policy accepted, degraded or rejected the evidence set. The researcher and legal branches, selected with intermediate_output_from, also surface intermediate events, which do not satisfy the caller's request for an aggregate.yield_outputInputresearchermarketerlegalCollectorIntermediateeventsTerminal output:accepted, degraded,or rejected

Key distinctions and trade-offs

Parallelism versus independence: Parallel execution is safe for work whose inputs exist. It does not remove data dependencies.

Collection versus consensus: The default AgentResponse collects contributions. A custom rule establishes agreement or acceptance.

Quorum versus mandatory role: A numeric threshold can tolerate missing optional workers. It cannot override a required specialist unless your policy explicitly permits that.

Waiting for all branches improves coverage but makes completion depend on slow or failing workers. Returning a subset improves availability but narrows what the answer can claim. Choose that trade-off before execution and encode it in the terminal result.

Architecture / decision scenario

A procurement assistant fans out one supplier dossier to financial, operational, and legal reviewers. The application requires two accepted commercial assessments and mandatory legal acceptance before recommending a contract.

Custom worker executors return normalized statuses with task and producer identifiers. The collector distinguishes completed reports from invalid responses and timeouts. A custom aggregator counts eligible financial and operational results, checks legal acceptance separately, and includes the exact missing or conflicting evidence in its terminal payload.

If the operational branch times out, the quorum is not met. If the legal branch fails, two positive commercial reports still cannot authorize a recommendation. If every role returns valid evidence but financial and operational recommendations conflict, participation is complete while agreement remains unresolved.

For an informational summary, a separate application policy can return a degraded report with missing roles disclosed. It cannot reuse that degraded result as contract approval. Intermediate worker events update the progress panel; only the collector’s output carries the aggregate decision.

Common exam traps

  • Assuming the default aggregator performs voting or majority selection.
  • Counting duplicate producer messages as independent votes.
  • Redefining the quorum denominator after failures occur.
  • Treating missing legal evidence as legal acceptance.
  • Assuming a custom aggregator automatically catches worker exceptions.
  • Returning an intermediate branch response as the completed aggregate.

What to remember

ConcurrentBuilder supplies parallel execution and collection; with_aggregator supplies the extension point for your policy. Validate contributions, preserve producer identity, and distinguish quorum from mandatory checks. Partial completion changes the permissible conclusion, not merely the formatting of the answer.

Knowledge check

References