Execution Model
Multi-agent workflows execute named agent nodes through explicit routes. The runtime decides which nodes are ready, runs ready nodes by layer, records node outputs, evaluates routes, and emits workflow events.
Nodes
A node is a named HPD agent plus node options.
.AddAgent("research", researchConfig, node => node
.WithOutputKey("research")
.WithTimeout(TimeSpan.FromSeconds(45)))The node id is the workflow identity for routing and events. The agent name is the child agent identity used inside child-agent events.
A node can be backed by:
- an
AgentConfig - a prebuilt
Agent - an inline
AgentBuilderconfiguration
Config-backed and inline-built agents are created when the workflow executes. If a child agent does not configure its own provider, it can inherit the parent chat client passed to ExecuteStreamingAsync(...).
Boundary Nodes
Workflows have implicit START and END boundaries.
If a node has no declared incoming edge, it is an entry node. If a node has no declared outgoing edge, it is an exit node.
START -> entry nodes
exit nodes -> ENDYou can still declare START and END explicitly when the entry or exit should be visible in the workflow definition:
.From("START").To("classify")
.From("final").To("END")Edges
Edges decide what can run after a node completes.
.From("draft").To("review")An edge can route to one node or several nodes:
.From("research").To("summarize", "fact_check")Several source nodes can also route to the same target:
.From("summarize", "fact_check").To("write")Use explicit edges whenever order matters. Agent insertion order is not execution order.
Layers
Nodes that are ready at the same time can run in the same layer. A workflow that fans out from one node to two downstream nodes can produce layer events like this:
Layer 0: research
Layer 1: summarize, fact_check
Layer 2: writeSubscribe to WorkflowLayerStartedEvent and WorkflowLayerCompletedEvent when the UI needs to show parallel progress. Subscribe to WorkflowAgentStartedEvent and WorkflowAgentCompletedEvent when the UI needs per-node status.
Inputs And Outputs
Each completed node writes an output dictionary. The most common shape is one string response stored under the node's output key:
.AddAgent("draft", draftConfig, node => node.WithOutputKey("draft"))
.AddAgent("review", reviewConfig, node => node.WithInputKey("draft"))Input resolution is documented in Data Flow Between Nodes. The important rule is to name the contract between nodes. Do not rely on downstream agents magically seeing the right upstream text.
Conditions
Route conditions read the source node's outputs.
.From("classify").To("billing").WhenEquals("intent", "billing")
.From("classify").To("technical").WhenEquals("intent", "technical")Declarative conditions can be exported and loaded from config:
using static HPD.MultiAgent.Routing.Condition;
.From("triage").To("vip_billing").When(And(
Equals("intent", "billing"),
Equals("tier", "VIP")))Predicate conditions are in-process only:
.From("classify").To("review").When(ctx =>
ctx.Outputs.TryGetValue("needs_review", out var value) &&
value is true)Use declarative conditions when a workflow must round-trip through JSON config. Use predicates when the workflow only runs from code and needs custom logic.
Handoffs
A router node can choose a downstream node by calling a generated handoff tool.
AgentWorkflow.Create()
.AddRouterAgent("router", routerConfig)
.WithHandoff("math", "Use for calculation-heavy requests.")
.WithHandoff("research", "Use for research-heavy requests.")
.AddAgent("math", mathConfig)
.AddAgent("research", researchConfig)
.BuildAsync();At runtime, HPD gives the router tools such as handoff_to_math and handoff_to_research. The selected target is written to handoff_target, and route conditions use that output.
Errors And Skips
Node options include error-mode helpers:
.AddAgent("review", reviewConfig, node => node.OnErrorSkip())Workflow events expose the public outcome:
WorkflowAgentCompletedEvent.SuccessWorkflowAgentCompletedEvent.ErrorMessageWorkflowAgentSkippedEventWorkflowCompletedEvent.SuccessfulNodesWorkflowCompletedEvent.FailedNodesWorkflowCompletedEvent.SkippedNodes
Validate error policy in the workflow shape you are using before treating an error mode as a product contract.
Loops And Limits
Cyclic workflows should set an iteration limit:
.WithMaxIterations(5)Use loops for bounded refinement, validation, or retry-style workflows where the route condition eventually stops traversing the loop.
Checkpointing
Checkpointing is opt-in:
.WithCheckpointing()
.WithJsonWorkflowStore("App_Data/workflows")Checkpoint storage is a workflow execution feature. It does not add a separate public checkpoint event family to the multi-agent event stream.
See Checkpointing.