Skip to content

Building Workflows

Entry Point

Use AgentWorkflow.Create() to start the fluent builder:

csharp
var workflow = await AgentWorkflow.Create()
    .WithName("ResearchPipeline")
    .AddAgent(...)
    .From(...).To(...)
    .BuildAsync();

Or load from a JSON file:

csharp
var workflow = await AgentWorkflow.FromJson("./pipeline.json").BuildAsync();

Note: FromJson accepts a file path string. Use AgentWorkflow.FromConfig(config) when you already have a MultiAgentWorkflowConfig object (e.g. loaded from a database or API) — see Loading from a Config Object below.


Adding Agents

Three ways to add an agent node:

csharp
.AddAgent("writer", new AgentConfig
{
    SystemInstructions = "Write clearly and concisely."
})

Agents built this way are deferred — they're built at execution time and automatically inherit the parent agent's chat client if no Provider is configured.

From a pre-built Agent instance

csharp
var myAgent = await new AgentBuilder()
    .WithToolkit<SearchTools>()
    .BuildAsync();

.AddAgent("researcher", myAgent)

Via inline AgentBuilder lambda

csharp
.AddAgent("analyzer", builder =>
{
    builder.WithToolkit<AnalysisTools>();
    builder.WithInstructions("Analyze data carefully.");
})

Defining Edges

Connect agents with .From().To():

csharp
// Linear chain
.From("researcher").To("writer")

// Fan-out: one feeds two (parallel)
.From("triage").To("researcher", "factChecker")

// Multiple sources into one
.From("researcher", "factChecker").To("writer")

Add conditions on edges — see 06.4 Routing & Edges.


Workflow Settings

csharp
AgentWorkflow.Create()
    .WithName("MyWorkflow")
    .WithMaxIterations(10)                    // For cyclic graphs
    .WithTimeout(TimeSpan.FromMinutes(5))
    .BuildAsync()

WorkflowResult

RunAsync() returns a WorkflowResult:

csharp
var result = await workflow.RunAsync("input text");

result.FinalAnswer   // string? — value of the "answer" key from the last node that produced one
result.Outputs       // Dictionary<string, object> — all node outputs, keyed as "{nodeId}.{key}"
result.Success       // bool
result.Duration      // TimeSpan
result.Error         // string? — error message if failed
result.Exception     // Exception? — underlying exception

Note: FinalAnswer is set from the "answer" output key. If nodes use WithOutputKey() to name their output differently (e.g. "report", "summary"), FinalAnswer will be null. In that case read from result.Outputs directly:

csharp
var report = result.Outputs["writer.report"] as string;

JSON Configuration

Define a workflow entirely in JSON:

json
{
  "name": "ResearchPipeline",
  "version": "1.0.0",
  "agents": {
    "researcher": {
      "agent": {
        "systemInstructions": "Research thoroughly.",
        "name": "Researcher"
      },
      "timeout": "00:00:30",
      "retry": {
        "maxAttempts": 3,
        "strategy": "Exponential"
      }
    },
    "writer": {
      "agent": {
        "systemInstructions": "Write clearly."
      },
      "outputKey": "report"
    }
  },
  "edges": [
    { "from": "researcher", "to": "writer" }
  ],
  "settings": {
    "maxIterations": 10,
    "streamingMode": "PerNode",
    "enableMetrics": true
  }
}
csharp
var workflow = await AgentWorkflow.FromJson("./pipeline.json").BuildAsync();

Settings reference

SettingTypeDefaultDescription
maxIterationsint25Max loop iterations for cyclic graphs.
streamingModePerNode | PerLayerPerNodeWhen to emit streaming events.
enableMetricsbooltrueCollect execution metrics (see 06.7 Observability).
enableCheckpointingboolfalseEnable durability checkpoints.
defaultTimeoutTimeSpan?nullDefault timeout applied to all nodes.
iterationOptionsobjectnullAdvanced loop control (see below).

Iteration options (cyclic graphs)

For cyclic workflows, you can enable change-aware iteration to stop looping early when outputs stabilise:

json
"settings": {
  "maxIterations": 10,
  "iterationOptions": {
    "useChangeAwareIteration": true,
    "enableAutoConvergence": true,
    "ignoreFieldsForChangeDetection": ["timestamp"],
    "alwaysDirtyNodes": ["monitor"]
  }
}
FieldTypeDefaultDescription
useChangeAwareIterationboolfalseHash node outputs each iteration; only re-run nodes whose inputs changed.
enableAutoConvergencebooltrueStop iterating automatically when no outputs changed between iterations.
ignoreFieldsForChangeDetectionstring[]nullOutput fields to exclude from change detection (e.g. timestamps).
alwaysDirtyNodesstring[]nullNodes that always re-execute regardless of input changes.

Mermaid Diagram

Every built workflow can export its structure:

csharp
Console.WriteLine(workflow.ToDiagram());
// graph TD
//   START --> researcher
//   researcher --> writer
//   writer --> END

Export the built workflow back to JSON (round-trip from programmatic builds):

csharp
string json = workflow.ExportConfigJson();
File.WriteAllText("./exported.json", json);

Warning: ExportConfigJson reconstructs configuration from the runtime graph. The following agents will not export a meaningful config and will fall back to an empty AgentConfig:

  • Agents added as pre-built Agent instances, unless Agent.Config is set on them.
  • Agents added via the inline builder lambda (AddAgent(id, builder => ...)) — the lambda is not serializable.

Type names for StructuredOutput<T> and UnionOutput<T> are serialized as assembly-qualified names.


Loading from a Config Object

In addition to FromJson(), you can build a workflow from a programmatic MultiAgentWorkflowConfig — useful when config is loaded from a database or API:

csharp
var config = new MultiAgentWorkflowConfig
{
    Name = "ResearchPipeline",
    Agents = { ... },
    Edges = { ... }
};

var workflow = await AgentWorkflow.FromConfig(config).BuildAsync();

Released under the MIT License.