MicrosoftMicrosoft Certified: Multi-Agent AI Solutions Expert2026-102.1 Design and implement advanced prompt engineering strategies in multi-agent solutions
Carry context without merging agent responsibilities
Completed by the knowledge check
Learning objective
Implement context-aware behavior across agent roles, handoffs, and shared workflow state.
Core idea
Context-aware behavior means that an agent responds to the request’s established facts, progress, and constraints while retaining its own responsibilities. A receiving agent should know what happened, what remains unresolved, and what it can do next. It should not acquire another role’s instructions or treat an earlier agent’s claim as proof that an action succeeded.
For an architect, the decision is which information travels through conversation history, which arrives as a bounded role input, and which lives in shared workflow state. These are different mechanisms. Microsoft Agent Framework provides agent sessions, handoff history synchronization, and workflow state, but none makes every stored value part of every model request automatically. State lifetime and ownership are covered in 0.7 and 1.1.10; this lesson focuses on how that state changes a role’s next response.
How it works
Select context for the receiving role
Give each role a stable behavioral contract and a request-specific view of the work. A claims reviewer might need the submitted claim, policy evidence, and unresolved discrepancies. A customer-facing writer might need the reviewed decision and its explanation, rather than the reviewer’s entire working conversation. The output contract belongs to 2.1.1; runtime prompt construction belongs to 2.1.2.
For Python workflow chains, AgentExecutor.context_mode controls the context consumed when an upstream AgentExecutorResponse reaches the executor’s from_response handler. The supported modes divide the choice precisely:
| Mode | Context received | Appropriate decision |
|---|---|---|
"full" | Upstream input messages and response messages | Preserve request details that the specialist needs to interpret the result. |
"last_agent" | Upstream response messages only | Transform a complete result, such as translating an approved explanation. |
"custom" | Messages selected by context_filter | Supply a deliberately selected subset for the receiving role. |
"full" is the default. "custom" requires a context_filter callable that takes and returns a list of messages. These are Python AgentExecutor controls for response chaining; they are not settings that universally change every orchestration’s history behavior.
For .NET agent executors, AIAgentHostOptions controls message handling. ReassignOtherAgentsAsUsers defaults to true: other agents’ messages take the User role, so the model treats them as task inputs. ForwardIncomingMessages also defaults to true: incoming messages travel downstream before the agent’s generated messages. These controls govern role assignment and forwarding, rather than selecting one of Python’s context modes.
Each agent executor maintains its own session by default; sharing requires an explicit session passed to compatible Python executors or a common session provider configured for .NET agents. For Python sequential workflows, SequentialBuilder(chain_only_agent_responses=True) sets context_mode="last_agent" for all agent participants. Choose that convenience only when every stage can work from the preceding response alone.
Select "last_agent" only when the previous response is sufficient. If the reviewer says “not eligible” without carrying the policy version or unresolved exception, a downstream writer cannot reconstruct those facts from an omitted input. Conversely, forwarding the entire conversation can expose irrelevant material and increase the opportunity for an upstream instruction to interfere with the receiving role. Filtering context is a design choice; enforcing tool permission remains the separate responsibility described in 1.1.7.
Preserve meaning across handoffs
Agent Framework handoff orchestration transfers task ownership to the receiving agent. It differs from agent-as-tool delegation, where the primary agent retains responsibility and receives a subtask result; see 1.1.3. Python HandoffBuilder.add_handoff() defines which agent can transfer control to which recipient. .with_start_agent() identifies the initial participant.
Handoff synchronization preserves user and agent conversation content but excludes tool and approval content; see 1.1.10 for the session and payload rules. Routing relationships control who takes over, not who receives synchronized messages. Restricting triage to an order specialist therefore does not exclude a refund specialist from conversation synchronization. Do not teach a receiving agent to infer a successful refund merely because a preceding assistant announced one. Supply a validated business result through an explicit workflow record or an authorized lookup. A handoff relationship transfers control to its recipient, while synchronization distributes conversation content across participants.
By default, a Python user response returns to the agent that requested it. enable_return_to_previous(False) instead returns it to the start agent for another routing decision. This changes which role interprets the next input; it does not erase the synchronized history. The page labels Python autonomous mode experimental. Its .with_autonomous_mode() continuation is a separate execution choice, not a requirement for preserving context.
Make shared state usable, not merely stored
Python WorkflowContext.set_state() and WorkflowContext.get_state() write and read workflow state available to downstream executors. Use consistent keys and make a deterministic executor validate a value before publishing it as an accepted result. For example, application-defined keys validated_claim and review_result can carry a request identifier, policy version, evidence references, and decision status. Those keys are your schema, not built-in Framework fields.
A state read does not itself explain the value to the model. The receiving executor must select the relevant fields and include them in the role’s input using the runtime context pattern in 2.1.2. Keep pending proposals separate from accepted outcomes so an agent can distinguish “review requested” from “review completed.” Define which executor updates each key, especially when several specialists contribute results.
.NET uses QueueStateUpdateAsync() and ReadStateAsync() with shared scopes and superstep visibility; see 1.2.5 for the scope and timing rules.
Key distinctions and trade-offs
Role instructions versus task context: Instructions define the specialist’s responsibilities. Shared history and state provide facts to interpret under those instructions. A preceding agent’s text does not become a trusted role definition; defensive instruction handling is covered in 2.1.3.
Routing versus disclosure: add_handoff() limits control transfer. Handoff synchronization distributes conversation content across participants. If two specialists must not receive the same data, choose separate workflows or explicitly filtered message paths instead of relying on handoff edges as a confidentiality control.
History versus accepted state: History preserves conversational meaning. A validated state record preserves the application’s decision. The two can disagree after a failed tool call or a corrected result, so define which record governs the next action.
Architecture / decision scenario
An insurer coordinates triage, review, and customer communication. Triage classifies a claim and hands it to review. Both roles need the customer’s corrections, so the team uses HandoffBuilder and configures add_handoff() relationships. It accepts that the handoff participants receive synchronized conversation messages and excludes a separate sensitive-fraud role from that workflow.
A deterministic executor validates the review output and publishes review_result through set_state(). The communication executor reads it through get_state(), then renders the accepted decision, policy version, and unresolved questions into the writer’s input. The writer does not treat a synchronized statement about a proposed payout as an executed payout.
A translation stage uses AgentExecutor(context_mode="last_agent") because the approved explanation is a complete translation input. If a customer disputes a policy detail, enable_return_to_previous(False) sends the next response back to triage for classification. In a .NET implementation, the accepted record uses a named shared scope and reaches other executors in the next superstep.
Common exam traps
- Treating a handoff edge as a filter on synchronized conversation content.
- Choosing
"last_agent"when the downstream role still needs the original request. - Setting
context_mode="custom"without supplying acontext_filter. - Applying Python
context_modesettings to .NET or to handoff synchronization. - Reading shared state without rendering the relevant fields into the agent input.
- Assuming the start agent reevaluates every user response without
enable_return_to_previous(False).
What to remember
Preserve enough meaning for the receiving role without transferring another role’s authority. Choose Python response-chain context explicitly, understand handoff synchronization, and render validated workflow state into the next agent input. Shared state needs consistent keys, defined writers, and language-specific visibility semantics.
Knowledge check
A Python response chain sends a reviewer’s conclusion to a writer. The writer also needs the original request and the policy evidence, which the conclusion omits, but not the reviewer’s long tool-exploration exchange. Which configuration fits?
context_mode="full" on the writer’s executorcontext_mode="last_agent" on the writer’s executorcontext_mode="custom" with a context_filter on the writer’s executorchain_only_agent_responses=True on the sequential buildercontext_filter selects, and it requires that filter. Full mode, the default, would forward the whole exchange, while last-agent mode and response-only chaining drop the original request the writer still needs.A handoff workflow contains triage, order support, and refund support. Order support must not receive conversations that involve refund support. Which design meets that requirement?
A deterministic executor validates a claim result and stores it with set_state("review_result", ...). The next agent must base its reply on the accepted decision and evidence references. What should that agent’s executor do?
In a Python handoff workflow, a refund specialist asks the customer a question. The team wants triage to evaluate every user response before it is routed again. Which change achieves that?
enable_return_to_previous(False) on the handoff builder.with_autonomous_mode() on the handoff buildercontext_mode="full" on the triage agent’s executorReferences
- Microsoft, Agent Executor
- Microsoft, Handoff orchestration
- Microsoft, Microsoft Agent Framework Workflows - State