Skip to content

How to build a graph

This guide explains the fundamental steps for creating and configuring graphs in SemanticKernel.Graph. You'll learn how to define nodes, connect them with conditional edges, and execute the resulting workflow.

Overview

Building a graph involves several key steps:

  1. Create and configure a Kernel with the necessary plugins and functions
  2. Define nodes (functions, conditionals, loops) that represent your workflow steps
  3. Connect nodes with conditional edges that control execution flow
  4. Execute with GraphExecutor to run the complete workflow

Step-by-Step Process

1. Create and Configure Kernel

Start by creating a Semantic Kernel instance with graph support enabled. The examples in this repository use a minimal kernel configured for graph scenarios.

using Microsoft.SemanticKernel;
using SemanticKernel.Graph.Core;
using SemanticKernel.Graph.Nodes;

// Create a kernel and enable graph support for examples
var kernel = Kernel.CreateBuilder()
    .AddGraphSupport()
    .Build();

2. Define Graph Nodes

Create nodes that represent different steps in your workflow. For small runnable documentation snippets we provide lightweight kernel functions wrapped into FunctionGraphNode instances:

// Create lightweight kernel functions for demo purposes
var fnA = KernelFunctionFactory.CreateFromMethod((KernelArguments args) =>
{
    // Returns a simple greeting message
    return "Hello from A";
}, "FnA");

var fnB = KernelFunctionFactory.CreateFromMethod((KernelArguments args) =>
{
    // Echoes previous message from the graph state
    var prev = args.ContainsName("message") ? args["message"]?.ToString() : string.Empty;
    return $"B received: {prev}";
}, "FnB");

// Wrap functions into graph nodes
var nodeA = new FunctionGraphNode(fnA, "nodeA", "Start node A");
var nodeB = new FunctionGraphNode(fnB, "nodeB", "Receiver node B");

3. Connect Nodes with Edges

Define the flow between nodes using the GraphExecutor API. For the minimal example above we connect nodeA to nodeB and set the start node:

var graph = new GraphExecutor("ExampleGraph", "A tiny demo graph for docs");

// Add nodes to the graph
graph.AddNode(nodeA).AddNode(nodeB);

// Connect A -> B and set start node
graph.Connect("nodeA", "nodeB");
graph.SetStartNode("nodeA");

4. Execute the Graph

Run the complete workflow using ExecuteAsync. The example uses KernelArguments as initial graph state and prints the final result.

// Prepare initial kernel arguments / graph state
var args = new KernelArguments();
args["message"] = "Initial message";

// Execute graph
var result = await graph.ExecuteAsync(kernel, args, CancellationToken.None);

Console.WriteLine("Graph execution completed.");
Console.WriteLine($"Final result: {result.GetValue<string>()}");

Alternative Builder Pattern

For simpler graphs, you can use the fluent builder pattern:

var graph = GraphBuilder.Create()
    .AddFunctionNode("plan", kernel, "Planner", "Plan")
    .When(state => state.GetString("needs_analysis") == "yes")
        .AddFunctionNode("analyze", kernel, "Analyzer", "Analyze")
    .AddFunctionNode("act", kernel, "Executor", "Act")
    .Build();

Advanced Patterns

Conditional Execution

Use conditional edges to create complex branching logic:

graph.AddConditionalEdge("start", "branch_a", 
    condition: state => state.GetInt("priority") > 5)
.AddConditionalEdge("start", "branch_b", 
    condition: state => state.GetInt("priority") <= 5);

Loop Control

Implement loops with iteration limits:

var loopNode = new WhileGraphNode(
    condition: state => state.GetInt("attempt") < 3,
    maxIterations: 5,
    nodeId: "retry_loop"
);

graph.AddNode(loopNode)
     .AddEdge("start", "retry_loop")
     .AddEdge("retry_loop", "process");

Error Handling

Add error handling nodes to your workflow:

var errorHandler = new ErrorHandlerGraphNode(
    errorTypes: new[] { ErrorType.Transient, ErrorType.External },
    recoveryAction: RecoveryAction.Retry,
    maxRetries: 3,
    nodeId: "error_handler"
);

graph.AddNode(errorHandler)
     .AddEdge("process", "error_handler")
     .AddEdge("error_handler", "fallback");

Best Practices

Node Design

  1. Single Responsibility: Each node should have one clear purpose
  2. Meaningful Names: Use descriptive node IDs that explain their function
  3. State Management: Design nodes to work with the graph state effectively
  4. Error Handling: Include error handling nodes for robust workflows

Graph Structure

  1. Logical Flow: Organize nodes in a logical sequence
  2. Conditional Logic: Use conditional edges for dynamic routing
  3. Loop Prevention: Set appropriate iteration limits to prevent infinite loops
  4. Start Node: Always define a clear starting point

Performance Considerations

  1. Node Efficiency: Optimize individual node performance
  2. State Size: Keep graph state manageable for memory efficiency
  3. Parallel Execution: Use parallel nodes where possible for better performance
  4. Caching: Implement caching for expensive operations

Troubleshooting

Common Issues

Graph not executing: Ensure you've set a start node with SetStartNode()

Nodes not connected: Verify all edges are properly defined with AddEdge() or AddConditionalEdge()

Infinite loops: Check loop conditions and set appropriate maxIterations

State not persisting: Use GraphState for persistent state across nodes

Debugging Tips

  1. Enable logging to trace execution flow
  2. Use breakpoints in conditional logic
  3. Inspect state at each node execution
  4. Validate graph integrity before execution

Concepts and Techniques

GraphExecutor: The main class responsible for executing graph workflows. It manages node execution order, state transitions, and error handling throughout the workflow lifecycle.

FunctionGraphNode: A graph node that wraps and executes Semantic Kernel functions. It handles input/output mapping between the graph state and the underlying kernel function.

ConditionalGraphNode: A node that evaluates predicates to determine execution flow. It enables dynamic routing based on the current state of the graph.

ConditionalEdge: A connection between nodes that includes a condition for execution. It allows for complex branching logic and dynamic workflow paths.

GraphState: A wrapper around KernelArguments that provides additional metadata, execution history, and validation capabilities for graph workflows.

See Also