Skip to lesson

NVIDIANVIDIA-Certified Professional: Agentic AI2026-101.5 Orchestrate multi-agent workflows and coordination.

Coordinate specialist execution through explicit outcomes

Completed by the knowledge check

Learning objective

Coordinate specialist agents: handoffs, shared state, cancellation, and failure propagation.

Core idea

Execution semantics define what happens when specialists overlap, fail, or stop. Distinguish control transfer, state updates, cancellation, and failure reporting.

For a production workflow, decide which results are required before synthesis. NeMo Agent Toolkit’s parallel executor can include branch failures as text alongside successful outputs. That is useful for partial analysis, but a downstream model can otherwise turn an incomplete set of results into a confident answer. The coordinator must preserve the difference between a completed assessment and an unavailable assessment.

How it works

Transfer control with the required context

In LangChain’s handoffs pattern, a tool updates a routing field such as active_agent. With separate agent subgraphs, Command combines goto for the destination, update for state changes, and graph=Command.PARENT for navigation through the parent graph. The next specialist receives the context selected by that transition.

A message-based handoff must preserve a valid tool exchange. The documented subgraph example passes both the AIMessage containing the handoff tool call and a corresponding ToolMessage; its tool_call_id matches the call. Passing the tool call without its response can produce invalid history and downstream errors. The example assumes no parallel tool calls, so do not copy its two-message shortcut into a transition with several outstanding calls without accounting for them.

Choose the context deliberately. Send the task, relevant findings, and unresolved questions rather than every specialist’s internal conversation. The handoffs documentation warns that forwarding full histories can confuse recipients and inflate tokens. Tenant memory retention belongs to 1.4.2.

Merge state through defined update rules

LangGraph defines shared execution state through a schema and per-key reducers. Nodes return partial updates. With the default reducer, an update replaces the previous value; with a custom reducer such as operator.add, updates can accumulate a list. The add_messages reducer appends messages and updates existing messages by ID.

Choose merge semantics for what the field represents. Accumulate specialist findings, but give a routing field one decision owner. Appending incompatible eligibility decisions does not resolve them; a coordinator node must choose the authoritative outcome.

When a subgraph updates a parent-shared key through Command.PARENT, the graph documentation requires a reducer for that key in the parent state. Also avoid mixing Command(goto=...) with a static outgoing edge from the same node: the static edge still runs, potentially starting another specialist unintentionally. See 1.6.3 for graph design and 5.4.2 for durable checkpoints; this lesson concerns the exchange between specialists within an execution.

Make partial failure explicit

NeMo Agent Toolkit’s workflow._type: parallel_executor fans out the same input to the functions in tool_list. It runs those branches concurrently and appends their outputs as separate text blocks in configured order. It requires the nvidia-nat[langchain] plugin.

Its return_error_on_exception setting controls the returned failure representation. With true, the executor captures branch exceptions and appends ERROR: blocks. With false, the documented default, it raises the first branch exception. Set detailed_logs: true when branch-start, completion, and fan-in logs are needed to diagnose which specialist failed.

Use the text-error mode when partial results are useful and the next stage explicitly handles missing assessments. For a required specialist, a completed fan-in containing an ERROR: block is insufficient to authorize the final action. In a LangGraph coordinator, use add_conditional_edges after the fan-in node with a routing function that checks required specialists’ blocks for ERROR:. Route missing required assessments to an incomplete-outcome node and complete assessments to synthesis. The routing function implements business eligibility; return_error_on_exception chooses the failure representation.

A fan-in that contains an ERROR: block for a required specialist routes to the incomplete-outcome node, not to synthesis.

A fan-in containing an ERROR block for a required specialist routes to the incomplete-outcome node, not to synthesisStarts at the parallel executor, which appends successful outputs and, on the dotted failure path, ERROR blocks. Both reach the fan-in node. The routing function after the fan-in sends missing required assessments to the incomplete-outcome node and complete assessments to synthesis. An ERROR block for a required specialist never reaches synthesis as a completed assessment.missing requiredcompleteparallel executorsuccessful outputsERROR: blocksfan-in noderouting functionincomplete-outcomenodesynthesis

A ReAct supervisor also needs an explicit specialist-error policy. Its pass_tool_call_errors_to_agent defaults to true, turning a specialist tool-call exception into an observation the supervisor can respond to; false raises the exception to the caller. A specialist configured with raise_on_parsing_failure: false returns parsing-error text as its answer, so its supervisor can receive an ordinary tool result that still represents failure. With the default true, exhausted parsing retries raise ReActAgentParsingFailedError. See 1.2.2 for these loop controls; here the decision is whether a required specialist’s failure reaches the coordinator as an exception or as text that needs checking.

