MultiAgent
Expose a full multi-agent workflow as a single tool inside a parent agent using the [MultiAgent] attribute. The parent agent invokes the whole workflow like any other tool call — it just describes the task and the workflow handles the rest.
┌─────────────────────────────┐
│ Parent Agent │
│ "Research quantum computing"│
└──────────────┬──────────────┘
│ invokes ResearchPipeline (tool call)
▼
┌─────────────────────────────┐
│ Workflow │
│ ┌──────────────────────┐ │
│ │ researcher │ │
│ └──────────┬───────────┘ │
│ │ │
│ ┌──────────▼───────────┐ │
│ │ writer │ │
│ └──────────────────────┘ │
└──────────────┬──────────────┘
│ final result
▼
┌─────────────────────────────┐
│ Parent Agent │
│ "Here's what I found..." │
└─────────────────────────────┘Basic Usage
public class ResearchToolkit
{
[MultiAgent("Run a full research and writing pipeline")]
public async Task<AgentWorkflowInstance> ResearchPipeline()
{
return await AgentWorkflow.Create()
.AddAgent("researcher", new AgentConfig
{
SystemInstructions = "Research the topic thoroughly."
})
.AddAgent("writer", new AgentConfig
{
SystemInstructions = "Write a clear answer based on the research."
})
.From("researcher").To("writer")
.BuildAsync();
}
}
var agent = await new AgentBuilder()
.WithToolkit<ResearchToolkit>()
.BuildAsync();The parent agent sees ResearchPipeline as a callable tool. When invoked, the full workflow runs and all its events bubble up into the parent's RunAsync stream.
[MultiAgent] Attribute
| Property | Type | Default | Description |
|---|---|---|---|
Description | string? | Method name | Description shown to the LLM — be specific so the agent knows when to use it |
Name | string? | Method name | Custom tool name |
StreamEvents | bool | true | Stream inner workflow events into the parent's event stream |
TimeoutSeconds | int | 300 | Workflow execution timeout |
[MultiAgent(
"Analyze documents and produce a structured report",
Name = "DocumentAnalysis",
TimeoutSeconds = 600
)]
public async Task<AgentWorkflowInstance> AnalysisPipeline() { ... }Provider Inheritance
Agents inside the workflow that don't have their own Provider configured automatically inherit the parent agent's chat client — no extra configuration needed:
// These agents use whatever model the parent uses
.AddAgent("researcher", new AgentConfig
{
SystemInstructions = "..."
// No Provider — inherits from parent at runtime
})Event Bubbling
All workflow events and each node's agent events flow into the same RunAsync loop — no separate stream. Use ExecutionContext on AgentEvent to identify which agent emitted what:
await foreach (var evt in agent.RunAsync("Research quantum computing", sessionId: sessionId))
{
switch (evt)
{
// Parent agent's own response (Depth == 0)
case TextDeltaEvent delta when delta.ExecutionContext?.Depth == 0:
Console.Write(delta.Text);
break;
// Workflow lifecycle events — no ExecutionContext, match directly
case WorkflowNodeStartedEvent node:
Console.WriteLine($"\n[{node.NodeId} started]");
break;
case WorkflowNodeCompletedEvent node:
Console.WriteLine($"[{node.NodeId} done — {node.Duration.TotalSeconds:F1}s]");
break;
// Text from a specific workflow node
case TextDeltaEvent delta when delta.ExecutionContext?.AgentName == "writer":
Console.Write(delta.Text);
break;
}
}Set StreamEvents = false to suppress all workflow events — only the final tool result will be visible to the parent:
[MultiAgent("Run research pipeline", StreamEvents = false)]
public async Task<AgentWorkflowInstance> ResearchPipeline() { ... }MultiAgent vs SubAgent
Both delegate work to child agents, but they serve different purposes:
[SubAgent] | [MultiAgent] | |
|---|---|---|
| Structure | Single autonomous agent | Graph of specialized agents |
| Routing | Agent decides its own path | Declarative edges and conditions |
| Use when | One expert, unpredictable path | Distinct stages, parallel work, conditional routing |
Use [SubAgent] when one agent can handle the whole task autonomously. Use [MultiAgent] when the task benefits from distinct specialized stages or parallel execution.
Typed Metadata
For compile-time validation with conditional registration:
public class WorkflowMetadata : IToolMetadata
{
public bool HasResearchCapability { get; set; }
}
public class PipelineToolkit
{
[MultiAgent<WorkflowMetadata>("Full research pipeline")]
[ConditionalFunction("HasResearchCapability")]
public async Task<AgentWorkflowInstance> ResearchPipeline() { ... }
}
var agent = await new AgentBuilder()
.WithToolkit<PipelineToolkit>(new WorkflowMetadata { HasResearchCapability = true })
.BuildAsync();→ See 02.1.4 Tool Dynamic Metadata for conditional registration details.
Next Steps
- 06.5 As a Toolkit Capability — full reference including event filtering details
- 06.6 Workflow Events — complete workflow event reference
- 06.2 Building Workflows — how to build the workflow itself