Conditional Nodes Example¶
This example demonstrates conditional routing and decision-making in graph-based workflows using ConditionalGraphNode
and ConditionalEdge
. It shows how to implement dynamic execution paths based on state conditions and user input.
Objective¶
Learn how to implement conditional logic in graph-based workflows to: * Route execution based on dynamic conditions * Implement if/else branching logic * Use conditional expressions for complex routing * Handle multiple execution paths efficiently * Integrate conditional nodes with other graph patterns
Prerequisites¶
- .NET 8.0 or later
- OpenAI API Key configured in
appsettings.json
- Semantic Kernel Graph package installed
- Basic understanding of Graph Concepts and Node Types
Key Components¶
Concepts and Techniques¶
- Conditional Routing: Dynamic execution path selection based on state conditions
- Conditional Expressions: Boolean expressions that determine execution flow
- Branching Logic: Multiple execution paths with different outcomes
- State Evaluation: Runtime evaluation of graph state for decision making
Core Classes¶
ConditionalGraphNode
: Node that evaluates conditions and routes executionConditionalEdge
: Edge that connects nodes based on conditional logicConditionalExpressionEvaluator
: Evaluates boolean expressions for routingGraphState
: Carries state information used in conditional evaluation
Running the Example¶
Getting Started¶
This example demonstrates conditional routing and decision-making with the Semantic Kernel Graph package. The code snippets below show you how to implement this pattern in your own applications.
Step-by-Step Implementation¶
1. Basic Conditional Routing¶
This example demonstrates simple if/else branching based on input values.
// Create kernel with mock configuration
var kernel = CreateKernel();
// Create conditional node for basic routing
var conditionalNode = new ConditionalGraphNode(
"BasicConditional",
"Basic conditional routing example",
logger)
{
ConditionExpression = "input_value > 10",
TrueNodeId = "high-value-processor",
FalseNodeId = "low-value-processor"
};
// Create processing nodes
var highValueProcessor = new FunctionGraphNode(
"high-value-processor",
"Process high value inputs",
async (context) =>
{
var inputValue = context.GetValue<int>("input_value");
var result = inputValue * 2;
context.SetValue("processed_result", result);
context.SetValue("processing_type", "high-value");
return result;
});
var lowValueProcessor = new FunctionGraphNode(
"low-value-processor",
"Process low value inputs",
async (context) =>
{
var inputValue = context.GetValue<int>("input_value");
var result = inputValue + 5;
context.SetValue("processed_result", result);
context.SetValue("processing_type", "low-value");
return result;
});
// Create executor and add nodes
var executor = new GraphExecutor("ConditionalExample", "Basic conditional routing", logger);
executor.AddNode(conditionalNode);
executor.AddNode(highValueProcessor);
executor.AddNode(lowValueProcessor);
// Set start node
executor.SetStartNode(conditionalNode.NodeId);
// Test with different input values
var testValues = new[] { 5, 15, 8, 20 };
foreach (var testValue in testValues)
{
var arguments = new KernelArguments
{
["input_value"] = testValue
};
Console.WriteLine($"๐งช Testing with input value: {testValue}");
var result = await executor.ExecuteAsync(kernel, arguments);
var processedResult = result.GetValue<int>("processed_result");
var processingType = result.GetValue<string>("processing_type");
Console.WriteLine($" Result: {processedResult} (Type: {processingType})");
}
2. Complex Conditional Logic¶
Demonstrates advanced conditional expressions with multiple conditions.
// Create complex conditional node
var complexConditional = new ConditionalGraphNode(
"ComplexConditional",
"Complex conditional logic example",
logger)
{
ConditionExpression = "(user_age >= 18) && (user_income > 50000) && (credit_score >= 700)",
TrueNodeId = "approve-loan",
FalseNodeId = "review-application"
};
// Create loan approval nodes
var approveLoan = new FunctionGraphNode(
"approve-loan",
"Approve loan application",
async (context) =>
{
var userName = context.GetValue<string>("user_name");
var loanAmount = context.GetValue<decimal>("loan_amount");
context.SetValue("loan_status", "approved");
context.SetValue("approval_reason", "All criteria met");
context.SetValue("interest_rate", 0.045m);
return $"Loan approved for {userName}: ${loanAmount:N2} at 4.5% interest";
});
var reviewApplication = new FunctionGraphNode(
"review-application",
"Review loan application",
async (context) =>
{
var userName = context.GetValue<string>("user_name");
var userAge = context.GetValue<int>("user_age");
var userIncome = context.GetValue<decimal>("user_income");
var creditScore = context.GetValue<int>("credit_score");
var reasons = new List<string>();
if (userAge < 18) reasons.Add("Underage");
if (userIncome <= 50000) reasons.Add("Insufficient income");
if (creditScore < 700) reasons.Add("Low credit score");
context.SetValue("loan_status", "under_review");
context.SetValue("review_reasons", reasons);
context.SetValue("next_steps", "Manual review required");
return $"Application under review for {userName}. Reasons: {string.Join(", ", reasons)}";
});
// Add nodes to executor
executor.AddNode(complexConditional);
executor.AddNode(approveLoan);
executor.AddNode(reviewApplication);
// Test complex conditions
var testApplications = new[]
{
new { Name = "Alice", Age = 25, Income = 75000m, CreditScore = 750, LoanAmount = 50000m },
new { Name = "Bob", Age = 17, Income = 60000m, CreditScore = 720, LoanAmount = 30000m },
new { Name = "Carol", Age = 30, Income = 45000m, CreditScore = 800, LoanAmount = 40000m },
new { Name = "David", Age = 28, Income = 80000m, CreditScore = 650, LoanAmount = 60000m }
};
foreach (var app in testApplications)
{
var arguments = new KernelArguments
{
["user_name"] = app.Name,
["user_age"] = app.Age,
["user_income"] = app.Income,
["credit_score"] = app.CreditScore,
["loan_amount"] = app.LoanAmount
};
Console.WriteLine($"\n๐ฆ Processing loan application for {app.Name}:");
Console.WriteLine($" Age: {app.Age}, Income: ${app.Income:N0}, Credit: {app.CreditScore}");
var result = await executor.ExecuteAsync(kernel, arguments);
var loanStatus = result.GetValue<string>("loan_status");
Console.WriteLine($" Status: {loanStatus}");
if (loanStatus == "approved")
{
var interestRate = result.GetValue<decimal>("interest_rate");
Console.WriteLine($" Interest Rate: {interestRate:P1}");
}
else
{
var reasons = result.GetValue<List<string>>("review_reasons");
var nextSteps = result.GetValue<string>("next_steps");
Console.WriteLine($" Review Reasons: {string.Join(", ", reasons)}");
Console.WriteLine($" Next Steps: {nextSteps}");
}
}
3. Dynamic Conditional Routing¶
Shows how to implement dynamic routing based on runtime state changes.
// Create dynamic conditional node
var dynamicConditional = new ConditionalGraphNode(
"DynamicConditional",
"Dynamic conditional routing example",
logger)
{
ConditionExpression = "current_step < max_steps && !is_complete",
TrueNodeId = "continue-processing",
FalseNodeId = "finalize-workflow"
};
// Create processing nodes
var continueProcessing = new FunctionGraphNode(
"continue-processing",
"Continue workflow processing",
async (context) =>
{
var currentStep = context.GetValue<int>("current_step");
var maxSteps = context.GetValue<int>("max_steps");
// Simulate processing
await Task.Delay(100);
// Update state
context.SetValue("current_step", currentStep + 1);
context.SetValue("last_processed_step", currentStep);
// Check if we should continue
if (currentStep + 1 >= maxSteps)
{
context.SetValue("is_complete", true);
}
return $"Processed step {currentStep} of {maxSteps}";
});
var finalizeWorkflow = new FunctionGraphNode(
"finalize-workflow",
"Finalize workflow execution",
async (context) =>
{
var totalSteps = context.GetValue<int>("max_steps");
var finalResult = context.GetValue<string>("workflow_result") ?? "Default result";
context.SetValue("workflow_status", "completed");
context.SetValue("completion_timestamp", DateTime.UtcNow);
return $"Workflow completed after {totalSteps} steps. Final result: {finalResult}";
});
// Add nodes to executor
executor.AddNode(dynamicConditional);
executor.AddNode(continueProcessing);
executor.AddNode(finalizeWorkflow);
// Test dynamic routing
var workflowArgs = new KernelArguments
{
["current_step"] = 0,
["max_steps"] = 5,
["is_complete"] = false,
["workflow_result"] = "Dynamic processing completed"
};
Console.WriteLine("๐ Starting dynamic workflow...");
var workflowResult = await executor.ExecuteAsync(kernel, workflowArgs);
var workflowStatus = workflowResult.GetValue<string>("workflow_status");
var finalStep = workflowResult.GetValue<int>("current_step");
Console.WriteLine($"โ
Workflow {workflowStatus} at step {finalStep}");
4. Multi-Conditional Workflow¶
Demonstrates a workflow with multiple conditional branches and complex routing.
// Create multi-conditional workflow
var multiConditional = new ConditionalGraphNode(
"MultiConditional",
"Multi-conditional workflow example",
logger)
{
ConditionExpression = "request_type == 'urgent' && priority_level >= 8",
TrueNodeId = "urgent-processor",
FalseNodeId = "standard-processor"
};
var urgentProcessor = new ConditionalGraphNode(
"UrgentConditional",
"Urgent request conditional",
logger)
{
ConditionExpression = "available_resources >= 2",
TrueNodeId = "immediate-processing",
FalseNodeId = "resource-wait"
});
var standardProcessor = new ConditionalGraphNode(
"StandardConditional",
"Standard request conditional",
logger)
{
ConditionExpression = "queue_length < 10",
TrueNodeId = "queue-processing",
FalseNodeId = "delayed-processing"
});
// Create processing nodes
var immediateProcessing = new FunctionGraphNode(
"immediate-processing",
"Process urgent request immediately",
async (context) =>
{
var requestId = context.GetValue<string>("request_id");
context.SetValue("processing_time", "immediate");
context.SetValue("priority_handled", "urgent");
return $"Urgent request {requestId} processed immediately";
});
var resourceWait = new FunctionGraphNode(
"resource-wait",
"Wait for available resources",
async (context) =>
{
var requestId = context.GetValue<string>("request_id");
context.SetValue("processing_time", "delayed");
context.SetValue("wait_reason", "insufficient_resources");
return $"Urgent request {requestId} waiting for resources";
});
var queueProcessing = new FunctionGraphNode(
"queue-processing",
"Process standard request from queue",
async (context) =>
{
var requestId = context.GetValue<string>("request_id");
context.SetValue("processing_time", "queued");
context.SetValue("queue_position", context.GetValue<int>("queue_length"));
return $"Standard request {requestId} processed from queue";
});
var delayedProcessing = new FunctionGraphNode(
"delayed-processing",
"Delay standard request processing",
async (context) =>
{
var requestId = context.GetValue<string>("request_id");
context.SetValue("processing_time", "delayed");
context.SetValue("delay_reason", "queue_full");
return $"Standard request {requestId} delayed due to queue capacity";
});
// Add all nodes to executor
executor.AddNode(multiConditional);
executor.AddNode(urgentProcessor);
executor.AddNode(standardProcessor);
executor.AddNode(immediateProcessing);
executor.AddNode(resourceWait);
executor.AddNode(queueProcessing);
executor.AddNode(delayedProcessing);
// Test multi-conditional workflow
var testRequests = new[]
{
new { Id = "REQ-001", Type = "urgent", Priority = 9, Resources = 3, QueueLength = 5 },
new { Id = "REQ-002", Type = "urgent", Priority = 7, Resources = 1, QueueLength = 8 },
new { Id = "REQ-003", Type = "standard", Priority = 5, Resources = 2, QueueLength = 8 },
new { Id = "REQ-004", Type = "standard", Priority = 3, Resources = 2, QueueLength = 15 }
};
foreach (var req in testRequests)
{
var arguments = new KernelArguments
{
["request_id"] = req.Id,
["request_type"] = req.Type,
["priority_level"] = req.Priority,
["available_resources"] = req.Resources,
["queue_length"] = req.QueueLength
};
Console.WriteLine($"\n๐ Processing request {req.Id}:");
Console.WriteLine($" Type: {req.Type}, Priority: {req.Priority}, Resources: {req.Resources}, Queue: {req.QueueLength}");
var result = await executor.ExecuteAsync(kernel, arguments);
var processingTime = result.GetValue<string>("processing_time");
var processingDetails = result.GetValue<string>();
Console.WriteLine($" Processing: {processingTime}");
Console.WriteLine($" Details: {processingDetails}");
}
Expected Output¶
Basic Conditional Routing Example¶
๐งช Testing with input value: 5
Result: 10 (Type: low-value)
๐งช Testing with input value: 15
Result: 30 (Type: high-value)
๐งช Testing with input value: 8
Result: 13 (Type: low-value)
๐งช Testing with input value: 20
Result: 40 (Type: high-value)
Complex Conditional Logic Example¶
๐ฆ Processing loan application for Alice:
Age: 25, Income: $75,000, Credit: 750
Status: approved
Interest Rate: 4.5%
๐ฆ Processing loan application for Bob:
Age: 17, Income: $60,000, Credit: 720
Status: under_review
Review Reasons: Underage
Next Steps: Manual review required
๐ฆ Processing loan application for Carol:
Age: 30, Income: $45,000, Credit: 800
Status: under_review
Review Reasons: Insufficient income
Next Steps: Manual review required
๐ฆ Processing loan application for David:
Age: 28, Income: $80,000, Credit: 650
Status: under_review
Review Reasons: Low credit score
Next Steps: Manual review required
Dynamic Conditional Routing Example¶
Multi-Conditional Workflow Example¶
๐ Processing request REQ-001:
Type: urgent, Priority: 9, Resources: 3, Queue: 5
Processing: immediate
Details: Urgent request REQ-001 processed immediately
๐ Processing request REQ-002:
Type: urgent, Priority: 7, Resources: 1, Queue: 8
Processing: delayed
Details: Urgent request REQ-002 waiting for resources
๐ Processing request REQ-003:
Type: standard, Priority: 5, Resources: 2, Queue: 8
Processing: queued
Details: Standard request REQ-003 processed from queue
๐ Processing request REQ-004:
Type: standard, Priority: 3, Resources: 2, Queue: 15
Processing: delayed
Details: Standard request REQ-004 delayed due to queue capacity
Configuration Options¶
Conditional Node Configuration¶
var conditionalOptions = new ConditionalNodeOptions
{
ConditionExpression = "input_value > threshold", // Boolean expression
TrueNodeId = "success-path", // Node ID for true condition
FalseNodeId = "failure-path", // Node ID for false condition
EnableExpressionCaching = true, // Cache expression evaluation
ExpressionTimeout = TimeSpan.FromSeconds(5), // Expression evaluation timeout
EnableDetailedLogging = true, // Log condition evaluation details
FallbackNodeId = "default-path" // Fallback node if evaluation fails
};
Conditional Edge Configuration¶
var conditionalEdge = new ConditionalEdge
{
SourceNodeId = "source-node",
TargetNodeId = "target-node",
Condition = "state_value == 'expected'",
Priority = 1, // Edge priority for routing
Metadata = new Dictionary<string, object> // Additional metadata
{
["edge_type"] = "conditional",
["description"] = "Route based on state value"
}
};
Troubleshooting¶
Common Issues¶
Condition Evaluation Fails¶
# Problem: Conditional expression fails to evaluate
# Solution: Check expression syntax and variable names
ConditionExpression = "simple_condition == true";
EnableDetailedLogging = true;
Routing Not Working¶
# Problem: Execution doesn't follow expected path
# Solution: Verify node IDs and edge connections
TrueNodeId = "correct-node-id";
FalseNodeId = "correct-node-id";
Performance Issues¶
# Problem: Conditional evaluation is slow
# Solution: Enable expression caching and optimize expressions
EnableExpressionCaching = true;
ExpressionTimeout = TimeSpan.FromSeconds(1);
Debug Mode¶
Enable detailed logging for troubleshooting:
// Enable debug logging
var logger = LoggerFactory.Create(builder =>
{
builder.AddConsole();
builder.SetMinimumLevel(LogLevel.Debug);
}).CreateLogger<ConditionalNodesExample>();
// Configure conditional node with debug logging
var debugConditional = new ConditionalGraphNode(
"DebugConditional",
"Debug conditional example",
logger)
{
EnableDetailedLogging = true,
LogConditionEvaluation = true,
LogRoutingDecisions = true
};
Advanced Patterns¶
Dynamic Condition Generation¶
// Generate conditions dynamically based on context
var dynamicCondition = new DynamicConditionGenerator
{
ConditionTemplate = "{field_name} {operator} {threshold_value}",
FieldMappings = new Dictionary<string, string>
{
["age"] = "user_age",
["income"] = "user_income",
["credit"] = "credit_score"
},
OperatorMappings = new Dictionary<string, string>
{
["greater_than"] = ">",
["less_than"] = "<",
["equals"] = "=="
}
};
var generatedCondition = dynamicCondition.GenerateCondition(
field: "age",
operator: "greater_than",
threshold: "18"
);
// Result: "user_age > 18"
Conditional Expression Builder¶
// Build complex conditional expressions programmatically
var expressionBuilder = new ConditionalExpressionBuilder();
var complexExpression = expressionBuilder
.StartGroup()
.AddCondition("user_age >= 18")
.And()
.AddCondition("user_income > 50000")
.And()
.StartGroup()
.AddCondition("credit_score >= 700")
.Or()
.AddCondition("co_signer_available == true")
.EndGroup()
.EndGroup()
.Build();
// Result: "(user_age >= 18) && (user_income > 50000) && ((credit_score >= 700) || (co_signer_available == true))"
Conditional Workflow Orchestration¶
// Orchestrate multiple conditional workflows
var orchestrator = new ConditionalWorkflowOrchestrator
{
WorkflowDefinitions = new Dictionary<string, ConditionalWorkflowDefinition>
{
["loan_approval"] = new ConditionalWorkflowDefinition
{
EntryCondition = "request_type == 'loan'",
WorkflowGraph = loanApprovalGraph,
Priority = 1
},
["insurance_quote"] = new ConditionalWorkflowDefinition
{
EntryCondition = "request_type == 'insurance'",
WorkflowGraph = insuranceQuoteGraph,
Priority = 2
}
},
DefaultWorkflow = defaultProcessingGraph
};
var selectedWorkflow = orchestrator.SelectWorkflow(workflowContext);
Related Examples¶
- Chain of Thought: Reasoning and decision making
- Dynamic Routing: Advanced routing patterns
- Multi-Agent: Coordinated decision making
- State Management: Graph state and argument handling
See Also¶
- Conditional Nodes Concepts: Understanding conditional routing
- Graph Concepts: Graph-based workflow fundamentals
- Node Types: Graph node fundamentals
- API Reference: Complete API documentation