Loop Nodes Example¶
This example demonstrates how to implement various types of loop patterns in Semantic Kernel Graph workflows. It shows how to create controlled loops, iterative processing, and loop-based decision making using different loop node types.
Objective¶
Learn how to implement loop patterns in graph-based workflows to: * Create controlled loops with exit conditions * Implement iterative processing with state management * Handle loop-based decision making and routing * Manage loop performance and resource consumption * Implement loop monitoring and debugging
Prerequisites¶
- .NET 8.0 or later
- OpenAI API Key configured in
appsettings.json
- Semantic Kernel Graph package installed
- Basic understanding of Graph Concepts and Loop Patterns
Key Components¶
Concepts and Techniques¶
- Loop Control: Managing loop execution and exit conditions
- Iterative Processing: Processing data in repeated cycles
- State Management: Maintaining state across loop iterations
- Loop Monitoring: Tracking loop performance and progress
- Resource Management: Controlling resource consumption in loops
Core Classes¶
LoopGraphNode
: Base loop node implementationReActLoopGraphNode
: Reasoning and action loop patternIterativeGraphNode
: Simple iterative processingLoopControlManager
: Loop execution controlLoopPerformanceMetrics
: Loop performance monitoring
Running the Example¶
Getting Started¶
This example demonstrates loop control and iteration patterns 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 Loop Implementation¶
This example demonstrates basic loop creation and control.
// Minimal while-loop example using the documented loop node API.
// This snippet is compatible with the examples project and can be executed as a self-contained demo.
// Create a lightweight kernel for local execution
var kernel = Kernel.CreateBuilder().Build();
// Create a graph state and initialize counters
var state = new SemanticKernel.Graph.State.GraphState();
state.SetValue("counter", 0);
state.SetValue("max_count", 5);
// Create a WhileLoopGraphNode with a simple condition that reads from the GraphState
var whileLoop = new SemanticKernel.Graph.Nodes.WhileLoopGraphNode(
condition: s => s.GetValue<int>("counter") < s.GetValue<int>("max_count"),
maxIterations: 100,
nodeId: "basic_while_loop",
name: "basic_while_loop",
description: "Increments counter until max_count"
);
// Create a KernelFunction that increments the captured GraphState counter
var incrementFn = KernelFunctionFactory.CreateFromMethod((KernelArguments args) =>
{
var current = state.GetValue<int>("counter");
state.SetValue("counter", current + 1);
return $"counter={current + 1}";
}, "doc_increment", "Increment counter");
var incrementNode = new SemanticKernel.Graph.Nodes.FunctionGraphNode(incrementFn, "increment_node");
// Add the increment node to the loop and execute
whileLoop.AddLoopNode(incrementNode);
Console.WriteLine("🔄 Testing basic while-loop implementation...");
var iterations = await whileLoop.ExecuteAsync(kernel, state.KernelArguments);
Console.WriteLine($" Total Iterations: {iterations}");
Console.WriteLine($" Final counter: {state.GetValue<int>("counter")}");
2. ReAct Loop Pattern¶
Demonstrates the Reasoning and Action loop pattern for iterative problem solving.
// Create ReAct loop workflow
var reActLoopWorkflow = new GraphExecutor("ReActLoopWorkflow", "ReAct loop pattern implementation", logger);
// Configure ReAct loop options
var reActLoopOptions = new ReActLoopOptions
{
MaxIterations = 8,
EnableReasoningValidation = true,
EnableActionValidation = true,
EnableGoalTracking = true,
EnableProgressMonitoring = true,
ReasoningTimeout = TimeSpan.FromSeconds(30),
ActionTimeout = TimeSpan.FromSeconds(60)
};
reActLoopWorkflow.ConfigureReActLoop(reActLoopOptions);
// ReAct reasoning node (KernelFunction wrapper). Replace body with real reasoning logic as needed.
var reActReasoning = new FunctionGraphNode(
KernelFunctionFactory.CreateFromMethod((KernelArguments args) =>
{
// Example: read inputs from args and return a reasoning summary string.
// Replace with actual reasoning implementation that updates graph state when running inside executor.
return "Reasoning completed";
}, "react_reasoning_fn", "Perform reasoning step"),
"react-reasoning",
"Perform reasoning step in ReAct loop");
// ReAct action node (KernelFunction wrapper). Replace body with action execution logic.
var reActAction = new FunctionGraphNode(
KernelFunctionFactory.CreateFromMethod((KernelArguments args) =>
{
// Execute action based on reasoning outputs (placeholder)
return "Action executed";
}, "react_action_fn", "Execute action"),
"react-action",
"Execute action based on reasoning");
// ReAct controller node (KernelFunction wrapper). Implement loop control logic here.
var reActController = new FunctionGraphNode(
KernelFunctionFactory.CreateFromMethod((KernelArguments args) =>
{
// Determine continuation / goal achieved based on action outputs (placeholder)
return "Controller evaluated";
}, "react_controller_fn", "Control ReAct loop"),
"react-controller",
"Control ReAct loop execution and determine continuation");
// Add nodes to ReAct workflow
reActLoopWorkflow.AddNode(reActReasoning);
reActLoopWorkflow.AddNode(reActAction);
reActLoopWorkflow.AddNode(reActController);
// Set start node
reActLoopWorkflow.SetStartNode(reActReasoning.NodeId);
// Test ReAct loop
Console.WriteLine("🧠Testing ReAct loop pattern...");
var reActArguments = new KernelArguments
{
["iteration"] = 0,
["max_iterations"] = 6,
["problem"] = "Solve a complex mathematical problem step by step",
["current_state"] = "initial",
["previous_actions"] = new List<string>()
};
var reActResult = await reActLoopWorkflow.ExecuteAsync(kernel, reActArguments);
var reActSummary = reActResult.GetValue<Dictionary<string, object>>("react_summary");
var iteration = reActResult.GetValue<int>("iteration");
var goalAchieved = reActResult.GetValue<bool>("goal_achieved");
Console.WriteLine($" Iteration: {iteration}");
Console.WriteLine($" Goal Achieved: {goalAchieved}");
Console.WriteLine($" Summary: {string.Join(", ", reActSummary.Select(kvp => $"{kvp.Key}={kvp.Value}"))}");
3. Iterative Processing Loop¶
Shows how to implement iterative processing with data transformation.
// Create iterative processing workflow
var iterativeWorkflow = new GraphExecutor("IterativeWorkflow", "Iterative data processing", logger);
// Configure iterative processing options
var iterativeOptions = new IterativeProcessingOptions
{
MaxIterations = 15,
EnableBatchProcessing = true,
EnableProgressTracking = true,
EnableQualityMetrics = true,
BatchSize = 5,
QualityThreshold = 0.8
};
iterativeWorkflow.ConfigureIterativeProcessing(iterativeOptions);
// Data generator node
var dataGenerator = new FunctionGraphNode(
"data-generator",
"Generate data for iterative processing",
async (context) =>
{
var iteration = context.GetValue<int>("iteration", 0);
var batchSize = context.GetValue<int>("batch_size", 5);
// Generate sample data
var data = new List<string>();
for (int i = 0; i < batchSize; i++)
{
data.Add($"Data_{iteration}_{i}_{DateTime.UtcNow:HHmmss}");
}
context.SetValue("generated_data", data);
context.SetValue("data_count", data.Count);
context.SetValue("generation_timestamp", DateTime.UtcNow);
return $"Generated {data.Count} data items for iteration {iteration}";
});
// Data processor node
var dataProcessor = new FunctionGraphNode(
"data-processor",
"Process data in current iteration",
async (context) =>
{
var iteration = context.GetValue<int>("iteration");
var generatedData = context.GetValue<List<string>>("generated_data");
var batchSize = context.GetValue<int>("batch_size");
// Simulate data processing
await Task.Delay(Random.Shared.Next(200, 600));
var processedData = new List<string>();
var processingQuality = new List<double>();
foreach (var data in generatedData)
{
var processed = $"Processed_{data}";
processedData.Add(processed);
// Simulate quality score
var quality = Random.Shared.NextDouble();
processingQuality.Add(quality);
}
// Calculate quality metrics
var averageQuality = processingQuality.Average();
var qualityThreshold = context.GetValue<double>("quality_threshold", 0.8);
var qualityMet = averageQuality >= qualityThreshold;
// Update processing state
context.SetValue("processed_data", processedData);
context.SetValue("processing_quality", processingQuality);
context.SetValue("average_quality", averageQuality);
context.SetValue("quality_threshold_met", qualityMet);
context.SetValue("processing_complete", true);
return $"Processed {processedData.Count} items with quality {averageQuality:F2}";
});
// Iteration controller
var iterationController = new FunctionGraphNode(
"iteration-controller",
"Control iteration flow and determine continuation",
async (context) =>
{
var iteration = context.GetValue<int>("iteration");
var maxIterations = context.GetValue<int>("max_iterations", 15);
var qualityThresholdMet = context.GetValue<bool>("quality_threshold_met");
var averageQuality = context.GetValue<double>("average_quality");
// Determine if iteration should continue
var shouldContinue = iteration < maxIterations && qualityThresholdMet;
var iterationComplete = !shouldContinue;
// Update iteration state
context.SetValue("should_continue", shouldContinue);
context.SetValue("iteration_complete", iterationComplete);
if (shouldContinue)
{
context.SetValue("next_iteration", iteration + 1);
}
// Update iteration summary
var iterationSummary = new Dictionary<string, object>
{
["current_iteration"] = iteration,
["max_iterations"] = maxIterations,
["quality_threshold_met"] = qualityThresholdMet,
["average_quality"] = averageQuality,
["should_continue"] = shouldContinue,
["iteration_complete"] = iterationComplete
};
context.SetValue("iteration_summary", iterationSummary);
return $"Iteration {iteration} control: Continue={shouldContinue}";
});
// Add nodes to iterative workflow
iterativeWorkflow.AddNode(dataGenerator);
iterativeWorkflow.AddNode(dataProcessor);
iterativeWorkflow.AddNode(iterationController);
// Set start node
iterativeWorkflow.SetStartNode(dataGenerator.NodeId);
// Test iterative processing
Console.WriteLine("📊 Testing iterative processing...");
var iterativeArguments = new KernelArguments
{
["iteration"] = 0,
["max_iterations"] = 8,
["batch_size"] = 3,
["quality_threshold"] = 0.75
};
var iterativeResult = await iterativeWorkflow.ExecuteAsync(kernel, iterativeArguments);
var iterationSummary = iterativeResult.GetValue<Dictionary<string, object>>("iteration_summary");
var currentIteration = iterativeResult.GetValue<int>("current_iteration");
var qualityThresholdMet = iterativeResult.GetValue<bool>("quality_threshold_met");
Console.WriteLine($" Current Iteration: {currentIteration}");
Console.WriteLine($" Quality Threshold Met: {qualityThresholdMet}");
Console.WriteLine($" Summary: {string.Join(", ", iterationSummary.Select(kvp => $"{kvp.Key}={kvp.Value}"))}");
4. Advanced Loop Patterns¶
Demonstrates advanced loop patterns including nested loops and conditional loops.
// Create advanced loop workflow
var advancedLoopWorkflow = new GraphExecutor("AdvancedLoopWorkflow", "Advanced loop patterns", logger);
// Configure advanced loop options
var advancedLoopOptions = new AdvancedLoopOptions
{
EnableNestedLoops = true,
EnableConditionalLoops = true,
EnableLoopOptimization = true,
EnableResourceMonitoring = true,
MaxNestingDepth = 3,
ResourceThreshold = 0.8
};
advancedLoopWorkflow.ConfigureAdvancedLoop(advancedLoopOptions);
// Nested loop controller
var nestedLoopController = new FunctionGraphNode(
"nested-loop-controller",
"Control nested loop execution",
async (context) =>
{
var outerIteration = context.GetValue<int>("outer_iteration", 0);
var innerIteration = context.GetValue<int>("inner_iteration", 0);
var maxOuterIterations = context.GetValue<int>("max_outer_iterations", 3);
var maxInnerIterations = context.GetValue<int>("max_inner_iterations", 4);
// Determine loop flow
var outerComplete = outerIteration >= maxOuterIterations;
var innerComplete = innerIteration >= maxInnerIterations;
if (!outerComplete)
{
if (!innerComplete)
{
// Continue inner loop
context.SetValue("next_inner_iteration", innerIteration + 1);
context.SetValue("loop_level", "inner");
}
else
{
// Move to next outer iteration
context.SetValue("next_outer_iteration", outerIteration + 1);
context.SetValue("next_inner_iteration", 0);
context.SetValue("loop_level", "outer");
}
}
// Update loop state
context.SetValue("outer_complete", outerComplete);
context.SetValue("inner_complete", innerComplete);
context.SetValue("nested_loop_complete", outerComplete);
var loopState = new Dictionary<string, object>
{
["outer_iteration"] = outerIteration,
["inner_iteration"] = innerIteration,
["loop_level"] = context.GetValue<string>("loop_level", "unknown"),
["outer_complete"] = outerComplete,
["inner_complete"] = innerComplete,
["nested_loop_complete"] = nestedLoopComplete
};
context.SetValue("nested_loop_state", loopState);
return $"Nested loop: Outer={outerIteration}, Inner={innerIteration}, Level={loopState["loop_level"]}";
});
// Conditional loop processor
var conditionalLoopProcessor = new FunctionGraphNode(
"conditional-loop-processor",
"Process data with conditional loop logic",
async (context) =>
{
var iteration = context.GetValue<int>("iteration", 0);
var condition = context.GetValue<string>("condition", "default");
var data = context.GetValue<string>("data", "sample");
// Simulate conditional processing
await Task.Delay(Random.Shared.Next(150, 400));
var processingResult = "";
var shouldContinue = false;
switch (condition)
{
case "quality_check":
var quality = Random.Shared.NextDouble();
processingResult = $"Quality check result: {quality:F2}";
shouldContinue = quality < 0.9; // Continue if quality < 90%
break;
case "convergence_check":
var convergence = Random.Shared.NextDouble();
processingResult = $"Convergence result: {convergence:F2}";
shouldContinue = convergence < 0.95; // Continue if convergence < 95%
break;
case "error_check":
var error = Random.Shared.NextDouble();
processingResult = $"Error check result: {error:F2}";
shouldContinue = error > 0.1; // Continue if error > 10%
break;
default:
processingResult = $"Default processing: {data}";
shouldContinue = iteration < 5; // Default limit
break;
}
// Update conditional state
context.SetValue("processing_result", processingResult);
context.SetValue("should_continue", shouldContinue);
context.SetValue("condition_met", !shouldContinue);
context.SetValue("conditional_processing_complete", true);
return processingResult;
});
// Add nodes to advanced workflow
advancedLoopWorkflow.AddNode(nestedLoopController);
advancedLoopWorkflow.AddNode(conditionalLoopProcessor);
// Set start node
advancedLoopWorkflow.SetStartNode(nestedLoopController.NodeId);
// Test advanced loop patterns
Console.WriteLine("🚀 Testing advanced loop patterns...");
var advancedArguments = new KernelArguments
{
["outer_iteration"] = 0,
["inner_iteration"] = 0,
["max_outer_iterations"] = 3,
["max_inner_iterations"] = 4,
["condition"] = "quality_check",
["data"] = "Advanced loop data"
};
var advancedResult = await advancedLoopWorkflow.ExecuteAsync(kernel, advancedArguments);
var nestedLoopState = advancedResult.GetValue<Dictionary<string, object>>("nested_loop_state");
var conditionalProcessingComplete = advancedResult.GetValue<bool>("conditional_processing_complete");
Console.WriteLine($" Nested Loop State: {string.Join(", ", nestedLoopState.Select(kvp => $"{kvp.Key}={kvp.Value}"))}");
Console.WriteLine($" Conditional Processing Complete: {conditionalProcessingComplete}");
Expected Output¶
Basic Loop Implementation Example¶
🔄 Testing basic loop implementation...
Total Iterations: 5
Loop Complete: True
Summary Keys: total_iterations, last_processed_data, last_processing_result, loop_complete, completion_timestamp
ReAct Loop Pattern Example¶
🧠Testing ReAct loop pattern...
Iteration: 6
Goal Achieved: False
Summary: iteration=6, action_success=True, new_state=State_6, should_continue=False, goal_achieved=False, loop_complete=True
Iterative Processing Example¶
📊 Testing iterative processing...
Current Iteration: 8
Quality Threshold Met: True
Summary: current_iteration=8, max_iterations=8, quality_threshold_met=True, average_quality=0.82, should_continue=False, iteration_complete=True
Advanced Loop Patterns Example¶
🚀 Testing advanced loop patterns...
Nested Loop State: outer_iteration=0, inner_iteration=0, loop_level=inner, outer_complete=False, inner_complete=False, nested_loop_complete=False
Conditional Processing Complete: True
Configuration Options¶
Loop Configuration¶
var loopOptions = new LoopOptions
{
MaxIterations = 10, // Maximum number of iterations
EnableLoopMonitoring = true, // Enable loop monitoring
EnablePerformanceMetrics = true, // Enable performance metrics
EnableStatePersistence = true, // Enable state persistence
LoopTimeout = TimeSpan.FromMinutes(5), // Loop execution timeout
EnableResourceMonitoring = true, // Monitor resource usage
ResourceThreshold = 0.8, // Resource usage threshold
EnableLoopOptimization = true, // Enable loop optimization
EnableNestedLoops = true, // Allow nested loops
MaxNestingDepth = 3 // Maximum nesting depth
};
ReAct Loop Configuration¶
var reActLoopOptions = new ReActLoopOptions
{
MaxIterations = 8, // Maximum reasoning-action cycles
EnableReasoningValidation = true, // Validate reasoning steps
EnableActionValidation = true, // Validate action results
EnableGoalTracking = true, // Track goal achievement
EnableProgressMonitoring = true, // Monitor progress
ReasoningTimeout = TimeSpan.FromSeconds(30), // Reasoning step timeout
ActionTimeout = TimeSpan.FromSeconds(60), // Action execution timeout
EnableConfidenceScoring = true, // Score reasoning confidence
EnableActionSuccessTracking = true, // Track action success rates
GoalAchievementThreshold = 0.9 // Goal achievement threshold
};
Iterative Processing Configuration¶
var iterativeOptions = new IterativeProcessingOptions
{
MaxIterations = 15, // Maximum iterations
EnableBatchProcessing = true, // Enable batch processing
EnableProgressTracking = true, // Track progress
EnableQualityMetrics = true, // Track quality metrics
BatchSize = 5, // Items per batch
QualityThreshold = 0.8, // Quality threshold
EnableConvergenceChecking = true, // Check for convergence
ConvergenceThreshold = 0.001, // Convergence threshold
EnableErrorTracking = true, // Track errors
ErrorThreshold = 0.1 // Error threshold
};
Troubleshooting¶
Common Issues¶
Infinite Loops¶
# Problem: Loop runs indefinitely
# Solution: Set proper exit conditions and max iterations
MaxIterations = 10;
EnableLoopMonitoring = true;
LoopTimeout = TimeSpan.FromMinutes(5);
Performance Issues¶
# Problem: Loop performance degrades over iterations
# Solution: Enable optimization and resource monitoring
EnableLoopOptimization = true;
EnableResourceMonitoring = true;
ResourceThreshold = 0.8;
State Corruption¶
# Problem: Loop state becomes corrupted
# Solution: Enable state persistence and validation
EnableStatePersistence = true;
EnableStateValidation = true;
EnableStateRecovery = true;
Debug Mode¶
Enable detailed loop monitoring for troubleshooting:
// Enable debug loop monitoring
var debugLoopOptions = new LoopOptions
{
MaxIterations = 10,
EnableLoopMonitoring = true,
EnablePerformanceMetrics = true,
EnableDebugLogging = true,
EnableStateInspection = true,
EnableLoopVisualization = true,
LogLoopIterations = true,
LogLoopState = true
};
Advanced Patterns¶
Custom Loop Controllers¶
// Implement custom loop controller
public class CustomLoopController : ILoopController
{
public async Task<LoopControlDecision> ShouldContinueAsync(LoopContext context)
{
var iteration = context.GetValue<int>("iteration");
var customCondition = context.GetValue<string>("custom_condition");
// Custom loop logic
switch (customCondition)
{
case "adaptive":
return await HandleAdaptiveLoop(context);
case "quality_based":
return await HandleQualityBasedLoop(context);
case "resource_based":
return await HandleResourceBasedLoop(context);
default:
return new LoopControlDecision { ShouldContinue = iteration < 10 };
}
}
private async Task<LoopControlDecision> HandleAdaptiveLoop(LoopContext context)
{
// Implement adaptive loop logic
var performance = context.GetValue<double>("performance", 0.0);
var shouldContinue = performance < 0.9;
return new LoopControlDecision
{
ShouldContinue = shouldContinue,
Reason = $"Performance {performance:F2} below threshold 0.9"
};
}
}
Loop Performance Optimization¶
// Implement loop performance optimizer
public class LoopPerformanceOptimizer : ILoopOptimizer
{
public async Task<LoopOptimizationResult> OptimizeLoopAsync(LoopContext context)
{
var optimization = new LoopOptimizationResult();
// Analyze loop performance
var iterations = context.GetValue<int>("iteration");
var averageTime = context.GetValue<double>("average_iteration_time");
var resourceUsage = context.GetValue<double>("resource_usage");
// Suggest optimizations
if (averageTime > 1000) // More than 1 second
{
optimization.Suggestions.Add("Consider reducing processing complexity");
optimization.Suggestions.Add("Enable parallel processing if possible");
}
if (resourceUsage > 0.8) // More than 80%
{
optimization.Suggestions.Add("Reduce batch size");
optimization.Suggestions.Add("Implement resource throttling");
}
if (iterations > 20)
{
optimization.Suggestions.Add("Consider early termination conditions");
optimization.Suggestions.Add("Implement convergence checking");
}
return optimization;
}
}
Loop State Management¶
// Implement advanced loop state management
public class AdvancedLoopStateManager : ILoopStateManager
{
public async Task<LoopState> GetLoopStateAsync(string loopId)
{
// Retrieve loop state from persistent storage
var state = await LoadStateFromStorage(loopId);
// Validate state integrity
if (!await ValidateStateIntegrity(state))
{
state = await RecoverState(loopId);
}
return state;
}
public async Task SaveLoopStateAsync(string loopId, LoopState state)
{
// Add metadata
state.Metadata["last_updated"] = DateTime.UtcNow;
state.Metadata["version"] = state.Version + 1;
// Compress state if large
if (state.Size > 1024 * 1024) // 1MB
{
state = await CompressState(state);
}
// Save to persistent storage
await SaveStateToStorage(loopId, state);
}
}
Related Examples¶
- ReAct Agent: Advanced reasoning and action patterns
- Graph Metrics: Loop performance monitoring
- State Management: Loop state persistence
- Performance Optimization: Loop optimization techniques
See Also¶
- Loop Patterns: Understanding loop concepts
- Performance Monitoring: Loop performance analysis
- State Management: Loop state handling
- API Reference: Complete API documentation