Skip to lesson

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

Resume approved work from durable state

Completed by the knowledge check

Learning objective

Implement durable human approvals, overrides, timeouts, checkpoints, and resumption.

Core idea

A human approval is a decision about a particular proposed action, not permission to continue any execution that happens to share its conversation. Persist the proposal, the pending decision, and the execution position together. Then validate a response against that proposal before releasing the consequential action.

A process can stop while a reviewer considers the request. An approval held only in memory disappears with that process; a saved chat transcript does not reconstruct the workflow’s pending request. Microsoft Agent Framework checkpoint storage and its Durable Extension address execution persistence through different mechanisms. Choose the one that matches your hosting and wait requirements.

See 1.1.6 for deciding where human review belongs and 1.1.9 for human accountability. This lesson implements a gate that survives interruption and resumes only the work the reviewer actually authorized.

How it works

Make approval an explicit request

In Agent Framework’s Python workflow request/response mechanism, an executor calls ctx.request_info(). The workflow emits an event with type == "request_info", including request_id and the request data. Return the decision through workflow.run(..., responses=...), using the request ID as the response-map key. The framework routes the response to the executor’s @response_handler.

In .NET, a RequestPort defines a typed request/response channel and emits a RequestInfoEvent. The streaming handle’s SendResponseAsync(request.CreateResponse(...)) supplies the answer. These are language-specific interfaces; do not copy the Python event shape into .NET code.

Define an application decision contract containing the proposal ID, proposal version, decision, reviewer identity, and any permitted correction. These fields are application design, not built-in approval fields. Validate them in the response handler. A model-generated statement that a manager approved the action is not an authenticated response to the pending request.

Persist pending requests with execution state

Supply checkpoint_storage when constructing a Python graph workflow with WorkflowBuilder. Agent Framework ships three built-in checkpoint storage implementations: InMemoryCheckpointStorage for in-process state, FileCheckpointStorage for local-disk persistence, and CosmosCheckpointStorage from agent-framework-azure-cosmos for durable cross-process storage. File storage can survive a process restart on the same retained disk; it is not a shared production store across independent machines.

Graph workflow checkpoints capture executor state, pending messages, and pending requests and responses at superstep boundaries. Save the checkpoint identifier and associate it with the business operation and tenant. A checkpoint is a snapshot of execution, not proof that an external transaction committed.

Resume Python execution with workflow.run(checkpoint_id=..., responses=..., stream=True). Pending requests restored from a checkpoint are re-emitted, so your approval interface must recognize the same request rather than create a second independent business proposal. In .NET, the checkpoint page documents RestoreCheckpointAsync(savedCheckpoint) for restoring the same run. Keep storage access restricted because checkpoints can contain sensitive inputs and pending approvals.

Use a durable wait for long deadlines

The Durable Extension runs agent orchestration and graph workflows on Durable Task infrastructure, with Durable Task Scheduler as the recommended backend. It supports Azure Functions hosting and self-hosted C# and Python applications. The Durable Extension packages for Python and .NET install as prerelease packages (--pre, --prerelease). Persisted external-event waits survive worker restarts; they are different from keeping a coroutine blocked in one process.

For Python Durable Functions orchestration, use context.wait_for_external_event("ApprovalDecision"), create a competing durable timer with context.create_timer(...), and select the winner through context.task_any(...). Calculate its deadline from context.current_utc_datetime, as the documented example does. Cancel the timer if approval wins. If the timer wins, route to an escalation or rejection activity instead of silently treating timeout as approval.

The example’s 24-hour deadline is a business choice, not a service-wide approval limit. In C#, the example uses WaitForExternalEvent<HumanApprovalResponse> with eventName: "ApprovalDecision" and an explicit timeout. The reviewer-facing service delivers the decision through RaiseEventAsync(instanceId, "ApprovalDecision", ...); Python uses client.raise_event(...). The approval event and the durable timer compete, and only the winner moves the orchestration on.

The approval event and the durable timer compete, and only the winner moves the orchestration onThe reviewer-facing service raises the ApprovalDecision event, which the orchestration awaits as a durable external event. A competing durable timer runs against the deadline. task_any selects the winner: if approval wins, the orchestration cancels the timer; if the timer wins, it routes to an escalation or rejection activity instead of treating the timeout as approval.ApprovalDecisionapproval winstimer winsReviewer-facingserviceWait for theexternal eventCompeting durabletimertask_any selectsthe winnerCancel the timerEscalation orrejection activity

Keep overrides and resumption accountable

An override changes a validated business decision. Implement it as an authorized application response or a separate reviewed proposal, rather than editing checkpoint bytes. Bind any corrected amount or destination to a revised proposal version. If the correction expands authority, require another gate before execution.

A timeout and a late approval can race. Record a terminal decision in the application’s operation record and reject responses for expired or superseded proposals before raising an external event or supplying workflow responses. The workflow engine transports the response; your approval endpoint determines whether that reviewer can make that decision.

For external writes, use an operation identifier and destination-side deduplication where the destination supports it. Restoring execution state cannot undo a payment already accepted by another service. Keep the durable decision separate from the action’s execution receipt so recovery can determine whether to continue, query the destination, or escalate.

Key distinctions and trade-offs

An in-process pause is sufficient for short interactions only when losing that wait is acceptable. Persistent graph checkpoints support restoring execution, but you own the approval endpoint, scheduling, and storage lifecycle. The Durable Extension adds persisted event and timer orchestration when waits and recovery must span worker executions.

Approval, override, and timeout also have different meanings. Approval releases the exact validated proposal. An override changes the decision or proposal under explicit authority. Timeout ends or escalates the wait. None implies that an external side effect completed successfully.

Choose storage from deployment needs, not the presence of the word “checkpoint”. InMemoryCheckpointStorage offers no restart durability; retained local files do not solve distributed-worker access. An approval service also needs authorization independently of the workflow’s ability to accept a response.

Architecture / decision scenario

A procurement workflow proposes a purchase and needs finance approval within a business-defined 24 hours. Workers can restart, reviewers can correct quantities, and the purchasing API might accept a request before its response reaches the workflow.

Persist a versioned proposal and pending approval. Use the Durable Extension’s ApprovalDecision external event and a durable timer for the long wait. The reviewer endpoint authenticates finance authority and checks the proposal version before raising the event. A corrected quantity creates a revised proposal; it does not reuse approval for the original amount.

If the deadline wins, an escalation activity records expiry. A late approval receives an expired-proposal outcome. If approval wins, cancel the timer and submit the purchase with the application’s operation identifier. On restart, use durable progress and the purchasing receipt to determine whether the action already completed. For a graph workflow using standard checkpoints instead, restore the saved checkpoint and respond to its pending request ID; do not restart the purchase from its first prompt.

This design covers the decision, the wait, and the external effect separately. The design prevents a resume from releasing a changed order.

Common exam traps

  • Selecting InMemoryCheckpointStorage for approvals that must survive process loss.
  • Treating a saved conversation as a saved pending workflow request.
  • Turning timeout into implicit approval or accepting a response for a superseded proposal.
  • Editing checkpoint state to implement an override without an authorized decision record.
  • Assuming checkpoint resumption proves an external write happened exactly once.

What to remember

Use request IDs to correlate workflow approvals, persistent checkpoint storage to recover graph execution, and durable external events and timers for long-lived waits. Resume the validated proposal, enforce overrides in application policy, and reconcile external effects independently of execution snapshots.

Knowledge check

References