Conditional Nodes Quickstart¶
This quick tutorial will teach you how to use conditional nodes and edges in SemanticKernel.Graph to create dynamic workflows that can make decisions and route execution based on state.
What You'll Learn¶
- Creating
ConditionalGraphNode
for if/else logic - Using
ConditionalEdge
for dynamic routing - Building workflows that adapt to different scenarios
- Template-based and function-based conditions
Concepts and Techniques¶
ConditionalGraphNode: A specialized node type that evaluates conditions and routes execution to different paths based on the result.
ConditionalEdge: Edges that connect conditional nodes to their true/false execution paths, enabling dynamic workflow routing.
Dynamic Routing: The ability of a graph to change its execution path based on runtime conditions and state values.
Template Expressions: String-based conditions that can reference state variables and be evaluated at runtime.
Prerequisites¶
- First Graph Tutorial completed
- State Quickstart completed
- Basic understanding of SemanticKernel.Graph concepts
Step 1: Basic Conditional Node¶
Simple If/Else Logic¶
using SemanticKernel.Graph.Nodes;
// Create a conditional node that checks user age
var ageCheckNode = new ConditionalGraphNode(
state => state.GetValue<int>("userAge") >= 18,
"age_check",
"AgeVerification",
"Checks if user is 18 or older"
);
Using Template-Based Conditions¶
// Alternative: Use template expression
var templateAgeCheck = new ConditionalGraphNode(
"{{userAge}} >= 18",
"template_age_check",
"TemplateAgeCheck",
"Template-based age verification"
);
Step 2: Connecting Conditional Paths¶
Add True/False Nodes¶
// Create function nodes for different paths
var adultNode = new FunctionGraphNode(
KernelFunctionFactory.CreateFromMethod(
() => "Welcome! You have access to adult content.",
"AdultAccess",
"Provides adult content access"
),
"adult_node"
);
var minorNode = new FunctionGraphNode(
KernelFunctionFactory.CreateFromMethod(
() => "Sorry, you must be 18 or older to access this content.",
"MinorAccess",
"Informs user about age restriction"
),
"minor_node"
);
// Connect conditional paths
ageCheckNode
.AddTrueNode(adultNode) // Execute if condition is true
.AddFalseNode(minorNode); // Execute if condition is false
Step 3: Building a Complete Conditional Workflow¶
Create the Graph¶
using Microsoft.SemanticKernel;
using SemanticKernel.Graph.Core;
using SemanticKernel.Graph.Extensions;
using SemanticKernel.Graph.Nodes;
// Helper extension class for KernelArguments
public static class KernelArgumentsExtensions
{
// Helper method to safely get typed values from KernelArguments
public static T GetValue<T>(this KernelArguments args, string key, T defaultValue = default!)
{
if (args.TryGetValue(key, out var value) && value is T typedValue)
{
return typedValue;
}
return defaultValue;
}
}
class Program
{
static async Task Main(string[] args)
{
var builder = Kernel.CreateBuilder();
builder.AddOpenAIChatCompletion("gpt-3.5-turbo", Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
builder.AddGraphSupport();
var kernel = builder.Build();
// Node 1: Input processing
var inputNode = new FunctionGraphNode(
KernelFunctionFactory.CreateFromMethod(
(KernelArguments args) =>
{
var age = args.GetValue<int>("userAge");
var name = args.GetValue<string>("userName");
// Add some analysis
var isVip = age > 25 && name.Length > 5;
args["isVip"] = isVip;
args["ageGroup"] = age < 18 ? "minor" : age < 65 ? "adult" : "senior";
return $"Processed user: {name}, Age: {age}, VIP: {isVip}";
},
"ProcessUser",
"Processes user input and determines characteristics"
),
"input_node"
).StoreResultAs("inputResult");
// Node 2: Age verification
var ageCheckNode = new ConditionalGraphNode(
state => state.GetValue<int>("userAge") >= 18,
"age_check",
"AgeVerification",
"Verifies user is 18 or older"
);
// Node 3: VIP status check
var vipCheckNode = new ConditionalGraphNode(
"{{isVip}} == true",
"vip_check",
"VipStatusCheck",
"Checks if user has VIP status"
);
// Node 4: Adult content access
var adultNode = new FunctionGraphNode(
KernelFunctionFactory.CreateFromMethod(
(KernelArguments args) =>
{
var name = args.GetValue<string>("userName");
var age = args.GetValue<int>("userAge");
var isVip = args.GetValue<bool>("isVip");
var access = isVip ? "Premium" : "Standard";
args["accessLevel"] = access;
args["contentType"] = "adult";
return $"Welcome {name}! You have {access} access to adult content.";
},
"AdultAccess",
"Provides adult content access"
),
"adult_node"
).StoreResultAs("adultResult");
// Node 5: VIP upgrade suggestion
var vipUpgradeNode = new FunctionGraphNode(
KernelFunctionFactory.CreateFromMethod(
(KernelArguments args) =>
{
var name = args.GetValue<string>("userName");
var age = args.GetValue<int>("userAge");
args["upgradeSuggested"] = true;
args["upgradeReason"] = "Age and activity qualify for VIP";
return $"Hello {name}! Based on your age ({age}), you might qualify for VIP status.";
},
"VipUpgrade",
"Suggests VIP upgrade for eligible users"
),
"vip_upgrade_node"
).StoreResultAs("upgradeResult");
// Node 6: Minor access restriction
var minorNode = new FunctionGraphNode(
KernelFunctionFactory.CreateFromMethod(
(KernelArguments args) =>
{
var name = args.GetValue<string>("userName");
var age = args.GetValue<int>("userAge");
args["accessLevel"] = "restricted";
args["contentType"] = "family";
args["restrictionReason"] = "Age requirement not met";
return $"Hello {name}! You're {age} years old. This content requires you to be 18 or older.";
},
"MinorAccess",
"Handles access for users under 18"
),
"minor_node"
).StoreResultAs("minorResult");
// Node 7: Final summary
var summaryNode = new FunctionGraphNode(
KernelFunctionFactory.CreateFromMethod(
(KernelArguments args) =>
{
var name = args.GetValue<string>("userName");
var age = args.GetValue<int>("userAge");
var accessLevel = args.GetValue<string>("accessLevel");
var contentType = args.GetValue<string>("contentType");
var summary = $"User: {name}, Age: {age}, Access: {accessLevel}, Content: {contentType}";
args["finalSummary"] = summary;
return summary;
},
"CreateSummary",
"Creates final summary of user access"
),
"summary_node"
).StoreResultAs("summaryResult");
// Build and configure the graph
var graph = new GraphExecutor("ConditionalWorkflowExample", "Demonstrates conditional routing based on user characteristics");
graph.AddNode(inputNode);
graph.AddNode(ageCheckNode);
graph.AddNode(vipCheckNode);
graph.AddNode(adultNode);
graph.AddNode(vipUpgradeNode);
graph.AddNode(minorNode);
graph.AddNode(summaryNode);
// Connect nodes with conditional logic
graph.Connect(inputNode.NodeId, ageCheckNode.NodeId);
// Age check paths - use ConnectWhen for conditional routing
graph.ConnectWhen(ageCheckNode.NodeId, vipCheckNode.NodeId,
args => args.GetValue<int>("userAge") >= 18);
graph.ConnectWhen(ageCheckNode.NodeId, minorNode.NodeId,
args => args.GetValue<int>("userAge") < 18);
// VIP check paths
graph.ConnectWhen(vipCheckNode.NodeId, adultNode.NodeId,
args => args.GetValue<bool>("isVip") == true);
graph.ConnectWhen(vipCheckNode.NodeId, vipUpgradeNode.NodeId,
args => args.GetValue<bool>("isVip") == false);
// Connect all paths to summary
graph.Connect(adultNode.NodeId, summaryNode.NodeId);
graph.Connect(vipUpgradeNode.NodeId, summaryNode.NodeId);
graph.Connect(minorNode.NodeId, summaryNode.NodeId);
graph.SetStartNode(inputNode.NodeId);
// Execute with different user scenarios
var scenarios = new[]
{
new { Name = "Alice", Age = 25, ExpectedPath = "VIP Adult" },
new { Name = "Bob", Age = 17, ExpectedPath = "Minor" },
new { Name = "Charlie", Age = 30, ExpectedPath = "Standard Adult" }
};
foreach (var scenario in scenarios)
{
Console.WriteLine($"\n=== Testing: {scenario.Name}, Age {scenario.Age} ===");
var initialState = new KernelArguments
{
["userName"] = scenario.Name,
["userAge"] = scenario.Age
};
var result = await graph.ExecuteAsync(kernel, initialState);
Console.WriteLine($"Path taken: {scenario.ExpectedPath}");
Console.WriteLine($"Final summary: {initialState.GetValue<string>("finalSummary")}");
Console.WriteLine($"Access level: {initialState.GetValue<string>("accessLevel")}");
}
Console.WriteLine("\n✅ Conditional workflow completed successfully!");
}
}
Step 4: Run Your Conditional Example¶
Set Up Environment Variables¶
# Windows
setx OPENAI_API_KEY "your-api-key-here"
# macOS/Linux
export OPENAI_API_KEY="your-api-key-here"
Execute the Graph¶
You should see output like:
=== Testing: Alice, Age 25 ===
Path taken: VIP Adult
Final summary: User: Alice, Age: 25, Access: Premium, Content: adult
Access level: Premium
=== Testing: Bob, Age 17 ===
Path taken: Minor
Final summary: User: Bob, Age: 17, Access: restricted, Content: family
Access level: restricted
=== Testing: Charlie, Age 30 ===
Path taken: Standard Adult
Final summary: User: Charlie, Age: 30, Access: Standard, Content: adult
Access level: Standard
✅ Conditional workflow completed successfully!
What Just Happened?¶
1. Conditional Node Creation¶
var ageCheckNode = new ConditionalGraphNode(
state => state.GetValue<int>("userAge") >= 18,
"age_check"
);
2. Conditional Edge Routing¶
graph.ConnectWhen(ageCheckNode.NodeId, vipCheckNode.NodeId,
args => args.GetValue<int>("userAge") >= 18);
ConnectWhen
to create dynamic routing based on state conditions.
3. Multiple Execution Paths¶
The graph creates different execution paths:
* Alice (25): Input → Age Check (true) → VIP Check (true) → Adult Access → Summary
* Bob (17): Input → Age Check (false) → Minor Access → Summary
* Charlie (30): Input → Age Check (true) → VIP Check (false) → VIP Upgrade → Summary
4. State-Based Decision Making¶
Each conditional node reads from the current state to make routing decisions, creating a dynamic workflow that adapts to different inputs.
Key Concepts¶
- ConditionalGraphNode: Evaluates conditions and routes execution to different paths
- ConditionalEdge: Creates guarded transitions between nodes based on state conditions
- Template Conditions: Use Handlebars-like syntax for simple conditions
- Function Conditions: Use C# functions for complex conditional logic
- Dynamic Routing: Execution flow changes based on the current state
Common Patterns¶
Simple Boolean Conditions¶
var simpleCheck = new ConditionalGraphNode(
state => state.GetValue<bool>("isEnabled"),
"simple_check"
);
Complex State Analysis¶
var complexCheck = new ConditionalGraphNode(
state =>
{
var score = state.GetValue<int>("score");
var attempts = state.GetValue<int>("attempts");
var isVip = state.GetValue<bool>("isVip");
return score > 80 && attempts < 3 || isVip;
},
"complex_check"
);
Template-Based Conditions¶
var templateCheck = new ConditionalGraphNode(
"{{score}} > 80 && {{attempts}} < 3 || {{isVip}} == true",
"template_check"
);
Multiple Conditional Paths¶
// Create a switch-like structure
var primaryCheck = new ConditionalGraphNode("{{priority}} == 'high'");
var secondaryCheck = new ConditionalGraphNode("{{priority}} == 'medium'");
graph.ConnectWhen(inputNode.NodeId, primaryCheck.NodeId,
args => args.GetValue<string>("priority") == "high");
graph.ConnectWhen(inputNode.NodeId, secondaryCheck.NodeId,
args => args.GetValue<string>("priority") == "medium");
Troubleshooting¶
Condition Always Evaluates to False¶
Solution: Check your condition logic and verify state values are as expected.Multiple Paths Executing¶
Solution: Ensure your conditions are mutually exclusive or use proper conditional edges.State Values Not Available¶
Solution: Use the helper method with a default value or check withTryGetValue()
before reading state values.
Template Condition Syntax Errors¶
Solution: Verify template syntax and ensure all referenced variables exist in state.Next Steps¶
- Conditional Nodes Tutorial: Advanced conditional patterns and best practices
- State Management: Advanced state manipulation and validation
- Switch Nodes: Multi-branch conditional logic
- Core Concepts: Understanding graphs, nodes, and execution
Concepts and Techniques¶
This tutorial introduces several key concepts:
- Conditional Routing: The ability to direct execution flow based on state conditions
- ConditionalGraphNode: A node that evaluates conditions and routes execution
- ConditionalEdge: An edge that only allows traversal when specific conditions are met
- Template Conditions: Natural language expressions for simple conditional logic
- Function Conditions: C# functions for complex conditional evaluation
- Dynamic Workflows: Graphs that adapt their execution path based on runtime state
Prerequisites and Minimum Configuration¶
To complete this tutorial, you need: * .NET 8.0+ runtime and SDK * SemanticKernel.Graph package installed * LLM Provider configured with valid API keys * Environment Variables set up for your API credentials
See Also¶
- First Graph Tutorial: Create your first graph workflow
- State Quickstart: Manage data flow between nodes
- Conditional Nodes Tutorial: Advanced conditional patterns
- Core Concepts: Understanding graphs, nodes, and execution
- API Reference: Complete node API documentation
Reference APIs¶
- ConditionalGraphNode: Conditional routing node
- ConditionalEdge: Conditional connection edge
- IGraphNode: Base node interface
- GraphExecutor: Graph execution engine