Give cancellation a runtime owner

For custom Python coordination, asyncio.TaskGroup provides structured concurrency: when a child raises an exception other than CancelledError, it cancels the remaining tasks, waits for them, and raises grouped failures through ExceptionGroup or BaseExceptionGroup. In contrast, asyncio.gather() with its default return_exceptions=False propagates the first exception while other awaitables continue running.

This distinction matters when wrapping specialist calls. Receiving an exception does not by itself prove that siblings stopped. Use TaskGroup when all branches form one required computation and sibling failure should end the others. For independent optional analyses, collecting individual outcomes can preserve useful work instead.

Cancellation is cooperative. Task.cancel() requests cancellation; CancelledError is raised at the next opportunity. Coroutines should use try/finally for cleanup and normally propagate CancelledError after cleanup. Swallowing it can break structured-concurrency behavior. An asyncio.timeout() scope uses cancellation internally and exposes TimeoutError outside the scope, providing a concrete deadline scope for a custom coordinator. Cleanup can extend observed completion beyond that deadline; cancellation is not an instantaneous stop.

These are Python runtime semantics, separate from the parallel executor’s return_error_on_exception switch. Test the actual wrapper’s cancellation path. User-facing overrides and cancellation acknowledgments are covered in 10.4.2.

Key distinctions and trade-offs

The lifecycle decisions have different mechanisms and consequences:

DecisionMechanismWhat it establishesWhat still needs a decision
HandoffCommand(goto, update, graph=Command.PARENT)Destination and transferred stateWhether the recipient has sufficient context
Shared updateState schema and per-key reducerHow findings combineWho resolves conflicting findings
Supervisor tool failurepass_tool_call_errors_to_agentError observation or raised exceptionWhether the required specialist actually succeeded
Partial failurereturn_error_on_exception: trueSuccessful outputs plus ERROR: blocksWhether missing results permit a final answer
Fail-fast executionPython asyncio.TaskGroupSibling cancellation after a child failureCleanup and interpretation of grouped errors
DeadlinePython asyncio.timeout()Cancellation-backed time scopeWhich incomplete outcome the caller receives

Fail-fast reduces wasted local work when every branch is mandatory. Partial-result collection preserves availability when some branches are optional. Neither choice repairs an invalid tool history or an ambiguous state merge.

Architecture / decision scenario

A manufacturer’s procurement assistant compares a supplier’s price and mandatory export-eligibility assessment. An optional delivery-risk specialist adds context. The application deadline is eight seconds. In a hypothetical incident, price would take five seconds, export eligibility fails after three, and delivery analysis would take seven.

The tempting design is NVIDIA’s own composition: a sequential_executor with tool_list: [parallel_analysis, final_synthesis_agent], where parallel_analysis is a parallel_executor with return_error_on_exception: true, so the concatenated text, ERROR: blocks included, goes straight to synthesis. The procurement workflow owner rejects it: a price and an ERROR: block do not establish export eligibility, and sequential_executor documents no branch. The replacement coordinator is a LangGraph graph whose fan-in node collects the three results and whose add_conditional_edges routing function sends the missing required assessment to the incomplete-outcome node, preventing a purchase recommendation.

For a custom Python implementation, put the required price and eligibility calls in a TaskGroup within asyncio.timeout(8). At three seconds, eligibility failure cancels the still-running price task; cleanup runs in finally, and grouped errors reach the coordinator. With default gather(), the price task instead continues toward its five-second completion. The coordinator collects optional delivery analysis separately within the same eight-second deadline: it attaches the result if it completes and reports it as unavailable otherwise. Delivery context cannot turn the failed eligibility assessment into permission to recommend a purchase.

If a sourcing specialist hands control to an export specialist, use the declared Command destination and carry a valid tool-call/response pair. Store their findings through the parent state’s reducers, with export eligibility owned by its designated specialist. Safe completion requires correct handoff context, unambiguous state, observed cancellation, and visible failure.

Common exam traps

  • Treating an ERROR: text block as successful completion of a required assessment.
  • Assuming an exception raised to the caller automatically cancels every sibling task.
  • Suppressing CancelledError and expecting structured cancellation to behave normally.
  • Sending a handoff tool call without its matching ToolMessage.
  • Adding a static edge beside Command(goto=...) and expecting only one destination to run.

What to remember

Make control transfer, state merging, stopping, and error reporting explicit. Choose partial-result or fail-fast semantics according to which assessments the business requires. The next specialist needs valid context, and the final coordinator needs outcomes that preserve missing work rather than disguising it as a completed answer.

Knowledge check

References